Skip to content

Commit

Permalink
implement userAgent option
Browse files Browse the repository at this point in the history
Convenient alias for `headers['user-agent']`
  • Loading branch information
shinnn committed Jan 12, 2017
1 parent c9283fd commit 6497de2
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 25 deletions.
24 changes: 10 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,12 @@
[![Dependency Status](https://david-dm.org/shinnn/gh-get.svg)](https://david-dm.org/shinnn/gh-get)
[![devDependency Status](https://david-dm.org/shinnn/gh-get/dev-status.svg)](https://david-dm.org/shinnn/gh-get#info=devDependencies)

A [Node](https://nodejs.org/) module to create a request to the [Github API](https://developer.github.com/v3/)
A [Node.js](https://nodejs.org/) module to create a request to the [Github API](https://developer.github.com/v3/)

```javascript
const ghGet = require('gh-get');

ghGet('users/isaacs', {
headers: {
'user-agent': 'your application name'
}
}).then(response => {
ghGet('users/isaacs', {userAgent: 'your application name'}).then(response => {
response.body.login; //=> 'isaacs'
});
```
Expand All @@ -38,19 +34,21 @@ const ghGet = require('gh-get');

*url*: `String` ("path" part of a Github API URL)
*options*: `Object`
Return: `Object` ([`Promise`](https://promisesaplus.com/) instance)
Return: [`Promise`](https://promisesaplus.com/)

It makes a `GET` request to the [Github API](https://developer.github.com/v3/#overview) and returns a promise. Request method is overridable with the `method` [option](https://github.com/shinnn/gh-get#options).

When the API request finishes successfully, the promise will be [*fulfilled*](https://promisesaplus.com/#point-26) with the [`http.IncomingMessage`](https://nodejs.org/api/http.html#http_http_incomingmessage) object with the additional `body` property that contains a JSON object of the API response.

When the API request fails, the promise will be [*rejected*](https://promisesaplus.com/#point-30) with an error object.

#### Options

You can use [`Request` options](https://github.com/request/request#requestoptions-callback) and [the](https://github.com/shinnn/gh-get#optionstoken) [additional](https://github.com/shinnn/gh-get#optionsverbose) [ones](https://github.com/shinnn/gh-get#optionsbaseurl).
You can use [`Request` options](https://github.com/request/request#requestoptions-callback) and the following.

##### options.userAgent

Type: `String` (GitHub username or the name of your application)

Note that `headers['user-agent']` option is [required](https://developer.github.com/v3/#user-agent-required).
[Required](https://developer.github.com/v3/#user-agent-required). Add `user-agent` to the request header.

##### options.token

Expand All @@ -62,9 +60,7 @@ Use specific [GitHub access token](https://github.com/blog/1509-personal-api-tok
```javascript
ghGet('user', {
token: 'xxxxx' //=> for example @shinnn's access token
headers: {
'user-agent': 'Shinnosuke Watanabe https://github.com/shinnn/'
},
userAgent: 'Shinnosuke Watanabe https://github.com/shinnn/'
}).then(response => {
response.body.login; //=> 'shinnn'
});
Expand Down
29 changes: 22 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const inspect = require('util').inspect;
const fettucine = require('fettuccine');
const ghifyRequestOptions = require('ghify-request-options');

const UA_ERROR = 'Expected `userAgent` option to be a string of valid `user-agent` header';

module.exports = function ghGet(url, options) {
if (typeof url !== 'string') {
return Promise.reject(new TypeError(
Expand All @@ -18,18 +20,31 @@ module.exports = function ghGet(url, options) {
));
}

if (
!options ||
!options.headers ||
!Object.keys(options.headers).some(key => /^user-agent$/i.test(key) && options.headers[key])
) {
if (!options || (
!('userAgent' in options) && (
!options.headers ||
!Object.keys(options.headers).some(key => /^user-agent$/i.test(key) && options.headers[key])
)
)) {
return Promise.reject(new TypeError(
'`headers` option with a valid `user-agent` header is required,' +
'`userAgent` option (string) is required,' +
' because you must tell your username or application name to Github every API request.' +
' https://developer.github.com/v3/#user-agent-required'
));
}

if (options.userAgent !== undefined) {
if (typeof options.userAgent !== 'string') {
return Promise.reject(new TypeError(`${UA_ERROR}, but got ${inspect(options.userAgent)}.`));
}

if (options.userAgent.length === 0) {
return Promise.reject(new Error(`${UA_ERROR}, but got '' (empty string).`));
}

options.headers = Object.assign({}, options.headers, {'user-agent': options.userAgent});
}

if (options.verbose !== undefined && typeof options.verbose !== 'boolean') {
return Promise.reject(new TypeError(
inspect(options.verbose) +
Expand Down Expand Up @@ -57,6 +72,6 @@ module.exports = function ghGet(url, options) {
return Promise.reject(error);
}

return Promise.resolve(response);
return response;
});
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@shinnn/eslint-config-node": "^3.0.0",
"eslint": "^3.11.1",
"istanbul": "^0.4.5",
"lodash": "^4.17.4",
"tape": "^4.6.3"
},
"eslintConfig": {
Expand Down
45 changes: 41 additions & 4 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
'use strict';

const ghGet = require('.');
const pickBy = require('lodash/pickBy');
const test = require('tape');

process.env.GITHUB_TOKEN = '';

test('ghGet()', t => {
t.plan(12);
t.plan(16);

t.strictEqual(ghGet.name, 'ghGet', 'should have a function name.');

Expand All @@ -19,6 +20,22 @@ test('ghGet()', t => {
t.strictEqual(body.login, 'isaacs', 'should create an API request.');
}).catch(t.fail);

ghGet('rate_limit', {
userAgent: 'Shinnosuke Watanabe https://github.com/shinnn/gh-get',
jsonReviver: (key, value) => typeof value === 'number' ? Math.PI : value
}).then(({body, request}) => {
t.deepEqual(
pickBy(request.headers, (value, key) => /^user-agent$/i.test(key)),
{'user-agent': 'Shinnosuke Watanabe https://github.com/shinnn/gh-get'},
'should add user-agent header via `userAgent` option.'
);
t.strictEqual(
body.resources.core.limit,
Math.PI,
'should support Request options.'
);
}).catch(t.fail);

ghGet('foo123', {
headers: {
'USeR-aGenT': 'github:shinnn https://github.com/shinnn/gh-get'
Expand Down Expand Up @@ -63,18 +80,38 @@ test('ghGet()', t => {

ghGet('users/isaacs').then(t.fail, ({message}) => {
t.ok(
message.includes('`headers` option with a valid `user-agent` header is required'),
message.startsWith('`userAgent` option (string) is required, '),
'should fail when it doesn\'t take the seond argument.'
);
}).catch(t.fail);

ghGet('users/isaacs', {}).then(t.fail, err => {
ghGet('users/isaacs', {
headers: {
'usr-agent': '@shinnn https://github.com/shinnn/gh-get'
}
}).then(t.fail, ({message}) => {
t.ok(
err.message.includes('you must tell your username or application name to Github every API request. '),
message.includes('you must tell your username or application name to Github every API request. '),
'should fail when no user agent is specified.'
);
}).catch(t.fail);

ghGet('users/isaacs', {userAgent: new Set()}).then(t.fail, ({message}) => {
t.strictEqual(
message,
'Expected `userAgent` option to be a string of valid `user-agent` header, but got Set {}.',
'should fail when `userAgent` option is not a string.'
);
}).catch(t.fail);

ghGet('users/isaacs', {userAgent: ''}).then(t.fail, ({message}) => {
t.strictEqual(
message,
'Expected `userAgent` option to be a string of valid `user-agent` header, but got \'\' (empty string).',
'should fail when `userAgent` option is an empty string.'
);
}).catch(t.fail);

ghGet('users/isaacs', {
headers: {
'UseR-AgenT': 'https://github.com/shinnn/gh-get'
Expand Down

0 comments on commit 6497de2

Please sign in to comment.