1 /** 2 * @fileOverview API class definition 3 */ 4 5 var Resource = require('./api/resource') 6 , Account = require('./api/account') 7 , Billing = require('./api/billing') 8 , Channel = require('./api/channel') 9 , Message = require('./api/message') 10 , Session = require('./api/session') 11 , Subscription = require('./api/subscription') 12 , Application = require('./api/application') 13 , Member = require('./api/member') 14 ; 15 16 /** 17 * Abstraction for the Spire API 18 * 19 * @class Collection of API methods 20 * 21 * @example 22 * var api = new API(options); 23 * 24 * @constructor 25 * @param {object} spire Spire object 26 * @param {object} [opts] Options 27 * @param {string} [opts.url] Spire api url 28 * @param {string} [opts.version] Version of the Spire api to use 29 */ 30 function API(spire, opts) { 31 /** 32 * Reference to spire object. 33 */ 34 this.spire = spire; 35 36 opts = opts || {}; 37 38 /** 39 * URL or spire.io API. 40 * Defaults to 'https://api.spire.io' 41 */ 42 this.url = opts.url || 'https://api.spire.io'; 43 44 /** 45 * Version of spire.io API to use. 46 * Defaults to '1.0' 47 */ 48 this.version = opts.version || '1.0'; 49 50 /** 51 * Schema definition for spire API. 52 */ 53 this.schema = null; 54 } 55 56 module.exports = API; 57 58 59 /** 60 * Make requests to the api. 61 * @function 62 * @see Resourse.prototype.request 63 */ 64 API.prototype.request = Resource.prototype.request; 65 66 /** 67 * Discovers urls from Spire API. Since this description does not change often, 68 * we only make the request once and cache the result for subsequent calls. 69 * 70 * @example 71 * api.discover(function (err, discovered) { 72 * if (!err) { 73 * // ... 74 * } 75 * }); 76 * 77 * @param {function(err, discovered)} cb Callback 78 */ 79 API.prototype.discover = function (cb) { 80 var api = this; 81 82 if (this.description) { 83 return process.nextTick(function () { 84 cb(null, api.description); 85 }); 86 } 87 88 this.request('discover', function (err, description) { 89 if (err) return cb(err); 90 api.description = description; 91 api.schema = description.schema[api.version]; 92 cb(null, description); 93 }); 94 }; 95 96 /** 97 * Creates a spire session from an account secret. 98 * 99 * @param {string} secret The acccount secret 100 * @param {function(err)} cb Callback 101 */ 102 API.prototype.createSession = function (secret, cb) { 103 var api = this; 104 this.discover(function (err) { 105 if (err) return cb(err); 106 api.request('create_session', secret, function (err, sessionData) { 107 if (err) return cb(err); 108 var session = new Session(api.spire, sessionData); 109 cb(null, session); 110 }); 111 }); 112 }; 113 114 /** 115 * Logs in with the given email and password. 116 * 117 * @param {string} email Email 118 * @param {string} password Password 119 * @param {function(err)} cb Callback 120 */ 121 API.prototype.login = function (email, password, cb) { 122 var api = this; 123 this.discover(function (err) { 124 if (err) return cb(err); 125 api.request('login', email, password, function (err, sessionData) { 126 if (err) return cb(err); 127 var session = new Session(api.spire, sessionData); 128 cb(null, session); 129 }); 130 }); 131 }; 132 133 /** 134 * Register for a new spire account, and authenticates as the newly created account 135 * 136 * @param {object} user User info 137 * @param {string} user.email Email 138 * @param {string} user.password Password 139 * @param {string} [user.password_confirmation] Optional password confirmation 140 * @param {function (err)} cb Callback 141 */ 142 API.prototype.createAccount = function (info, cb) { 143 var api = this; 144 this.discover(function (err) { 145 if (err) return cb(err); 146 api.request('create_account', info, function (err, sessionData) { 147 if (err) return cb(err); 148 var session = new Session(api.spire, sessionData); 149 cb(null, session); 150 }); 151 }); 152 }; 153 154 /** 155 * Request a password reset for email. 156 * 157 * @param {string} email Email 158 * @param {function (err)} cb Callback 159 */ 160 API.prototype.passwordResetRequest = function (email, cb) { 161 var api = this; 162 this.discover(function (err) { 163 if (err) return cb(err); 164 api.request('password_reset', email, cb); 165 }); 166 }; 167 168 /** 169 * Get billing information for the account. 170 * 171 * @param {function (err, billingResource)} cb Callback 172 */ 173 API.prototype.billing = function (cb) { 174 var api = this; 175 this.discover(function (err) { 176 if (err) return cb(err); 177 api.request('billing', function (err, billingData) { 178 if (err) return cb(err); 179 var billing = new Billing(api.spire, billingData); 180 cb(null, billing); 181 }); 182 }); 183 }; 184 185 /** 186 * Get Account from url and capabilities. 187 * 188 * Use this method to get the account without starting a spire session. 189 * 190 * If you have a spire session, you should use <code>spire.session.account</code>. 191 * 192 * @example 193 * var spire = new Spire(); 194 * spire.api.accountFromUrlAndCapabilities({ 195 * url: account_url, 196 * capabilities: account_capabilities 197 * }, function (err, account) { 198 * if (!err) { 199 * // ... 200 * } 201 * }) 202 * 203 * @param {object} creds Url and Capabilities 204 * @param {string} creds.url Url 205 * @param {string} creds.capabilities Capabilities 206 * @param {function (err, account)} cb Callback 207 */ 208 API.prototype.accountFromUrlAndCapabilities = function (creds, cb) { 209 var api = this; 210 this.discover(function (err) { 211 if (err) return cb(err); 212 var account = new Account(api.spire, creds); 213 account.get(cb); 214 }); 215 }; 216 217 /** 218 * Update Account from url and capability. 219 * 220 * @param {object} account Must contain at least Url and Capability 221 * @param {string} creds.url Url 222 * @param {string} creds.capability Capability 223 * @param {function (err, account)} cb Callback 224 */ 225 API.prototype.updateAccountWithUrlAndCapability = function (accountData, cb) { 226 var api = this; 227 this.discover(function (err) { 228 if (err) return cb(err); 229 api.request('update_account', accountData, function (err, acc) { 230 if (err) return cb(err); 231 var account = new Account(api.spire, acc); 232 cb(null, account); 233 }); 234 }); 235 }; 236 237 /** 238 * Get Channel from url and capabilities. 239 * 240 * Use this method to get a channel without starting a spire session. 241 * 242 * If you have a spire session, you should use <code>spire.channel</code>. 243 * 244 * @example 245 * var spire = new Spire(); 246 * spire.api.channelFromUrlAndCapabilities({ 247 * url: channel_url, 248 * capabilities: channel_capabilities 249 * }, function (err, channel) { 250 * if (!err) { 251 * // ... 252 * } 253 * }) 254 * 255 * @param {object} creds Url and Capabilities 256 * @param {string} creds.url Url 257 * @param {string} creds.capabilities Capabilities 258 * @param {function (err, channel)} cb Callback 259 */ 260 API.prototype.channelFromUrlAndCapabilities = function (creds, cb) { 261 var api = this; 262 this.discover(function (err) { 263 if (err) return cb(err); 264 var channel = new Channel(api.spire, creds); 265 channel.getIfCapable(cb); 266 }); 267 }; 268 269 /** 270 * Get Session from url and capabilities. 271 * 272 * @param {object} creds Url and Capabilities 273 * @param {string} creds.url Url 274 * @param {string} creds.capabilities Capabilities 275 * @param {function (err, subscription)} cb Callback 276 */ 277 API.prototype.sessionFromUrlAndCapabilities = function (creds, cb) { 278 var api = this; 279 this.discover(function (err) { 280 if (err) return cb(err); 281 var session = new Session(api.spire, creds); 282 api.spire.session = session; 283 session.getIfCapable(cb); 284 }); 285 }; 286 287 /** 288 * Get Subscription from url and capabilities. 289 * 290 * Use this method to get a subscription without starting a spire session. 291 * 292 * If you have a spire session, you should use <code>spire.subscription</code>. 293 * 294 * @example 295 * var spire = new Spire(); 296 * spire.api.subscriptionFromUrlAndCapabilities({ 297 * url: subscription_url, 298 * capabilities: subscription_capabilities 299 * }, function (err, subscription) { 300 * if (!err) { 301 * // ... 302 * } 303 * }) 304 * 305 * @param {object} creds Url and Capabilities 306 * @param {string} creds.url Url 307 * @param {string} creds.capabilities Capabilities 308 * @param {function (err, subscription)} cb Callback 309 */ 310 API.prototype.subscriptionFromUrlAndCapabilities = function (creds, cb) { 311 var api = this; 312 this.discover(function (err) { 313 if (err) return cb(err); 314 var subscription = new Subscription(api.spire, creds); 315 process.nextTick(function () { 316 cb(null, subscription); 317 }); 318 }); 319 }; 320 321 /** 322 * Returns the MIME type for resourceName. 323 * 324 * @param {string} [name] Name of the resource MIME type to return 325 * @returns {string} MIME type of the resource 326 */ 327 API.prototype.mediaType = function (resourceName) { 328 if (!this.schema) { 329 throw "No description object. Run `spire.api.discover` first."; 330 } 331 332 if (!this.schema[resourceName]) { 333 throw "No schema for resource " + resourceName; 334 } 335 336 return this.schema[resourceName].mediaType; 337 }; 338 339 /** 340 * Returns the Authorization header for the resource and method. 341 * 342 * @param {Resource} resource Resource 343 * @param {string} method Method 344 * @returns {string} Authorization header for the resource 345 */ 346 API.prototype.authorization = function (method, resource) { 347 return ['Capability', resource.capabilities[method]].join(' '); 348 }; 349 350 /** 351 * Returns an application when passed an application key. 352 * 353 * @param {string} key Application key 354 * @param {function(err)} cb Callback 355 */ 356 API.prototype.getApplication = function (key, cb) { 357 var api = this; 358 this.discover(function (err) { 359 if (err) return cb(err); 360 api.request('get_application', key, function (err, appData) { 361 if (err) return cb(err); 362 var application = new Application(api.spire, appData); 363 cb(null, application); 364 }); 365 }); 366 }; 367 368 /** 369 * Requests 370 * These define API calls and have no side effects. They can be run by calling 371 * this.request(<request name>); 372 */ 373 374 /** 375 * Gets the api description resource. 376 * @name discover 377 * @ignore 378 */ 379 Resource.defineRequest(API.prototype, 'discover', function () { 380 return { 381 method: 'get', 382 url: this.url, 383 headers: { 384 accept: "application/json" 385 } 386 }; 387 }); 388 389 /** 390 * Posts to sessions url with the accont secret. 391 * @name create_session 392 * @ignore 393 */ 394 Resource.defineRequest(API.prototype, 'create_session', function (secret) { 395 return { 396 method: 'post', 397 url: this.description.resources.sessions.url, 398 headers: { 399 'Content-Type': this.mediaType('account'), 400 'Accept': this.mediaType('session') 401 }, 402 content: {secret: secret} 403 }; 404 }); 405 406 /** 407 * Posts to sessions url with email and password. 408 * @name login 409 * @ignore 410 */ 411 Resource.defineRequest(API.prototype, 'login', function (email, password) { 412 return { 413 method: 'post', 414 url: this.description.resources.sessions.url, 415 headers: { 416 'Content-Type': this.mediaType('account'), 417 'Accept': this.mediaType('session') 418 }, 419 content: { 420 email: email, 421 password: password 422 } 423 }; 424 }); 425 426 /** 427 * Posts to accounts url with user info. 428 * @name create_account 429 * @ignore 430 */ 431 Resource.defineRequest(API.prototype, 'create_account', function (account) { 432 return { 433 method: 'post', 434 url: this.description.resources.accounts.url, 435 headers: { 436 'Content-Type': this.mediaType('account'), 437 'Accept': this.mediaType('session') 438 }, 439 content: account 440 }; 441 }); 442 443 /** 444 * Posts to accounts url with object containing email. 445 * @name password_reset 446 * @ignore 447 */ 448 Resource.defineRequest(API.prototype, 'password_reset', function (email) { 449 return { 450 method: 'post', 451 url: this.description.resources.accounts.url, 452 content: "", 453 query: { email: email } 454 }; 455 }); 456 457 /** 458 * Gets billing resource. 459 * @name billing 460 * @ignore 461 */ 462 Resource.defineRequest(API.prototype, 'billing', function () { 463 return { 464 method: 'get', 465 url: this.description.resources.billing.url, 466 content: "", 467 headers: { 468 'Accept': 'application/json' 469 } 470 }; 471 }); 472 473 /** 474 * Updates (puts) to the resouce. 475 * @name update 476 * @ignore 477 */ 478 Resource.defineRequest(API.prototype, 'update_account', function (data) { 479 return { 480 method: 'put', 481 url: data.url, 482 content: data, 483 headers: { 484 'Authorization': "Capability " + data.capability, 485 'Accept': this.mediaType('account'), 486 'Content-Type': this.mediaType('account') 487 } 488 }; 489 }); 490 491 /** 492 * Gets an application resource. 493 * @name application 494 * @ignore 495 */ 496 Resource.defineRequest(API.prototype, 'get_application', function (app_key) { 497 return { 498 method: 'get', 499 url: this.description.resources.applications.url, 500 content: "", 501 query: { application_key: app_key }, 502 headers: { 503 'Accept': this.mediaType('applications'), 504 'Content-Type': this.mediaType('applications') 505 } 506 }; 507 }); 508