├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── config.sample.json ├── examples └── api-proxy │ ├── README.md │ ├── app.js │ ├── config │ └── usergrid.example.json │ └── package.json ├── helpers ├── args.js ├── build.js ├── cb.js ├── client.js ├── config.js ├── index.js ├── mutability.js ├── query.js ├── time.js └── user.js ├── lib ├── appAuth.js ├── asset.js ├── auth.js ├── client.js ├── entity.js ├── query.js ├── request.js ├── response.js ├── responseError.js ├── user.js ├── userAuth.js └── usergrid.js ├── package.json ├── tests ├── config.test.json ├── lib │ ├── asset.test.js │ ├── client.auth.test.js │ ├── client.connections.test.js │ ├── client.init.test.js │ ├── client.rest.test.js │ ├── entity.test.js │ ├── image.jpg │ ├── image_test.jpg │ ├── query.test.js │ ├── response.test.js │ ├── responseError.test.js │ ├── user.test.js │ ├── usergrid.init.test.js │ ├── usergrid.singleton.test.js │ └── usergrid.teardown.test.js └── main.test.js └── usergrid.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '5.1.0' 4 | install: 5 | - 'npm install' 6 | - 'npm -g install mocha' 7 | script: 8 | - 'mocha tests --bail --target=2.1' 9 | notifications: 10 | email: 11 | on_failure: change 12 | on_success: change -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); 2 | you may not use this file except in compliance with the License. 3 | You may obtain a copy of the License at 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Codacy Badge](https://api.codacy.com/project/badge/grade/034bc34302b646bf932c7c0307e0e313)](https://www.codacy.com/app/brandonscript/usergrid-nodejs) 2 | [![Travis CI Badge](https://travis-ci.org/brandonscript/usergrid-nodejs.svg?branch=master)](https://travis-ci.org/brandonscript/usergrid-nodejs) 3 | [![npm version](https://badge.fury.io/js/usergrid.svg)](https://badge.fury.io/js/usergrid) 4 | 5 | # usergrid-nodejs 6 | Node.js SDK 2.0 for Usergrid 7 | 8 | Version 2.0 of this SDK is currently a work in progress; documentation and implementation are subject to change. 9 | 10 | _**Note:** This Node.js SDK 2.0 for Usergrid is **not** backwards compatible with 0.1X versions of the SDK. If your application is dependent on the 0.1X set of Node.js APIs, you will need to continue using the 0.1X version (see below for installation instructions)._ 11 | 12 | ## Current Release 13 | 14 | - Pre-release: [2.0 Release Candidate 2](https://github.com/brandonscript/usergrid-nodejs/releases), available here or on npm 15 | - Stable: [0.10.11](https://github.com/apache/usergrid/tree/master/sdks/nodejs) 16 | 17 | ## 2.X Bugs 18 | 19 | Please open an [issue](https://github.com/brandonscript/usergrid-nodejs/issues/new) 20 | 21 | ## Known Issues 22 | 23 | - Native support for push notifications is slated for RC3. Workaround is to send a regular POST request to `'devices//notifications'` 24 | - There is no clean way to require submodules (e.g. `UsergridClient` or `UsergridEntity`) modules without referencing the full path to `../lib/`. 25 | - Any other functionality that is missing or expected, please open an issue. 26 | 27 | ## Installation 28 | 29 | To install the latest **stable** 0.1X build: 30 | 31 | npm install usergrid 32 | 33 | (Or add `"usergrid": "~0.10.11"` to your package.json) 34 | 35 | To install the 2.0 release candidates, install from [npm](https://www.npmjs.com/package/usergrid), specifying the version `~2.0.0-rc`: 36 | 37 | npm install usergrid@~2.0.0-rc 38 | 39 | (Or add `"usergrid": "~2.0.0-rc"` to your package.json) 40 | 41 | If you want access to the latest development build (you will need to run `npm install` to keep it up to date): 42 | 43 | npm install brandonscript/usergrid-nodejs 44 | 45 | ## Usage 46 | 47 | _**Note:** This section is a work in progress. In its current release candidate state, this SDK is only recommended for developers familiar with Usergrid, Node.js, and ideally Mocha tests. It is not recommended for production applications. For additional advanced/comprehensive usage, see `/tests`._ 48 | 49 | The Usergrid Node.js SDK is built on top of [request](https://github.com/request/request). As such, it behaves almost as a drop-in replacement. Where you would expect a standard error-first callback from request, the same is true of the Usergrid SDK methods. Where you would expect a response object as the second parameter in the callback, the same is true for the Usergrid SDK. 50 | 51 | 52 | ### Initialization 53 | 54 | There are two different ways of initializing the Usergrid Node.js SDK: 55 | 56 | 1. The singleton pattern is both convenient and enables the developer to use a globally available and always-initialized shared instance of Usergrid. 57 | 58 | ```js 59 | var Usergrid = require('usergrid') 60 | Usergrid.init({ 61 | orgId: '', 62 | appId: '' 63 | }) 64 | 65 | // or you can load from a config file; see config.sample.json 66 | 67 | var Usergrid = require('usergrid') 68 | Usergrid.init() // defaults to use config.json 69 | ``` 70 | **Config File:** Optionally, you can use a config file to provide the usergrid credentials for your app. The usergrid module crawls your App file structure to find files named `usergrid.json` or a `config.json`. If there are multiple files with one of these names present at different locations under the app, only one of them will be used and the others are ignored. This may cause use of an unintended backend. Please make sure you have only one of these files present in the root and subdirectories of your app. 71 | 72 | 2. The instance pattern enables the developer to manage instances of the Usergrid client independently and in an isolated fashion. The primary use-case for this is when an application connects to multiple Usergrid targets. 73 | 74 | ```js 75 | var UsergridClient = require('./node_modules/usergrid/lib/client') 76 | var client = new UsergridClient(config) 77 | ``` 78 | 79 | _**Note:** Examples in this readme assume you are using the `Usergrid` shared instance. If you've implemented the instance pattern instead, simply replace `Usergrid` with your client instance variable. See `/tests` for additional examples._ 80 | 81 | ## RESTful operations 82 | 83 | When making any RESTful call, a `type` parameter (or `path`) is always required. Whether you specify this as an argument, in an object as a parameter, or as part of a `UsergridQuery` object is up to you. 84 | 85 | ### GET() 86 | 87 | To get entities in a collection: 88 | 89 | ```js 90 | Usergrid.GET('collection', function(error, usergridResponse, entities) { 91 | // entities is an array of UsergridEntity objects 92 | }) 93 | ``` 94 | 95 | To get a specific entity in a collection by uuid or name: 96 | 97 | ```js 98 | Usergrid.GET('collection', '', function(error, usergridResponse, entity) { 99 | // entity, if found, is a UsergridEntity object 100 | }) 101 | ``` 102 | 103 | To get specific entities in a collection by passing a UsergridQuery object: 104 | 105 | ```js 106 | var query = new UsergridQuery('cats') 107 | .gt('weight', 2.4) 108 | .contains('color', 'bl*') 109 | .not 110 | .eq('color', 'blue') 111 | .or 112 | .eq('color', 'orange') 113 | 114 | // this will build out the following query: 115 | // select * where weight > 2.4 and color contains 'bl*' and not color = 'blue' or color = 'orange' 116 | 117 | Usergrid.GET(query, function(error, usergridResponse) { 118 | // entities is an array of UsergridEntity objects matching the specified query 119 | }) 120 | ``` 121 | 122 | ### POST() and PUT() 123 | 124 | POST and PUT requests both require a JSON body payload. You can pass either a standard JavaScript object or a `UsergridEntity` instance. While the former works in principle, best practise is to use a `UsergridEntity` wherever practical. When an entity has a uuid or name property and already exists on the server, use a PUT request to update it. If it does not, use POST to create it. 125 | 126 | To create a new entity in a collection (POST): 127 | 128 | ```js 129 | var entity = new UsergridEntity({ 130 | type: 'restaurant', 131 | restaurant: 'Dino's Deep Dish, 132 | cuisine: 'pizza' 133 | }) 134 | 135 | // or 136 | 137 | var entity = { 138 | type: 'restaurant', 139 | restaurant: 'Dino's Deep Dish, 140 | cuisine: 'pizza' 141 | } 142 | 143 | Usergrid.POST(entity, function(error, usergridResponse, entity) { 144 | // entity should now have a uuid property and be created 145 | }) 146 | 147 | // you can also POST an array of entities: 148 | 149 | var entities = [ 150 | new UsergridEntity({ 151 | type: 'restaurant', 152 | restaurant: 'Dino's Deep Dish, 153 | cuisine: 'pizza' 154 | }), 155 | new UsergridEntity({ 156 | type: 'restaurant', 157 | restaurant: 'Pizza da Napoli', 158 | cuisine: 'pizza' 159 | }) 160 | ] 161 | 162 | Usergrid.POST(entities, function(error, usergridResponse, entities) { 163 | // 164 | }) 165 | ``` 166 | 167 | To update an entity in a collection (PUT request): 168 | 169 | ```js 170 | var entity = new UsergridEntity({ 171 | type: 'restaurant', 172 | restaurant: 'Pizza da Napoli', 173 | cuisine: 'pizza' 174 | }) 175 | 176 | Usergrid.POST(entity, function(error, usergridResponse, entity) { 177 | entity.owner = 'Mia Carrara' 178 | Usergrid.PUT(entity, function(error, usergridResponse, entity) { 179 | // entity now has the property 'owner' 180 | }) 181 | }) 182 | 183 | // or update a set of entities by passing a UsergridQuery object 184 | 185 | var query = new UsergridQuery('restaurants') 186 | .eq('cuisine', 'italian') 187 | 188 | // this will build out the following query: 189 | // select * where cuisine = 'italian' 190 | 191 | Usergrid.PUT(query, { keywords: ['pasta'] }, function(error, usergridResponse) { 192 | /* the first 10 entities matching this query criteria will be updated: 193 | e.g.: 194 | [ 195 | { 196 | "type": "restaurant", 197 | "restaurant": "Il Tarazzo", 198 | "cuisine": "italian", 199 | "keywords": [ 200 | "pasta" 201 | ] 202 | }, 203 | { 204 | "type": "restaurant", 205 | "restaurant": "Cono Sur Pizza & Pasta", 206 | "cuisine": "italian", 207 | "keywords": [ 208 | "pasta" 209 | ] 210 | } 211 | ] 212 | */ 213 | }) 214 | ``` 215 | 216 | ### DELETE() 217 | 218 | DELETE requests require either a specific entity or a `UsergridQuery` object to be passed as an argument. 219 | 220 | To delete a specific entity in a collection by uuid or name: 221 | 222 | ```js 223 | Usergrid.DELETE('collection', '', function(error, usergridResponse) { 224 | // if successful, entity will now be deleted 225 | }) 226 | ``` 227 | 228 | To specific entities in a collection by passing a `UsergridQuery` object: 229 | 230 | ```js 231 | var query = new UsergridQuery('cats') 232 | .eq('color', 'black') 233 | .or 234 | .eq('color', 'white') 235 | 236 | // this will build out the following query: 237 | // select * where color = 'black' or color = 'white' 238 | 239 | Usergrid.DELETE(query, function(error, usergridResponse) { 240 | // the first 10 entities matching this query criteria will be deleted 241 | }) 242 | ``` 243 | 244 | ## Entity operations and convenience methods 245 | 246 | `UsergridEntity` has a number of helper/convenience methods to make working with entities more convenient. If you are _not_ utilizing the `Usergrid` shared instance, you must pass an instance of `UsergridClient` as the first argument to any of these helper methods. 247 | 248 | ### reload() 249 | 250 | Reloads the entity from the server 251 | 252 | ```js 253 | entity.reload(function(error, usergridResponse) { 254 | // entity is now reloaded from the server 255 | }) 256 | ``` 257 | 258 | ### save() 259 | 260 | Saves (or creates) the entity on the server 261 | 262 | ```js 263 | entity.aNewProperty = 'A new value' 264 | entity.save(function(error, usergridResponse) { 265 | // entity is now updated on the server 266 | }) 267 | ``` 268 | 269 | ### remove() 270 | 271 | Deletes the entity from the server 272 | 273 | ```js 274 | entity.remove(function(error, usergridResponse) { 275 | // entity is now deleted on the server and the local instance should be destroyed 276 | }) 277 | ``` 278 | 279 | ## Authentication, current user, and authMode 280 | 281 | ### appAuth and authenticateApp() 282 | 283 | `Usergrid` can use the app client ID and secret that were passed upon initialization and automatically retrieve an app-level token for these credentials. 284 | 285 | ```js 286 | Usergrid.setAppAuth('', '') 287 | Usergrid.authenticateApp(function(error, usergridResponse, token) { 288 | // Usergrid.appAuth is created automatically when this call is successful 289 | }) 290 | ``` 291 | 292 | ### currentUser and authenticateUser() 293 | 294 | `Usergrid` has a special `currentUser` property. By default, when calling `authenticateUser()`, `.currentUser` will be set to this user if the authentication flow is successful. 295 | 296 | ```js 297 | Usergrid.authenticateUser({ 298 | username: '', 299 | password: '' 300 | }, function(error, usergridResponse, token) { 301 | // Usergrid.currentUser is set to the authenticated user and the token is stored within that context 302 | }) 303 | ``` 304 | 305 | If you want to utilize authenticateUser without setting as the current user, simply pass a `false` boolean value as the second parameter: 306 | 307 | ```js 308 | Usergrid.authenticateUser({ 309 | username: '', 310 | password: '' 311 | }, false, function(error, usergridResponse, token) { 312 | 313 | }) 314 | ``` 315 | 316 | ### authMode 317 | 318 | Auth-mode is used to determine what the `UsergridClient` will use for authorization. 319 | 320 | By default, `Usergrid.authMode` is set to `UsergridAuth.AUTH_MODE_USER`, whereby if a non-expired `UsergridUserAuth` exists in `UsergridClient.currentUser`, this token is used to authenticate all API calls. 321 | 322 | If instead `Usergrid.authMode` is set to `UsergridAuth.AUTH_MODE_NONE`, all API calls will be performed unauthenticated. 323 | 324 | If instead `Usergrid.authMode` is set to `UsergridAuth.AUTH_MODE_APP`, all API calls will be performed using the client credentials token, _if_ they're available (i.e. `authenticateApp()` was performed at some point). 325 | 326 | ### usingAuth() 327 | 328 | At times it is desireable to have complete, granular control over the authentication context of an API call. To facilitate this, the passthrough function `.usingAuth()` allows you to pre-define the auth context of the next API call. 329 | 330 | ```js 331 | // assume Usergrid.authMode = UsergridAuth.AUTH_MODE_NONE 332 | 333 | Usergrid.usingAuth(Usergrid.appAuth).POST('roles/guest/permissions', { 334 | permission: "get,post,put,delete:/**" 335 | }, function(error, usergridResponse) { 336 | // here we've temporarily used the client credentials to modify permissions 337 | // subsequent calls will not use this auth context 338 | }) 339 | ``` 340 | 341 | ## User operations and convenience methods 342 | 343 | `UsergridUser` has a number of helper/convenience methods to make working with user entities more convenient. If you are _not_ utilizing the `Usergrid` shared instance, you must pass an instance of `UsergridClient` as the first argument to any of these helper methods. 344 | 345 | ### create() 346 | 347 | Creating a new user: 348 | 349 | ```js 350 | var user = new UsergridUser({ 351 | username: 'username', 352 | password: 'password' 353 | }) 354 | 355 | user.create(function(error, usergridResponse, user) { 356 | // user has now been created and should have a valid uuid 357 | }) 358 | ``` 359 | 360 | ### login() 361 | 362 | A simpler means of retrieving a user-level token: 363 | 364 | ```js 365 | var user = new UsergridUser({ 366 | username: 'username', 367 | password: 'password' 368 | }) 369 | 370 | user.login(function(error, usergridResponse, token) { 371 | // user is now logged in 372 | }) 373 | ``` 374 | 375 | ### logout() 376 | 377 | Logs out the selected user. You can also use this convenience method on `Usergrid.currentUser`. 378 | 379 | ```js 380 | user.logout(function(error, usergridResponse) { 381 | // user is now logged out 382 | }) 383 | ``` 384 | 385 | ### logoutAllSessions() 386 | 387 | Logs out all sessions for the selected user and destroys all active tokens. You can also use this convenience method on `Usergrid.currentUser`. 388 | 389 | ```js 390 | user.logoutAllSessions(function(error, usergridResponse) { 391 | // user is now logged out from everywhere 392 | }) 393 | ``` 394 | 395 | ### resetPassword() 396 | 397 | Resets the password for the selected user. 398 | 399 | ```js 400 | user.resetPassword({ 401 | oldPassword: '2cool4u', 402 | newPassword: 'correct-horse-battery-staple', 403 | }, function(error, response, success) { 404 | // if it was done correctly, the new password will be changed 405 | // 'success' is a boolean value that indicates whether it was changed successfully 406 | }) 407 | ``` 408 | 409 | ### UsergridUser.CheckAvailable() 410 | 411 | This is a class (static) method that allows you to check whether a username or email address is available or not. 412 | 413 | ```js 414 | UsergridUser.CheckAvailable(client, { 415 | email: 'email' 416 | }, function(err, response, exists) { 417 | // 'exists' is a boolean value that indicates whether a user already exists 418 | }) 419 | 420 | UsergridUser.CheckAvailable(client, { 421 | username: 'username' 422 | }, function(err, response, exists) { 423 | 424 | }) 425 | 426 | UsergridUser.CheckAvailable(client, { 427 | email: 'email', 428 | username: 'username', // checks both email and username 429 | }, function(err, response, exists) { 430 | // 'exists' returns true if either username or email exist 431 | }) 432 | ``` 433 | 434 | ## Querying and filtering data 435 | 436 | ### UsergridQuery initialization 437 | 438 | The `UsergridQuery` class allows you to build out complex query filters using the Usergrid [query syntax](http://docs.apigee.com/app-services/content/querying-your-data). 439 | 440 | The first parameter of the `UsergridQuery` builder pattern should be the collection (or type) you intend to query. You can either pass this as an argument, or as the first builder object: 441 | 442 | ```js 443 | var query = new UsergridQuery('cats') 444 | // or 445 | var query = new UsergridQuery().type('cats') 446 | var query = new UsergridQuery().collection('cats') 447 | ``` 448 | 449 | You then can layer on additional queries: 450 | 451 | ```js 452 | var query = new UsergridQuery('cats') 453 | .gt('weight', 2.4) 454 | .contains('color', 'bl*') 455 | .not 456 | .eq('color', 'white') 457 | .or 458 | .eq('color', 'orange') 459 | ``` 460 | 461 | You can also adjust the number of results returned: 462 | 463 | ```js 464 | var query = new UsergridQuery('cats').eq('color', 'black').limit(100) 465 | // returns a maximum of 100 entiteis 466 | ``` 467 | 468 | And sort the results: 469 | 470 | ```js 471 | var query = new UsergridQuery('cats').eq('color', 'black').asc('name') 472 | // sorts by 'name', ascending 473 | ``` 474 | 475 | And you can do geo-location queries: 476 | 477 | ```js 478 | var query = new UsergridQuery('devices').locationWithin(, , ) 479 | ``` 480 | 481 | ### Using a query in a request 482 | 483 | Queries can be passed as parameters to GET, PUT, and DELETE requests: 484 | 485 | ```js 486 | Usergrid.GET(query, function(error, usergridResponse, entities) { 487 | // 488 | }) 489 | 490 | Usergrid.PUT(query, { aNewProperty: "A new value" }, function(error, usergridResponse, entities) { 491 | // 492 | }) 493 | 494 | Usergrid.DELETE(query, function(error, usergridResponse, entities) { 495 | // 496 | }) 497 | ``` 498 | 499 | While not a typical use case, sometimes it is useful to be able to create a query that works on multiple collections. Therefore, in each one of these RESTful calls, you can optionally pass a 'type' string as the first argument: 500 | 501 | ```js 502 | Usergrid.GET('cats', query, function(error, usergridResponse, entities) { 503 | // 504 | }) 505 | ``` 506 | 507 | ### List of query builder objects 508 | 509 | `type('string')` 510 | 511 | > The collection name to query 512 | 513 | `collection('string')` 514 | 515 | > An alias for `type` 516 | 517 | `eq('key', 'value')` or `equal('key', 'value')` 518 | 519 | > Equal to (e.g. `where color = 'black'`) 520 | 521 | `contains('key', 'value')` 522 | 523 | > Contains a string (e.g.` where color contains 'bl*'`) 524 | 525 | `gt('key', 'value')` or `greaterThan('key', 'value')` 526 | 527 | > Greater than (e.g. `where weight > 2.4`) 528 | 529 | `gte('key', 'value')` or `greaterThanOrEqual('key', 'value')` 530 | 531 | > Greater than or equal to (e.g. `where weight >= 2.4`) 532 | 533 | `lt('key', 'value')` or `lessThan('key', 'value')` 534 | 535 | > Less than (e.g. `where weight < 2.4`) 536 | 537 | `lte('key', 'value')` or `lessThanOrEqual('key', 'value')` 538 | 539 | > Less than or equal to (e.g. `where weight <= 2.4`) 540 | 541 | `not` 542 | 543 | > Negates the next block in the builder pattern, e.g.: 544 | 545 | ```js 546 | var query = new UsergridQuery('cats').not.eq('color', 'black') 547 | // select * from cats where not color = 'black' 548 | ``` 549 | 550 | `and` 551 | 552 | > Joins two queries by requiring both of them. `and` is also implied when joining two queries _without_ an operator. E.g.: 553 | 554 | ```js 555 | var query = new UsergridQuery('cats').eq('color', 'black').eq('fur', 'longHair') 556 | // is identical to: 557 | var query = new UsergridQuery('cats').eq('color', 'black').and.eq('fur', 'longHair') 558 | ``` 559 | 560 | `or` 561 | 562 | > Joins two queries by requiring only one of them. `or` is never implied. E.g.: 563 | 564 | ```js 565 | var query = new UsergridQuery('cats').eq('color', 'black').or.eq('color', 'white') 566 | ``` 567 | 568 | > When using `or` and `and` operators, `and` joins will take precedence over `or` joins. You can read more about query operators and precedence [here](http://docs.apigee.com/api-baas/content/supported-query-operators-data-types). 569 | 570 | `locationWithin(distanceInMeters, latitude, longitude)` 571 | 572 | > Returns entities which have a location within the specified radius. Arguments can be `float` or `int`. 573 | 574 | `asc('key')` 575 | 576 | > Sorts the results by the specified property, ascending 577 | 578 | `desc('key')` 579 | 580 | > Sorts the results by the specified property, descending 581 | 582 | `sort('key', 'order')` 583 | 584 | > Sorts the results by the specified property, in the specified order (`asc` or `desc`). 585 | 586 | `limit(int)` 587 | 588 | > The maximum number of entities to return 589 | 590 | `cursor('string')` 591 | 592 | > A pagination cursor string 593 | 594 | `fromString('query string')` 595 | 596 | > A special builder property that allows you to input a pre-defined query string. All other builder properties will be ignored when this property is defined. For example: 597 | 598 | ```js 599 | var query = new UsergridQuery().fromString("select * where color = 'black' order by name asc") 600 | ``` 601 | 602 | ## UsergridResponse object 603 | 604 | `UsergridResponse` implements several Usergrid-specific enhancements to [request](https://github.com/request/request). Notably: 605 | 606 | ### ok 607 | 608 | You can check `usergridResponse.ok`, a `bool` value, to see if the response was successful. Any status code < 400 returns true. 609 | 610 | ```js 611 | Usergrid.GET('collection', function(error, usergridResponse, entities) { 612 | if (usergridResponse.ok) { 613 | // woo! 614 | } 615 | }) 616 | ``` 617 | 618 | ### entity, entities, user, users, first, last 619 | 620 | Depending on the call you make, you will receive either an array of UsergridEntity objects, or a single entity as the third parameter in the callback. If you're querying the `users` collection, these will also be `UsergridUser` objects, a subclass of `UsergridEntity`. 621 | 622 | - `.first` returns the first entity in an array of entities; `.entity` is an alias to `.first`. If there are no entities, both of these will be undefined. 623 | - `.last` returns the last entity in an array of entities; if there is only one entity in the array, this will be the same as `.first` _and_ `.entity`, and will be undefined if there are no entities in the response. 624 | - `.entities` will either be an array of entities in the response, or an empty array. 625 | - `.user` is a special alias for `.entity` for when querying the `users` collection. Instead of being a `UsergridEntity`, it will be its subclass, `UsergridUser`. 626 | - `.users` is the same as `.user`, though behaves as `.entities` does by returning either an array of UsergridUser objects or an empty array. 627 | 628 | Examples: 629 | 630 | ```js 631 | Usergrid.GET('collection', function(error, usergridResponse, entities) { 632 | // third param is an array of entities because no specific entity was referenced 633 | // you can also access: 634 | // usergridResponse.entities 635 | // usergridResponse.first 636 | // usergridResponse.entity (the first entity) 637 | // usergridResponse.last 638 | }) 639 | 640 | Usergrid.GET('collection', '', function(error, usergridResponse, entity) { 641 | // third param is a single entity object 642 | // you can also access: 643 | // usergridResponse.entity 644 | // usergridResponse.first 645 | // usergridResponse.last 646 | }) 647 | 648 | Usergrid.GET('users', function(error, usergridResponse, users) { 649 | // third param is an array of users because no specific user was referenced 650 | // you can also access: 651 | // usergridResponse.users 652 | // usergridResponse.user (the first user) 653 | // usergridResponse.last 654 | }) 655 | 656 | Usergrid.GET('users', '', function(error, usergridResponse, user) { 657 | // third param is a single user object 658 | // you can also access: 659 | // usergridResponse.user 660 | }) 661 | ``` 662 | 663 | ## Connections 664 | 665 | Connections can be managed using `Usergrid.connect()`, `Usergrid.disconnect()`, and `Usergrid.getConnections()`, or entity convenience methods of the same name. 666 | 667 | ### connect 668 | 669 | Create a connection between two entities: 670 | 671 | ```js 672 | Usergrid.connect(entity1, 'relationship', entity2, function(error, usergridResponse) { 673 | // entity1 now has an outbound connection to entity2 674 | }) 675 | ``` 676 | 677 | ### getConnections 678 | 679 | Retrieve outbound connections: 680 | 681 | ```js 682 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, 'relationship', function(error, usergridResponse, entities) { 683 | // entities is an array of entities that entity1 is connected to via 'relationship' 684 | // in this case, we'll see entity2 in the array 685 | }) 686 | ``` 687 | 688 | Retrieve inbound connections: 689 | 690 | ```js 691 | client.getConnections(UsergridClient.Connections.DIRECTION_IN, entity2, 'relationship', function(error, usergridResponse, entities) { 692 | // entities is an array of entities that connect to entity2 via 'relationship' 693 | // in this case, we'll see entity1 in the array 694 | })``` 695 | 696 | ### disconnect 697 | 698 | Delete a connection between two entities: 699 | 700 | ```js 701 | Usergrid.disconnect(entity1, 'relationship', entity2, function(error, usergridResponse) { 702 | // entity1's outbound connection to entity2 has been destroyed 703 | }) 704 | ``` 705 | 706 | ## Assets 707 | 708 | Assets can be uploaded and downloaded either directly using `Usergrid.POST` or `Usergrid.PUT`, or via `UsergridEntity` convenience methods. Before uploading an asset, you will need to initialize a `UsergridAsset` instance. 709 | 710 | ### UsergridAsset init 711 | 712 | Loading a file system image via `fs.readFile()`: 713 | 714 | ```js 715 | var asset = new UsergridAsset('myImage') 716 | fs.readFile(_dirname + '/image.jpg', function(error, data) { 717 | asset.data = data 718 | }) 719 | ``` 720 | 721 | Loading a file system image from a read stream (`fs.createReadStream()`): 722 | 723 | ```js 724 | var asset = new UsergridAsset('myImage') 725 | fs.createReadStream(_dirname + '/image.jpg').pipe(asset).on('finish', function() { 726 | // now contains Buffer stream at asset.data 727 | }) 728 | ``` 729 | 730 | You can also access `asset.contentType` and `asset.contentLength` once data has been loaded into a `UsergridAsset`. 731 | 732 | ### .POST and .PUT 733 | 734 | POST binary data to a collection by creating a new entity: 735 | 736 | ```js 737 | var asset = new UsergridAsset('myImage') 738 | fs.createReadStream(_dirname + '/image.jpg').pipe(asset).on('finish', function() { 739 | client.POST('collection', asset, function(error, assetResponse, entityWithAsset) { 740 | // asset is now uploaded to Usergrid 741 | }) 742 | }) 743 | ``` 744 | 745 | PUT binary data to an existing entity via attachAsset(): 746 | 747 | ```js 748 | var asset = new UsergridAsset('myImage') 749 | fs.createReadStream(_dirname + '/image.jpg').pipe(asset).on('finish', function() { 750 | // assume entity already exists; attach it to the entity: 751 | entity.attachAsset(asset) 752 | client.PUT(entity, asset, function(error, assetResponse, entityWithAsset) { 753 | // asset is now uploaded to Usergrid 754 | }) 755 | }) 756 | ``` 757 | 758 | ### UsergridEntity convenience methods 759 | 760 | `entity.uploadAsset()` is a convenient way to upload an asset that is attached to an entity: 761 | 762 | ```js 763 | var asset = new UsergridAsset('myImage') 764 | fs.createReadStream(_dirname + '/image.jpg').pipe(asset).on('finish', function() { 765 | // assume entity already exists; attach it to the entity: 766 | entity.attachAsset(asset) 767 | entity.uploadAsset(function(error, assetResponse, entityWithAsset) { 768 | // asset is now uploaded to Usergrid 769 | }) 770 | }) 771 | ``` 772 | 773 | `entity.downloadAsset()` allows you to download a binary asset: 774 | 775 | ```js 776 | entity.uploadAsset(function(error, assetResponse, entityWithAsset) { 777 | // access the asset via entityWithAsset.asset 778 | })``` 779 | -------------------------------------------------------------------------------- /config.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "yourAppId", 3 | "authMode": "NONE", 4 | "baseUrl": "https://api.usergrid.com", 5 | "clientId": "yourClientId", 6 | "clientSecret": "yourClientSecret", 7 | "orgId": "yourOrgId" 8 | } -------------------------------------------------------------------------------- /examples/api-proxy/README.md: -------------------------------------------------------------------------------- 1 | To use this example, rename the `usergrid.example.json` to `usergrid.json`. 2 | This file is located at `examples/api-proxy/config`. 3 | 4 | **Note:** The usergrid module crawls your App file structure to find files named `usergrid.json` or a `config.json`. If there are multiple files with one of these names present at different locations under the app, only one of them will be used and the others are ignored. This may cause use of an unintended backend. 5 | -------------------------------------------------------------------------------- /examples/api-proxy/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | var express = require('express'), 16 | app = express(), 17 | Usergrid = require('usergrid') 18 | 19 | Usergrid.init() 20 | 21 | // Usergrid.setAppAuth(id, secret) 22 | // console.log(Usergrid.appAuth) 23 | Usergrid.authenticateApp(function(err, usergridResponse) { 24 | if (usergridResponse.ok) { 25 | console.log('app is now authenticated') 26 | } 27 | }) 28 | 29 | app.get('/:collection/:uuidOrName?', function(req, res) { 30 | Usergrid.GET(req.params.collection, req.params.uuidOrName, function(error, usergridResponse, entities) { 31 | res.json(entities); 32 | }) 33 | }) 34 | 35 | // app.listen(process.env.port || 9000) 36 | 37 | /* 38 | 39 | 1. Start the server using > node app.js 40 | 2. Call the api at http://localhost:9000/cats/test 41 | 42 | */ -------------------------------------------------------------------------------- /examples/api-proxy/config/usergrid.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "sandbox", 3 | "orgId": "brandon.apigee", 4 | "authMode": "NONE", 5 | "baseUrl": "https://api.usergrid.com", 6 | "clientId": "YXA6GXSAACS2EeOYd20aP4G6Lw", 7 | "clientSecret": "YXA66BeEvgNpJBwc4PAbvZZGTVS_SSw" 8 | } -------------------------------------------------------------------------------- /examples/api-proxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "async": "latest", 4 | "express": "latest", 5 | "usergrid": "r3mus/usergrid-nodejs", 6 | "chance": "^0.8.0" 7 | }, 8 | "description": "A sample API proxy built with Express and Usergrid", 9 | "keywords": [], 10 | "license": "MIT", 11 | "main": "app.js", 12 | "name": "usergrid-example-api-proxy", 13 | "private": false, 14 | "scripts": { 15 | "start": "node app.js", 16 | "test": "mocha tests" 17 | }, 18 | "version": "2.0.0-rc.1" 19 | } -------------------------------------------------------------------------------- /helpers/args.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var _ = require('lodash') 18 | 19 | module.exports = function(args) { 20 | return _.flattenDeep(Array.prototype.slice.call(args)) 21 | } -------------------------------------------------------------------------------- /helpers/build.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var urljoin = require('url-join'), 18 | config = require('./config'), 19 | helpers = require('./'), 20 | UsergridQuery = require('../lib/query'), 21 | UsergridEntity = require('../lib/entity'), 22 | UsergridAuth = require('../lib/auth'), 23 | UsergridAsset = require('../lib/asset'), 24 | util = require('util'), 25 | version = require('../package.json').version, 26 | _ = require('lodash') 27 | 28 | var assignPrefabOptions = function(args) { 29 | // if a preformatted options argument passed, assign it to options 30 | if (_.isObject(args[0]) && !_.isFunction(args[0]) && _.has(this,"method")) { 31 | _.assign(this, args[0]) 32 | } 33 | return this 34 | } 35 | 36 | var setEntity = function(args) { 37 | this.entity = _.first([this.entity, args[0]].filter(function(property) { 38 | return (property instanceof UsergridEntity) 39 | })) 40 | if (this.entity !== undefined) { 41 | this.type = this.entity.type 42 | } 43 | return this 44 | } 45 | 46 | var setAsset = function(args) { 47 | this.asset = _.first([this.asset, _.get(this,'entity.asset'), args[1], args[0]].filter(function(property) { 48 | return (property instanceof UsergridAsset) 49 | })) 50 | return this 51 | } 52 | 53 | var setUuidOrName = function(args) { 54 | this.uuidOrName = _.first([ 55 | this.uuidOrName, 56 | this.uuid, 57 | this.name, 58 | _.get(this,'entity.uuid'), 59 | _.get(this,'body.uuid'), 60 | _.get(this,'entity.name'), 61 | _.get(this,'body.name'), 62 | _.get(args,'2'), 63 | _.get(args,'1') 64 | ].filter(_.isString)) 65 | return this 66 | } 67 | 68 | var setPathOrType = function(args) { 69 | var pathOrType = _.first([ 70 | this.type, 71 | _.get(args,'0._type'), 72 | _.get(this,'entity.type'), 73 | _.get(this,'body.type'), 74 | _.get(this,'body.0.type'), 75 | _.isArray(args) ? args[0] : undefined 76 | ].filter(_.isString)) 77 | this[(/\//.test(pathOrType)) ? 'path' : 'type'] = pathOrType 78 | return this 79 | } 80 | 81 | var setQs = function(args) { 82 | if (this.path) { 83 | this.qs = _.first([this.qs, args[2], args[1], args[0]].filter(_.isPlainObject)) 84 | } 85 | return this 86 | } 87 | 88 | var setQuery = function(args) { 89 | this.query = _.first([this.query, args[0]].filter(function(property) { 90 | return (property instanceof UsergridQuery) 91 | })) 92 | return this 93 | } 94 | 95 | var setBody = function(args) { 96 | this.body = _.first([this.entity, this.body, args[2], args[1], args[0]].filter(function(property) { 97 | return _.isObject(property) && !_.isFunction(property) && !(property instanceof UsergridQuery) && !(property instanceof UsergridAsset) 98 | })) 99 | if (this.body === undefined && this.asset === undefined) { 100 | throw new Error(util.format('"body" is required when making a %s request', this.method)) 101 | } 102 | return this 103 | } 104 | 105 | module.exports = { 106 | uri: function(client, options) { 107 | return urljoin( 108 | client.baseUrl, 109 | client.orgId, 110 | client.appId, 111 | options.path || options.type, 112 | options.method !== "POST" ? _.first([ 113 | options.uuidOrName, 114 | options.uuid, 115 | options.name, 116 | _.get(options,'entity.uuid'), 117 | _.get(options,'entity.name'), 118 | "" 119 | ].filter(_.isString)) : "" 120 | ) 121 | }, 122 | headers: function(client, options) { 123 | var headers = { 124 | 'User-Agent': util.format("usergrid-nodejs/v%s", version) 125 | } 126 | 127 | _.assign(headers, options.headers) 128 | 129 | var token 130 | var clientTempAuth = _.get(client,"tempAuth") 131 | if( !_.isUndefined(clientTempAuth) ) { 132 | if( clientTempAuth !== UsergridAuth.NO_AUTH && clientTempAuth.isValid ) { 133 | token = client.tempAuth.token; 134 | } 135 | client.tempAuth = undefined 136 | } else { 137 | var clientAuthMode = _.get(client,"authMode"); 138 | if( _.get(client,"currentUser.auth.isValid") && clientAuthMode === UsergridAuth.AUTH_MODE_USER ) { 139 | token = client.currentUser.auth.token; 140 | } else if( _.get(client,"appAuth.isValid") && clientAuthMode === UsergridAuth.AUTH_MODE_APP ) { 141 | token = client.appAuth.token; 142 | } 143 | } 144 | 145 | if (token) { 146 | _.assign(headers, { 147 | authorization: util.format("Bearer %s", token) 148 | }) 149 | } 150 | 151 | return headers 152 | }, 153 | userLoginBody: function(options) { 154 | var body = { 155 | grant_type: 'password', 156 | password: options.password 157 | } 158 | if (options.tokenTtl) { 159 | body.ttl = options.tokenTtl 160 | } 161 | body[(options.username) ? "username" : "email"] = (options.username) ? options.username : options.email 162 | return body 163 | }, 164 | appLoginBody: function(options) { 165 | var body = { 166 | grant_type: 'client_credentials', 167 | client_id: options.clientId, 168 | client_secret: options.clientSecret 169 | } 170 | if (options.tokenTtl) { 171 | body.ttl = options.tokenTtl 172 | } 173 | return body 174 | }, 175 | GET: function(client, args) { 176 | 177 | /* GET supports the following constructor patterns: 178 | 179 | client.GET('type', 'uuidOrName', optionalCallback) 180 | client.GET('type', optionalCallback) 181 | client.GET(query, optionalCallback) 182 | client.GET({ 183 | query: query, // takes precedence 184 | type: type, // required if query not defined 185 | uuid: uuid, // will be set to uuidOrName on init (priority) 186 | name: name, // will be set to uuidOrName on init (if no uuid specified) 187 | uuidOrName: uuidOrName // the definitive key for name or uuid 188 | }, optionalCallback) 189 | 190 | */ 191 | 192 | var options = { 193 | client: client, 194 | method: 'GET', 195 | callback: helpers.cb(args) 196 | } 197 | assignPrefabOptions.call(options, args) 198 | setEntity.call(options, args) 199 | setUuidOrName.call(options, args) 200 | setPathOrType.call(options, args) 201 | setQs.call(options, args) 202 | setQuery.call(options, args) 203 | return options 204 | }, 205 | PUT: function(client, args) { 206 | 207 | /* PUT supports the following constructor patterns: 208 | 209 | client.PUT('type', 'uuidOrName', bodyObject, optionalCallback) 210 | client.PUT('type', bodyObject, optionalCallback) // if no uuid, will create a new record 211 | client.PUT(bodyObjectOrEntity, optionalCallback) // if no uuid, will create a new record; must include type 212 | client.PUT(query, bodyObjectOrEntity, optionalCallback) // will update all entities matching query 213 | client.PUT(entity, optionalCallback) 214 | client.PUT({ 215 | *entity = alias to body* 216 | query: query, // takes precedence over type/body 217 | type: type, // required if query not defined 218 | body: bodyObject or bodyObjectOrEntity, // if includes type, type will be inferred from body 219 | *uuid, name* = alias to uuidOrName* 220 | uuidOrName: uuidOrName // the definitive key for name or uuid 221 | }, optionalCallback) 222 | 223 | */ 224 | 225 | var options = { 226 | client: client, 227 | method: 'PUT', 228 | callback: helpers.cb(args) 229 | } 230 | assignPrefabOptions.call(options, args) 231 | setEntity.call(options, args) 232 | setAsset.call(options, args) 233 | setBody.call(options, args) 234 | setUuidOrName.call(options, args) 235 | setPathOrType.call(options, args) 236 | setQuery.call(options, args) 237 | return options 238 | }, 239 | POST: function(client, args) { 240 | 241 | /* POST supports the following constructor patterns: 242 | 243 | client.POST('type', bodyObjectOrArray, optionalCallback) 244 | client.POST(bodyObjectOrArray, optionalCallback) // must include type in body 245 | client.POST(entityOrEntities, optionalCallback) 246 | client.POST({ 247 | *entity, entities = alias to body* 248 | type: type, // required 249 | body: bodyObjectOrArray or entityOrEntities, // if the first entity includes type, type will be inferred from body 250 | }, optionalCallback) 251 | 252 | */ 253 | 254 | var options = { 255 | client: client, 256 | method: 'POST', 257 | callback: helpers.cb(args) 258 | } 259 | assignPrefabOptions.call(options, args) 260 | setEntity.call(options, args) 261 | setAsset.call(options, args) 262 | setBody.call(options, args) 263 | setPathOrType.call(options, args) 264 | return options 265 | }, 266 | DELETE: function(client, args) { 267 | 268 | /* DELETE supports the following constructor patterns: 269 | 270 | client.DELETE('type', 'uuidOrName', optionalCallback) 271 | client.DELETE(entity, optionalCallback) // must include type in body 272 | client.DELETE(query, optionalCallback) 273 | client.DELETE({ 274 | *uuid, name* = alias to uuidOrName* 275 | uuidOrName: uuidOrName, 276 | type: type, // required if query not defined 277 | query: query // takes precedence over type/uuid 278 | }, optionalCallback) 279 | 280 | */ 281 | 282 | var options = { 283 | client: client, 284 | method: 'DELETE', 285 | callback: helpers.cb(args) 286 | } 287 | assignPrefabOptions.call(options, args) 288 | setEntity.call(options, args) 289 | setUuidOrName.call(options, args) 290 | setPathOrType.call(options, args) 291 | setQs.call(options, args) 292 | setQuery.call(options, args) 293 | return options 294 | }, 295 | connection: function(client, method, args) { 296 | 297 | /* connect supports the following constructor patterns: 298 | 299 | client.connect(entity, "relationship", toEntity); 300 | // POST entity.type/entity.uuid/"relationship"/toEntity.uuid 301 | 302 | client.connect("type", , "relationship", ); 303 | // POST type/uuidOrName/relationship/toUuid 304 | 305 | client.connect("type", , "relationship", "toType", "toName"); 306 | // POST type/uuidOrName/relationship/toType/toName 307 | 308 | client.connect({ 309 | entity: { // or UsergridEntity 310 | type: "type", 311 | uuidOrName: 312 | }, 313 | relationship: "likes", 314 | to: { // or UsergridEntity 315 | "type": "(required if not using uuid)", 316 | "uuidOrName": , 317 | "name": "alias to uuidOrName" // if uuid not specified, requires "type" 318 | "uuid": "alias to uuidOrName" 319 | } 320 | ); 321 | 322 | disconnect supports the identical patters, but uses DELETE instead of POST; it is therefore a reference to this function 323 | 324 | */ 325 | 326 | var options = { 327 | client: client, 328 | method: method, 329 | entity: {}, 330 | to: {}, 331 | callback: helpers.cb(args) 332 | } 333 | 334 | assignPrefabOptions.call(options, args) 335 | 336 | // handle DELETE using "from" preposition 337 | if (_.isObject(options.from)) { 338 | options.to = options.from 339 | } 340 | 341 | // if an entity object or UsergridEntity instance is the first argument (source) 342 | if (_.isObject(args[0]) && !_.isFunction(args[0]) && _.isString(args[1])) { 343 | _.assign(options.entity, args[0]) 344 | options.relationship = _.first([options.relationship, args[1]].filter(_.isString)) 345 | } 346 | 347 | // if an entity object or UsergridEntity instance is the third argument (target) 348 | if (_.isObject(args[2]) && !_.isFunction(args[2])) { 349 | _.assign(options.to, args[2]) 350 | } 351 | 352 | options.entity.uuidOrName = _.first([options.entity.uuidOrName, options.entity.uuid, options.entity.name, args[1]].filter(_.isString)) 353 | if (!options.entity.type) { 354 | options.entity.type = _.first([options.entity.type, args[0]].filter(_.isString)) 355 | } 356 | options.relationship = _.first([options.relationship, args[2]].filter(_.isString)) 357 | 358 | if (_.isString(args[3]) && !_.isUuid(args[3]) && _.isString(args[4])) { 359 | options.to.type = args[3] 360 | } else if (_.isString(args[2]) && !_.isUuid(args[2]) && _.isString(args[3]) && _.isObject(args[0]) && !_.isFunction(args[0])) { 361 | options.to.type = args[2] 362 | } 363 | 364 | options.to.uuidOrName = _.first([options.to.uuidOrName, options.to.uuid, options.to.name, args[4], args[3], args[2]].filter(function(property) { 365 | return (_.isString(options.to.type) && _.isString(property) || _.isUuid(property)) 366 | })) 367 | 368 | if (!_.isString(options.entity.uuidOrName)) { 369 | throw new Error('source entity "uuidOrName" is required when connecting or disconnecting entities') 370 | } 371 | 372 | if (!_.isString(options.to.uuidOrName)) { 373 | throw new Error('target entity "uuidOrName" is required when connecting or disconnecting entities') 374 | } 375 | 376 | if (!_.isString(options.to.type) && !_.isUuid(options.to.uuidOrName)) { 377 | throw new Error('target "type" (collection name) parameter is required connecting or disconnecting entities by name') 378 | } 379 | 380 | options.uri = urljoin( 381 | config.baseUrl, 382 | client.orgId, 383 | client.appId, 384 | _.isString(options.entity.type) ? options.entity.type : "", 385 | _.isString(options.entity.uuidOrName) ? options.entity.uuidOrName : "", 386 | options.relationship, 387 | _.isString(options.to.type) ? options.to.type : "", 388 | _.isString(options.to.uuidOrName) ? options.to.uuidOrName : "" 389 | ) 390 | 391 | return options 392 | }, 393 | getConnections: function(client, args) { 394 | /* getConnections supports the following constructor patterns: 395 | 396 | client.getConnections(direction, entity, "relationship"); 397 | // GET OUT: /entity.type/entity.uuid/connections/relationship 398 | // GET IN: /entity.type/entity.uuid/connecting/relationship 399 | 400 | client.getConnections(direction, "type", "", "relationship"); 401 | // GET OUT: /type/uuidOrName/connections/relationship 402 | // GET IN: /type/uuidOrName/connecting/relationship 403 | 404 | client.getConnections({ 405 | type: "type", // or inferred, if second argument is an entity 406 | uuidOrName: "" // if entity not specified 407 | relationship: "relationship", 408 | direction: OUT or IN 409 | ); 410 | // GET OUT: /entity.type/entity.uuid/connections/relationship 411 | // GET IN: /entity.type/entity.uuid/connecting/relationship 412 | 413 | */ 414 | 415 | var options = { 416 | client: client, 417 | method: 'GET', 418 | callback: helpers.cb(args) 419 | } 420 | 421 | assignPrefabOptions.call(options, args) 422 | if (_.isObject(args[1]) && !_.isFunction(args[1])) { 423 | _.assign(options, args[1]) 424 | } 425 | 426 | options.direction = _.first([options.direction, args[0]].filter(function(property) { 427 | return (property === "IN" || property === "OUT") 428 | })) 429 | 430 | options.relationship = _.first([options.relationship, args[3], args[2]].filter(_.isString)) 431 | options.uuidOrName = _.first([options.uuidOrName, options.uuid, options.name, args[2]].filter(_.isString)) 432 | options.type = _.first([options.type, args[1]].filter(_.isString)) 433 | 434 | if (!_.isString(options.type)) { 435 | throw new Error('"type" (collection name) parameter is required when retrieving connections') 436 | } 437 | 438 | if (!_.isString(options.uuidOrName)) { 439 | throw new Error('target entity "uuidOrName" is required when retrieving connections') 440 | } 441 | 442 | options.uri = urljoin( 443 | config.baseUrl, 444 | client.orgId, 445 | client.appId, 446 | _.isString(options.type) ? options.type : "", 447 | _.isString(options.uuidOrName) ? options.uuidOrName : "", 448 | options.direction === "IN" ? "connecting" : "connections", 449 | options.relationship 450 | ) 451 | 452 | return options 453 | }, 454 | qs: function(options) { 455 | return (options.query instanceof UsergridQuery) ? { 456 | ql: options.query._ql || undefined, 457 | limit: options.query._limit, 458 | cursor: options.query._cursor 459 | } : options.qs 460 | }, 461 | formData: function(options) { 462 | if (_.get(options,'asset.data')) { 463 | var formData = {} 464 | formData.file = { 465 | value: options.asset.data, 466 | options: { 467 | filename: _.get(options,'asset.filename') || UsergridAsset.DEFAULT_FILE_NAME, 468 | contentType: _.get(options,'asset.contentType') || 'application/octet-stream' 469 | } 470 | } 471 | if (_.has(options,'asset.name')) { 472 | formData.name = options.asset.name 473 | } 474 | return formData 475 | } else { 476 | return undefined 477 | } 478 | } 479 | } -------------------------------------------------------------------------------- /helpers/cb.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var _ = require('lodash') 18 | 19 | module.exports = function() { 20 | var args = _.flattenDeep(Array.prototype.slice.call(arguments)).reverse() 21 | var emptyFunc = function() {} 22 | return _.first(_.flattenDeep([args, _.get(args,'0.callback'), emptyFunc]).filter(_.isFunction)) 23 | 24 | } -------------------------------------------------------------------------------- /helpers/client.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var Usergrid = require('../usergrid'), 18 | _ = require('lodash') 19 | 20 | 21 | module.exports = { 22 | validate: function(args) { 23 | var UsergridClient = require('../lib/client') 24 | var client 25 | if (args instanceof UsergridClient) { 26 | client = args 27 | } else if (args[0] instanceof UsergridClient) { 28 | client = args[0] 29 | } else if (Usergrid.isInitialized) { 30 | client = Usergrid 31 | } else { 32 | throw new Error("this method requires either the Usergrid shared instance to be initialized or a UsergridClient instance as the first argument") 33 | } 34 | return client 35 | }, 36 | configureTempAuth: function(auth) { 37 | var UsergridAuth = require('../lib/auth') 38 | if (_.isString(auth) && auth !== UsergridAuth.NO_AUTH) { 39 | return new UsergridAuth(auth) 40 | } else if (!auth || auth === UsergridAuth.NO_AUTH) { 41 | return UsergridAuth.NO_AUTH 42 | } else if (auth instanceof UsergridAuth) { 43 | return auth 44 | } else { 45 | return undefined 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /helpers/config.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var util = require('util'), 18 | path = require('path'), 19 | file = require("file"), 20 | _ = require('lodash'), 21 | appRoot = path.dirname(require.main.filename) 22 | 23 | if (/mocha$/i.test(process.argv[1])) { 24 | var target = (_.last(process.argv)).startsWith('--target=') ? _.last(process.argv).replace(/--target=/, '') : '2.1' 25 | var config = require('../tests/config.test.json')[target] 26 | if (config && target) { 27 | config.target = target 28 | } else { 29 | throw new Error(util.format("Could not load target '%s' from /tests/config.test.json", target)) 30 | } 31 | module.exports = config 32 | } else { 33 | try { 34 | file.walkSync(appRoot, function(start, dirs, names) { 35 | if (_.includes(names, "config.json") || _.includes(names, "usergrid.json")) { 36 | var name = _.first(names.filter(function(name) { 37 | return name === "config.json" || name === "usergrid.json" 38 | }).sort().reverse()) 39 | var configPath = util.format("%s/%s", start, name) 40 | module.exports = require(configPath) 41 | if (module.exports.orgId === undefined || module.exports.appId === undefined) { 42 | console.log(util.format("Config file '%s' is not a valid Usergrid configuration file", configPath)) 43 | module.exports = {} 44 | } else { 45 | console.log(util.format("Using config file '%s'", configPath)) 46 | } 47 | } 48 | }) 49 | } catch (e) { 50 | 51 | } 52 | } -------------------------------------------------------------------------------- /helpers/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var args = require('./args'), 18 | client = require('./client'), 19 | cb = require('./cb'), 20 | build = require('./build'), 21 | query = require('./query'), 22 | config = require('./config'), 23 | time = require('./time'), 24 | mutability = require('./mutability'), 25 | user = require('./user'), 26 | _ = require('lodash') 27 | 28 | // by mixing this in here, lodash-uuid is available everywhere lodash is used. 29 | _.mixin(require('lodash-uuid')) 30 | 31 | module.exports = _.assign(module.exports, { 32 | args: args, 33 | client: client, 34 | cb: cb, 35 | build: build, 36 | query: query, 37 | config: config, 38 | time: time, 39 | user: user 40 | }, mutability) -------------------------------------------------------------------------------- /helpers/mutability.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var _ = require('lodash') 18 | 19 | module.exports = { 20 | setReadOnly: function(obj, key) { 21 | if (_.isArray(key)) { 22 | return key.forEach(function(k) { 23 | module.exports.setReadOnly(obj, k) 24 | }) 25 | } else if (_.isPlainObject(obj[key])) { 26 | return Object.freeze(obj[key]) 27 | } else if (_.isPlainObject(obj) && key === undefined) { 28 | return Object.freeze(obj) 29 | } else if (_.has(obj,key)) { 30 | return Object.defineProperty(obj, key, { 31 | writable: false 32 | }) 33 | } else { 34 | return obj 35 | } 36 | }, 37 | setWritable: function(obj, key) { 38 | if (_.isArray(key)) { 39 | return key.forEach(function(k) { 40 | module.exports.setWritable(obj, k) 41 | }) 42 | // Note that once Object.freeze is called on an object, it cannot be unfrozen, so we need to clone it 43 | } else if (_.isPlainObject(obj[key])) { 44 | return _.clone(obj[key]) 45 | } else if (_.isPlainObject(obj) && key === undefined) { 46 | return _.clone(obj) 47 | } else if (_.has(obj,key)) { 48 | return Object.defineProperty(obj, key, { 49 | writable: true 50 | }) 51 | } else { 52 | return obj 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /helpers/query.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var util = require('util'), 18 | _ = require('lodash') 19 | 20 | module.exports = { 21 | useQuotesIfRequired: function(value) { 22 | return (_.isFinite(value) || _.isUuid(value) || _.isBoolean(value) || _.isObject(value) && !_.isFunction(value) || _.isArray(value)) ? value : util.format("'%s'", value) 23 | } 24 | } -------------------------------------------------------------------------------- /helpers/time.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | module.exports = { 18 | expiry: function(expires_in) { 19 | return Date.now() + ((expires_in ? expires_in - 5 : 0) * 1000) 20 | } 21 | } -------------------------------------------------------------------------------- /helpers/user.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var _ = require('lodash') 18 | 19 | module.exports = { 20 | uniqueId: function(user) { 21 | return _.first([user.uuid, user.username, user.email].filter(_.isString)) 22 | } 23 | } -------------------------------------------------------------------------------- /lib/appAuth.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var UsergridAuth = require('./auth'), 18 | helpers = require('../helpers'), 19 | util = require('util'), 20 | _ = require('lodash') 21 | 22 | var UsergridAppAuth = function() { 23 | var self = this 24 | var args = _.flattenDeep(helpers.args(arguments)) 25 | if (_.isPlainObject(args[0])) { 26 | self.clientId = args[0].clientId 27 | self.clientSecret = args[0].clientSecret 28 | self.tokenTtl = args[0].tokenTtl 29 | } else { 30 | self.clientId = args[0] 31 | self.clientSecret = args[1] 32 | self.tokenTtl = args[2] 33 | } 34 | UsergridAuth.call(self) 35 | _.assign(self, UsergridAuth) 36 | return self 37 | } 38 | 39 | util.inherits(UsergridAppAuth, UsergridAuth) 40 | 41 | module.exports = UsergridAppAuth -------------------------------------------------------------------------------- /lib/asset.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var fileType = require('file-type'), 18 | helpers = require('../helpers'), 19 | stream = require('stream'), 20 | util = require('util'), 21 | _ = require('lodash') 22 | 23 | var UsergridAsset = function() { 24 | var self = this 25 | var args = helpers.args(arguments) 26 | 27 | var __contentType 28 | var __binaryData = [] 29 | 30 | if (args.length === 0) { 31 | throw new Error('A UsergridAsset object cannot be initialized without passing one or more arguments') 32 | } 33 | 34 | if (_.isPlainObject(args[0])) { 35 | _.assign(self, args[0]) 36 | } else { 37 | self.filename = _.isString(args[0]) ? args[0] : undefined 38 | self.data = _.first(args.filter(Buffer.isBuffer)) || [] 39 | self.originalLocation = _.first([args[2], args[1]].filter(function(property) { 40 | return _.isString(property) 41 | })) 42 | 43 | self.contentType = _.isString(args[3]) ? args[3] : undefined 44 | 45 | stream.PassThrough.call(self) 46 | self._write = function(chunk, encoding, done) { 47 | __binaryData.push(chunk) 48 | done() 49 | } 50 | self.on('finish', function() { 51 | self.data = Buffer.concat(__binaryData) 52 | }) 53 | } 54 | 55 | Object.defineProperty(self, 'contentLength', { 56 | get: function() { 57 | return (self.data) ? self.data.byteLength : 0 58 | } 59 | }) 60 | 61 | Object.defineProperty(self, 'contentType', { 62 | get: function() { 63 | if (__contentType) { 64 | return __contentType 65 | } else if (Buffer.isBuffer(self.data)) { 66 | __contentType = fileType(self.data) != null ? fileType(self.data).mime : undefined 67 | return __contentType 68 | } 69 | }, 70 | set: function(contentType) { 71 | if (contentType) { 72 | __contentType = contentType 73 | } else if (Buffer.isBuffer(self.data)) { 74 | __contentType = fileType(self.data) != null ? fileType(self.data).mime : undefined 75 | } 76 | } 77 | }) 78 | 79 | return self 80 | } 81 | 82 | util.inherits(UsergridAsset, stream.PassThrough) 83 | 84 | 85 | module.exports = UsergridAsset 86 | module.exports.DEFAULT_FILE_NAME = 'file' -------------------------------------------------------------------------------- /lib/auth.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var UsergridAuth = function(token, expiry) { 18 | var self = this 19 | 20 | self.token = token 21 | self.expiry = expiry || 0 22 | 23 | var usingToken = (token) ? true : false 24 | 25 | Object.defineProperty(self, "hasToken", { 26 | get: function() { 27 | return (self.token) ? true : false 28 | }, 29 | configurable: true 30 | }) 31 | 32 | Object.defineProperty(self, "isExpired", { 33 | get: function() { 34 | return (usingToken) ? false : (Date.now() >= self.expiry) 35 | }, 36 | configurable: true 37 | }) 38 | 39 | Object.defineProperty(self, "isValid", { 40 | get: function() { 41 | return (!self.isExpired && self.hasToken) 42 | }, 43 | configurable: true 44 | }) 45 | 46 | Object.defineProperty(self, 'tokenTtl', { 47 | configurable: true, 48 | writable: true 49 | }) 50 | 51 | return self 52 | } 53 | 54 | UsergridAuth.prototype = { 55 | destroy: function() { 56 | this.token = undefined 57 | this.expiry = 0 58 | this.tokenTtl = undefined 59 | } 60 | } 61 | 62 | module.exports = UsergridAuth 63 | 64 | Object.defineProperty(module.exports, 'AUTH_MODE_APP', { 65 | enumerable: false, 66 | get: function() { return "APP" } 67 | }) 68 | Object.defineProperty(module.exports, 'AUTH_MODE_USER', { 69 | enumerable: false, 70 | get: function() { return "USER" } 71 | }) 72 | Object.defineProperty(module.exports, 'AUTH_MODE_NONE', { 73 | enumerable: false, 74 | get: function() { return "NONE" } 75 | }) 76 | Object.defineProperty(module.exports, 'NO_AUTH', { 77 | enumerable: false, 78 | get: function() { return "NO_AUTH" } 79 | }) -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var helpers = require('../helpers'), 18 | UsergridRequest = require('./request'), 19 | UsergridAuth = require('./auth'), 20 | UsergridAppAuth = require('./appAuth'), 21 | _ = require('lodash') 22 | 23 | var defaultOptions = { 24 | baseUrl: 'https://api.usergrid.com', 25 | authMode: UsergridAuth.AUTH_MODE_USER 26 | } 27 | 28 | var UsergridClient = function(options) { 29 | var self = this 30 | 31 | var __appAuth 32 | self.tempAuth = undefined 33 | self.isSharedInstance = false 34 | 35 | if (arguments.length === 2) { 36 | self.orgId = arguments[0] 37 | self.appId = arguments[1] 38 | } 39 | 40 | _.defaults(self, options, helpers.config, defaultOptions) 41 | 42 | if (!self.orgId || !self.appId) { 43 | throw new Error('"orgId" and "appId" parameters are required when instantiating UsergridClient') 44 | } 45 | 46 | Object.defineProperty(self, 'test', { 47 | enumerable: false 48 | }) 49 | 50 | Object.defineProperty(self, 'clientId', { 51 | enumerable: false 52 | }) 53 | 54 | Object.defineProperty(self, 'clientSecret', { 55 | enumerable: false 56 | }) 57 | 58 | Object.defineProperty(self, 'appAuth', { 59 | get: function() { 60 | return __appAuth 61 | }, 62 | set: function(options) { 63 | if (options instanceof UsergridAppAuth) { 64 | __appAuth = options 65 | } else if( _.isUndefined(options) ) { 66 | __appAuth = undefined 67 | } else { 68 | __appAuth = new UsergridAppAuth(options) 69 | } 70 | } 71 | }) 72 | 73 | // if client ID and secret are defined on initialization, initialize appAuth 74 | if (self.clientId && self.clientSecret) { 75 | self.setAppAuth(self.clientId, self.clientSecret) 76 | } 77 | return self 78 | } 79 | 80 | UsergridClient.prototype = { 81 | GET: function() { 82 | return new UsergridRequest(helpers.build.GET(this, helpers.args(arguments))) 83 | }, 84 | PUT: function() { 85 | return new UsergridRequest(helpers.build.PUT(this, helpers.args(arguments))) 86 | }, 87 | POST: function() { 88 | return new UsergridRequest(helpers.build.POST(this, helpers.args(arguments))) 89 | }, 90 | DELETE: function() { 91 | return new UsergridRequest(helpers.build.DELETE(this, helpers.args(arguments))) 92 | }, 93 | connect: function() { 94 | return new UsergridRequest(helpers.build.connection(this, 'POST', helpers.args(arguments))) 95 | }, 96 | disconnect: function() { 97 | return new UsergridRequest(helpers.build.connection(this, 'DELETE', helpers.args(arguments))) 98 | }, 99 | getConnections: function() { 100 | return new UsergridRequest(helpers.build.getConnections(this, helpers.args(arguments))) 101 | }, 102 | setAppAuth: function() { 103 | this.appAuth = new UsergridAppAuth(helpers.args(arguments)) 104 | }, 105 | authenticateApp: function(options) { 106 | var self = this 107 | var callback = helpers.cb(helpers.args(arguments)) 108 | // console.log(self.appAuth)//, self.appAuth, new UsergridAppAuth(options), new UsergridAppAuth(self.clientId, self.clientSecret)) 109 | var auth = _.first([options, self.appAuth, new UsergridAppAuth(options), new UsergridAppAuth(self.clientId, self.clientSecret)].filter(function(p) { 110 | return p instanceof UsergridAppAuth 111 | })) 112 | 113 | if (!(auth instanceof UsergridAppAuth)) { 114 | throw new Error('App auth context was not defined when attempting to call .authenticateApp()') 115 | } else if (!auth.clientId || !auth.clientSecret) { 116 | throw new Error('authenticateApp() failed because clientId or clientSecret are missing') 117 | } 118 | 119 | return new UsergridRequest({ 120 | client: self, 121 | path: 'token', 122 | method: 'POST', 123 | body: helpers.build.appLoginBody(auth) 124 | }, function(error, usergridResponse, body) { 125 | if (usergridResponse.ok) { 126 | if (!self.appAuth) { 127 | self.appAuth = auth 128 | } 129 | self.appAuth.token = body.access_token 130 | self.appAuth.expiry = helpers.time.expiry(body.expires_in) 131 | self.appAuth.tokenTtl = body.expires_in 132 | } 133 | callback(error, usergridResponse, body.access_token) 134 | }) 135 | }, 136 | authenticateUser: function(options) { 137 | var self = this 138 | var args = helpers.args(arguments) 139 | var callback = helpers.cb(args) 140 | var setAsCurrentUser = (_.last(args.filter(_.isBoolean))) !== undefined ? _.last(args.filter(_.isBoolean)) : true 141 | var UsergridUser = require('./user') 142 | var currentUser = new UsergridUser(options) 143 | currentUser.login(self, function(error, usergridResponse, token) { 144 | if (usergridResponse.ok && setAsCurrentUser) { 145 | self.currentUser = currentUser 146 | } 147 | callback(error, usergridResponse, token) 148 | }) 149 | }, 150 | usingAuth: function(auth) { 151 | this.tempAuth = helpers.client.configureTempAuth(auth) 152 | return this 153 | } 154 | } 155 | 156 | module.exports = UsergridClient 157 | Object.defineProperty(module.exports, 'Connections', { 158 | enumerable: false, 159 | writable: true, 160 | configurable: true 161 | }) 162 | module.exports.Connections = {} 163 | Object.defineProperty(module.exports.Connections, 'DIRECTION_IN', { 164 | enumerable: false, 165 | get: function() { return "IN" } 166 | }) 167 | Object.defineProperty(module.exports.Connections, 'DIRECTION_OUT', { 168 | enumerable: false, 169 | get: function() { return "OUT" } 170 | }) -------------------------------------------------------------------------------- /lib/entity.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var UsergridRequest = require('./request'), 18 | UsergridAsset = require('./asset'), 19 | helpers = require('../helpers'), 20 | _ = require('lodash') 21 | 22 | function updateEntityFromRemote(usergridResponse) { 23 | helpers.setWritable(this, ['uuid', 'name', 'type', 'created']) 24 | _.assign(this, usergridResponse.entity) 25 | helpers.setReadOnly(this, ['uuid', 'name', 'type', 'created']) 26 | } 27 | 28 | var UsergridEntity = function() { 29 | var self = this 30 | var args = helpers.args(arguments) 31 | 32 | if (args.length === 0) { 33 | throw new Error('A UsergridEntity object cannot be initialized without passing one or more arguments') 34 | } 35 | 36 | var firstArg = args[0] 37 | if (_.isPlainObject(firstArg) || firstArg instanceof UsergridEntity ) { 38 | _.assign(self, args[0]); 39 | } else { 40 | if( !self.type ) { 41 | self.type = _.isString(args[0]) ? args[0] : undefined; 42 | } 43 | if( !self.name ) { 44 | self.name = _.isString(args[1]) ? args[1] : undefined; 45 | } 46 | } 47 | 48 | if (!_.isString(self.type)) { 49 | throw new Error('"type" (or "collection") parameter is required when initializing a UsergridEntity object') 50 | } 51 | 52 | Object.defineProperty(self, 'tempAuth', { 53 | enumerable: false, 54 | configurable: true, 55 | writable: true 56 | }) 57 | 58 | Object.defineProperty(self, 'asset', { 59 | enumerable: false, 60 | configurable: true, 61 | writable: true 62 | }) 63 | 64 | Object.defineProperty(self, 'isUser', { 65 | get: function() { 66 | return (self.type.toLowerCase() === 'user') 67 | } 68 | }) 69 | 70 | Object.defineProperty(self, 'hasAsset', { 71 | get: function() { 72 | return _.has(self,'file-metadata') 73 | } 74 | }) 75 | 76 | helpers.setReadOnly(self, ['uuid', 'name', 'type', 'created']) 77 | 78 | return self 79 | } 80 | 81 | UsergridEntity.prototype = { 82 | putProperty: function(key, value) { 83 | this[key] = value 84 | }, 85 | putProperties: function(obj) { 86 | _.assign(this, obj) 87 | }, 88 | removeProperty: function(key) { 89 | this.removeProperties([key]) 90 | }, 91 | removeProperties: function(keys) { 92 | var self = this 93 | keys.forEach(function(key) { 94 | delete self[key] 95 | }) 96 | }, 97 | insert: function(key, value, idx) { 98 | if (!_.isArray(this[key])) { 99 | this[key] = this[key] ? [this[key]] : [] 100 | } 101 | this[key].splice.apply(this[key], [idx, 0].concat(value)) 102 | }, 103 | append: function(key, value) { 104 | this.insert(key, value, Number.MAX_SAFE_INTEGER) 105 | }, 106 | prepend: function(key, value) { 107 | this.insert(key, value, 0) 108 | }, 109 | pop: function(key) { 110 | if (_.isArray(this[key])) { 111 | this[key].pop() 112 | } 113 | }, 114 | shift: function(key) { 115 | if (_.isArray(this[key])) { 116 | this[key].shift() 117 | } 118 | }, 119 | reload: function() { 120 | var args = helpers.args(arguments) 121 | var client = helpers.client.validate(args) 122 | client.tempAuth = this.tempAuth 123 | this.tempAuth = undefined 124 | var callback = helpers.cb(args) 125 | client.GET(this, function(error, usergridResponse) { 126 | updateEntityFromRemote.call(this, usergridResponse) 127 | callback(error, usergridResponse, this) 128 | }.bind(this)) 129 | }, 130 | save: function() { 131 | var args = helpers.args(arguments) 132 | var client = helpers.client.validate(args) 133 | client.tempAuth = this.tempAuth 134 | this.tempAuth = undefined 135 | var callback = helpers.cb(args) 136 | client.PUT(this, function(error, usergridResponse) { 137 | updateEntityFromRemote.call(this, usergridResponse) 138 | callback(error, usergridResponse, this) 139 | }.bind(this)) 140 | }, 141 | remove: function() { 142 | var args = helpers.args(arguments) 143 | var client = helpers.client.validate(args) 144 | client.tempAuth = this.tempAuth 145 | this.tempAuth = undefined 146 | var callback = helpers.cb(args) 147 | client.DELETE(this, function(error, usergridResponse) { 148 | callback(error, usergridResponse, this) 149 | }.bind(this)) 150 | }, 151 | attachAsset: function(asset) { 152 | this.asset = asset 153 | }, 154 | uploadAsset: function() { 155 | var args = helpers.args(arguments) 156 | var client = helpers.client.validate(args) 157 | var callback = helpers.cb(args) 158 | client.POST(this, this.asset, function(error, usergridResponse) { 159 | updateEntityFromRemote.call(this, usergridResponse) 160 | callback(error, usergridResponse, this) 161 | }.bind(this)) 162 | }, 163 | downloadAsset: function() { 164 | var args = helpers.args(arguments) 165 | var client = helpers.client.validate(args) 166 | var callback = helpers.cb(args) 167 | var self = this 168 | 169 | if (_.has(self,'asset.contentType')) { 170 | var options = { 171 | client: client, 172 | entity: self, 173 | type: this.type, 174 | method: 'GET', 175 | encoding: null, 176 | headers: { 177 | "Accept": self.asset.contentType || _.first(args.filter(_.isString)) 178 | } 179 | } 180 | options.uri = helpers.build.uri(client, options) 181 | return new UsergridRequest(options, function(error, usergridResponse) { 182 | if (usergridResponse.ok) { 183 | self.attachAsset(new UsergridAsset(new Buffer(usergridResponse.body))) 184 | } 185 | callback(error, usergridResponse, self) 186 | }) 187 | } else { 188 | callback({ 189 | name: "asset_not_found", 190 | description: "The specified entity does not have a valid asset attached" 191 | }) 192 | } 193 | }, 194 | connect: function() { 195 | var args = helpers.args(arguments) 196 | var client = helpers.client.validate(args) 197 | client.tempAuth = this.tempAuth 198 | this.tempAuth = undefined 199 | args[0] = this 200 | return client.connect.apply(client, args) 201 | }, 202 | disconnect: function() { 203 | var args = helpers.args(arguments) 204 | var client = helpers.client.validate(args) 205 | client.tempAuth = this.tempAuth 206 | this.tempAuth = undefined 207 | args[0] = this 208 | return client.disconnect.apply(client, args) 209 | }, 210 | getConnections: function() { 211 | var args = helpers.args(arguments) 212 | var client = helpers.client.validate(args) 213 | client.tempAuth = this.tempAuth 214 | this.tempAuth = undefined 215 | args.shift() 216 | args.splice(1, 0, this) 217 | return client.getConnections.apply(client, args) 218 | }, 219 | usingAuth: function(auth) { 220 | this.tempAuth = helpers.client.configureTempAuth(auth) 221 | return this 222 | } 223 | } 224 | 225 | module.exports = UsergridEntity -------------------------------------------------------------------------------- /lib/query.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var helpers = require('../helpers'), 18 | util = require('util'), 19 | _ = require('lodash') 20 | 21 | var UsergridQuery = function(type) { 22 | 23 | var self = this 24 | 25 | var query = '', 26 | queryString, 27 | sort, 28 | __nextIsNot = false 29 | 30 | // builder pattern 31 | _.assign(self, { 32 | type: function(value) { 33 | self._type = value 34 | return self 35 | }, 36 | collection: function(value) { 37 | self._type = value 38 | return self 39 | }, 40 | limit: function(value) { 41 | self._limit = value 42 | return self 43 | }, 44 | cursor: function(value) { 45 | self._cursor = value 46 | return self 47 | }, 48 | eq: function(key, value) { 49 | query = self.andJoin(util.format('%s = %s', key, helpers.query.useQuotesIfRequired(value))) 50 | return self 51 | }, 52 | equal: this.eq, 53 | gt: function(key, value) { 54 | query = self.andJoin(util.format('%s > %s', key, helpers.query.useQuotesIfRequired(value))) 55 | return self 56 | }, 57 | greaterThan: this.gt, 58 | gte: function(key, value) { 59 | query = self.andJoin(util.format('%s >= %s', key, helpers.query.useQuotesIfRequired(value))) 60 | return self 61 | }, 62 | greaterThanOrEqual: this.gte, 63 | lt: function(key, value) { 64 | query = self.andJoin(util.format('%s < %s', key, helpers.query.useQuotesIfRequired(value))) 65 | return self 66 | }, 67 | lessThan: this.lt, 68 | lte: function(key, value) { 69 | query = self.andJoin(util.format('%s <= %s', key, helpers.query.useQuotesIfRequired(value))) 70 | return self 71 | }, 72 | lessThanOrEqual: this.lte, 73 | contains: function(key, value) { 74 | query = self.andJoin(util.format('%s contains %s', key, helpers.query.useQuotesIfRequired(value))) 75 | return self 76 | }, 77 | locationWithin: function(distanceInMeters, lat, lng) { 78 | query = self.andJoin(util.format('location within %s of %s, %s', distanceInMeters, lat, lng)) 79 | return self 80 | }, 81 | asc: function(key) { 82 | self.sort(key, 'asc') 83 | return self 84 | }, 85 | desc: function(key) { 86 | self.sort(key, 'desc') 87 | return self 88 | }, 89 | sort: function(key, order) { 90 | sort = (key && order) ? util.format(' order by %s %s', key, order) : '' 91 | return self 92 | }, 93 | fromString: function(string) { 94 | queryString = string 95 | return self 96 | }, 97 | andJoin: function(append) { 98 | if (__nextIsNot) { 99 | append = util.format("not %s", append) 100 | __nextIsNot = false 101 | } 102 | if (!append) { 103 | return query 104 | } else if (query.length === 0) { 105 | return append 106 | } else { 107 | return (_.endsWith(query, 'and') || _.endsWith(query, 'or')) ? util.format('%s %s', query, append) : util.format('%s and %s', query, append) 108 | } 109 | }, 110 | orJoin: function() { 111 | return (query.length > 0 && !_.endsWith(query, 'or')) ? util.format('%s or', query) : query 112 | } 113 | }) 114 | 115 | // required properties 116 | self._type = self._type || type 117 | 118 | // public accessors 119 | Object.defineProperty(self, '_ql', { 120 | get: function() { 121 | if (queryString !== undefined) { 122 | return queryString 123 | } else { 124 | return util.format('select * %s %s', ((query.length > 0) ? 'where ' + (query || '') : ''),((sort !== undefined) ? sort : '')).trim() 125 | } 126 | } 127 | }) 128 | 129 | Object.defineProperty(self, 'and', { 130 | get: function() { 131 | query = self.andJoin('') 132 | return self 133 | } 134 | }) 135 | 136 | Object.defineProperty(self, 'or', { 137 | get: function() { 138 | query = self.orJoin() 139 | return self 140 | } 141 | }) 142 | 143 | Object.defineProperty(self, 'not', { 144 | get: function() { 145 | __nextIsNot = true 146 | return self 147 | } 148 | }) 149 | 150 | return self 151 | } 152 | 153 | module.exports = UsergridQuery -------------------------------------------------------------------------------- /lib/request.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var request = require('request'), 18 | helpers = require('../helpers'), 19 | UsergridResponse = require('../lib/response'), 20 | _ = require('lodash') 21 | 22 | var UsergridRequest = function(options) { 23 | var client = helpers.client.validate(options.client) 24 | var callback = helpers.cb(helpers.args(arguments)) 25 | 26 | if (!_.isString(options.type) && !_.isString(options.path) && !_.isString(options.uri)) { 27 | throw new Error('one of "type" (collection name), "path", or "uri" parameters are required when initializing a UsergridRequest') 28 | } 29 | 30 | if (!_.includes(['GET', 'PUT', 'POST', 'DELETE'], options.method)) { 31 | throw new Error('"method" parameter is required when initializing a UsergridRequest') 32 | } 33 | 34 | var uri = options.uri || helpers.build.uri(client, options) 35 | var formData = helpers.build.formData(options) 36 | var body = ((formData !== undefined) ? undefined : options.body) 37 | 38 | request(uri, { 39 | headers: helpers.build.headers(client, options), 40 | body: body, 41 | encoding: options.encoding || null, 42 | json: true, 43 | method: options.method, 44 | qs: helpers.build.qs(options), 45 | formData: formData 46 | }, function(error, response) { 47 | var usergridResponse = new UsergridResponse(response) 48 | var returnBody = _.first([usergridResponse.user, usergridResponse.users, usergridResponse.entity, usergridResponse.entities, usergridResponse.body].filter(_.isObject)) 49 | callback(error || usergridResponse.error, usergridResponse, returnBody) 50 | }) 51 | } 52 | 53 | module.exports = UsergridRequest -------------------------------------------------------------------------------- /lib/response.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var UsergridQuery = require('./query'), 18 | UsergridResponseError = require('./responseError'), 19 | helpers = require('../helpers'), 20 | _ = require('lodash') 21 | 22 | var UsergridResponse = function(response) { 23 | var self = this 24 | self.ok = false 25 | if (!response) { 26 | return 27 | } else if (response.statusCode < 400) { 28 | self.ok = true 29 | var UsergridEntity = require('./entity.js'), 30 | UsergridUser = require('./user.js') 31 | 32 | _.assign(self, response) 33 | 34 | if (_.has(response,'body.entities')) { 35 | var entities = response.body.entities.map(function(en) { 36 | var entity = new UsergridEntity(en) 37 | if (entity.isUser) { 38 | entity = new UsergridUser(entity) 39 | } 40 | return entity 41 | }) 42 | _.assign(self, { 43 | metadata: _.cloneDeep(response.body), 44 | entities: entities 45 | }) 46 | delete self.metadata.entities 47 | self.first = _.first(entities) || undefined 48 | self.entity = self.first 49 | self.last = _.last(entities) || undefined 50 | if (_.get(self,'metadata.path') === '/users') { 51 | self.user = self.first 52 | self.users = self.entities 53 | } 54 | 55 | Object.defineProperty(self, 'hasNextPage', { 56 | get: function() { 57 | return _.has(self,'metadata.cursor') 58 | } 59 | }) 60 | 61 | helpers.setReadOnly(self.metadata) 62 | } 63 | } else { 64 | _.assign(self, response, { 65 | error: new UsergridResponseError(response.body) 66 | }) 67 | } 68 | return self; 69 | } 70 | 71 | UsergridResponse.prototype = { 72 | loadNextPage: function() { 73 | var args = helpers.args(arguments) 74 | var callback = helpers.cb(args) 75 | if (!this.metadata.cursor) { 76 | callback() 77 | } 78 | var client = helpers.client.validate(args) 79 | var type = _.last(_.get(this,'metadata.path').split('/')) 80 | var limit = _.first(_.get(this,'metadata.params.limit')) 81 | var ql = _.first(_.get(this,'metadata.params.ql')) 82 | var query = new UsergridQuery(type).fromString(ql).cursor(this.metadata.cursor).limit(limit) 83 | return client.GET(query, callback) 84 | } 85 | } 86 | 87 | module.exports = UsergridResponse -------------------------------------------------------------------------------- /lib/responseError.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var _ = require('lodash') 18 | 19 | var UsergridResponseError = function(responseErrorObject) { 20 | if (_.get(responseErrorObject,'error') === false) { 21 | return 22 | } 23 | this.name = responseErrorObject.error 24 | this.description = responseErrorObject.error_description || responseErrorObject.description 25 | this.exception = responseErrorObject.exception 26 | return this 27 | } 28 | 29 | module.exports = UsergridResponseError -------------------------------------------------------------------------------- /lib/user.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var UsergridEntity = require('./entity'), 18 | UsergridQuery = require('./query'), 19 | UsergridUserAuth = require('./userAuth'), 20 | UsergridRequest = require('./request'), 21 | UsergridClient = require('../lib/client'), 22 | helpers = require('../helpers'), 23 | util = require('util'), 24 | _ = require('lodash') 25 | 26 | var UsergridUser = function(obj) { 27 | 28 | if (!_.has(obj,'email') && !_.has(obj,'username')) { 29 | // This is not a user entity 30 | throw new Error('"email" or "username" property is required when initializing a UsergridUser object') 31 | } 32 | 33 | var self = this 34 | self.type = "user" 35 | 36 | _.assign(self, obj, UsergridEntity) 37 | UsergridEntity.call(self, self) 38 | 39 | helpers.setWritable(self, 'name') 40 | return self 41 | } 42 | 43 | var CheckAvailable = function() { 44 | var self = this 45 | var args = helpers.args(arguments) 46 | var client = helpers.client.validate(args) 47 | if (args[0] instanceof UsergridClient) { 48 | args.shift() 49 | } 50 | var callback = helpers.cb(args) 51 | var checkQuery 52 | 53 | if (args[0].username && args[0].email) { 54 | checkQuery = new UsergridQuery('users').eq('username', args[0].username).or.eq('email', args[0].email) 55 | } else if (args[0].username) { 56 | checkQuery = new UsergridQuery('users').eq('username', args[0].username) 57 | } else if (args[0].email) { 58 | checkQuery = new UsergridQuery('users').eq('email', args[0].email) 59 | } else { 60 | throw new Error("'username' or 'email' property is required when checking for available users") 61 | } 62 | 63 | client.GET(checkQuery, function(error, usergridResponse) { 64 | callback(error, usergridResponse, (usergridResponse.entities.length > 0)) 65 | }.bind(self)) 66 | } 67 | 68 | UsergridUser.prototype = { 69 | create: function() { 70 | var self = this 71 | var args = helpers.args(arguments) 72 | var client = helpers.client.validate(args) 73 | var callback = helpers.cb(args) 74 | client.POST(self, function(error, usergridResponse) { 75 | delete self.password 76 | _.assign(self, usergridResponse.user) 77 | callback(error, usergridResponse, usergridResponse.user) 78 | }.bind(self)) 79 | }, 80 | login: function() { 81 | var self = this 82 | var args = helpers.args(arguments) 83 | var callback = helpers.cb(args) 84 | return new UsergridRequest({ 85 | client: helpers.client.validate(args), 86 | path: 'token', 87 | method: 'POST', 88 | body: helpers.build.userLoginBody(self) 89 | }, function(error, usergridResponse, body) { 90 | delete self.password 91 | if (usergridResponse.ok) { 92 | self.auth = new UsergridUserAuth(body.user) 93 | self.auth.token = body.access_token 94 | self.auth.expiry = helpers.time.expiry(body.expires_in) 95 | self.auth.tokenTtl = body.expires_in 96 | } 97 | callback(error, usergridResponse, body.access_token) 98 | }) 99 | }, 100 | logout: function() { 101 | var self = this 102 | var args = helpers.args(arguments) 103 | var callback = helpers.cb(args) 104 | if (!self.auth || !self.auth.isValid) { 105 | return callback({ 106 | name: 'no_valid_token', 107 | description: 'this user does not have a valid token' 108 | }) 109 | } 110 | 111 | var revokeAll = _.first(args.filter(_.isBoolean)) || false 112 | 113 | return new UsergridRequest({ 114 | client: helpers.client.validate(args), 115 | path: util.format("users/%s/revoketoken%s", helpers.user.uniqueId(self), (revokeAll) ? "s" : ""), 116 | method: 'PUT', 117 | qs: (!revokeAll) ? { 118 | token: self.auth.token 119 | } : undefined 120 | }, function(error, usergridResponse) { 121 | self.auth.destroy() 122 | callback(error, usergridResponse, usergridResponse.ok) 123 | }) 124 | }, 125 | logoutAllSessions: function() { 126 | var args = helpers.args(arguments) 127 | args = _.concat([helpers.client.validate(args), true], args) 128 | return this.logout.apply(this, args) 129 | }, 130 | resetPassword: function() { 131 | var self = this 132 | var args = helpers.args(arguments) 133 | var callback = helpers.cb(args) 134 | var client = helpers.client.validate(args) 135 | if (args[0] instanceof UsergridClient) { 136 | args.shift() 137 | } 138 | var body = { 139 | oldpassword: _.isPlainObject(args[0]) ? args[0].oldPassword : _.isString(args[0]) ? args[0] : undefined, 140 | newpassword: _.isPlainObject(args[0]) ? args[0].newPassword : _.isString(args[1]) ? args[1] : undefined 141 | } 142 | if (!body.oldpassword || !body.newpassword) { 143 | throw new Error('"oldPassword" and "newPassword" properties are required when resetting a user password') 144 | } 145 | return new UsergridRequest({ 146 | client: client, 147 | path: util.format('users/%s/password', helpers.user.uniqueId(self)), 148 | method: 'PUT', 149 | body: body 150 | }, function(error, usergridResponse) { 151 | callback(error, usergridResponse, usergridResponse.ok) 152 | }) 153 | } 154 | } 155 | 156 | util.inherits(UsergridUser, UsergridEntity) 157 | 158 | module.exports = UsergridUser 159 | module.exports.CheckAvailable = CheckAvailable -------------------------------------------------------------------------------- /lib/userAuth.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var UsergridAuth = require('./auth'), 18 | helpers = require('../helpers'), 19 | util = require('util'), 20 | _ = require('lodash') 21 | 22 | var UsergridUserAuth = function(options) { 23 | var self = this 24 | var args = _.flattenDeep(helpers.args(arguments)) 25 | if (_.isPlainObject(args[0])) { 26 | options = args[0] 27 | } 28 | self.username = options.username || args[0] 29 | self.email = options.email 30 | if (options.password || args[1]) { 31 | self.password = options.password || args[1] 32 | } 33 | self.tokenTtl = options.tokenTtl || args[2] 34 | UsergridAuth.call(self) 35 | _.assign(self, UsergridAuth) 36 | return self 37 | } 38 | 39 | util.inherits(UsergridUserAuth, UsergridAuth) 40 | 41 | module.exports = UsergridUserAuth -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Brandon Shelley", 3 | "contributors": [{ 4 | "name": "Brandon Shelley", 5 | "email": "brandon@codeblooded.io" 6 | }, { 7 | "name": "Robert Walsh", 8 | "email": "rjwalsh1985@gmail.com" 9 | }], 10 | "dependencies": { 11 | "async": "latest", 12 | "file": "latest", 13 | "file-type": "^3.4.0", 14 | "lodash": "~4.0", 15 | "lodash-inflection": "latest", 16 | "lodash-uuid": "latest", 17 | "read-chunk": "^1.0.1", 18 | "request": "latest", 19 | "url-join": "latest", 20 | "validator": "^4.5.0" 21 | }, 22 | "description": "The official Node.js SDK for Usergrid", 23 | "devDependencies": { 24 | "chance": "^0.8.0", 25 | "mocha": "latest", 26 | "should": "latest" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git://github.com/brandonscript/usergrid-nodejs.git" 31 | }, 32 | "keywords": [], 33 | "license": "Apache 2.0", 34 | "main": "usergrid.js", 35 | "name": "usergrid", 36 | "private": false, 37 | "scripts": { 38 | "start": "node usergrid.js", 39 | "test": "mocha tests" 40 | }, 41 | "version": "2.0.0-rc.2" 42 | } 43 | -------------------------------------------------------------------------------- /tests/config.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.0": { 3 | "orgId": "rwalsh", 4 | "appId": "nodejs", 5 | "baseUrl": "https://api.usergrid.com", 6 | "clientId": "YXA68wzo4KbAEea4p6tiytxmag", 7 | "clientSecret": "YXA671McJDKY_C3oU1HpdbJBqGMf_Y0", 8 | "test": { 9 | "collection": "nodejs", 10 | "email": "authtest@test.com", 11 | "password": "P@ssw0rd", 12 | "username": "authtest" 13 | } 14 | }, 15 | "2.1": { 16 | "appId": "sdksandbox", 17 | "baseUrl": "https://api-connectors-prod.apigee.net/appservices", 18 | "clientId": "YXA6WMhAuFJTEeWoggrRE9kXrQ", 19 | "clientSecret": "YXA6zZbat7PKgOlN73rpByc36LWaUhw", 20 | "orgId": "api-connectors", 21 | "test": { 22 | "collection": "nodejs", 23 | "email": "authtest@test.com", 24 | "password": "P@ssw0rd", 25 | "username": "authtest" 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /tests/lib/asset.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var config = require('../../helpers').config, 18 | UsergridEntity = require('../../lib/entity'), 19 | UsergridAsset = require('../../lib/asset'), 20 | UsergridClient = require('../../lib/client'), 21 | util = require('util'), 22 | fs = require('fs') 23 | 24 | var _slow = 6000, 25 | _timeout = 12000, 26 | filename = 'old_man', 27 | file = __dirname + '/image.jpg', 28 | testFile = __dirname + '/image_test.jpg', 29 | expectedContentLength = 109055 30 | 31 | describe('init from fs.readFile()', function() { 32 | var asset = new UsergridAsset(filename, file) 33 | 34 | before(function(done) { 35 | fs.readFile(file, function(err, data) { 36 | asset.data = data 37 | done() 38 | }) 39 | }) 40 | 41 | it('asset.data should be a binary Buffer', function() { 42 | asset.data.should.be.a.buffer() 43 | }) 44 | 45 | it('asset.contentType should be inferred from Buffer', function() { 46 | asset.contentType.should.equal('image/jpeg') 47 | }) 48 | 49 | it(util.format('asset.contentLength should be %s bytes', expectedContentLength), function() { 50 | asset.contentLength.should.equal(expectedContentLength) 51 | }) 52 | }) 53 | 54 | describe('init from piped writable stream', function() { 55 | var asset = new UsergridAsset(filename, file) 56 | var writeTestAsset = new UsergridAsset('image_test', testFile) 57 | before(function(done) { 58 | var stream = fs.createReadStream(file).pipe(asset), 59 | writeTest 60 | stream.on('finish', function() { 61 | fs.writeFile(testFile, asset.data) 62 | writeTest = fs.createReadStream(file).pipe(writeTestAsset) 63 | writeTest.on('finish', function() { 64 | done() 65 | }) 66 | }) 67 | }) 68 | 69 | it('asset.data should be a binary Buffer', function() { 70 | asset.data.should.be.a.buffer() 71 | }) 72 | 73 | it('asset.contentType should be inferred from Buffer', function() { 74 | asset.contentType.should.equal('image/jpeg') 75 | }) 76 | 77 | it(util.format('asset.contentLength should be %s bytes', expectedContentLength), function() { 78 | asset.contentLength.should.equal(expectedContentLength) 79 | }) 80 | 81 | it('should write an identical asset to the filesystem', function() { 82 | writeTestAsset.contentType.should.equal('image/jpeg') 83 | writeTestAsset.contentLength.should.equal(expectedContentLength) 84 | }) 85 | }) 86 | 87 | describe('upload via client.POST to a specific entity', function() { 88 | 89 | this.slow(_slow) 90 | this.timeout(_timeout) 91 | 92 | var client = new UsergridClient() 93 | it('should upload a binary asset and create a new entity', function(done) { 94 | var asset = new UsergridAsset(filename, file) 95 | fs.createReadStream(file).pipe(asset).on('finish', function() { 96 | client.POST(config.test.collection, asset, function(err, assetResponse, entityWithAsset) { 97 | assetResponse.statusCode.should.equal(200) 98 | entityWithAsset.should.have.property('file-metadata') 99 | entityWithAsset['file-metadata'].should.have.property('content-type').equal('image/jpeg') 100 | entityWithAsset['file-metadata'].should.have.property('content-length').equal(expectedContentLength) 101 | entityWithAsset.remove(client) 102 | done() 103 | }) 104 | }) 105 | }) 106 | }) 107 | 108 | describe('upload via client.PUT to a specific entity', function() { 109 | 110 | this.slow(_slow) 111 | this.timeout(_timeout) 112 | 113 | var client = new UsergridClient() 114 | it('should upload a binary asset to an existing entity', function(done) { 115 | var entity = new UsergridEntity({ 116 | type: config.test.collection, 117 | name: "AssetTestPUT" 118 | }) 119 | var asset = new UsergridAsset(filename, file) 120 | client.PUT(entity, function(err, entityResponse, createdEntity) { 121 | fs.createReadStream(file).pipe(asset).on('finish', function() { 122 | client.PUT(createdEntity, asset, function(err, assetResponse, entityWithAsset) { 123 | assetResponse.statusCode.should.equal(200) 124 | entityWithAsset.should.have.property('file-metadata') 125 | entityWithAsset['file-metadata'].should.have.property('content-type').equal('image/jpeg') 126 | entityWithAsset['file-metadata'].should.have.property('content-length').equal(expectedContentLength) 127 | done() 128 | }) 129 | }) 130 | }) 131 | }) 132 | }) -------------------------------------------------------------------------------- /tests/lib/client.auth.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var should = require('should'), 18 | chance = new require('chance').Chance(), 19 | util = require('util'), 20 | config = require('../../helpers').config, 21 | UsergridClient = require('../../lib/client'), 22 | UsergridAuth = require('../../lib/auth'), 23 | UsergridAppAuth = require('../../lib/appAuth'), 24 | UsergridUserAuth = require('../../lib/userAuth'), 25 | UsergridUser = require('../../lib/user') 26 | 27 | var _slow = 500, 28 | _timeout = 4000 29 | 30 | describe('authMode', function() { 31 | 32 | this.slow(_slow) 33 | this.timeout(_timeout) 34 | 35 | var token, client = new UsergridClient() 36 | before(function(done) { 37 | // authenticate app and remove sandbox permissions 38 | client.setAppAuth(config.clientId, config.clientSecret) 39 | client.authenticateApp(function(e, r, t) { 40 | token = t 41 | client.usingAuth(client.appAuth).DELETE('roles/guest/permissions', { 42 | permission: "get,post,put,delete:/**" 43 | }, function() { 44 | done() 45 | }) 46 | }) 47 | }) 48 | 49 | it('should fall back to using no authentication when currentUser is not authenticated and authMode is set to NONE', function(done) { 50 | client.authMode = UsergridAuth.AUTH_MODE_NONE 51 | client.GET('users', function(error, usergridResponse) { 52 | should(client.currentUser).be.undefined() 53 | usergridResponse.request.headers.should.not.have.property('authorization') 54 | error.name.should.equal('unauthorized') 55 | usergridResponse.ok.should.be.false() 56 | done() 57 | }) 58 | }) 59 | 60 | it('should fall back to using the app token when currentUser is not authenticated and authMode is set to APP', function(done) { 61 | client.authMode = UsergridAuth.AUTH_MODE_APP 62 | client.GET('users', function(error, usergridResponse, user) { 63 | should(client.currentUser).be.undefined() 64 | usergridResponse.request.headers.should.have.property('authorization').equal(util.format('Bearer %s', token)) 65 | usergridResponse.ok.should.be.true() 66 | user.should.be.an.instanceof(UsergridUser) 67 | done() 68 | }) 69 | }) 70 | 71 | after(function(done) { 72 | // re-add sandbox permissions 73 | client.authMode = UsergridAuth.AUTH_MODE_NONE 74 | client.usingAuth(client.appAuth).POST('roles/guest/permissions', { 75 | permission: "get,post,put,delete:/**" 76 | }, function() { 77 | done() 78 | }) 79 | }) 80 | }) 81 | 82 | describe('authenticateApp()', function() { 83 | 84 | this.slow(_slow) 85 | this.timeout(_timeout) 86 | 87 | var response, token, client = new UsergridClient() 88 | before(function(done) { 89 | client.setAppAuth(config.clientId, config.clientSecret) 90 | client.authenticateApp(function(err, r, t) { 91 | response = r 92 | token = t 93 | done() 94 | }) 95 | }) 96 | 97 | it('response.ok should be true', function() { 98 | response.ok.should.be.true() 99 | }) 100 | 101 | it('should have a valid token', function() { 102 | token.should.be.a.String() 103 | token.length.should.be.greaterThan(10) 104 | }) 105 | 106 | it('client.appAuth.token should be set to the token returned from Usergrid', function() { 107 | client.appAuth.should.have.property('token').equal(token) 108 | }) 109 | 110 | it('client.appAuth.isValid should be true', function() { 111 | client.appAuth.should.have.property('isValid').which.is.true() 112 | }) 113 | 114 | it('client.appAuth.expiry should be set to a future date', function() { 115 | client.appAuth.should.have.property('expiry').greaterThan(Date.now()) 116 | }) 117 | 118 | it('should fail when called without a clientId and clientSecret', function() { 119 | should(function() { 120 | var client = new UsergridClient() 121 | client.setAppAuth(undefined, undefined, 0) 122 | client.authenticateApp() 123 | }).throw() 124 | }) 125 | 126 | it('should authenticate by passing clientId and clientSecret in an object', function(done) { 127 | var isolatedClient = new UsergridClient() 128 | isolatedClient.authenticateApp(config, function(err, reponse, token) { 129 | isolatedClient.appAuth.should.have.property('token').equal(token) 130 | done() 131 | }) 132 | }) 133 | 134 | it('should authenticate by passing a UsergridAppAuth instance with a custom ttl', function(done) { 135 | var isolatedClient = new UsergridClient() 136 | var ttlInMilliseconds = 500000 137 | var appAuth = new UsergridAppAuth(config.clientId, config.clientSecret, ttlInMilliseconds) 138 | isolatedClient.authenticateApp(appAuth, function(err, response, token) { 139 | isolatedClient.appAuth.should.have.property('token').equal(token) 140 | response.body.expires_in.should.equal(ttlInMilliseconds / 1000) 141 | done() 142 | }) 143 | }) 144 | 145 | it('should not set client.appAuth when authenticating with a bad clientId and clientSecret in an object', function(done) { 146 | var failClient = new UsergridClient() 147 | failClient.appAuth = undefined 148 | failClient.authenticateApp(new UsergridAppAuth('BADCLIENTID', 'BADCLIENTSECRET'), function(e, r, token) { 149 | e.should.containDeep({ 150 | name: 'invalid_grant', 151 | description: 'invalid username or password' 152 | }) 153 | should(token).be.undefined() 154 | should(failClient.appAuth).be.undefined() 155 | done() 156 | }) 157 | }) 158 | 159 | it('should not set client.appAuth when authenticating with a bad UsergridAppAuth instance (using an object)', function(done) { 160 | var failClient = new UsergridClient() 161 | failClient.appAuth = undefined 162 | failClient.authenticateApp(new UsergridAppAuth('BADCLIENTID', 'BADCLIENTSECRET'), function(e, r, token) { 163 | e.should.containDeep({ 164 | name: 'invalid_grant', 165 | description: 'invalid username or password' 166 | }) 167 | should(token).be.undefined() 168 | should(failClient.appAuth).be.undefined() 169 | done() 170 | }) 171 | }) 172 | 173 | 174 | it('should not set client.appAuth when authenticating with a bad UsergridAppAuth instance (using arguments)', function(done) { 175 | var failClient = new UsergridClient() 176 | failClient.appAuth = undefined 177 | failClient.authenticateApp(new UsergridAppAuth('BADCLIENTID', 'BADCLIENTSECRET'), function(e, r, token) { 178 | e.should.containDeep({ 179 | name: 'invalid_grant', 180 | description: 'invalid username or password' 181 | }) 182 | should(token).be.undefined() 183 | should(failClient.appAuth).be.undefined() 184 | done() 185 | }) 186 | }) 187 | }) 188 | 189 | describe('authenticateUser()', function() { 190 | 191 | this.slow(_slow) 192 | this.timeout(_timeout) 193 | 194 | var response, token, email = util.format("%s@%s.com", chance.word(), chance.word()), 195 | client = new UsergridClient() 196 | before(function(done) { 197 | client.authenticateUser({ 198 | username: config.test.username, 199 | password: config.test.password, 200 | email: email 201 | }, function(err, r, t) { 202 | response = r 203 | token = t 204 | done() 205 | }) 206 | }) 207 | 208 | it('should fail when called without a email (or username) and password', function() { 209 | should(function() { 210 | var badClient = new UsergridClient() 211 | badClient.authenticateUser({}) 212 | }).throw() 213 | }) 214 | 215 | it('response.ok should be true', function() { 216 | response.ok.should.be.true() 217 | }) 218 | 219 | it('should have a valid token', function() { 220 | token.should.be.a.String() 221 | token.length.should.be.greaterThan(10) 222 | }) 223 | 224 | it('client.currentUser.auth.token should be set to the token returned from Usergrid', function() { 225 | client.currentUser.auth.should.have.property('token').equal(token) 226 | }) 227 | 228 | it('client.currentUser.auth.isValid should be true', function() { 229 | client.currentUser.auth.should.have.property('isValid').which.is.true() 230 | }) 231 | 232 | it('client.currentUser.auth.expiry should be set to a future date', function() { 233 | client.currentUser.auth.should.have.property('expiry').greaterThan(Date.now()) 234 | }) 235 | 236 | it('client.currentUser should have a username and email', function() { 237 | client.currentUser.should.have.property('username') 238 | client.currentUser.should.have.property('email').equal(email) 239 | }) 240 | 241 | it('client.currentUser and client.currentUser.auth should not store password', function() { 242 | client.currentUser.should.not.have.property('password') 243 | client.currentUser.auth.should.not.have.property('password') 244 | }) 245 | 246 | it('should support an optional bool to not set as current user', function(done) { 247 | var noCurrentUserClient = new UsergridClient() 248 | noCurrentUserClient.authenticateUser({ 249 | username: config.test.username, 250 | password: config.test.password, 251 | email: email 252 | }, false, function() { 253 | should(noCurrentUserClient.currentUser).be.undefined() 254 | done() 255 | }) 256 | }) 257 | 258 | it('should support passing a UsergridUserAuth instance with a custom ttl', function(done) { 259 | var newClient = new UsergridClient() 260 | var ttlInMilliseconds = 500000 261 | var userAuth = new UsergridUserAuth(config.test.username, config.test.password, ttlInMilliseconds) 262 | newClient.authenticateUser(userAuth, function(err, usergridResponse, token) { 263 | usergridResponse.ok.should.be.true() 264 | newClient.currentUser.auth.token.should.equal(token) 265 | usergridResponse.body.expires_in.should.equal(ttlInMilliseconds / 1000) 266 | done() 267 | }) 268 | }) 269 | }) 270 | 271 | describe('appAuth, setAppAuth()', function() { 272 | it('should initialize by passing a list of arguments', function() { 273 | var client = new UsergridClient() 274 | client.setAppAuth(config.clientId, config.clientSecret, config.tokenTtl) 275 | client.appAuth.should.be.instanceof(UsergridAppAuth) 276 | }) 277 | 278 | it('should be a subclass of UsergridAuth', function() { 279 | var client = new UsergridClient() 280 | client.setAppAuth(config.clientId, config.clientSecret, config.tokenTtl) 281 | client.appAuth.should.be.instanceof(UsergridAuth) 282 | }) 283 | 284 | it('should initialize by passing an object', function() { 285 | var client = new UsergridClient() 286 | client.setAppAuth({ 287 | clientId: config.clientId, 288 | clientSecret: config.clientSecret, 289 | tokenTtl: config.tokenTtl 290 | }) 291 | client.appAuth.should.be.instanceof(UsergridAppAuth) 292 | }) 293 | 294 | it('should initialize by passing an instance of UsergridAppAuth', function() { 295 | var client = new UsergridClient() 296 | client.setAppAuth(new UsergridAppAuth(config.clientId, config.clientSecret, config.tokenTtl)) 297 | client.appAuth.should.be.instanceof(UsergridAppAuth) 298 | }) 299 | 300 | it('should initialize by setting to an instance of UsergridAppAuth', function() { 301 | var client = new UsergridClient() 302 | client.appAuth = new UsergridAppAuth(config.clientId, config.clientSecret, config.tokenTtl) 303 | client.appAuth.should.be.instanceof(UsergridAppAuth) 304 | }) 305 | }) 306 | 307 | describe('usingAuth()', function() { 308 | 309 | this.slow(_slow + 500) 310 | this.timeout(_timeout) 311 | 312 | var client = new UsergridClient(), 313 | authFromToken 314 | 315 | before(function(done) { 316 | client.authenticateUser({ 317 | username: config.test.username, 318 | password: config.test.password 319 | }, function(error, response, token) { 320 | authFromToken = new UsergridAuth(token) 321 | done() 322 | }) 323 | }) 324 | 325 | it('should authenticate using an ad-hoc token', function(done) { 326 | authFromToken.isValid.should.be.true() 327 | authFromToken.should.have.property('token') 328 | client.usingAuth(authFromToken).GET({ 329 | path: '/users/me' 330 | }, function(error, usergridResponse) { 331 | usergridResponse.ok.should.be.true() 332 | usergridResponse.should.have.property('user').which.is.an.instanceof(UsergridUser) 333 | usergridResponse.user.should.have.property('uuid').which.is.a.uuid() 334 | done() 335 | }) 336 | }) 337 | 338 | it('client.tempAuth should be destroyed after making a request with ad-hoc authentication', function(done) { 339 | should(client.tempAuth).be.undefined() 340 | done() 341 | }) 342 | 343 | it('should send an unauthenticated request when UsergridAuth.NO_AUTH is passed to .usingAuth()', function(done) { 344 | client.usingAuth(UsergridAuth.NO_AUTH).GET({ 345 | path: '/users/me' 346 | }, function(error, usergridResponse) { 347 | usergridResponse.ok.should.be.false() 348 | usergridResponse.request.headers.should.not.have.property('authentication') 349 | usergridResponse.should.not.have.property('user') 350 | done() 351 | }) 352 | }) 353 | 354 | it('should send an unauthenticated request when no arguments are passed to .usingAuth()', function(done) { 355 | client.usingAuth().GET({ 356 | path: '/users/me' 357 | }, function(error, usergridResponse) { 358 | usergridResponse.ok.should.be.false() 359 | usergridResponse.request.headers.should.not.have.property('authentication') 360 | usergridResponse.should.not.have.property('user') 361 | done() 362 | }) 363 | }) 364 | }) -------------------------------------------------------------------------------- /tests/lib/client.connections.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var should = require('should'), 18 | urljoin = require('url-join'), 19 | config = require('../../helpers').config, 20 | UsergridClient = require('../../lib/client'), 21 | UsergridQuery = require('../../lib/query') 22 | 23 | var _slow = 500, 24 | _timeout = 4000 25 | 26 | describe('connect()', function() { 27 | 28 | this.slow(_slow + 1000) 29 | this.timeout(_timeout + 4000) 30 | 31 | var response, 32 | entity1, 33 | entity2, 34 | client = new UsergridClient(), 35 | query = new UsergridQuery(config.test.collection).eq('name', 'testClientConnectOne').or.eq('name', 'testClientConnectTwo').asc('name') 36 | 37 | before(function(done) { 38 | // Create the entities we're going to use for connections 39 | client.POST(config.test.collection, [{ 40 | "name": "testClientConnectOne" 41 | }, { 42 | "name": "testClientConnectTwo" 43 | }], function() { 44 | client.GET(query, function(err, usergridResponse) { 45 | response = usergridResponse 46 | entity1 = response.first 47 | entity2 = response.last 48 | done() 49 | }) 50 | }) 51 | }) 52 | 53 | it('should connect entities by passing UsergridEntity objects as parameters', function(done) { 54 | var relationship = "foos" 55 | 56 | client.connect(entity1, relationship, entity2, function(err, usergridResponse) { 57 | usergridResponse.ok.should.be.true() 58 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 59 | usergridResponse.first.metadata.connecting[relationship].should.equal(urljoin( 60 | "", 61 | config.test.collection, 62 | entity1.uuid, 63 | relationship, 64 | entity2.uuid, 65 | "connecting", 66 | relationship 67 | )) 68 | done() 69 | }) 70 | }) 71 | }) 72 | 73 | it('should connect entities by passing a source UsergridEntity object and a target uuid', function(done) { 74 | var relationship = "bars" 75 | 76 | client.connect(entity1, relationship, entity2.uuid, function(err, usergridResponse) { 77 | usergridResponse.ok.should.be.true() 78 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 79 | usergridResponse.first.metadata.connecting[relationship].should.equal(urljoin( 80 | "", 81 | config.test.collection, 82 | entity1.uuid, 83 | relationship, 84 | entity2.uuid, 85 | "connecting", 86 | relationship 87 | )) 88 | done() 89 | }) 90 | }) 91 | }) 92 | 93 | it('should connect entities by passing source type, source uuid, and target uuid as parameters', function(done) { 94 | var relationship = "bazzes" 95 | 96 | client.connect(entity1.type, entity1.uuid, relationship, entity2.uuid, function(err, usergridResponse) { 97 | usergridResponse.ok.should.be.true() 98 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 99 | usergridResponse.first.metadata.connecting[relationship].should.equal(urljoin( 100 | "", 101 | config.test.collection, 102 | entity1.uuid, 103 | relationship, 104 | entity2.uuid, 105 | "connecting", 106 | relationship 107 | )) 108 | done() 109 | }) 110 | }) 111 | }) 112 | 113 | it('should connect entities by passing source type, source name, target type, and target name as parameters', function(done) { 114 | var relationship = "quxes" 115 | 116 | client.connect(entity1.type, entity1.name, relationship, entity2.type, entity2.name, function(err, usergridResponse) { 117 | usergridResponse.ok.should.be.true() 118 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 119 | usergridResponse.first.metadata.connecting[relationship].should.equal(urljoin( 120 | "", 121 | config.test.collection, 122 | entity1.uuid, 123 | relationship, 124 | entity2.uuid, 125 | "connecting", 126 | relationship 127 | )) 128 | done() 129 | }) 130 | }) 131 | }) 132 | 133 | it('should connect entities by passing a preconfigured options object', function(done) { 134 | var options = { 135 | entity: entity1, 136 | relationship: "quuxes", 137 | to: entity2 138 | } 139 | 140 | client.connect(options, function(err, usergridResponse) { 141 | usergridResponse.ok.should.be.true() 142 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, options.relationship, function(err, usergridResponse) { 143 | usergridResponse.first.metadata.connecting[options.relationship].should.equal(urljoin( 144 | "", 145 | config.test.collection, 146 | entity1.uuid, 147 | options.relationship, 148 | entity2.uuid, 149 | "connecting", 150 | options.relationship 151 | )) 152 | done() 153 | }) 154 | }) 155 | }) 156 | 157 | it('should fail to connect entities when specifying target name without type', function() { 158 | should(function() { 159 | client.connect(entity1.type, entity1.name, "fails", 'badName', function() {}) 160 | }).throw() 161 | }) 162 | }) 163 | 164 | describe('getConnections()', function() { 165 | 166 | this.slow(_slow + 1000) 167 | this.timeout(_timeout + 4000) 168 | 169 | var response, 170 | client = new UsergridClient(), 171 | query = new UsergridQuery(config.test.collection).eq('name', 'testClientConnectOne').or.eq('name', 'testClientConnectTwo').asc('name') 172 | 173 | before(function(done) { 174 | client.GET(query, function(err, usergridResponse) { 175 | response = usergridResponse 176 | done() 177 | }) 178 | }) 179 | 180 | it('should get an entity\'s outbound connections', function(done) { 181 | var entity1 = response.first 182 | var entity2 = response.last 183 | 184 | var relationship = "foos" 185 | 186 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 187 | usergridResponse.first.metadata.connecting[relationship].should.equal(urljoin( 188 | "", 189 | config.test.collection, 190 | entity1.uuid, 191 | relationship, 192 | entity2.uuid, 193 | "connecting", 194 | relationship 195 | )) 196 | done() 197 | }) 198 | }) 199 | 200 | it('should get an entity\'s inbound connections', function(done) { 201 | var entity1 = response.first 202 | var entity2 = response.last 203 | 204 | var relationship = "foos" 205 | 206 | client.getConnections(UsergridClient.Connections.DIRECTION_IN, entity2, relationship, function(err, usergridResponse) { 207 | usergridResponse.first.metadata.connections[relationship].should.equal(urljoin( 208 | "", 209 | config.test.collection, 210 | entity2.uuid, 211 | "connecting", 212 | entity1.uuid, 213 | relationship 214 | )) 215 | done() 216 | }) 217 | }) 218 | }) 219 | 220 | describe('disconnect()', function() { 221 | 222 | this.slow(_slow + 1000) 223 | this.timeout(_timeout + 4000) 224 | 225 | var response, 226 | client = new UsergridClient(), 227 | query = new UsergridQuery(config.test.collection).eq('name', 'testClientConnectOne').or.eq('name', 'testClientConnectTwo').asc('name') 228 | 229 | before(function(done) { 230 | client.GET(query, function(err, usergridResponse) { 231 | response = usergridResponse 232 | done() 233 | }) 234 | }) 235 | 236 | it('should disconnect entities by passing UsergridEntity objects as parameters', function(done) { 237 | var entity1 = response.first 238 | var entity2 = response.last 239 | 240 | var relationship = "foos" 241 | 242 | client.disconnect(entity1, relationship, entity2, function(err, usergridResponse) { 243 | usergridResponse.ok.should.be.true() 244 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 245 | usergridResponse.entities.should.be.an.Array().with.lengthOf(0) 246 | done() 247 | }) 248 | }) 249 | }) 250 | 251 | it('should disconnect entities by passing source type, source uuid, and target uuid as parameters', function(done) { 252 | var entity1 = response.first 253 | var entity2 = response.last 254 | 255 | var relationship = "bars" 256 | 257 | client.disconnect(entity1.type, entity1.uuid, relationship, entity2.uuid, function(err, usergridResponse) { 258 | usergridResponse.ok.should.be.true() 259 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 260 | usergridResponse.entities.should.be.an.Array().with.lengthOf(0) 261 | done() 262 | }) 263 | }) 264 | }) 265 | 266 | it('should disconnect entities by passing source type, source name, target type, and target name as parameters', function(done) { 267 | var entity1 = response.first 268 | var entity2 = response.last 269 | 270 | var relationship = "bazzes" 271 | 272 | client.disconnect(entity1.type, entity1.name, relationship, entity2.type, entity2.name, function(err, usergridResponse) { 273 | usergridResponse.ok.should.be.true() 274 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 275 | usergridResponse.entities.should.be.an.Array().with.lengthOf(0) 276 | done() 277 | }) 278 | }) 279 | }) 280 | 281 | it('should disconnect entities by passing a preconfigured options object', function(done) { 282 | var entity1 = response.first 283 | var entity2 = response.last 284 | 285 | var options = { 286 | entity: entity1, 287 | relationship: "quxes", 288 | to: entity2 289 | } 290 | 291 | client.disconnect(options, function(err, usergridResponse) { 292 | usergridResponse.ok.should.be.true() 293 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, options.relationship, function(err, usergridResponse) { 294 | usergridResponse.entities.should.be.an.Array().with.lengthOf(0) 295 | done() 296 | }) 297 | }) 298 | }) 299 | 300 | it('should fail to disconnect entities when specifying target name without type', function() { 301 | var entity1 = response.first 302 | var entity2 = response.last 303 | 304 | should(function() { 305 | client.disconnect(entity1.type, entity1.name, "fails", entity2.name, function() {}) 306 | }).throw() 307 | }) 308 | }) -------------------------------------------------------------------------------- /tests/lib/client.init.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var should = require('should'), 18 | config = require('../../helpers').config, 19 | UsergridClient = require('../../lib/client') 20 | 21 | describe('initialization', function() { 22 | it('should fail to initialize without an orgId and appId', function() { 23 | should(function() { 24 | var client = new UsergridClient(null, null) 25 | client.GET() 26 | }).throw() 27 | }) 28 | 29 | it('should initialize using properties defined in config.json', function() { 30 | var client = new UsergridClient() 31 | client.should.be.an.instanceof(UsergridClient).with.property('orgId').equal(config.orgId) 32 | client.should.have.property('appId').equal(config.appId) 33 | }) 34 | 35 | it('should initialize when passing orgId and appId as arguments, taking precedence over config', function() { 36 | var client = new UsergridClient('foo', 'bar') 37 | client.should.be.an.instanceof(UsergridClient).with.property('orgId').equal('foo') 38 | client.should.have.property('appId').equal('bar') 39 | }) 40 | 41 | it('should initialize when passing an object containing orgId and appId, taking precedence over config', function() { 42 | var client = new UsergridClient({ 43 | orgId: 'foo', 44 | appId: 'bar', 45 | baseUrl: 'https://sdk-example-test.apigee.net/appservices' 46 | }) 47 | client.should.be.an.instanceof(UsergridClient).with.property('orgId').equal('foo') 48 | client.should.have.property('appId').equal('bar') 49 | client.should.have.property('baseUrl').equal('https://sdk-example-test.apigee.net/appservices') 50 | }) 51 | }) -------------------------------------------------------------------------------- /tests/lib/client.rest.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var config = require('../../helpers').config, 18 | chance = new require('chance').Chance(), 19 | UsergridClient = require('../../lib/client'), 20 | UsergridEntity = require('../../lib/entity'), 21 | UsergridQuery = require('../../lib/query'), 22 | _ = require('lodash') 23 | 24 | var _uuid, 25 | _slow = 500, 26 | _timeout = 4000 27 | 28 | describe('GET()', function() { 29 | this.slow(_slow) 30 | this.timeout(_timeout) 31 | 32 | var response, client 33 | before(function(done) { 34 | client = new UsergridClient(config) 35 | client.GET(config.test.collection, function(err, usergridResponse) { 36 | response = usergridResponse 37 | done() 38 | }) 39 | }) 40 | 41 | it('should not fail when a callback function is not passed', function() { 42 | // note: this test will NOT fail gracefully inside the Mocha event chain 43 | client.GET(config.test.collection) 44 | }) 45 | 46 | it('response.ok should be true', function() { 47 | response.ok.should.be.true() 48 | }) 49 | 50 | it('response.entities should be an array', function() { 51 | response.entities.should.be.an.Array() 52 | }) 53 | 54 | it('response.first should exist and have a valid uuid', function() { 55 | response.first.should.be.an.instanceof(UsergridEntity).with.property('uuid').which.is.a.uuid() 56 | }) 57 | 58 | it('response.entity should exist and have a valid uuid', function() { 59 | response.entity.should.be.an.instanceof(UsergridEntity).with.property('uuid').which.is.a.uuid() 60 | }) 61 | 62 | it('response.last should exist and have a valid uuid', function() { 63 | response.last.should.be.an.instanceof(UsergridEntity).with.property('uuid').which.is.a.uuid() 64 | }) 65 | 66 | it('each entity should match the search criteria when passing a UsergridQuery object', function(done) { 67 | 68 | this.slow(_slow) 69 | this.timeout(_timeout) 70 | 71 | var query = new UsergridQuery(config.test.collection).eq('color', 'black') 72 | 73 | client.GET(query, function(err, usergridResponse) { 74 | usergridResponse.entities.forEach(function(entity) { 75 | entity.should.be.an.Object().with.property('color').equal('black') 76 | }) 77 | done() 78 | }) 79 | }) 80 | 81 | it('a single entity should be retrieved when specifying a uuid', function(done) { 82 | 83 | this.slow(_slow) 84 | this.timeout(_timeout) 85 | 86 | client.GET(config.test.collection, response.entity.uuid, function(err, usergridResponse) { 87 | usergridResponse.should.have.property('entity').which.is.an.instanceof(UsergridEntity) 88 | usergridResponse.body.entities.should.be.an.Array().with.a.lengthOf(1) 89 | done() 90 | }) 91 | }) 92 | }) 93 | 94 | describe('POST()', function() { 95 | 96 | this.slow(_slow) 97 | this.timeout(3000) 98 | 99 | var response, client 100 | before(function(done) { 101 | client = new UsergridClient() 102 | client.POST(config.test.collection, { 103 | author: 'Sir Arthur Conan Doyle' 104 | }, function(err, usergridResponse) { 105 | response = usergridResponse 106 | _uuid = usergridResponse.entity.uuid 107 | done() 108 | }) 109 | }) 110 | 111 | it('should not fail when a callback function is not passed', function() { 112 | // note: this test will NOT fail gracefully inside the Mocha event chain 113 | client.POST(config.test.collection, {}) 114 | }) 115 | 116 | it('response.ok should be true', function() { 117 | response.ok.should.be.true() 118 | }) 119 | 120 | it('response.entities should be an array', function() { 121 | response.entities.should.be.an.Array().with.a.lengthOf(1) 122 | }) 123 | 124 | it('response.entity should exist and have a valid uuid', function() { 125 | response.entity.should.be.an.instanceof(UsergridEntity).with.property('uuid').which.is.a.uuid() 126 | }) 127 | 128 | it('response.entity.author should equal "Sir Arthur Conan Doyle"', function() { 129 | response.entity.should.have.property('author').equal('Sir Arthur Conan Doyle') 130 | }) 131 | 132 | it('should support creating an entity by passing a UsergridEntity object', function(done) { 133 | 134 | this.slow(_slow) 135 | this.timeout(_timeout) 136 | 137 | var entity = new UsergridEntity({ 138 | type: config.test.collection, 139 | restaurant: "Dino's Deep Dish", 140 | cuisine: "pizza" 141 | }) 142 | 143 | client.POST(entity, function(err, usergridResponse) { 144 | usergridResponse.entity.should.be.an.Object().with.property('restaurant').equal(entity.restaurant) 145 | done() 146 | }) 147 | }) 148 | 149 | it('should support creating an entity by passing a UsergridEntity object with a unique name', function(done) { 150 | 151 | this.slow(_slow) 152 | this.timeout(_timeout) 153 | 154 | var entity = new UsergridEntity({ 155 | type: config.test.collection, 156 | name: chance.word() 157 | }) 158 | client.POST(entity, function(err, usergridResponse) { 159 | usergridResponse.entity.should.be.an.Object().with.property('name').equal(entity.name) 160 | usergridResponse.entity.remove(client) 161 | done() 162 | }) 163 | }) 164 | 165 | it('should support creating an entity by passing type and a body object', function(done) { 166 | 167 | this.slow(_slow) 168 | this.timeout(_timeout) 169 | 170 | client.POST(config.test.collection, { 171 | restaurant: "Dino's Deep Dish", 172 | cuisine: "pizza" 173 | }, function(err, usergridResponse) { 174 | usergridResponse.entity.should.be.an.Object().with.property('restaurant').equal("Dino's Deep Dish") 175 | done() 176 | }) 177 | }) 178 | 179 | it('should support creating an entity by passing a body object that includes type', function(done) { 180 | 181 | this.slow(_slow) 182 | this.timeout(_timeout) 183 | 184 | client.POST({ 185 | type: config.test.collection, 186 | restaurant: "Dino's Deep Dish", 187 | cuisine: "pizza" 188 | }, function(err, usergridResponse) { 189 | usergridResponse.entity.should.be.an.Object().with.property('restaurant').equal("Dino's Deep Dish") 190 | done() 191 | }) 192 | }) 193 | 194 | it('should support creating an entity by passing an array of UsergridEntity objects', function(done) { 195 | 196 | this.slow(_slow) 197 | this.timeout(_timeout) 198 | 199 | var entities = [ 200 | new UsergridEntity({ 201 | type: config.test.collection, 202 | restaurant: "Dino's Deep Dish", 203 | cuisine: "pizza" 204 | }), new UsergridEntity({ 205 | type: config.test.collection, 206 | restaurant: "Chipotle", 207 | cuisine: "mexican" 208 | }) 209 | ] 210 | 211 | client.POST(entities, function(err, usergridResponse) { 212 | usergridResponse.entities.forEach(function(entity) { 213 | entity.should.be.an.Object().with.property('restaurant').equal(entity.restaurant) 214 | }) 215 | done() 216 | }) 217 | }) 218 | 219 | it('should support creating an entity by passing a preformatted POST builder object', function(done) { 220 | 221 | this.slow(_slow) 222 | this.timeout(_timeout) 223 | 224 | var options = { 225 | type: config.test.collection, 226 | body: { 227 | restaurant: "Chipotle", 228 | cuisine: "mexican" 229 | } 230 | } 231 | 232 | client.POST(options, function(err, usergridResponse) { 233 | usergridResponse.entity.should.be.an.Object().with.property('restaurant').equal(usergridResponse.entity.restaurant) 234 | done() 235 | }) 236 | }) 237 | }) 238 | 239 | describe('PUT()', function() { 240 | 241 | this.slow(_slow) 242 | this.timeout(_timeout) 243 | 244 | var response, client 245 | before(function(done) { 246 | client = new UsergridClient() 247 | client.PUT(config.test.collection, _uuid, { 248 | narrator: 'Peter Doyle' 249 | }, function(err, usergridResponse) { 250 | response = usergridResponse 251 | done() 252 | }) 253 | }) 254 | 255 | it('should not fail when a callback function is not passed', function() { 256 | // note: this test will NOT fail gracefully inside the Mocha event chain 257 | client.PUT(config.test.collection, _uuid, {}) 258 | }) 259 | 260 | it('response.ok should be true', function() { 261 | response.ok.should.be.true() 262 | }) 263 | 264 | it('response.entities should be an array with a single entity', function() { 265 | response.entities.should.be.an.Array().with.a.lengthOf(1) 266 | }) 267 | 268 | it('response.entity should exist and its uuid should the uuid from the previous POST request', function() { 269 | response.entity.should.be.an.Object().with.property('uuid').equal(_uuid) 270 | }) 271 | 272 | it('response.entity.narrator should be updated to "Peter Doyle"', function() { 273 | response.entity.should.have.property('narrator').equal('Peter Doyle') 274 | }) 275 | 276 | it('should create a new entity when no uuid or name is passed', function(done) { 277 | 278 | this.slow(_slow) 279 | this.timeout(_timeout) 280 | 281 | var newEntity = new UsergridEntity({ 282 | type: config.test.collection, 283 | author: 'Frank Mills' 284 | }) 285 | 286 | client.PUT(newEntity, function(err, usergridResponse) { 287 | usergridResponse.entity.should.be.an.Object() 288 | usergridResponse.entity.should.be.an.instanceof(UsergridEntity).with.property('uuid').which.is.a.uuid() 289 | usergridResponse.entity.should.have.property('author').equal('Frank Mills') 290 | usergridResponse.entity.created.should.equal(usergridResponse.entity.modified) 291 | done() 292 | }) 293 | }) 294 | 295 | it('should support updating the entity by passing a UsergridEntity object', function(done) { 296 | 297 | this.slow(_slow) 298 | this.timeout(_timeout) 299 | 300 | var updateEntity = _.assign(response.entity, { 301 | publisher: { 302 | name: "George Newns", 303 | date: "14 October 1892", 304 | country: "United Kingdom" 305 | } 306 | }) 307 | 308 | client.PUT(updateEntity, function(err, usergridResponse) { 309 | usergridResponse.entity.should.be.an.Object().with.property('publisher').deepEqual(updateEntity.publisher) 310 | done() 311 | }) 312 | }) 313 | 314 | it('should support updating an entity by passing type and a body object', function(done) { 315 | 316 | this.slow(_slow) 317 | this.timeout(_timeout) 318 | client.PUT(config.test.collection, { 319 | uuid: response.entity.uuid, 320 | updateByPassingTypeAndBody: true 321 | }, function(err, usergridResponse) { 322 | usergridResponse.entity.should.be.an.Object().with.property('updateByPassingTypeAndBody').equal(true) 323 | done() 324 | }) 325 | }) 326 | 327 | it('should support updating an entity by passing a body object that includes type', function(done) { 328 | 329 | this.slow(_slow) 330 | this.timeout(_timeout) 331 | 332 | client.PUT(config.test.collection, { 333 | type: config.test.collection, 334 | uuid: response.entity.uuid, 335 | updateByPassingBodyIncludingType: true 336 | }, function(err, usergridResponse) { 337 | usergridResponse.entity.should.be.an.Object().with.property('updateByPassingBodyIncludingType').equal(true) 338 | done() 339 | }) 340 | }) 341 | 342 | it('should support updating a set of entities by passing an UsergridQuery object', function(done) { 343 | 344 | this.slow(_slow + 1000) 345 | this.timeout(_timeout + 10000) 346 | 347 | var query = new UsergridQuery(config.test.collection).eq('cuisine', 'pizza').limit(2) 348 | var body = { 349 | testUuid: _.uuid() 350 | } 351 | 352 | client.PUT(query, body, function(err, usergridResponse) { 353 | usergridResponse.entities.forEach(function(entity) { 354 | entity.should.be.an.Object().with.property('testUuid').equal(body.testUuid) 355 | }) 356 | done() 357 | }) 358 | }) 359 | 360 | it('should support updating an entity by passing a preformatted PUT builder object', function(done) { 361 | 362 | this.slow(_slow) 363 | this.timeout(_timeout) 364 | 365 | var options = { 366 | uuidOrName: response.entity.uuid, 367 | type: config.test.collection, 368 | body: { 369 | relatedUuid: _.uuid() 370 | } 371 | } 372 | 373 | client.PUT(options, function(err, usergridResponse) { 374 | usergridResponse.entity.should.be.an.Object().with.property('relatedUuid').equal(options.body.relatedUuid) 375 | done() 376 | }) 377 | }) 378 | }) 379 | 380 | describe('DELETE()', function() { 381 | 382 | this.slow(_slow) 383 | this.timeout(_timeout) 384 | 385 | var response, client 386 | before(function(done) { 387 | client = new UsergridClient() 388 | client.DELETE(config.test.collection, _uuid, function() { 389 | client.GET(config.test.collection, _uuid, function(err, usergridResponse) { 390 | response = usergridResponse 391 | done() 392 | }) 393 | }) 394 | }) 395 | 396 | it('should not fail when a callback function is not passed', function() { 397 | // note: this test will NOT fail gracefully inside the Mocha event chain 398 | client.DELETE(config.test.collection, _uuid) 399 | }) 400 | 401 | it('should return a 404 not found', function() { 402 | response.statusCode.should.equal(404) 403 | }) 404 | 405 | it('response.error.name should equal "entity_not_found"', function() { 406 | response.error.name.should.equal((config.target === '1.0') ? 'service_resource_not_found' : 'entity_not_found') 407 | }) 408 | 409 | it('should support deleting an entity by passing a UsergridEntity object', function(done) { 410 | 411 | this.slow(_slow + 1000) 412 | this.timeout(_timeout + 1000) 413 | 414 | var entity = new UsergridEntity({ 415 | type: config.test.collection, 416 | command: "CTRL+ALT+DEL" 417 | }) 418 | 419 | client.POST(entity, function(err, usergridResponse) { 420 | client.DELETE(usergridResponse.entity, function() { 421 | client.GET(config.test.collection, usergridResponse.entity.uuid, function(err, delResponse) { 422 | delResponse.ok.should.be.false() 423 | delResponse.error.name.should.equal((config.target === '1.0') ? 'service_resource_not_found' : 'entity_not_found') 424 | done() 425 | }) 426 | }) 427 | }) 428 | }) 429 | 430 | it('should support deleting an entity by passing type and uuid', function(done) { 431 | 432 | this.slow(_slow + 1000) 433 | this.timeout(_timeout + 1000) 434 | 435 | var body = { 436 | command: "CTRL+ALT+DEL" 437 | } 438 | 439 | client.POST(config.test.collection, body, function(err, usergridResponse) { 440 | client.DELETE(config.test.collection, usergridResponse.entity.uuid, function() { 441 | client.GET(config.test.collection, usergridResponse.entity.uuid, function(err, delResponse) { 442 | delResponse.error.name.should.equal((config.target === '1.0') ? 'service_resource_not_found' : 'entity_not_found') 443 | done() 444 | }) 445 | }) 446 | }) 447 | }) 448 | 449 | it('should support deleting multiple entities by passing a UsergridQuery object', function(done) { 450 | 451 | this.slow(_slow + 1000) 452 | this.timeout(_timeout + 6000) 453 | 454 | var entity = new UsergridEntity({ 455 | type: config.test.collection, 456 | command: "CMD+TAB" 457 | }) 458 | 459 | var query = new UsergridQuery(config.test.collection).eq('command', 'CMD+TAB') 460 | 461 | client.POST([entity, entity, entity, entity], function() { 462 | client.DELETE(query, function() { 463 | client.GET(query, function(err, usergridResponse) { 464 | usergridResponse.entities.should.be.an.Array().with.lengthOf(0) 465 | done() 466 | }) 467 | }) 468 | }) 469 | }) 470 | 471 | it('should support deleting an entity by passing a preformatted DELETE builder object', function(done) { 472 | 473 | this.slow(_slow + 1000) 474 | this.timeout(_timeout + 1000) 475 | 476 | var options = { 477 | type: config.test.collection, 478 | body: { 479 | restaurant: "IHOP", 480 | cuisine: "breakfast" 481 | } 482 | } 483 | 484 | client.POST(options, function(err, usergridResponse) { 485 | client.DELETE(_.assign(options, { 486 | uuid: usergridResponse.entity.uuid 487 | }), function() { 488 | client.GET(config.test.collection, usergridResponse.entity.uuid, function(err, delResponse) { 489 | delResponse.error.name.should.equal((config.target === '1.0') ? 'service_resource_not_found' : 'entity_not_found') 490 | done() 491 | }) 492 | }) 493 | }) 494 | }) 495 | }) -------------------------------------------------------------------------------- /tests/lib/entity.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var should = require('should'), 18 | urljoin = require('url-join'), 19 | config = require('../../helpers').config, 20 | UsergridClient = require('../../lib/client'), 21 | UsergridEntity = require('../../lib/entity'), 22 | UsergridQuery = require('../../lib/query'), 23 | UsergridAuth = require('../../lib/auth'), 24 | UsergridAsset = require('../../lib/asset'), 25 | fs = require('fs') 26 | 27 | var _slow = 1500, 28 | _timeout = 4000, 29 | filename = 'old_man', 30 | file = __dirname + '/image.jpg', 31 | expectedContentLength = 109055, 32 | assetEntity = new UsergridEntity({ 33 | type: config.test.collection, 34 | info: "assetTestEntity" 35 | }) 36 | 37 | describe('putProperty()', function() { 38 | it('should set the value for a given key if the key does not exist', function() { 39 | var entity = new UsergridEntity('cat', 'Cosmo') 40 | entity.putProperty('foo', ['bar']) 41 | entity.should.have.property('foo').deepEqual(['bar']) 42 | }) 43 | 44 | it('should overwrite the value for a given key if the key exists', function() { 45 | var entity = new UsergridEntity({ 46 | type: 'cat', 47 | name: 'Cosmo', 48 | foo: 'baz' 49 | }) 50 | entity.putProperty('foo', 'bar') 51 | entity.should.have.property('foo').deepEqual('bar') 52 | }) 53 | 54 | it('should not be able to set the name key (name is immutable)', function() { 55 | var entity = new UsergridEntity({ 56 | type: 'cat', 57 | name: 'Cosmo', 58 | foo: 'baz' 59 | }) 60 | should(function() { 61 | entity.putProperty('name', 'Bazinga') 62 | }).throw() 63 | }) 64 | }) 65 | 66 | describe('putProperties()', function() { 67 | it('should set properties for a given object, overwriting properties that exist and creating those that don\'t', function() { 68 | var entity = new UsergridEntity({ 69 | type: 'cat', 70 | name: 'Cosmo', 71 | foo: 'bar' 72 | }) 73 | entity.putProperties({ 74 | foo: 'baz', 75 | qux: 'quux', 76 | barray: [1, 2, 3, 4] 77 | }) 78 | entity.should.containEql({ 79 | type: 'cat', 80 | name: 'Cosmo', 81 | foo: 'baz', 82 | qux: 'quux', 83 | barray: [1, 2, 3, 4] 84 | }) 85 | }) 86 | 87 | it('should not be able to set properties for immutable keys', function() { 88 | var entity = new UsergridEntity({ 89 | type: 'cat', 90 | name: 'Cosmo', 91 | foo: 'baz' 92 | }) 93 | entity.putProperties({ 94 | name: 'Bazinga', 95 | uuid: 'BadUuid', 96 | bar: 'qux' 97 | }) 98 | entity.should.containEql({ 99 | type: 'cat', 100 | name: 'Cosmo', 101 | bar: 'qux', 102 | foo: 'baz' 103 | }) 104 | }) 105 | }) 106 | 107 | describe('removeProperty()', function() { 108 | it('should remove a given property if it exists', function() { 109 | var entity = new UsergridEntity({ 110 | type: 'cat', 111 | name: 'Cosmo', 112 | foo: 'baz' 113 | }) 114 | entity.removeProperty('foo') 115 | entity.should.not.have.property('foo') 116 | }) 117 | 118 | it('should fail gracefully when removing an undefined property', function() { 119 | var entity = new UsergridEntity('cat', 'Cosmo') 120 | entity.removeProperty('foo') 121 | entity.should.not.have.property('foo') 122 | }) 123 | }) 124 | 125 | describe('removeProperties()', function() { 126 | it('should remove an array of properties for a given object, failing gracefully for undefined properties', function() { 127 | var entity = new UsergridEntity({ 128 | type: 'cat', 129 | name: 'Cosmo', 130 | foo: 'bar', 131 | baz: 'qux' 132 | }) 133 | entity.removeProperties(['foo', 'baz']) 134 | entity.should.containEql({ 135 | type: 'cat', 136 | name: 'Cosmo' 137 | }) 138 | }) 139 | }) 140 | 141 | describe('insert()', function() { 142 | it('should insert a single value into an existing array at the specified index', function() { 143 | var entity = new UsergridEntity({ 144 | type: 'cat', 145 | name: 'Cosmo', 146 | foo: [1, 2, 3, 5, 6] 147 | }) 148 | entity.insert('foo', 4, 3) 149 | entity.should.have.property('foo').deepEqual([1, 2, 3, 4, 5, 6]) 150 | }) 151 | 152 | it('should merge an array of values into an existing array at the specified index', function() { 153 | var entity = new UsergridEntity({ 154 | type: 'cat', 155 | name: 'Cosmo', 156 | foo: [1, 2, 3, 7, 8] 157 | }) 158 | entity.insert('foo', [4, 5, 6], 3) 159 | entity.should.have.property('foo').deepEqual([1, 2, 3, 4, 5, 6, 7, 8]) 160 | }) 161 | 162 | it('should convert an existing value into an array when inserting a second value', function() { 163 | var entity = new UsergridEntity({ 164 | type: 'cat', 165 | name: 'Cosmo', 166 | foo: 'bar' 167 | }) 168 | entity.insert('foo', 'baz', 1) 169 | entity.should.have.property('foo').deepEqual(['bar', 'baz']) 170 | }) 171 | 172 | it('should create a new array when a property does not exist', function() { 173 | var entity = new UsergridEntity({ 174 | type: 'cat', 175 | name: 'Cosmo' 176 | }) 177 | entity.insert('foo', 'bar', 1000) 178 | entity.should.have.property('foo').deepEqual(['bar']) 179 | }) 180 | 181 | it('should gracefully handle indexes out of range', function() { 182 | var entity = new UsergridEntity({ 183 | type: 'cat', 184 | name: 'Cosmo', 185 | foo: ['bar'] 186 | }) 187 | entity.insert('foo', 'baz', 1000) 188 | entity.should.have.property('foo').deepEqual(['bar', 'baz']) 189 | entity.insert('foo', 'qux', -1000) 190 | entity.should.have.property('foo').deepEqual(['qux', 'bar', 'baz']) 191 | }) 192 | }) 193 | 194 | describe('append()', function() { 195 | it('should append a value to the end of an existing array', function() { 196 | var entity = new UsergridEntity({ 197 | type: 'cat', 198 | name: 'Cosmo', 199 | foo: [1, 2, 3] 200 | }) 201 | entity.append('foo', 4) 202 | entity.should.have.property('foo').deepEqual([1, 2, 3, 4]) 203 | }) 204 | 205 | it('should create a new array if a property does not exist', function() { 206 | var entity = new UsergridEntity({ 207 | type: 'cat', 208 | name: 'Cosmo' 209 | }) 210 | entity.append('foo', 'bar') 211 | entity.should.have.property('foo').deepEqual(['bar']) 212 | }) 213 | }) 214 | 215 | describe('prepend()', function() { 216 | it('should prepend a value to the beginning of an existing array', function() { 217 | var entity = new UsergridEntity({ 218 | type: 'cat', 219 | name: 'Cosmo', 220 | foo: [1, 2, 3] 221 | }) 222 | entity.prepend('foo', 0) 223 | entity.should.have.property('foo').deepEqual([0, 1, 2, 3]) 224 | }) 225 | 226 | it('should create a new array if a property does not exist', function() { 227 | var entity = new UsergridEntity({ 228 | type: 'cat', 229 | name: 'Cosmo' 230 | }) 231 | entity.prepend('foo', 'bar') 232 | entity.should.have.property('foo').deepEqual(['bar']) 233 | }) 234 | }) 235 | 236 | describe('pop()', function() { 237 | it('should remove the last value of an existing array', function() { 238 | var entity = new UsergridEntity({ 239 | type: 'cat', 240 | name: 'Cosmo', 241 | foo: [1, 2, 3] 242 | }) 243 | entity.pop('foo') 244 | entity.should.have.property('foo').deepEqual([1, 2]) 245 | }) 246 | 247 | it('value should remain unchanged if it is not an array', function() { 248 | var entity = new UsergridEntity({ 249 | type: 'cat', 250 | name: 'Cosmo', 251 | foo: 'bar' 252 | }) 253 | entity.pop('foo') 254 | entity.should.have.property('foo').deepEqual('bar') 255 | }) 256 | 257 | it('should gracefully handle empty arrays', function() { 258 | var entity = new UsergridEntity({ 259 | type: 'cat', 260 | name: 'Cosmo', 261 | foo: [] 262 | }) 263 | entity.pop('foo') 264 | entity.should.have.property('foo').deepEqual([]) 265 | }) 266 | }) 267 | 268 | describe('shift()', function() { 269 | it('should remove the first value of an existing array', function() { 270 | var entity = new UsergridEntity({ 271 | type: 'cat', 272 | name: 'Cosmo', 273 | foo: [1, 2, 3] 274 | }) 275 | entity.shift('foo') 276 | entity.should.have.property('foo').deepEqual([2, 3]) 277 | }) 278 | 279 | it('value should remain unchanged if it is not an array', function() { 280 | var entity = new UsergridEntity({ 281 | type: 'cat', 282 | name: 'Cosmo', 283 | foo: 'bar' 284 | }) 285 | entity.shift('foo') 286 | entity.should.have.property('foo').deepEqual('bar') 287 | }) 288 | 289 | it('should gracefully handle empty arrays', function() { 290 | var entity = new UsergridEntity({ 291 | type: 'cat', 292 | name: 'Cosmo', 293 | foo: [] 294 | }) 295 | entity.shift('foo') 296 | entity.should.have.property('foo').deepEqual([]) 297 | }) 298 | }) 299 | 300 | describe('reload()', function() { 301 | 302 | this.slow(_slow + 1000) 303 | this.timeout(_timeout + 4000) 304 | 305 | it('should refresh an entity with the latest server copy of itself', function(done) { 306 | var client = new UsergridClient(config), 307 | now = Date.now() 308 | client.GET(config.test.collection, function(err, getResponse) { 309 | var entity = new UsergridEntity(getResponse.first) 310 | var modified = entity.modified 311 | getResponse.first.putProperty('reloadTest', now) 312 | client.PUT(getResponse.first, function() { 313 | entity.reload(client, function() { 314 | client.isSharedInstance.should.be.false() 315 | entity.reloadTest.should.equal(now) 316 | entity.modified.should.not.equal(modified) 317 | done() 318 | }) 319 | }) 320 | }) 321 | }) 322 | }) 323 | 324 | describe('save()', function() { 325 | 326 | this.slow(_slow + 1000) 327 | this.timeout(_timeout + 4000) 328 | 329 | it('should save an updated entity to the server', function(done) { 330 | var client = new UsergridClient(config), 331 | now = Date.now() 332 | client.GET(config.test.collection, function(err, getResponse) { 333 | var entity = new UsergridEntity(getResponse.first) 334 | entity.putProperty('saveTest', now) 335 | entity.save(client, function() { 336 | client.isSharedInstance.should.be.false() 337 | entity.saveTest.should.equal(now) 338 | done() 339 | }) 340 | }) 341 | }) 342 | }) 343 | 344 | describe('remove()', function() { 345 | 346 | this.slow(_slow + 1000) 347 | this.timeout(_timeout + 4000) 348 | 349 | it('should remove an entity from the server', function(done) { 350 | var client = new UsergridClient(config) 351 | client.POST(config.test.collection, { 352 | removeTest: 'test' 353 | }, function(err, postResponse) { 354 | var entity = new UsergridEntity(postResponse.first) 355 | entity.remove(client, function(err, deleteResponse) { 356 | client.isSharedInstance.should.be.false() 357 | deleteResponse.ok.should.be.true() 358 | // best practice is to desroy the 'entity' instance here, because it no longer exists on the server 359 | entity = null 360 | done() 361 | }) 362 | }) 363 | }) 364 | }) 365 | 366 | describe('attachAsset()', function() { 367 | 368 | var asset = new UsergridAsset(filename, file), 369 | entity = new UsergridEntity({ 370 | type: config.test.collection, 371 | info: "attachAssetTest" 372 | }) 373 | before(function(done) { 374 | fs.readFile(file, function(err, data) { 375 | asset.data = data 376 | done() 377 | }) 378 | }) 379 | 380 | it('should attach a UsergridAsset to the entity', function(done) { 381 | entity.attachAsset(asset) 382 | entity.should.have.property('asset').equal(asset) 383 | done() 384 | }) 385 | }) 386 | 387 | describe('uploadAsset()', function() { 388 | 389 | this.slow(_slow) 390 | this.timeout(_timeout) 391 | 392 | var asset = new UsergridAsset(filename, file) 393 | before(function(done) { 394 | fs.readFile(file, function(err, data) { 395 | asset.data = data 396 | done() 397 | }) 398 | }) 399 | 400 | it('should upload an image asset to the remote entity', function(done) { 401 | var client = new UsergridClient(config) 402 | assetEntity.attachAsset(asset) 403 | assetEntity.uploadAsset(client, function(err, usergridResponse, entity) { 404 | assetEntity = entity 405 | entity.should.have.property('file-metadata') 406 | entity['file-metadata'].should.have.property('content-length').equal(expectedContentLength) 407 | entity['file-metadata'].should.have.property('content-type').equal('image/jpeg') 408 | done() 409 | }) 410 | }) 411 | }) 412 | 413 | describe('downloadAsset()', function() { 414 | 415 | this.slow(_slow) 416 | this.timeout(_timeout) 417 | 418 | it('should download a an image from the remote entity', function(done) { 419 | var client = new UsergridClient(config) 420 | assetEntity.downloadAsset(client, 'image/jpeg', function(err, usergridResponse, entityWithAsset) { 421 | entityWithAsset.should.have.property('asset').which.is.an.instanceof(UsergridAsset) 422 | entityWithAsset.asset.should.have.property('contentType').equal(assetEntity['file-metadata']['content-type']) 423 | entityWithAsset.asset.should.have.property('contentLength').equal(assetEntity['file-metadata']['content-length']) 424 | // clean up the now un-needed asset entity 425 | entityWithAsset.remove(client) 426 | done() 427 | }) 428 | }) 429 | }) 430 | 431 | describe('connect()', function() { 432 | 433 | this.slow(_slow) 434 | this.timeout(_timeout + 4000) 435 | 436 | var response, 437 | entity1, 438 | entity2, 439 | client = new UsergridClient(), 440 | query = new UsergridQuery(config.test.collection).eq('name', 'testEntityConnectOne').or.eq('name', 'testEntityConnectTwo').asc('name') 441 | 442 | before(function(done) { 443 | // Create the entities we're going to use for connections 444 | client.POST(config.test.collection, [{ 445 | "name": "testEntityConnectOne" 446 | }, { 447 | "name": "testEntityConnectTwo" 448 | }], function() { 449 | client.GET(query, function(err, usergridResponse) { 450 | response = usergridResponse 451 | entity1 = response.first 452 | entity2 = response.last 453 | done() 454 | }) 455 | }) 456 | }) 457 | 458 | it('should connect entities by passing a target UsergridEntity object as a parameter', function(done) { 459 | var relationship = "foos" 460 | 461 | entity1.connect(client, relationship, entity2, function(err, usergridResponse) { 462 | usergridResponse.ok.should.be.true() 463 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 464 | usergridResponse.first.metadata.connecting[relationship].should.equal(urljoin( 465 | "", 466 | config.test.collection, 467 | entity1.uuid, 468 | relationship, 469 | entity2.uuid, 470 | "connecting", 471 | relationship 472 | )) 473 | done() 474 | }) 475 | }) 476 | }) 477 | 478 | it('should connect entities by passing target uuid as a parameter', function(done) { 479 | var relationship = "bars" 480 | 481 | entity1.connect(client, relationship, entity2.uuid, function(err, usergridResponse) { 482 | usergridResponse.ok.should.be.true() 483 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 484 | usergridResponse.first.metadata.connecting[relationship].should.equal(urljoin( 485 | "", 486 | config.test.collection, 487 | entity1.uuid, 488 | relationship, 489 | entity2.uuid, 490 | "connecting", 491 | relationship 492 | )) 493 | done() 494 | }) 495 | }) 496 | }) 497 | 498 | it('should connect entities by passing target type and name as parameters', function(done) { 499 | var relationship = "bazzes" 500 | 501 | entity1.connect(client, relationship, entity2.type, entity2.name, function(err, usergridResponse) { 502 | usergridResponse.ok.should.be.true() 503 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 504 | usergridResponse.first.metadata.connecting[relationship].should.equal(urljoin( 505 | "", 506 | config.test.collection, 507 | entity1.uuid, 508 | relationship, 509 | entity2.uuid, 510 | "connecting", 511 | relationship 512 | )) 513 | done() 514 | }) 515 | }) 516 | }) 517 | 518 | it('should fail to connect entities when specifying target name without type', function() { 519 | should(function() { 520 | entity1.connect("fails", 'badName', function() {}) 521 | }).throw() 522 | }) 523 | }) 524 | 525 | describe('getConnections()', function() { 526 | 527 | this.slow(_slow) 528 | this.timeout(_timeout + 4000) 529 | 530 | var response, 531 | client = new UsergridClient(), 532 | query = new UsergridQuery(config.test.collection).eq('name', 'testEntityConnectOne').or.eq('name', 'testEntityConnectTwo').asc('name') 533 | 534 | before(function(done) { 535 | client.GET(query, function(err, usergridResponse) { 536 | response = usergridResponse 537 | done() 538 | }) 539 | }) 540 | 541 | it('should get an entity\'s outbound connections', function(done) { 542 | var entity1 = response.first 543 | var entity2 = response.last 544 | 545 | var relationship = "foos" 546 | 547 | entity1.getConnections(client, UsergridClient.Connections.DIRECTION_OUT, relationship, function(err, usergridResponse) { 548 | usergridResponse.first.metadata.connecting[relationship].should.equal(urljoin( 549 | "", 550 | config.test.collection, 551 | entity1.uuid, 552 | relationship, 553 | entity2.uuid, 554 | "connecting", 555 | relationship 556 | )) 557 | done() 558 | }) 559 | }) 560 | 561 | it('should get an entity\'s inbound connections', function(done) { 562 | var entity1 = response.first 563 | var entity2 = response.last 564 | 565 | var relationship = "foos" 566 | 567 | entity2.getConnections(client, UsergridClient.Connections.DIRECTION_IN, relationship, function(err, usergridResponse) { 568 | usergridResponse.first.metadata.connections[relationship].should.equal(urljoin( 569 | "", 570 | config.test.collection, 571 | entity2.uuid, 572 | "connecting", 573 | entity1.uuid, 574 | relationship 575 | )) 576 | done() 577 | }) 578 | }) 579 | }) 580 | 581 | describe('disconnect()', function() { 582 | 583 | this.slow(_slow) 584 | this.timeout(_timeout + 4000) 585 | 586 | var response, 587 | client = new UsergridClient(), 588 | query = new UsergridQuery(config.test.collection).eq('name', 'testEntityConnectOne').or.eq('name', 'testEntityConnectTwo').asc('name') 589 | 590 | before(function(done) { 591 | client.GET(query, function(err, usergridResponse) { 592 | response = usergridResponse 593 | done() 594 | }) 595 | }) 596 | 597 | it('should disconnect entities by passing a target UsergridEntity object as a parameter', function(done) { 598 | var entity1 = response.first 599 | var entity2 = response.last 600 | 601 | var relationship = "foos" 602 | 603 | entity1.disconnect(client, relationship, entity2, function(err, usergridResponse) { 604 | usergridResponse.ok.should.be.true() 605 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 606 | usergridResponse.entities.should.be.an.Array().with.lengthOf(0) 607 | done() 608 | }) 609 | }) 610 | }) 611 | 612 | it('should disconnect entities by passing target uuid as a parameter', function(done) { 613 | var entity1 = response.first 614 | var entity2 = response.last 615 | 616 | var relationship = "bars" 617 | 618 | entity1.disconnect(client, relationship, entity2.uuid, function(err, usergridResponse) { 619 | usergridResponse.ok.should.be.true() 620 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 621 | usergridResponse.entities.should.be.an.Array().with.lengthOf(0) 622 | done() 623 | }) 624 | }) 625 | }) 626 | 627 | it('should disconnect entities by passing target type and name as parameters', function(done) { 628 | var entity1 = response.first 629 | var entity2 = response.last 630 | 631 | var relationship = "bazzes" 632 | 633 | entity1.disconnect(client, relationship, entity2.type, entity2.name, function(err, usergridResponse) { 634 | usergridResponse.ok.should.be.true() 635 | client.getConnections(UsergridClient.Connections.DIRECTION_OUT, entity1, relationship, function(err, usergridResponse) { 636 | usergridResponse.entities.should.be.an.Array().with.lengthOf(0) 637 | done() 638 | }) 639 | }) 640 | }) 641 | 642 | it('should fail to disconnect entities when specifying target name without type', function() { 643 | var entity1 = response.first 644 | var entity2 = response.last 645 | 646 | should(function() { 647 | entity1.disconnect("fails", entity2.name, function() {}) 648 | }).throw() 649 | }) 650 | }) 651 | 652 | describe('usingAuth()', function() { 653 | 654 | this.slow(_slow + 500) 655 | this.timeout(_timeout) 656 | 657 | var client = new UsergridClient(), 658 | authFromToken = new UsergridAuth('BADTOKEN'), 659 | _entity 660 | 661 | it('should fail to reload an entity when using a bad ad-hoc token', function(done) { 662 | client.GET(config.test.collection, function(err, getResponse) { 663 | _entity = new UsergridEntity(getResponse.first) 664 | _entity.usingAuth(authFromToken).reload(client, function(error, usergridResponse) { 665 | usergridResponse.request.headers.should.not.have.property('authentication') 666 | usergridResponse.ok.should.be.false() 667 | error.name.should.equal('auth_bad_access_token') 668 | done() 669 | }) 670 | }) 671 | }) 672 | 673 | it('client.tempAuth should be destroyed after making a request with ad-hoc authentication', function(done) { 674 | should(client.tempAuth).be.undefined() 675 | done() 676 | }) 677 | 678 | it('entity.tempAuth should be destroyed after making a request with ad-hoc authentication', function(done) { 679 | should(_entity.tempAuth).be.undefined() 680 | done() 681 | }) 682 | 683 | it('should send an unauthenticated request when UsergridAuth.NO_AUTH is passed to .usingAuth()', function(done) { 684 | // hack this using 'me' in case test apps have sandbox permissions 685 | var entity = new UsergridEntity({ 686 | uuid: 'me', 687 | type: 'users' 688 | }) 689 | entity.usingAuth(UsergridAuth.NO_AUTH).reload(client, function(error, usergridResponse) { 690 | usergridResponse.request.headers.should.not.have.property('authentication') 691 | usergridResponse.ok.should.be.false() 692 | error.name.should.equal('service_resource_not_found') 693 | done() 694 | }) 695 | }) 696 | 697 | it('should send an unauthenticated request when no arguments are passed to .usingAuth()', function(done) { 698 | // hack this using 'me' in case test apps have sandbox permissions 699 | var entity = new UsergridEntity({ 700 | uuid: 'me', 701 | type: 'users' 702 | }) 703 | entity.usingAuth().reload(client, function(error, usergridResponse) { 704 | usergridResponse.request.headers.should.not.have.property('authentication') 705 | usergridResponse.ok.should.be.false() 706 | error.name.should.equal('service_resource_not_found') 707 | done() 708 | }) 709 | }) 710 | }) -------------------------------------------------------------------------------- /tests/lib/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apache/usergrid-nodejs/b5c8b4ca6505d5e2fc614590fa92b036ca7b621b/tests/lib/image.jpg -------------------------------------------------------------------------------- /tests/lib/image_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apache/usergrid-nodejs/b5c8b4ca6505d5e2fc614590fa92b036ca7b621b/tests/lib/image_test.jpg -------------------------------------------------------------------------------- /tests/lib/query.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var UsergridQuery = require('../../lib/query') 18 | 19 | describe('_type', function() { 20 | it('_type should equal "cats" when passing "type" as a parameter to UsergridQuery', function() { 21 | var query = new UsergridQuery('cats') 22 | query.should.have.property('_type').equal('cats') 23 | }) 24 | 25 | it('_type should equal "cats" when calling .type() builder method', function() { 26 | var query = new UsergridQuery().type('cats') 27 | query.should.have.property('_type').equal('cats') 28 | }) 29 | 30 | it('_type should equal "cats" when calling .collection() builder method', function() { 31 | var query = new UsergridQuery().collection('cats') 32 | query.should.have.property('_type').equal('cats') 33 | }) 34 | }) 35 | 36 | describe('_limit', function() { 37 | it('_limit should equal 2 when setting .limit(2)', function() { 38 | var query = new UsergridQuery('cats').limit(2) 39 | query.should.have.property('_limit').equal(2) 40 | }) 41 | 42 | it('_limit should equal 10 when setting .limit(10)', function() { 43 | var query = new UsergridQuery('cats').limit(10) 44 | query.should.have.property('_limit').equal(10) 45 | }) 46 | }) 47 | 48 | describe('_ql', function() { 49 | it('should equal \'select *\' if query or sort are empty or underfined', function() { 50 | var query = new UsergridQuery('cats') 51 | query.should.have.property('_ql').equal('select *') 52 | }) 53 | 54 | it('should support complex builder pattern syntax (chained constructor methods)', function() { 55 | var query = new UsergridQuery('cats') 56 | .gt('weight', 2.4) 57 | .contains('color', 'bl*') 58 | .not 59 | .eq('color', 'blue') 60 | .or 61 | .eq('color', 'orange') 62 | query.should.have.property('_ql').equal("select * where weight > 2.4 and color contains 'bl*' and not color = 'blue' or color = 'orange'") 63 | }) 64 | 65 | it('and operator should be implied when joining multiple conditions', function() { 66 | var query1 = new UsergridQuery('cats') 67 | .gt('weight', 2.4) 68 | .contains('color', 'bl*') 69 | query1.should.have.property('_ql').equal("select * where weight > 2.4 and color contains 'bl*'") 70 | var query2 = new UsergridQuery('cats') 71 | .gt('weight', 2.4) 72 | .and 73 | .contains('color', 'bl*') 74 | query2.should.have.property('_ql').equal("select * where weight > 2.4 and color contains 'bl*'") 75 | }) 76 | 77 | it('not operator should precede conditional statement', function() { 78 | var query = new UsergridQuery('cats') 79 | .not 80 | .eq('color', 'white') 81 | query.should.have.property('_ql').equal("select * where not color = 'white'") 82 | }) 83 | 84 | it('fromString should set _ql directly, bypassing builder pattern methods', function() { 85 | var q = "where color = 'black' or color = 'orange'" 86 | var query = new UsergridQuery('cats') 87 | .fromString(q) 88 | query.should.have.property('_ql').equal(q) 89 | }) 90 | 91 | it('string values should be contained in single quotes', function() { 92 | var query = new UsergridQuery('cats') 93 | .eq('color', 'black') 94 | query.should.have.property('_ql').equal("select * where color = 'black'") 95 | }) 96 | 97 | it('boolean values should not be contained in single quotes', function() { 98 | var query = new UsergridQuery('cats') 99 | .eq('longHair', true) 100 | query.should.have.property('_ql').equal("select * where longHair = true") 101 | }) 102 | 103 | it('float values should not be contained in single quotes', function() { 104 | var query = new UsergridQuery('cats') 105 | .lt('weight', 18) 106 | query.should.have.property('_ql').equal("select * where weight < 18") 107 | }) 108 | 109 | it('integer values should not be contained in single quotes', function() { 110 | var query = new UsergridQuery('cats') 111 | .gte('weight', 2) 112 | query.should.have.property('_ql').equal("select * where weight >= 2") 113 | }) 114 | 115 | it('uuid values should not be contained in single quotes', function() { 116 | var query = new UsergridQuery('cats') 117 | .eq('uuid', 'a61e29ba-944f-11e5-8690-fbb14f62c803') 118 | query.should.have.property('_ql').equal("select * where uuid = a61e29ba-944f-11e5-8690-fbb14f62c803") 119 | }) 120 | }) -------------------------------------------------------------------------------- /tests/lib/response.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var should = require('should'), 18 | config = require('../../helpers').config, 19 | UsergridClient = require('../../lib/client'), 20 | UsergridEntity = require('../../lib/entity'), 21 | UsergridUser = require('../../lib/user'), 22 | UsergridQuery = require('../../lib/query'), 23 | UsergridResponseError = require('../../lib/responseError'), 24 | _ = require('lodash') 25 | 26 | var client = new UsergridClient() 27 | 28 | var _response, 29 | _slow = 500, 30 | _timeout = 4000 31 | 32 | before(function(done) { 33 | 34 | this.slow(_slow) 35 | this.timeout(_timeout) 36 | 37 | client.GET(config.test.collection, function(err, usergridResponse) { 38 | _response = usergridResponse 39 | done() 40 | }) 41 | }) 42 | 43 | describe('headers', function() { 44 | it('should be an object', function() { 45 | _response.headers.should.be.an.Object().with.property('content-type') 46 | }) 47 | }) 48 | 49 | describe('statusCode', function() { 50 | it('should be a number', function() { 51 | _response.statusCode.should.be.a.Number() 52 | }) 53 | }) 54 | 55 | describe('ok', function() { 56 | it('should be a bool', function() { 57 | _response.ok.should.be.a.Boolean() 58 | }) 59 | }) 60 | 61 | describe('metadata', function() { 62 | it('should be a read-only object', function() { 63 | _response.metadata.should.be.an.Object().with.any.properties(['action', 'application', 'path', 'uri', 'timestamp', 'duration']) 64 | Object.isFrozen(_response.metadata).should.be.true() 65 | should(function() { 66 | _response.metadata.uri = 'TEST' 67 | }).throw() 68 | }) 69 | }) 70 | 71 | describe('error', function() { 72 | it('should be a UsergridResponseError object', function(done) { 73 | 74 | this.slow(_slow) 75 | this.timeout(_timeout) 76 | 77 | client.GET(config.test.collection, 'BADNAMEORUUID', function(err, usergridResponse) { 78 | usergridResponse.error.should.be.an.instanceof(UsergridResponseError) 79 | done() 80 | }) 81 | }) 82 | }) 83 | 84 | describe('users', function() { 85 | 86 | this.slow(_slow) 87 | this.timeout(_timeout) 88 | 89 | it('response.users should be an array of UsergridUser objects', function(done) { 90 | client.setAppAuth(config.clientId, config.clientSecret, config.tokenTtl) 91 | client.authenticateApp(function(err) { 92 | should(err).be.undefined() 93 | client.GET('users', function(err, usergridResponse) { 94 | usergridResponse.ok.should.be.true() 95 | usergridResponse.users.should.be.an.Array() 96 | usergridResponse.users.forEach(function(user) { 97 | user.should.be.an.instanceof(UsergridUser) 98 | }) 99 | done() 100 | }) 101 | }) 102 | }) 103 | }) 104 | 105 | describe('user', function() { 106 | 107 | this.slow(_slow) 108 | this.timeout(_timeout) 109 | 110 | var user 111 | 112 | it('response.user should be a UsergridUser object and have a valid uuid matching the first object in response.users', function(done) { 113 | client.setAppAuth(config.clientId, config.clientSecret, config.tokenTtl) 114 | client.authenticateApp(function(err) { 115 | should(err).be.undefined() 116 | client.GET('users', function(err, usergridResponse) { 117 | user = usergridResponse.user 118 | user.should.be.an.instanceof(UsergridUser).with.property('uuid').equal(_.first(usergridResponse.entities).uuid) 119 | done() 120 | }) 121 | }) 122 | }) 123 | 124 | it('response.user should be a subclass of UsergridEntity', function(done) { 125 | user.isUser.should.be.true() 126 | user.should.be.an.instanceof(UsergridEntity) 127 | done() 128 | }) 129 | }) 130 | 131 | describe('entities', function() { 132 | it('should be an array of UsergridEntity objects', function() { 133 | _response.entities.should.be.an.Array() 134 | _response.entities.forEach(function(entity) { 135 | entity.should.be.an.instanceof(UsergridEntity) 136 | }) 137 | }) 138 | }) 139 | 140 | describe('first, entity', function() { 141 | it('response.first should be a UsergridEntity object and have a valid uuid matching the first object in response.entities', function() { 142 | _response.first.should.be.an.instanceof(UsergridEntity).with.property('uuid').equal(_.first(_response.entities).uuid) 143 | }) 144 | 145 | it('response.entity should be a reference to response.first', function() { 146 | _response.should.have.property('entity').deepEqual(_response.first) 147 | }) 148 | }) 149 | 150 | describe('last', function() { 151 | it('last should be a UsergridEntity object and have a valid uuid matching the last object in response.entities', function() { 152 | _response.last.should.be.an.instanceof(UsergridEntity).with.property('uuid').equal(_.last(_response.entities).uuid) 153 | }) 154 | }) 155 | 156 | describe('hasNextPage', function() { 157 | this.slow(_slow) 158 | this.timeout(_timeout) 159 | 160 | it('should be true when more entities exist', function(done) { 161 | client.GET(config.test.collection, function(err, usergridResponse) { 162 | usergridResponse.hasNextPage.should.be.true() 163 | done() 164 | }) 165 | }) 166 | 167 | it('should be false when no more entities exist', function(done) { 168 | client.GET('users', function(err, usergridResponse) { 169 | usergridResponse.metadata.count.should.be.lessThan(10) 170 | usergridResponse.hasNextPage.should.not.be.true() 171 | done() 172 | }) 173 | }) 174 | }) 175 | 176 | describe('loadNextPage()', function() { 177 | this.slow(_slow + 800) 178 | this.timeout(_timeout + 2000) 179 | 180 | var firstResponse 181 | 182 | before(function(done) { 183 | 184 | this.slow(_slow) 185 | this.timeout(_timeout) 186 | 187 | var query = new UsergridQuery(config.test.collection).limit(10) 188 | 189 | client.GET(query, function(err, usergridResponse) { 190 | firstResponse = usergridResponse 191 | done() 192 | }) 193 | }) 194 | 195 | it('should load a new page of entities by passing an instance of UsergridClient', function(done) { 196 | firstResponse.loadNextPage(client, function(err, usergridResponse) { 197 | usergridResponse.first.uuid.should.not.equal(firstResponse.first.uuid) 198 | usergridResponse.entities.length.should.equal(10) 199 | done() 200 | }) 201 | }) 202 | }) -------------------------------------------------------------------------------- /tests/lib/responseError.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var should = require('should'), 18 | config = require('../../helpers').config, 19 | UsergridClient = require('../../lib/client'), 20 | UsergridResponseError = require('../../lib/responseError') 21 | 22 | var client = new UsergridClient() 23 | 24 | var _response, 25 | _slow = 500, 26 | _timeout = 4000 27 | 28 | describe('name, description, exception', function() { 29 | 30 | before(function(done) { 31 | 32 | this.slow(_slow) 33 | this.timeout(_timeout) 34 | 35 | client.GET(config.test.collection, 'BADNAMEORUUID', function(err, usergridResponse) { 36 | _response = usergridResponse 37 | done() 38 | }) 39 | }) 40 | 41 | it('response.statusCode should be greater than or equal to 400', function() { 42 | _response.ok.should.be.false() 43 | }) 44 | 45 | it('response.error should be a UsergridResponseError object with name, description, and exception keys', function() { 46 | _response.ok.should.be.false() 47 | _response.error.should.be.an.instanceof(UsergridResponseError).with.properties(['name', 'description', 'exception']) 48 | }) 49 | }) 50 | 51 | describe('undefined check', function() { 52 | it('response.error should be undefined on a successful response', function(done) { 53 | this.slow(_slow) 54 | this.timeout(_timeout) 55 | client.GET(config.test.collection, function(err, usergridResponse) { 56 | usergridResponse.ok.should.be.true() 57 | should(usergridResponse.error).be.undefined() 58 | done() 59 | }) 60 | }) 61 | }) -------------------------------------------------------------------------------- /tests/lib/user.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var should = require('should'), 18 | util = require('util'), 19 | chance = new require('chance').Chance(), 20 | config = require('../../helpers').config, 21 | UsergridClient = require('../../lib/client'), 22 | UsergridUser = require('../../lib/user'), 23 | UsergridQuery = require('../../lib/query') 24 | 25 | var _slow = 1500, 26 | _timeout = 4000, 27 | _username1 = chance.word(), 28 | _user1 = new UsergridUser({ 29 | username: _username1, 30 | password: config.test.password 31 | }), 32 | client = new UsergridClient(config) 33 | 34 | before(function(done) { 35 | 36 | this.slow(_slow) 37 | this.timeout(_timeout) 38 | 39 | var query = new UsergridQuery('users').not.eq('username', config.test.username).limit(20) 40 | // clean up old user entities as the UsergridResponse tests rely on this collection containing less than 10 entities 41 | client.DELETE(query, function() { 42 | _user1.create(client, function() { 43 | done() 44 | }) 45 | }) 46 | }) 47 | 48 | describe('create()', function() { 49 | 50 | it(util.format("should create a new user with the username '%s'", _username1), function() { 51 | _user1.username.should.equal(_username1) 52 | }) 53 | 54 | it('should have a valid uuid', function() { 55 | _user1.should.have.property('uuid').which.is.a.uuid() 56 | }) 57 | 58 | it('should have a created date', function() { 59 | _user1.should.have.property('created') 60 | }) 61 | 62 | it('should be activated (i.e. has a valid password)', function() { 63 | _user1.should.have.property('activated').true() 64 | }) 65 | 66 | it('should not have a password property', function() { 67 | _user1.should.not.have.property('password') 68 | }) 69 | 70 | it('should fail gracefully when a username already exists', function(done) { 71 | var user = new UsergridUser({ 72 | username: _username1, 73 | password: config.test.password 74 | }) 75 | user.create(client, function(err, usergridResponse) { 76 | err.should.not.be.null() 77 | err.should.containDeep({ 78 | name: 'duplicate_unique_property_exists' 79 | }) 80 | usergridResponse.ok.should.be.false() 81 | done() 82 | }) 83 | }) 84 | 85 | it('should create a new user on the server', function(done) { 86 | var username = chance.word() 87 | var user = new UsergridUser({ 88 | username: username, 89 | password: config.test.password 90 | }) 91 | user.create(client, function(err, usergridResponse, user) { 92 | client.isSharedInstance.should.be.false() 93 | user.username.should.equal(username) 94 | user.should.have.property('uuid').which.is.a.uuid() 95 | user.should.have.property('created') 96 | user.should.have.property('activated').true() 97 | user.should.not.have.property('password') 98 | // cleanup 99 | user.remove(client, function() { 100 | done() 101 | }) 102 | }) 103 | }) 104 | }) 105 | 106 | describe('login()', function() { 107 | 108 | this.slow(_slow) 109 | this.timeout(_timeout) 110 | 111 | it(util.format("it should log in the user '%s' and receive a token", _username1), function(done) { 112 | _user1.password = config.test.password 113 | _user1.login(client, function(err, response, token) { 114 | _user1.auth.should.have.property('token').equal(token) 115 | _user1.should.not.have.property('password') 116 | _user1.auth.should.not.have.property('password') 117 | done() 118 | }) 119 | }) 120 | }) 121 | 122 | describe('logout()', function() { 123 | 124 | this.slow(_slow) 125 | this.timeout(_timeout) 126 | 127 | it(util.format("it should log out '%s' and destroy the saved UsergridUserAuth instance", _username1), function(done) { 128 | _user1.logout(client, function(err, response) { 129 | response.ok.should.be.true() 130 | response.body.action.should.equal("revoked user token") 131 | _user1.auth.isValid.should.be.false() 132 | done() 133 | }) 134 | }) 135 | 136 | it("it should return an error when attempting to log out a user that does not have a valid token", function(done) { 137 | _user1.logout(client, function(err) { 138 | err.should.containDeep({ 139 | name: 'no_valid_token' 140 | }) 141 | done() 142 | }) 143 | }) 144 | }) 145 | 146 | describe('logoutAllSessions()', function() { 147 | it(util.format("it should log out all tokens for the user '%s' destroy the saved UsergridUserAuth instance", _username1), function(done) { 148 | _user1.password = config.test.password 149 | _user1.login(client, function() { 150 | _user1.logoutAllSessions(client, function(err, response) { 151 | response.ok.should.be.true() 152 | response.body.action.should.equal("revoked user tokens") 153 | _user1.auth.isValid.should.be.false() 154 | done() 155 | }) 156 | }) 157 | }) 158 | }) 159 | 160 | describe('resetPassword()', function() { 161 | 162 | this.slow(_slow) 163 | this.timeout(_timeout) 164 | 165 | it(util.format("it should reset the password for '%s' by passing parameters", _username1), function(done) { 166 | _user1.resetPassword(client, config.test.password, '2cool4u', function(err, response) { 167 | response.ok.should.be.true() 168 | response.body.action.should.equal("set user password") 169 | done() 170 | }) 171 | }) 172 | 173 | it(util.format("it should reset the password for '%s' by passing an object", _username1), function(done) { 174 | _user1.resetPassword(client, { 175 | oldPassword: '2cool4u', 176 | newPassword: config.test.password 177 | }, function(err, response) { 178 | response.ok.should.be.true() 179 | response.body.action.should.equal("set user password") 180 | done() 181 | }) 182 | }) 183 | 184 | it(util.format("it should not reset the password for '%s' when passing a bad old password", _username1), function(done) { 185 | _user1.resetPassword(client, { 186 | oldPassword: 'BADOLDPASSWORD', 187 | newPassword: config.test.password 188 | }, function(err, response) { 189 | response.ok.should.be.false() 190 | err.name.should.equal('auth_invalid_username_or_password') 191 | _user1.remove(client, function() { 192 | done() 193 | }) 194 | }) 195 | }) 196 | 197 | it("it should return an error when attempting to reset a password with missing arguments", function() { 198 | should(function() { 199 | _user1.resetPassword(client, 'NEWPASSWORD', function() {}) 200 | }).throw() 201 | }) 202 | }) 203 | 204 | describe('CheckAvailable()', function() { 205 | 206 | this.slow(_slow) 207 | this.timeout(_timeout) 208 | 209 | var nonExistentEmail = util.format('%s@%s.com', chance.word(), chance.word()) 210 | var nonExistentUsername = chance.word() 211 | 212 | it(util.format("it should return true for username '%s'", config.test.username), function(done) { 213 | UsergridUser.CheckAvailable(client, { 214 | username: config.test.username 215 | }, function(err, response, exists) { 216 | exists.should.be.true() 217 | done() 218 | }) 219 | }) 220 | 221 | it(util.format("it should return true for email '%s'", config.test.email), function(done) { 222 | UsergridUser.CheckAvailable(client, { 223 | email: config.test.email 224 | }, function(err, response, exists) { 225 | exists.should.be.true() 226 | done() 227 | }) 228 | }) 229 | 230 | it(util.format("it should return true for email '%s' and non-existent username '%s'", config.test.email, nonExistentUsername), function(done) { 231 | UsergridUser.CheckAvailable(client, { 232 | email: config.test.email, 233 | username: nonExistentUsername 234 | }, function(err, response, exists) { 235 | exists.should.be.true() 236 | done() 237 | }) 238 | }) 239 | 240 | it(util.format("it should return true for non-existent email '%s' and username '%s'", nonExistentEmail, config.test.username), function(done) { 241 | UsergridUser.CheckAvailable(client, { 242 | email: nonExistentEmail, 243 | username: config.test.username 244 | }, function(err, response, exists) { 245 | exists.should.be.true() 246 | done() 247 | }) 248 | }) 249 | 250 | it(util.format("it should return true for email '%s' and username '%s'", config.test.email, config.test.username), function(done) { 251 | UsergridUser.CheckAvailable(client, { 252 | email: config.test.email, 253 | username: config.test.useranme 254 | }, function(err, response, exists) { 255 | exists.should.be.true() 256 | done() 257 | }) 258 | }) 259 | 260 | it(util.format("it should return false for non-existent email '%s'", nonExistentEmail), function(done) { 261 | UsergridUser.CheckAvailable(client, { 262 | email: nonExistentEmail 263 | }, function(err, response, exists) { 264 | exists.should.be.false() 265 | done() 266 | }) 267 | }) 268 | 269 | it(util.format("it should return false for non-existent username '%s'", nonExistentUsername), function(done) { 270 | UsergridUser.CheckAvailable(client, { 271 | username: nonExistentUsername 272 | }, function(err, response, exists) { 273 | exists.should.be.false() 274 | done() 275 | }) 276 | }) 277 | 278 | it(util.format("it should return false for non-existent email '%s' and non-existent username '%s'", nonExistentEmail, nonExistentUsername), function(done) { 279 | UsergridUser.CheckAvailable(client, { 280 | email: nonExistentEmail, 281 | username: nonExistentUsername 282 | }, function(err, response, exists) { 283 | exists.should.be.false() 284 | done() 285 | }) 286 | }) 287 | }) -------------------------------------------------------------------------------- /tests/lib/usergrid.init.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var config = require('../../helpers').config, 18 | Usergrid = require('../../usergrid'), 19 | UsergridClient = require('../../lib/client'), 20 | util = require('util'), 21 | _ = require('lodash') 22 | 23 | describe('init() / initSharedInstance()', function() { 24 | it('should be an instance of UsergridClient', function(done) { 25 | Usergrid.init() 26 | Usergrid.initSharedInstance() 27 | Usergrid.should.be.an.instanceof(UsergridClient) 28 | done() 29 | }) 30 | 31 | it(util.format('should be testing against a Usergrid v%s instance', config.target), function(done) { 32 | util.format('%s', config.target).should.equal((_.last(process.argv)).startsWith('--target=') ? _.last(process.argv).replace(/--target=/, '') : '2.1') 33 | done() 34 | }) 35 | }) -------------------------------------------------------------------------------- /tests/lib/usergrid.singleton.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var Usergrid = require('../../usergrid') 18 | 19 | it('should be initialized when defined in another module', function(done) { 20 | Usergrid.should.have.property('isInitialized').which.is.true() 21 | Usergrid.should.have.property('isSharedInstance').which.is.true() 22 | done() 23 | }) -------------------------------------------------------------------------------- /tests/lib/usergrid.teardown.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var Usergrid = require('../../usergrid'), 18 | UsergridClient = require('../../lib/client') 19 | 20 | it('should be destroyed', function(done) { 21 | // destroy shared instance to prevent other tests from defaulting to use the shared instance 22 | var UsergridRef = require.resolve('../../usergrid') 23 | delete require.cache[UsergridRef] 24 | Usergrid = require('../../usergrid') 25 | Usergrid.should.have.property('isInitialized').which.is.false() 26 | Usergrid.should.not.be.an.instanceof(UsergridClient) 27 | done() 28 | }) -------------------------------------------------------------------------------- /tests/main.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | // module config 18 | var should = require('should'), 19 | _ = require('lodash') 20 | 21 | _.mixin(require('lodash-uuid')) 22 | 23 | should.Assertion.add('uuid', function() { 24 | this.params = { 25 | operator: 'to be a valid uuid' 26 | } 27 | this.assert(_.isUuid(this.obj)) 28 | }) 29 | 30 | should.Assertion.add('buffer', function() { 31 | this.params = { 32 | operator: 'to be buffer data' 33 | } 34 | this.assert(Buffer.isBuffer(this.obj)) 35 | }) 36 | 37 | // end module config 38 | 39 | describe('Usergrid initialization', function() { 40 | return require('./lib/usergrid.init.test') 41 | }) 42 | 43 | describe('Usergrid singleton', function() { 44 | return require('./lib/usergrid.singleton.test') 45 | }) 46 | 47 | describe('Usergrid teardown', function() { 48 | return require('./lib/usergrid.teardown.test') 49 | }) 50 | 51 | describe('UsergridClient initialization', function() { 52 | return require('./lib/client.init.test') 53 | }) 54 | 55 | describe('UsergridClient REST operations', function() { 56 | return require('./lib/client.rest.test') 57 | }) 58 | 59 | describe('UsergridClient connections', function() { 60 | return require('./lib/client.connections.test') 61 | }) 62 | 63 | describe('UsergridClient authentication', function() { 64 | return require('./lib/client.auth.test') 65 | }) 66 | 67 | describe('UsergridQuery', function() { 68 | return require('./lib/query.test') 69 | }) 70 | 71 | describe('UsergridResponse', function() { 72 | return require('./lib/response.test') 73 | }) 74 | 75 | describe('UsergridResponseError', function() { 76 | return require('./lib/responseError.test') 77 | }) 78 | 79 | describe('UsergridEntity', function() { 80 | return require('./lib/entity.test') 81 | }) 82 | 83 | describe('UsergridUser', function() { 84 | return require('./lib/user.test') 85 | }) 86 | 87 | describe('UsergridAsset', function() { 88 | return require('./lib/asset.test') 89 | }) -------------------------------------------------------------------------------- /usergrid.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | 'use strict' 16 | 17 | var _ = require('lodash') 18 | 19 | var Usergrid = { 20 | isInitialized: false, 21 | isSharedInstance: true, 22 | initSharedInstance: function(options) { 23 | var self = this 24 | if (self.isInitialized) { 25 | console.log('Usergrid shared instance is already initialized') 26 | return self 27 | } 28 | var UsergridClient = require('./lib/client') 29 | Object.setPrototypeOf(self, new UsergridClient(options)) 30 | _.assign(self, new UsergridClient(options)) 31 | self.isInitialized = true 32 | self.isSharedInstance = true 33 | } 34 | } 35 | 36 | Usergrid.init = Usergrid.initSharedInstance 37 | module.exports = Usergrid --------------------------------------------------------------------------------