Skip to content

Commit

Permalink
Refactor headers
Browse files Browse the repository at this point in the history
  • Loading branch information
rattrayalex-stripe committed Jul 31, 2019
1 parent 800f098 commit 87fd537
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 59 deletions.
101 changes: 56 additions & 45 deletions lib/StripeResource.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ StripeResource.prototype = {

const requestDurationMs = Date.now() - req._requestStart;

const responseEvent = utils.removeEmpty({
const responseEvent = utils.removeNullish({
api_version: headers['stripe-version'],
account: headers['stripe-account'],
idempotency_key: headers['idempotency-key'],
Expand Down Expand Up @@ -262,38 +262,50 @@ StripeResource.prototype = {
return sleepSeconds * 1000;
},

_defaultHeaders(auth, contentLength, apiVersion) {
let userAgentString = `Stripe/v1 NodeBindings/${this._stripe.getConstant(
'PACKAGE_VERSION'
)}`;

if (this._stripe._appInfo) {
userAgentString += ` ${this._stripe.getAppInfoAsString()}`;
}

const headers = {
_makeHeaders(
auth,
contentLength,
apiVersion,
clientUserAgent,
defaultIdempotencyKey,
userSuppliedHeaders
) {
const defaultHeaders = {
// Use specified auth token or use default from this stripe instance:
Authorization: auth ? `Bearer ${auth}` : this._stripe.getApiField('auth'),
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': contentLength,
'User-Agent': userAgentString,
'User-Agent': this._getUserAgentString(),
'X-Stripe-Client-User-Agent': clientUserAgent,
'X-Stripe-Client-Telemetry': this._getTelemetryHeader(),
'Stripe-Version': apiVersion,
'Idempotency-Key': defaultIdempotencyKey,
};

if (apiVersion) {
headers['Stripe-Version'] = apiVersion;
}
return {
...utils.removeNullish(defaultHeaders),
// If the user supplied, say 'idempotency-key', override instead of appending.
...utils.normalizeHeaders(userSuppliedHeaders),
};
},

_getUserAgentString() {
const packageVersion = this._stripe.getConstant('PACKAGE_VERSION');
const appInfo = this._stripe._appInfo
? this._stripe.getAppInfoAsString()
: '';

return headers;
return `Stripe/v1 NodeBindings/${packageVersion} ${appInfo}`.trim();
},

_addTelemetryHeader(headers) {
_getTelemetryHeader() {
if (
this._stripe.getTelemetryEnabled() &&
this._stripe._prevRequestMetrics.length > 0
) {
const metrics = this._stripe._prevRequestMetrics.shift();
headers['X-Stripe-Client-Telemetry'] = JSON.stringify({
return JSON.stringify({
last_request_metrics: metrics,
});
}
Expand Down Expand Up @@ -339,15 +351,7 @@ StripeResource.prototype = {
ciphers: 'DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2:!MD5',
});

// If this is a POST and we allow multiple retries, set a idempotency key if one is not
// already provided.
if (method === 'POST' && this._stripe.getMaxNetworkRetries() > 0) {
if (!headers.hasOwnProperty('Idempotency-Key')) {
headers['Idempotency-Key'] = uuid();
}
}

const requestEvent = utils.removeEmpty({
const requestEvent = utils.removeNullish({
api_version: apiVersion,
account: headers['Stripe-Account'],
idempotency_key: headers['Idempotency-Key'],
Expand All @@ -373,7 +377,7 @@ StripeResource.prototype = {
}
});

req.on('error', (error) => {
req.once('error', (error) => {
if (this._shouldRetry(null, requestRetries)) {
return retryRequest(makeRequest, apiVersion, headers, requestRetries);
} else {
Expand Down Expand Up @@ -406,21 +410,23 @@ StripeResource.prototype = {

const apiVersion = this._stripe.getApiField('version');
requestData = data;
const headers = this._defaultHeaders(
auth,
requestData.length,
apiVersion
);

this._stripe.getClientUserAgent((cua) => {
headers['X-Stripe-Client-User-Agent'] = cua;

if (options.headers) {
Object.assign(headers, options.headers);
}

this._addTelemetryHeader(headers);

this._stripe.getClientUserAgent((clientUserAgent) => {
// If this is a POST and we allow multiple retries, set a idempotency key if one is not
// already provided.
const defaultIdempotencyKey =
method === 'POST' && this._stripe.getMaxNetworkRetries() > 0
? uuid()
: null;

const headers = this._makeHeaders(
auth,
requestData.length,
apiVersion,
clientUserAgent,
defaultIdempotencyKey,
options.headers
);
makeRequest(apiVersion, headers);
});
};
Expand All @@ -437,14 +443,19 @@ StripeResource.prototype = {
}

const retryRequest = (requestFn, apiVersion, headers, requestRetries) => {
requestRetries += 1;

return setTimeout(
// () => {
// try {
// requestFn(apiVersion, headers, requestRetries + 1)
// } catch (err) {

// }
// }
requestFn,
this._getSleepTimeInMS(requestRetries),
apiVersion,
headers,
requestRetries
requestRetries + 1
);
};
},
Expand Down
40 changes: 35 additions & 5 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,18 +202,48 @@ const utils = (module.exports = {
/**
* Remove empty values from an object
*/
removeEmpty: (obj) => {
removeNullish: (obj) => {
if (typeof obj !== 'object') {
throw new Error('Argument must be an object');
}

Object.keys(obj).forEach((key) => {
if (obj[key] === null || obj[key] === undefined) {
delete obj[key];
return Object.keys(obj).reduce((result, key) => {
if (obj[key] != null) {
result[key] = obj[key];
}
return result;
}, {});
},

/**
* Normalize standard HTTP Headers:
* {'foo-bar': 'hi'}
* becomes
* {'Foo-Bar': 'hi'}
*/
normalizeHeaders: (obj) => {
if (!(obj && typeof obj === 'object')) {
return obj;
}

const headers = {};
Object.keys(obj).forEach((header) => {
headers[utils.normalizeHeader(header)] = obj[header];
});
return headers;
},

return obj;
/**
* Stolen from https://github.com/marten-de-vries/header-case-normalizer/blob/master/index.js#L36-L41
* without the exceptions which are irrelevant to us.
*/
normalizeHeader: (header) => {
return header
.split('-')
.map(
(text) => text.charAt(0).toUpperCase() + text.substr(1).toLowerCase()
)
.join('-');
},

/**
Expand Down
10 changes: 5 additions & 5 deletions test/StripeResource.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,25 @@ describe('StripeResource', () => {
});
});

describe('_defaultHeaders', () => {
describe('_makeHeaders', () => {
it('sets the Authorization header with Bearer auth using the global API key', () => {
const headers = stripe.invoices._defaultHeaders(null, 0, null);
const headers = stripe.invoices._makeHeaders(null, 0, null);
expect(headers.Authorization).to.equal('Bearer fakeAuthToken');
});
it('sets the Authorization header with Bearer auth using the specified API key', () => {
const headers = stripe.invoices._defaultHeaders(
const headers = stripe.invoices._makeHeaders(
'anotherFakeAuthToken',
0,
null
);
expect(headers.Authorization).to.equal('Bearer anotherFakeAuthToken');
});
it('sets the Stripe-Version header if an API version is provided', () => {
const headers = stripe.invoices._defaultHeaders(null, 0, '1970-01-01');
const headers = stripe.invoices._makeHeaders(null, 0, '1970-01-01');
expect(headers['Stripe-Version']).to.equal('1970-01-01');
});
it('does not the set the Stripe-Version header if no API version is provided', () => {
const headers = stripe.invoices._defaultHeaders(null, 0, null);
const headers = stripe.invoices._makeHeaders(null, 0, null);
expect(headers).to.not.include.keys('Stripe-Version');
});
});
Expand Down
8 changes: 4 additions & 4 deletions test/utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,10 @@ describe('utils', () => {
});
});

describe('removeEmpty', () => {
describe('removeNullish', () => {
it('removes empty properties and leaves non-empty ones', () => {
expect(
utils.removeEmpty({
utils.removeNullish({
cat: 3,
dog: false,
rabbit: undefined,
Expand All @@ -327,9 +327,9 @@ describe('utils', () => {
});
});

it('throws an error if not given two things to compare', () => {
it('throws an error if not given an object', () => {
expect(() => {
utils.removeEmpty('potato');
utils.removeNullish('potato');
}).to.throw();
});
});
Expand Down

0 comments on commit 87fd537

Please sign in to comment.