koa-pagination
Advanced tools
Comparing version 0.0.2 to 0.1.0
55
index.js
@@ -7,6 +7,6 @@ | ||
var _ = require('lodash'); | ||
var InvalidRangeError = require('./errors/invalid-range-error'); | ||
var MalformedRangeError = require('./errors/malformed-range-error'); | ||
var contentRange = require('content-range'); | ||
var parseRange = require('range-parser'); | ||
var RangeNotSatisfiableError = require('./errors/range-not-satisfiable-error'); | ||
var contentRangeFormat = require('http-content-range-format'); | ||
var rangeSpecifierParser = require('range-specifier-parser'); | ||
@@ -19,3 +19,2 @@ /** | ||
options = _.assign({ | ||
limit: 20, | ||
maximum: 50 | ||
@@ -25,12 +24,13 @@ }, options); | ||
return function *paginate(next) { | ||
// Ensure that `limit` is never higher than `maximum`. | ||
var limit = options.limit > options.maximum ? options.maximum : options.limit; | ||
var first = 0; | ||
var last = options.maximum; | ||
var maximum = options.maximum; | ||
var offset = 0; | ||
var unit = 'bytes'; | ||
// Handle `Range` header. | ||
if (this.get('Range')) { | ||
var range = parseRange(maximum + 1, this.get('Range')); | ||
var range = rangeSpecifierParser(this.get('Range')); | ||
if (range === -1) { | ||
throw new InvalidRangeError(); | ||
throw new RangeNotSatisfiableError(); | ||
} | ||
@@ -43,27 +43,38 @@ | ||
// Update `limit` and `offset` values. | ||
limit = range[0].end; | ||
offset = range[0].start; | ||
first = range.first; | ||
last = range.last; | ||
unit = range.unit; | ||
} | ||
// Set range values on context. | ||
// Set pagination object on context. | ||
this.pagination = { | ||
limit: limit, | ||
offset: offset | ||
limit: last + 1, | ||
offset: first | ||
}; | ||
// Prevent pages with more items than allowed. | ||
if ((last - first + 1) > maximum) { | ||
last = first + maximum - 1; | ||
} | ||
yield* next; | ||
// Fix limit value if is higher than count. | ||
if (limit > this.pagination.count) { | ||
limit = this.pagination.count; | ||
var length = this.pagination.length; | ||
// Fix `last` value if `length` is lower. | ||
if (last > length) { | ||
last = length - 1; | ||
} | ||
// Set `Content-Range` based on available items. | ||
this.set('Content-Range', contentRange.format({ | ||
count: this.pagination.count, | ||
limit: limit, | ||
name: 'items', | ||
offset: this.pagination.offset | ||
this.set('Content-Range', contentRangeFormat({ | ||
first: first, | ||
last: last, | ||
length: length, | ||
unit: unit | ||
})); | ||
// Set the response as `Partial Content`. | ||
this.status = 206; | ||
}; | ||
}; |
{ | ||
"name": "koa-pagination", | ||
"version": "0.0.2", | ||
"version": "0.1.0", | ||
"description": "Koa Pagination", | ||
@@ -10,7 +10,8 @@ "main": "index.js", | ||
"dependencies": { | ||
"content-range": "0.2.0", | ||
"create-error": "0.3.1", | ||
"debug": "2.1.0", | ||
"http-content-range-format": "1.0.0", | ||
"lodash": "2.4.1", | ||
"range-parser": "1.0.2" | ||
"range-specifier-parser": "0.1.0", | ||
"util": "0.10.3" | ||
}, | ||
@@ -34,9 +35,5 @@ "devDependencies": { | ||
}, | ||
"precommit": [ | ||
"forbidden-keywords", | ||
"jshint" | ||
], | ||
"author": "Seegno", | ||
"license": "MIT", | ||
"repository": "git@github.com:seegno/koa-pagination" | ||
"repository": "git@github.com:seegno/koa-paginate" | ||
} |
# Koa Pagination | ||
[![Build Status](https://travis-ci.org/seegno/koa-pagination.svg?branch=master)](https://travis-ci.org/seegno/koa-pagination) | ||
Koa Pagination is a middleware to handle [Range Pagination Headers](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) using `Range` & `Content-Range` entity-headers. | ||
## Installation | ||
Choose your preferred method: | ||
* npm: `npm install --save koa-pagination` | ||
* Download: [koa-pagination](https://github.com/seegno/koa-pagination) | ||
### Configuration | ||
The middleware can be configured with the following parameters: | ||
- Maximum: Maximum number of items allowed per page (50 items by default). | ||
You can change the defaults by doing: | ||
```js | ||
paginate({ | ||
maximum: 100 | ||
}); | ||
``` | ||
## Usage | ||
```js | ||
var koa = require('koa'); | ||
var paginate = require('koa-pagination'); | ||
var app = koa(); | ||
app.get('/', paginate(), function *() { | ||
// `paginate` middleware will inject a `pagination` object in the `koa` context, | ||
// which will allow you to use the `pagination.offset` and `pagination.limit` | ||
// in your data retrieval methods. | ||
this.body = foobar.getData({ | ||
limit: this.pagination.limit, | ||
offset: this.pagination.offset | ||
}); | ||
// This is needed in order to expose the length in `Content-Range` header. | ||
this.pagination.length = foobar.count(); | ||
}); | ||
app.listen(3000); | ||
``` | ||
### Request | ||
You can provide the `Range` header specifying the items you want to retrieve. For instance to retrieve the first 5 elements: | ||
```js | ||
'Range: items=0-5' | ||
``` | ||
### Response | ||
This will generate a response with the following `Content-Range` header: | ||
```js | ||
'Content-Range: items 0-4/*' | ||
``` | ||
The `*` will be replaced with the total number of items provided in the `length` variable. | ||
## Running tests | ||
```sh | ||
npm test | ||
``` |
@@ -10,2 +10,3 @@ | ||
var request = require('./request')(); | ||
var util = require('util'); | ||
@@ -19,12 +20,11 @@ chai.should(); | ||
describe('paginate', function() { | ||
it('should accept a `limit` option', function *() { | ||
it('should use the default values', function *() { | ||
var app = koa(); | ||
app.use(paginate({ | ||
limit: 5 | ||
})); | ||
app.use(paginate()); | ||
yield request(app.listen()) | ||
.get('/') | ||
.expect('Content-Range', 'items 0-4/*') | ||
.expect(206) | ||
.expect('Content-Range', 'bytes 0-49/*') | ||
.end(); | ||
@@ -42,7 +42,8 @@ }); | ||
.get('/') | ||
.expect('Content-Range', 'items 0-2/*') | ||
.expect(206) | ||
.expect('Content-Range', 'bytes 0-2/*') | ||
.end(); | ||
}); | ||
it('should set `Content-Range` headers by default', function *() { | ||
it('should accept a `Range` header', function *() { | ||
var app = koa(); | ||
@@ -54,7 +55,9 @@ | ||
.get('/') | ||
.expect('Content-Range', 'items 0-19/*') | ||
.set('Range', 'items=0-5') | ||
.expect(206) | ||
.expect('Content-Range', 'items 0-5/*') | ||
.end(); | ||
}); | ||
it('should accept a `Range` header', function *() { | ||
it('should give an error if the `Range` is malformed', function *() { | ||
var app = koa(); | ||
@@ -66,8 +69,8 @@ | ||
.get('/') | ||
.set('Range', 'items=0-5') | ||
.expect('Content-Range', 'items 0-4/*') | ||
.set('Range', 'invalid') | ||
.expect(412, 'Precondition Failed') | ||
.end(); | ||
}); | ||
it('should allow specifying a `count` variable in the pagination', function *() { | ||
it('should give an error if the `Range` is invalid', function *() { | ||
var app = koa(); | ||
@@ -77,15 +80,25 @@ | ||
app.use(function *(next) { | ||
this.pagination.count = 10; | ||
yield request(app.listen()) | ||
.get('/') | ||
.set('Range', 'bytes=5-1') | ||
.expect(416, 'Range Not Satisfiable') | ||
.end(); | ||
}); | ||
yield* next; | ||
}); | ||
it('should not allow `limit` value superior to `maximum`', function *() { | ||
var app = koa(); | ||
app.use(paginate({ | ||
maximum: 3 | ||
})); | ||
yield request(app.listen()) | ||
.get('/') | ||
.expect('Content-Range', 'items 0-9/10') | ||
.expect(206) | ||
.set('Range', 'items=0-5') | ||
.expect('Content-Range', 'items 0-2/*') | ||
.end(); | ||
}); | ||
it('should give an error if the `Range` is malformed', function *() { | ||
it('should not allow `limit` value superior to `length`', function *() { | ||
var app = koa(); | ||
@@ -95,34 +108,47 @@ | ||
app.use(function *() { | ||
this.pagination.length = 3; | ||
}); | ||
yield request(app.listen()) | ||
.get('/') | ||
.set('Range', 'invalid') | ||
.expect(412, 'Precondition Failed') | ||
.expect(206) | ||
.set('Range', 'items=0-5') | ||
.expect('Content-Range', 'items 0-2/3') | ||
.end(); | ||
}); | ||
it('should give an error if the `Range` is invalid', function *() { | ||
it('should set `limit` to `N+1` when `Range` is `items=0-N`', function *() { | ||
var app = koa(); | ||
var n = 5; | ||
app.use(paginate()); | ||
app.use(function *() { | ||
this.pagination.limit.should.equal(n + 1); | ||
}); | ||
yield request(app.listen()) | ||
.get('/') | ||
.set('Range', 'items=5-1') | ||
.expect(412, 'Precondition Failed') | ||
.expect(206) | ||
.set('Range', util.format('items=0-%s', n)) | ||
.end(); | ||
}); | ||
it('should not allow `limit` value superior to `maximum`', function *() { | ||
it('should set `offset` to `N` when `Range` is `items=N-5`', function *() { | ||
var app = koa(); | ||
var n = 2; | ||
app.use(paginate({ | ||
limit: 5, | ||
maximum: 3 | ||
})); | ||
app.use(paginate()); | ||
app.use(function *() { | ||
this.pagination.offset.should.equal(n); | ||
}); | ||
yield request(app.listen()) | ||
.get('/') | ||
.expect('Content-Range', 'items 0-2/*') | ||
.expect(206) | ||
.set('Range', util.format('items=%s-5', n)) | ||
.end(); | ||
}); | ||
}); |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
11436
13
215
73
6
3
+ Addedrange-specifier-parser@0.1.0
+ Addedutil@0.10.3
+ Addedhttp-content-range-format@1.0.0(transitive)
+ Addedinherits@2.0.1(transitive)
+ Addedrange-specifier-parser@0.1.0(transitive)
+ Addedutil@0.10.3(transitive)
- Removedcontent-range@0.2.0
- Removedrange-parser@1.0.2
- Removedcontent-range@0.2.0(transitive)
- Removedrange-parser@1.0.2(transitive)