Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sundry fixes #9

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

.DS_Store
node_modules/

config/development.json
Expand Down
68 changes: 63 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,76 @@ Node.js api gateway to the Amazon Giftcard On Demand Web service

## Configuration

Create a directory `config` in the root of your app. And add a `development.json`, `sandbox.json` and `production.json` in it that look like `config/example.json` in this repo.

Create a directory `.agcod` in the root of your app, adding your credentials to files named with the expected values of `NODE_ENV` e.g. `.agcod/sandbox.json`. Credentials look like this:
```json
{
"partnerId": "A2c4E",
"credentials": {
"accessKeyId": "AKI12345678",
"secretAccessKey": "qwertyQWERTY1234567QWERTYqwerty1234567"
}
}
```
## Usage
```javascript
const Client = require('agcod')
const client = new Client()
// the client can be created a number of ways:
// 1. will use the NODE_ENV to look for a .json file in .agcod/
const client = new Client()
// 2. may be given a file name containing credentials
const client = new Client('./sandbox.json')
// 3. explicitly passed a credentials object
const client = new Client({
"partnerId": "A2c4E",
"credentials": {
"accessKeyId": "AKI12345678",
"secretAccessKey": "qwertyQWERTY1234567QWERTYqwerty1234567"
}
})

// now create a gift card, indicating the country for which
// to create it and the amount

client.createGiftCard('US', 100, (error, result) => {
console.log('createGiftCard response', error, result)
})

// or use promises, a more modern approach

client.createGiftCard('US', 100).then(({res, signedRequest}) => {
console.log('createGiftCard response', res)
console.log('signed request', signedRequest) // useful for debugging purposes as this may be passed to curl
})

// an explicit currency may be passed but may be rejected by the system

client.createGiftCard('US', 100, 'GBP', (error, result) => {
console.log('createGiftCard response', error, result)
})

client.createGiftCard('NA', 123, 'USD', (error, result) => {
// an explicit currency may be passed but may be rejected by the system

client.createGiftCard('US', 100, 'GBP', (error, result) => {
console.log('client.createGiftCard response', error, result)
})
```

