diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..c77963f --- /dev/null +++ b/.jshintrc @@ -0,0 +1,54 @@ +{ +"maxerr": 50, +"bitwise": true, +"camelcase": false, +"curly": true, +"eqeqeq": true, +"forin": true, +"freeze": true, +"immed": false, +"indent": 4, +"latedef": false, +"newcap": false, +"noarg": true, +"noempty": true, +"nonbsp": true, +"nonew": false, +"plusplus": false, +"quotmark": false, +"undef": true, +"unused": true, +"strict": true, +"maxparams": false, +"maxdepth": false, +"maxstatements": false, +"maxcomplexity": false, +"maxlen": false, +"varstmt": true, +"asi": false, +"boss": false, +"debug": false, +"eqnull": false, +"esnext": true, +"moz": false, +"evil": false, +"expr": false, +"funcscope": false, +"globalstrict": false, +"iterator": false, +"lastsemic": false, +"laxbreak": false, +"laxcomma": false, +"loopfunc": false, +"multistr": false, +"noyield": false, +"notypeof": false, +"proto": false, +"scripturl": false, +"shadow": false, +"sub": false, +"supernew": false, +"validthis": false, +"node": true, +"globals": {} +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..698f7cd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +### 1.0.0-beta +**2016-08-18** + +Initial Beta Pre-release diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4d990e5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2016 VHX, Inc. (https://vhx.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 0336fe1..1777c38 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ # VHX Javascript API Client -API applications can be created in the [VHX admin](https://www.vhx.tv/admin/platforms) or by emailing [api@vhx.tv](mailto:api@vhx.tv) +Currently not ready for production use, but in Pre-Release. Publishable VHX API keys with limited scope will be required for use. ### Installation -`npm install vhxjs` +`npm install vhxjs@beta` ### Documentation -Documentation, including a step-by-step tutorial is available on the [VHX Developer Docs ](http://dev.vhx.tv/api?javascript) site. For Full API reference [go here](http://dev.vhx.tv/docs/api?javascript). ### Getting Started @@ -23,17 +22,16 @@ Every resource method has two arguments. The first argument is an options object ```js -// example customer create -vhxjs.customers.list({ - email: 'customer@email.com', - name: 'First Last' -}, function(err, customer){ +// example collections request +vhxjs.collections.all({ + product: 'https://api.vhx.tv/products/1' +}, function(err, collections){ // err, = error is false if no error occurred - // customer = the created customer object + // collections = the response object }); ``` -### Resources & methods +### Resources & Methods products * [`retrieve`](http://dev.vhx.tv/docs/api/?javascript#product-retrieve) @@ -52,3 +50,46 @@ collections * [`retrieve`](http://dev.vhx.tv/docs/api?javascript#collections-retrieve) * [`all`](http://dev.vhx.tv/docs/api?javascript#collections-list) * [`items`](http://dev.vhx.tv/docs/api?javascript#collection-items-list) + + +### Pagination Methods +Paginated resources will have helper methods available in the response object for conveniently requesting the next, previous, first, last, and specific pages. + +```javascript +var collectionList; + +vhxjs.collections.all({ + per_page: 25 +}, function(err, collections) { + collectionList = collections; +}) + +// example next page request with jquery +$('.next-page').on('click', function() { + collectionList.nextPage(function(err, collections) { + // if you want the items and count to be concatenated + collectionList.merge(collections); + + // if you just want the one page + collectionList = collections; + }); +}) +``` + +* `nextPage(callback:Function)`
+Gets the next page in the request. If you are on the last page the *No more pages to request* error will be thrown. + +* `previousPage(callback:Function)`
+Gets the previous page in the request. If you are on the first page the *No previous pages to request* error will be thrown. + +* `firstPage(callback:Function)`
+Gets the first page in the request. + +* `lastPage(callback:Function)`
+Gets the last page in the request. + +* `goToPage(page:Integer, callback:Function)`
+Gets the specified page in the request. If a non-integer or page greater than pages available is requested the *You must pass a valid page number* error will be thrown. + +* `merge(response:Object)`
+Merges a page with previously requested page. It is recommended to use this when using the `nextPage` method in a "Load More" scenario where you are appending items to a growing cascade. diff --git a/lib/paginate.js b/lib/paginate.js new file mode 100644 index 0000000..fd4b83c --- /dev/null +++ b/lib/paginate.js @@ -0,0 +1,79 @@ +'use strict'; + +class Paginate { + constructor(resource, args) { + let _this = this; + + _this.resource = resource; + _this.response = args.body; + _this.options = args.options; + _this.page = _this.options.page ? _this.options.page : 1; + _this.method = args.method; + _this.last = Math.ceil(args.body.total / (args.body.count * _this.page)); + + ['nextPage','previousPage','firstPage','lastPage'].map(function(key) { + _this.response[key] = function(callback) { + _this[key](_this, callback); + }; + }); + + _this.response.goToPage = function(num, callback) { + _this.goToPage(_this, num, callback); + }; + + _this.response.merge = function(_this) { + _this._embedded[_this.object] = this._embedded[_this.object].concat(_this._embedded[_this.object]); + _this.count = this.count + _this.count; + + return _this; + }; + } + + get() { + let _this = this; + + return _this.response; + } + + nextPage(_this, callback) { + _this.options.page = _this.page + 1; + + if (_this.options.page > _this.last) { + throw 'No more pages to request'; + } + + _this.resource[_this.method](_this.options, callback); + } + + previousPage(_this, callback) { + if (_this.page === 1) { + throw 'No previous pages to request'; + } + + _this.options.page = _this.page - 1; + _this.resource[_this.method](_this.options, callback); + } + + firstPage(_this, callback) { + _this.options.page = 1; + _this.resource[_this.method](_this.options, callback); + } + + lastPage(_this, callback) { + _this.options.page = _this.last; + _this.resource[_this.method](_this.options, callback); + } + + goToPage(_this, num, callback) { + num = parseInt(num, 10); + + if (num > 0 && num <= _this.last) { + _this.options.page = num; + return _this.resource[_this.method](_this.options, callback); + } + + throw 'You must pass a valid page number'; + } +} + +module.exports = Paginate; \ No newline at end of file diff --git a/lib/resource.js b/lib/resource.js index a6465ad..ca96281 100644 --- a/lib/resource.js +++ b/lib/resource.js @@ -1,7 +1,7 @@ 'use strict'; const request = require('superagent'); -const defaults = require('./defaults'); +const paginate = require('./paginate'); class Resource { constructor(api, path, methods, isToken) { @@ -192,7 +192,10 @@ class Resource { if (!err && response.statusCode >= 200 && response.statusCode < 300) { _this.successHandler({ body: response.body || null, - callback: args.callback + callback: args.callback, + options: args.options, + object: _this.path, + method: args.client_method }); } else { _this.errorHandler({ @@ -205,7 +208,17 @@ class Resource { } successHandler(args) { - args.callback(false, args.body); + let response = args.body; + + if (args.body.count && args.body.count < args.body.total) { + response = new paginate(this, args).get(); + } + + response.object = args.object; + + if (args.callback) { + args.callback(false, response); + } } errorHandler(args) { diff --git a/lib/vhx.js b/lib/vhx.js index 1d6633e..d8bb201 100644 --- a/lib/vhx.js +++ b/lib/vhx.js @@ -1,5 +1,4 @@ import defaults from './defaults'; -import Resource from './resource'; import Collection from './resources/collections'; import Video from './resources/videos'; import Customer from './resources/customers'; @@ -32,7 +31,7 @@ class vhx { host: defaults.HOST, protocol: defaults.PROTOCOL, timeout: defaults.TIMEOUT - } + }; } setToken() { @@ -42,7 +41,7 @@ class vhx { protocol: defaults.PROTOCOL, timeout: defaults.TIMEOUT, token_host: defaults.TOKEN_HOST - } + }; } prepareResources() {