Skip to content

Commit 7cb1b7f

Browse files
Refactor codebase to use ES modules, and update all libraries
1 parent 9462efb commit 7cb1b7f

12 files changed

Lines changed: 2146 additions & 1191 deletions

README.md

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,36 @@ A NodeJS client library for the OAuth application for Invision Community/Invisio
44
## Usage
55
### Install
66
```
7-
$ npm install lmg-git/ipsoauth-node-client
7+
$ npm install fpm-git/ipsoauth-node-client
88
```
99
### Use
1010
```javascript
11-
const IPSApi = require('ipsoauth-client');
11+
import { Site, Api } from 'ipsoauth-client';
1212

1313
// Construct
14-
let site = new IPSApi.Site({
14+
const site = new Site({
1515
clientID: "YourClientID",
1616
clientSecret: "YourClientSecret",
1717
baseURL: "https://example.com/forum/"
1818
});
1919

2020
// Get the URL that a user should be redirected to to grant access
21-
let url = site.authorizationURL("http://localhost/process_authorization", ["basic_info.read"], "state");
21+
const url = site.getAuthorizationURL("http://localhost/process_authorization", ["basic_info.read"], "state");
2222

2323
// Once the user returns to the redirect URI, process the authorization response
24-
site.processAuthorizationResponse(req.query, "http://localhost/process_authorization", "state", function(err, tokens, api) {
25-
// save tokens.toString() as a string in the database for the user, so future requests can be made
26-
27-
// Get information about the member
28-
api.core.member.get(function(err, response) {
24+
(async () => {
25+
try {
26+
const { tokens, api } = await site.processAuthorizationResponse(req.query, "http://localhost/process_authorization", "state");
27+
// save tokens.toString() as a string in the database for the user, so future requests can be made
28+
29+
// Get information about the member
30+
const response = await api.core.member.get();
2931
// Process the response
30-
});
31-
});
32+
} catch (err) {
33+
// Handle error
34+
}
35+
})();
3236

3337
// In future requests, to access the API, get tokens from the database, then construct the API instance using
34-
let api = IPSApi.Api(tokens, api);
35-
```
38+
const api = new Api(tokens, site);
39+
```

index.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
module.exports = {
2-
Api: require('./lib/Api'),
3-
errors: require('./lib/errors'),
4-
Site: require('./lib/Site'),
5-
Token: require('./lib/Token')
6-
};
1+
import Api from './lib/Api.js';
2+
import * as errors from './lib/errors.js';
3+
import Site from './lib/Site.js';
4+
import Token from './lib/Token.js';
5+
6+
export {Api, errors, Site, Token};

lib/Api.js

Lines changed: 60 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,71 @@
1-
const request = require('request');
2-
3-
const Token = require('./Token');
4-
const errors = require('./errors');
5-
6-
/*********************
7-
* Define the APIs *
8-
*********************/
9-
10-
let addApis = function(self) {
11-
self.core = {
12-
member: {
13-
get: function (cb) {
14-
self.request('/core/member', 'get', {}, cb);
15-
}
16-
}
17-
};
18-
};
19-
20-
/*********************
21-
* Define API Class *
22-
*********************/
1+
import Token from './Token.js';
2+
import * as errors from './errors.js';
3+
4+
function addApis(self) {
5+
self.core = {
6+
member: {
7+
async get() {
8+
return self.request('/core/member', 'get', {});
9+
}
10+
}
11+
};
12+
}
2313

24-
/**
25-
* API Connection library
26-
*
27-
* @param {Token|string} tokens
28-
* @param {Site} site
29-
* @constructor
30-
*/
31-
let Api = function(tokens, site) {
14+
class Api {
15+
constructor(tokens, site) {
3216
if (!(tokens instanceof Token)) {
33-
// Convert it to a token object (it's either a refresh token or serialised Token)
34-
tokens = new Token(tokens, site);
17+
tokens = new Token(tokens, site);
3518
}
3619
this.token = tokens;
3720
this.site = site;
38-
39-
// Add APIs
4021
addApis(this);
41-
};
42-
43-
44-
/**
45-
* Make an API request
46-
*
47-
* @param {string} endpoint
48-
* @param {string} method
49-
* @param {object} opts
50-
* @param {function} cb
51-
* @private
52-
*/
53-
Api.prototype.request = function (endpoint, method, opts, cb) {
54-
let self = this;
55-
56-
if (typeof opts === "function") {
57-
cb = opts;
58-
opts = {};
59-
}
22+
}
23+
24+
/**
25+
* Make an API request
26+
* @param {string} endpoint
27+
* @param {string} method
28+
* @param {object} opts
29+
* @returns {Promise<any>}
30+
*/
31+
async request(endpoint, method, opts = {}) {
6032
opts.method = method.toUpperCase();
61-
if (opts.headers === undefined) {
62-
opts.headers = {};
63-
}
33+
opts.headers = opts.headers || {};
6434
opts.headers['User-Agent'] = 'Node IPS OAuth Client/1.0';
6535

66-
this.token.getAccessToken(function (err, accessToken) {
67-
if (err) {
68-
return cb(err);
69-
}
70-
71-
let makeRequest = function(accessToken, retryOnTokenError) {
72-
let uri = self.site.apiRoot + endpoint;
73-
if (self.token.type === "query") {
74-
uri += (uri.indexOf('?') === -1 ? '?' : '&') + 'token=' + accessToken;
75-
} else if (self.token.type === "bearer") {
76-
opts.headers.Authorization = "Bearer " + accessToken;
77-
} else {
78-
cb(new Error("Unsupported token type"));
79-
}
80-
81-
request(uri, opts, function (err, response, body) {
82-
if (err) {
83-
return cb(err);
84-
}
85-
86-
let result = null;
87-
if (body) {
88-
try {
89-
result = JSON.parse(body);
90-
} catch (e) {
91-
return cb(new errors.BadResponseError(response.statusCode, body));
92-
}
93-
}
94-
95-
if (response.statusCode >= 300) {
96-
if (retryOnTokenError && response.statusCode === 401 && result && result.error === "invalid_token") {
97-
// Maybe an invalid/expired token
98-
return self.token.refreshAccessToken(function(err, accessToken) {
99-
if (err) {
100-
return cb(err);
101-
}
102-
return makeRequest(accessToken, false); // don't try again if this fails
103-
});
104-
}
105-
106-
if (result) {
107-
return cb(new errors.ApiResponseError(response.statusCode, result.error, result.message));
108-
} else {
109-
return cb(new errors.BadResponseError(response.statusCode, body));
110-
}
111-
}
112-
113-
return cb(null, result);
114-
});
115-
};
116-
117-
makeRequest(accessToken, true);
118-
});
119-
};
36+
const accessToken = await this.token.getAccessToken();
37+
let uri = this.site.apiRoot + endpoint;
38+
if (this.token.type === 'query') {
39+
uri += (uri.indexOf('?') === -1 ? '?' : '&') + 'token=' + accessToken;
40+
} else if (this.token.type === 'bearer') {
41+
opts.headers.Authorization = 'Bearer ' + accessToken;
42+
} else {
43+
throw new Error('Unsupported token type');
44+
}
12045

46+
let response = await fetch(uri, opts);
47+
let body = await response.text();
48+
let result = null;
49+
if (body) {
50+
try {
51+
result = JSON.parse(body);
52+
} catch (e) {
53+
throw new errors.BadResponseError(response.status, body);
54+
}
55+
}
56+
if (response.status >= 300) {
57+
if (response.status === 401 && result && result.error === 'invalid_token') {
58+
// Try to refresh token and retry once
59+
await this.token.refreshAccessToken();
60+
return this.request(endpoint, method, opts);
61+
}
62+
if (result && typeof result === 'object' && result.error) {
63+
throw new errors.ApiResponseError(response.status, result.error, result.message);
64+
}
65+
throw new errors.BadResponseError(response.status, body);
66+
}
67+
return result;
68+
}
69+
}
12170

122-
module.exports = Api;
71+
export default Api;

0 commit comments

Comments
 (0)