```
The above card creation will result in an object like:
```js
client.createGiftCard response null {
cardInfo: {
cardNumber: null,
cardStatus: 'Fulfilled',
expirationDate: null,
value: { amount: 100, currencyCode: 'USD' }
},
creationRequestId: 'Lif132190216bqrq',
gcClaimCode: '6X6M-PHKYAW-JMAY',
gcExpirationDate: null,
gcId: '1511461022170005',
status: 'SUCCESS'
}
```
## Tests
During tests requests are intercepted by nock and responds with a desired response code and contents.
- https://davidwalsh.name/nock
Expand Down
51 changes: 44 additions & 7 deletions config/default.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,61 @@
{
"currency": {
"US": "USD",
"IT": "EUR",
"ES": "EUR",
"DE": "EUR",
"FR": "EUR",
"UK": "GBP",
"TR": "TRY",
"UAE": "AED",
"KSA": "SAR",
"PL": "EUR",
"NL": "EUR",
"SE": "SEK",
"JP": "JPY",
"AU": "AUD",
"SG": "SGD"
},
"endpoint": {
"NA": {
"host": "agcod-v2-gamma.amazon.com",
"region": "us-east-1",
"countries": [ "US", "CA"]
"countries": [
"US",
"CA",
"MX"
]
},
"EU": {
"host": "agcod-v2-eu-gamma.amazon.com",
"region": "eu-west-1",
"countries": [ "IT", "ES", "DE", "FR", "UK"]
"countries": [
"IT",
"ES",
"DE",
"FR",
"UK",
"TR",
"UAE",
"KSA",
"PL",
"NL",
"SE"
]
},
"FE": {
"host": "agcod-v2-fe-gamma.amazon.com",
"region": "us-west-2",
"countries": "JP"
"countries": [
"JP",
"AU",
"SG"
]
}
},
"partnerId": "PARTNERID",
"partnerId": "",
"credentials": {
"accessKeyId": "ACCESSKEYID",
"secretAccessKey": "SECRETACCESSKEY"
"accessKeyId": "",
"secretAccessKey": ""
}
}
}
100 changes: 64 additions & 36 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,90 @@
const BigNumber = require('bignumber.js')
const request = require('request')
const aws4 = require('aws4')
const config = require('./config/default.json')
const helpers = require('./lib/helpers')
const fetch = require('node-fetch')

module.exports = class {
constructor(cfg = {}) {
this.config = Object.assign({}, cfg);
var env = process.env.NODE_ENV || 'development'
var fn = process.cwd() + '/.agcod/' + env + '.json'
if (typeof cfg == 'string') { fn = cfg; cfg = {} }
if (Object.keys(cfg).length == 0) {
cfg = require(fn)
}
this.config = Object.assign(config, cfg)
if (!this.config.partnerId)
throw new Error('No partner ID supplied')
var creds = this.config.credentials
if (!creds.accessKeyId || !creds.secretAccessKey)
throw new Error('Invalid credentials supplied!')
}

createGiftCard(region, amount, currencyCode, cb) {
this._checkRegion(region)
createGiftCard(country, amount, currencyCode, cb) {
var region = this._getRegion(country)
if (amount < 0)
throw new Error('Amounts supplied must be positive!')
if (!currencyCode || typeof currencyCode === 'function') {
cb = currencyCode
currencyCode = this.config.currency[country]
if (!currencyCode)
throw new Error('No currency available for county selected!')
}
const sequentialId = this._getNewId()
const requestBody = this._getCreateGiftCardRequestBody(sequentialId, amount, currencyCode)
const signedRequest = this._getSignedRequest(region, 'CreateGiftCard', requestBody)
const req = this._doRequest(signedRequest, cb)
const ret = this._doRequest(signedRequest)
if (cb) {
ret.then(({ res }) => cb(null, res, signedRequest)).catch(e => cb(e))
return
}

return {req, sequentialId, requestBody, signedRequest}
return ret
}

createGiftCardAgain(region, amount, currencyCode, sequentialId, cb) {
this._checkRegion(region)
const requestBody = this._getCreateGiftCardRequestBody(sequentialId, amount, currencyCode)
const signedRequest = this._getSignedRequest(region, 'CreateGiftCard', requestBody)
const req = this._doRequest(signedRequest, cb)

return {req, sequentialId, requestBody, signedRequest}
const ret = this._doRequest(signedRequest)
if (cb) {
ret.then(({ res }) => cb(null, res, signedRequest)).catch(e => cb(e))
return
}
return ret
}

cancelGiftCard(region, sequentialId, gcId, cb) {
this._checkRegion(region)
const requestBody = this._getCancelGiftCardRequestBody(sequentialId, gcId)
const signedRequest = this._getSignedRequest(region, 'CancelGiftCard', requestBody)
const req = this._doRequest(signedRequest, cb)

return {req, requestBody, signedRequest}
const req = this._doRequest(signedRequest)
if (cb) {
ret.then(({ res }) => cb(null, res, signedRequest)).catch(e => cb(e))
return
}
return ret
}

/**
* Throws when region is not NA, EU or FE
*/
_checkRegion(region) {
if (['NA', 'EU', 'FE'].indexOf(region) === -1 ) {
throw new Error(`First argument must be string NA, EU or FE`)
var regions = Object.keys(this.config.endpoint)
if (regions.indexOf(region) === -1) {
throw new Error('Region must be one of: ' + regions.join(', '))
}
}

_getRegion(country) {
var e = this.config.endpoint
for (var k of Object.keys(e)) {
if (e[k].countries.indexOf(country) > -1)
return k
}
throw new Error(`No valid country code provided!`)
}

/**
* Builds the request body to be POSTed for creating a gift card
* @returns {Object}
Expand Down Expand Up @@ -76,7 +116,7 @@ module.exports = class {
* @returns {Object}
*/
_getSignedRequest(region, action, requestBody) {
const credentials = this.config.credentials;
const credentials = this.config.credentials
const endpoint = this.config.endpoint[region]
const opts = {
region: endpoint.region,
Expand All @@ -103,35 +143,23 @@ module.exports = class {
* @param {Function} cb - Callback function
* @returns {Object} - whatever node-request returns
*/
_doRequest(signedRequest, cb) {
const params = {
method: 'POST',
url: `https://${signedRequest.host}${signedRequest.path}`,
headers: signedRequest.headers,
body: signedRequest.body
}

return request(params, (error, response, result) => {
if (error) return cb(error)

if (response.statusCode !== 200) {
const err = Object.assign({
request: params,
statusCode: response.statusCode
}, JSON.parse(result))

return cb(err)
}

return cb(null, JSON.parse(result))
})
_doRequest(signedRequest) {
var url = `https://${signedRequest.host}${signedRequest.path}`
return fetch(url, {
method: 'post',
headers: signedRequest.headers,
body: signedRequest.body,
}).then(res => res.json())
.then(res => ({ res, signedRequest }))
}

/**
* Generates a unique sequential base-36 string based on processor time
* @returns string with length of 10
*/
_getNewId() {
_getNewId() {
let hrTime = process.hrtime()
let id = new BigNumber(hrTime[0]).times('1e9').plus(hrTime[1]).toString(36)
return id
Expand Down
Loading