Skip to content

Commit

Permalink
Release v1.1 (proxies)
Browse files Browse the repository at this point in the history
  • Loading branch information
6nv committed Nov 3, 2023
1 parent 9fa0a42 commit fb47261
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
dist/
node_modules/
package-lock.json
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 1.1.0

- Add client option to use a proxy
- Add brief documentation on connecting through a proxy

## 1.0.5

- Add `requests` dependency (Python)
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ const api = new Client('https://dctrack.example.com/', { username: 'user', passw
const api = new Client('https://dctrack.example.com/', { apiToken: 'token' });
```

## Advanced: Initialize a connection with a proxy
> Proxies can be used for either authentication method (username/password or API token.) For the Python library, specify an HTTP or HTTPS proxy, or both. For the JavaScript library, only 1 proxy is required and HTTPS will automatically upgraded to TLS.
### Python
```py
api = Client('https://dctrack.example.com/', username='user', password='pass', httpProxy='http://proxy:port', httpsProxy='https://proxy:port')
```

### JavaScript
```js
const api = new Client('https://dctrack.example.com/', { username: 'user', password: 'pass' }, 'http://proxy:port');
```

## Obtain an API Token
> Obtain an API token using the `Client.generateToken()` function provided. Re-authentication is not necessary, as the API token will automatically be used in subsequent API calls. The function returns the token's value in case the user wants to store the token for the next initialization of the API.
Expand Down
8 changes: 6 additions & 2 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,9 @@
"bugs": {
"url": "https://github.com/nicfv/dcTrackClient/issues"
},
"homepage": "https://github.com/nicfv/dcTrackClient#readme"
}
"homepage": "https://github.com/nicfv/dcTrackClient#readme",
"dependencies": {
"https-proxy-agent": "^7.0.2",
"node-fetch": "^3.3.2"
}
}
10 changes: 7 additions & 3 deletions js/src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import fetch from 'node-fetch';
import { HttpsProxyAgent } from 'https-proxy-agent';
/**
* Sunbird dcTrack API client version %VERSION% in JavaScript
*/
Expand All @@ -6,22 +8,24 @@ export class Client {
#username;
#password;
#apiToken;
#proxyAgent;
/**
* Provide either a username and password, or an API token to access the dcTrack database with JavaScript.
*/
constructor(base_url = '', credentials = { username: '', password: '', apiToken: '' }) {
constructor(base_url = '', credentials = { username: '', password: '', apiToken: '' }, proxy = '') {
this.#base_url = base_url;
this.#username = credentials.username;
this.#password = credentials.password;
this.#apiToken = credentials.apiToken;
this.#proxyAgent = new HttpsProxyAgent(proxy);
}

/**
* Generate and return an API token.
*/
async generateToken() {
if (this.#username && this.#password && !this.#apiToken) {
return (await fetch(this.#base_url + '/api/v2/authentication/login', { method: 'POST', headers: [['Authorization', 'Basic ' + btoa(this.#username + ':' + this.#password)]] })).headers.get('Authorization').split(' ')[1];
return (await fetch(this.#base_url + '/api/v2/authentication/login', { method: 'POST', agent: this.#proxyAgent, headers: [['Authorization', 'Basic ' + btoa(this.#username + ':' + this.#password)]] })).headers.get('Authorization').split(' ')[1];
} else {
throw new Error('Username/password undefined or token predefined.');
}
Expand All @@ -34,6 +38,6 @@ export class Client {
if (!this.#apiToken) {
this.#apiToken = await this.generateToken();
}
return (await fetch(this.#base_url + '/' + endpoint, { method: method, headers: [['Authorization', 'Bearer ' + this.#apiToken], ['Content-Type', 'application/json']], body: JSON.stringify(body) })).json();
return (await fetch(this.#base_url + '/' + endpoint, { method: method, agent: this.#proxyAgent, headers: [['Authorization', 'Bearer ' + this.#apiToken], ['Content-Type', 'application/json']], body: JSON.stringify(body) })).json();
}
}
11 changes: 8 additions & 3 deletions py/src/dcTrackClient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,27 @@
class Client:
"""Sunbird dcTrack API client version %VERSION% in Python"""

def __init__(self, baseUrl: str, username: str = '', password: str = '', apiToken: str = ''):
def __init__(self, baseUrl: str, username: str = '', password: str = '', apiToken: str = '', httpProxy: str = '', httpsProxy: str = ''):
"""Provide either a username and password, or an API token to access the dcTrack database with Python."""
self.__BASE_URL = baseUrl
self.__USERNAME = username
self.__PASSWORD = password
self.__APITOKEN = apiToken
self.__PROXY = {}
if httpProxy:
self.__PROXY['http'] = httpProxy
if httpsProxy:
self.__PROXY['https'] = httpsProxy

def generateToken(self) -> str:
"""Generate and return an API token."""
if self.__USERNAME and self.__PASSWORD and not self.__APITOKEN:
return requests.request('POST', self.__BASE_URL + '/api/v2/authentication/login', auth=(self.__USERNAME, self.__PASSWORD)).headers['Authorization'].split()[1]
return requests.request('POST', self.__BASE_URL + '/api/v2/authentication/login', auth=(self.__USERNAME, self.__PASSWORD), proxies=self.__PROXY).headers['Authorization'].split()[1]
else:
raise Exception('Username/password undefined or token predefined.')

def __request(self, method: str, endpoint: str, body: dict = None):
"""Internal class method."""
if not self.__APITOKEN:
self.__APITOKEN = self.generateToken()
return requests.request(method, self.__BASE_URL + '/' + endpoint, json=body, headers={'Authorization': 'Bearer ' + self.__APITOKEN}).json()
return requests.request(method, self.__BASE_URL + '/' + endpoint, json=body, headers={'Authorization': 'Bearer ' + self.__APITOKEN}, proxies=self.__PROXY).json()

0 comments on commit fb47261

Please sign in to comment.