@adobe/fetch
Advanced tools
Comparing version 0.1.7 to 0.1.8
34
index.js
@@ -15,4 +15,6 @@ /* | ||
const cache = require('./src/cache'); | ||
const uuid = require('uuid/v4'); | ||
const auth = require('@adobe/jwt-auth'); | ||
const merge = require('deepmerge'); | ||
const debug = require('debug')('@adobe/fetch'); | ||
const NO_CONFIG = 'Auth configuration missing.'; | ||
@@ -44,5 +46,11 @@ | ||
let apiKey = authOptions.clientId; | ||
let xrequestid = uuid().replace(/-/g, ''); | ||
if (options.headers && options.headers['x-api-key']) { | ||
apiKey = options.headers['x-api-key']; | ||
if (options.headers) { | ||
if (options.headers['x-api-key']) { | ||
apiKey = options.headers['x-api-key']; | ||
} | ||
if (options.headers['x-request-id']) { | ||
xrequestid = options.headers['x-request-id']; | ||
} | ||
} | ||
@@ -54,2 +62,3 @@ | ||
'x-api-key': apiKey, | ||
'x-request-id': xrequestid, | ||
'x-gw-ims-org-id': authOptions.orgId | ||
@@ -63,9 +72,22 @@ } | ||
const opts = addAuthHeaders(token, options, configOptions.auth); | ||
debug( | ||
`${opts.method || 'GET'} ${url} - x-request-id: ${ | ||
opts.headers['x-request-id'] | ||
}` | ||
); | ||
const res = await fetch(url, opts); | ||
if ((res.status === 401 || res.status === 403) && !forceNewToken) { | ||
return await _fetch(url, options, configOptions, tokenCache, true); | ||
} else { | ||
return res; | ||
if (!res.ok) { | ||
debug( | ||
`${opts.method || 'GET'} ${url} - status ${res.statusText} (${ | ||
res.status | ||
}). x-request-id: ${opts.headers['x-request-id']}` | ||
); | ||
if ((res.status === 401 || res.status === 403) && !forceNewToken) { | ||
debug(`${opts.method || 'GET'} ${url} - Will get new token.`); | ||
return await _fetch(url, options, configOptions, tokenCache, true); | ||
} | ||
} | ||
return res; | ||
} | ||
@@ -72,0 +94,0 @@ |
{ | ||
"name": "@adobe/fetch", | ||
"version": "0.1.7", | ||
"version": "0.1.8", | ||
"description": "Call Adobe APIs", | ||
@@ -30,14 +30,19 @@ "main": "index.js", | ||
"license": "Apache-2.0", | ||
"publishConfig": { | ||
"registry":"https://registry.npmjs.org" | ||
}, | ||
"dependencies": { | ||
"@adobe/jwt-auth": "^0.2.0", | ||
"debug": "^4.1.1", | ||
"deepmerge": "^4.0.0", | ||
"dotenv": "^8.0.0", | ||
"dotenv": "^8.1.0", | ||
"node-fetch": "^2.3.0", | ||
"node-persist": "^3.0.5" | ||
"node-persist": "^3.0.5", | ||
"uuid": "^3.3.3" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^6.1.0", | ||
"eslint-config-prettier": "^6.0.0", | ||
"eslint": "^6.2.1", | ||
"eslint-config-prettier": "^6.1.0", | ||
"eslint-plugin-prettier": "^3.1.0", | ||
"jest": "^24.1.0", | ||
"jest": "^24.9.0", | ||
"prettier": "^1.18.2" | ||
@@ -44,0 +49,0 @@ }, |
@@ -165,2 +165,17 @@ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) | ||
## Logging | ||
Every request will include a unique request identifier sent via the **x-request-id**. | ||
The request identifier can be overriden by providing it through the headers: | ||
```javascript | ||
fetch(url, { | ||
headers: { 'x-request-id': myRequestID } | ||
}); | ||
``` | ||
We use [debug](https://github.com/visionmedia/debug) to log requests. In order to see all the debug output, including the request identifiers, run your app with **DEBUG** environment variable including the **@adobe/fetch** scope as follows: | ||
``` | ||
DEBUG=@adobe/fetch | ||
``` | ||
### Contributing | ||
@@ -167,0 +182,0 @@ |
@@ -14,2 +14,3 @@ /* | ||
const auth = require('@adobe/jwt-auth'); | ||
const uuid = require('uuid/v4'); | ||
const storage = require('node-persist'); | ||
@@ -28,2 +29,11 @@ const fetch = require('node-fetch'); | ||
function expectHeaders(url, options, access_token, apikey, orgid) { | ||
expect(options.headers).toBeDefined(); | ||
expect(options.headers['authorization']).toBe(`bearer ${access_token}`); | ||
expect(options.headers['x-api-key']).toBe(apikey); | ||
expect(options.headers['x-gw-ims-org-id']).toBe(orgid); | ||
expect(options.headers['x-request-id']).toHaveLength(32); | ||
return Promise.resolve(mockData.responseOK); | ||
} | ||
describe('Validate auth behavior', () => { | ||
@@ -40,11 +50,11 @@ beforeEach(() => { | ||
// Default fetch mock - Returns status 200 and expects the auth headers. | ||
fetch.mockImplementation((url, options) => { | ||
expect(options.headers).toBeDefined(); | ||
expect(options.headers['authorization']).toBe( | ||
`${mockData.token.token_type} ${mockData.token.access_token}` | ||
); | ||
expect(options.headers['x-api-key']).toBe(mockData.config.clientId); | ||
expect(options.headers['x-gw-ims-org-id']).toBe(mockData.config.orgId); | ||
return Promise.resolve({ status: 200, url: url }); | ||
}); | ||
fetch.mockImplementation((url, options) => | ||
expectHeaders( | ||
url, | ||
options, | ||
mockData.token.access_token, | ||
mockData.config.clientId, | ||
mockData.config.orgId | ||
) | ||
); | ||
@@ -56,3 +66,3 @@ // New adobe fetch object. | ||
test('adds authentication headers', () => { | ||
expect.assertions(4); | ||
expect.assertions(5); | ||
return testFetch(mockData.url); | ||
@@ -62,3 +72,3 @@ }); | ||
test('caches access token', async () => { | ||
expect.assertions(9); | ||
expect.assertions(11); | ||
await testFetch(mockData.url); | ||
@@ -76,3 +86,3 @@ let authCalled = false; | ||
test('get stored token if valid', async () => { | ||
expect.assertions(4); | ||
expect.assertions(5); | ||
const token = mockData.valid_token[mockData.token_key]; | ||
@@ -82,11 +92,11 @@ storage.getItem = jest.fn(() => { | ||
}); | ||
fetch.mockImplementation((url, options) => { | ||
expect(options.headers).toBeDefined(); | ||
expect(options.headers['authorization']).toBe( | ||
`${token.token_type} ${token.access_token}` | ||
); | ||
expect(options.headers['x-api-key']).toBe(mockData.config.clientId); | ||
expect(options.headers['x-gw-ims-org-id']).toBe(mockData.config.orgId); | ||
return Promise.resolve({ status: 200 }); | ||
}); | ||
fetch.mockImplementation((url, options) => | ||
expectHeaders( | ||
url, | ||
options, | ||
token.access_token, | ||
mockData.config.clientId, | ||
mockData.config.orgId | ||
) | ||
); | ||
await testFetch(mockData.url); | ||
@@ -96,3 +106,3 @@ }); | ||
test('get new token when cached expires', async () => { | ||
expect.assertions(6); | ||
expect.assertions(7); | ||
storage.getItem = jest.fn(key => { | ||
@@ -106,19 +116,17 @@ expect(key).toBe(TOKENS_KEY); | ||
test('get new token when fetch returns 401', async () => { | ||
expect.assertions(4); | ||
expect.assertions(5); | ||
fetch.mockImplementation(() => { | ||
auth.mockImplementation(() => { | ||
fetch.mockImplementation((url, options) => { | ||
expect(options.headers).toBeDefined(); | ||
expect(options.headers['authorization']).toBe( | ||
`${mockData.token2.token_type} ${mockData.token2.access_token}` | ||
); | ||
expect(options.headers['x-api-key']).toBe(mockData.config.clientId); | ||
expect(options.headers['x-gw-ims-org-id']).toBe( | ||
fetch.mockImplementation((url, options) => | ||
expectHeaders( | ||
url, | ||
options, | ||
mockData.token2.access_token, | ||
mockData.config.clientId, | ||
mockData.config.orgId | ||
); | ||
return Promise.resolve({ status: 200 }); | ||
}); | ||
) | ||
); | ||
return Promise.resolve(mockData.token2); | ||
}); | ||
return Promise.resolve({ status: 401 }); | ||
return Promise.resolve(mockData.responseUnauthorized); | ||
}); | ||
@@ -129,19 +137,17 @@ await testFetch(mockData.url); | ||
test('get new token when fetch returns 403', async () => { | ||
expect.assertions(4); | ||
fetch.mockImplementation(url => { | ||
expect.assertions(5); | ||
fetch.mockImplementation(() => { | ||
auth.mockImplementation(() => { | ||
fetch.mockImplementation((url, options) => { | ||
expect(options.headers).toBeDefined(); | ||
expect(options.headers['authorization']).toBe( | ||
`${mockData.token2.token_type} ${mockData.token2.access_token}` | ||
); | ||
expect(options.headers['x-api-key']).toBe(mockData.config.clientId); | ||
expect(options.headers['x-gw-ims-org-id']).toBe( | ||
fetch.mockImplementation((url, options) => | ||
expectHeaders( | ||
url, | ||
options, | ||
mockData.token2.access_token, | ||
mockData.config.clientId, | ||
mockData.config.orgId | ||
); | ||
return Promise.resolve({ status: 200, url: url }); | ||
}); | ||
) | ||
); | ||
return Promise.resolve(mockData.token2); | ||
}); | ||
return Promise.resolve({ status: 403, url: url }); | ||
return Promise.resolve(mockData.responseForbidden); | ||
}); | ||
@@ -152,14 +158,33 @@ await testFetch(mockData.url); | ||
test('allows x-api-key override', async () => { | ||
expect.assertions(4); | ||
expect.assertions(5); | ||
fetch.mockImplementation((url, options) => | ||
expectHeaders( | ||
url, | ||
options, | ||
mockData.token.access_token, | ||
'test-override', | ||
mockData.config.orgId | ||
) | ||
); | ||
await testFetch(mockData.url, { | ||
headers: { 'x-api-key': 'test-override' } | ||
}); | ||
}); | ||
test('allows x-request-id override', async () => { | ||
expect.assertions(6); | ||
const xrequestid = uuid().replace(/-/g, ''); | ||
fetch.mockImplementation((url, options) => { | ||
expect(options.headers).toBeDefined(); | ||
expect(options.headers['authorization']).toBe( | ||
`${mockData.token2.token_type} ${mockData.token.access_token}` | ||
expect(options.headers['x-request-id']).toBe(xrequestid); | ||
return expectHeaders( | ||
url, | ||
options, | ||
mockData.token.access_token, | ||
mockData.config.clientId, | ||
mockData.config.orgId | ||
); | ||
expect(options.headers['x-api-key']).toBe('test-override'); | ||
expect(options.headers['x-gw-ims-org-id']).toBe(mockData.config.orgId); | ||
return Promise.resolve({ status: 200, url: url }); | ||
}); | ||
await testFetch(mockData.url, { | ||
headers: { 'x-api-key': 'test-override' } | ||
headers: { 'x-request-id': xrequestid } | ||
}); | ||
@@ -169,3 +194,3 @@ }); | ||
test('token stored in default storage', async () => { | ||
expect.assertions(6); | ||
expect.assertions(7); | ||
storage.setItem = jest.fn((key, value) => { | ||
@@ -202,3 +227,3 @@ expect(key).toBe(TOKENS_KEY); | ||
fetch.mockImplementation(() => { | ||
return Promise.resolve({ status: 200 }); | ||
return Promise.resolve(mockData.responseOK); | ||
}); | ||
@@ -214,7 +239,5 @@ return expect(testFetch(mockData.url)).rejects.toEqual(ERROR); | ||
}); | ||
fetch.mockImplementation(() => { | ||
return Promise.resolve({ status: 200 }); | ||
}); | ||
fetch.mockImplementation(() => Promise.resolve(mockData.responseOK)); | ||
return expect(testFetch(mockData.url)).rejects.toEqual(ERROR); | ||
}); | ||
}); |
@@ -21,4 +21,23 @@ /* | ||
const TOKEN_KEY = `${CLIENT_ID}|${SCOPES.join(',')}`; | ||
const MOCK_URL = 'https://mock.com/mock'; | ||
module.exports = { | ||
responseOK: { | ||
url: MOCK_URL, | ||
status: 200, | ||
statusText: 'OK', | ||
ok: true | ||
}, | ||
responseForbidden: { | ||
url: MOCK_URL, | ||
status: 403, | ||
statusText: 'Forbidden', | ||
ok: false | ||
}, | ||
responseUnauthorized: { | ||
url: MOCK_URL, | ||
status: 401, | ||
statusText: 'Unauthorized', | ||
ok: false | ||
}, | ||
config: { | ||
@@ -60,3 +79,3 @@ clientId: CLIENT_ID, | ||
}, | ||
url: 'https://mock.com/mock' | ||
url: MOCK_URL | ||
}; |
@@ -79,3 +79,3 @@ /* | ||
); | ||
return Promise.resolve({ status: 200 }); | ||
return Promise.resolve(mockData.responseOK); | ||
}); | ||
@@ -108,3 +108,3 @@ await adobefetch.config({ | ||
); | ||
return Promise.resolve({ status: 200 }); | ||
return Promise.resolve(mockData.responseOK); | ||
}); | ||
@@ -122,3 +122,3 @@ await adobefetch.config({ | ||
fetch.mockImplementation(() => { | ||
return Promise.resolve({ status: 200 }); | ||
return Promise.resolve(mockData.responseOK); | ||
}); | ||
@@ -153,3 +153,3 @@ | ||
fetch.mockImplementation(() => { | ||
return Promise.resolve({ status: 200 }); | ||
return Promise.resolve(mockData.responseOK); | ||
}); | ||
@@ -156,0 +156,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
894
187
92105
7
15
+ Addeddebug@^4.1.1
+ Addeduuid@^3.3.3
+ Addeddebug@4.3.7(transitive)
+ Addeduuid@3.4.0(transitive)
Updateddotenv@^8.1.0