Created
August 24, 2020 07:44
-
-
Save abhi5658/0ff6a73fd7521dde9ec5f6da8ff5019c to your computer and use it in GitHub Desktop.
Retry Refresh Token Pattern with Node Promises and Salesforce
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { httpGet } from '../util/http'; | |
import { getAccessToken, expireToken } from './salesforceOAuth'; | |
import { retryOnce } from '../util/retry'; | |
import logger from '../logger'; | |
/** | |
* API call to get data | |
* wrapped into retryOnce() util function with fallback unauthorised function | |
* -- wrapped into getAccessToken() function | |
*/ | |
export function listTravellers(abn) { | |
return retryOnce(() => { | |
return getAccessToken() | |
.then(accessToken => { | |
const getTravellersUrl = `${accessToken.instance_url}/services/sf_service/${abn}`; | |
return httpGet(getTravellersUrl, {}); | |
}) | |
.catch(err => { | |
logger.error(`Failed to list travellers from Salesforce: ${JSON.stringify(err)}`); | |
return Promise.reject(err); | |
}); | |
}, refreshTokenOnUnauthorizedError); | |
} | |
/** | |
* function which checks whether the reponse contains '401' | |
* if 401 present, declare as token expire => expireToken() | |
* and generate a new token promise => getAccessToken() | |
* else something is wrong and so promise reject | |
*/ | |
function refreshTokenOnUnauthorizedError(err) { | |
if(JSON.stringify(err).includes('401')) { | |
expireToken(); | |
return getAccessToken(); | |
} else { | |
return Promise.reject(err); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://tonytruong.net/retry-refresh-token-pattern-with-node-promises-and-salesforce/ | |
import logger from '../logger'; | |
/** retry once function used before calling the API for data, | |
* which takes a fallback(callback) function which will be used | |
* when the API call fails | |
*/ | |
export function retryOnce (func, recoverFunc) { | |
return func() | |
.catch( err => { | |
logger.info(`Calling function failed with error: ${JSON.stringify(err)}, retrying once after recovery`); | |
return recoverFunc(err).then(() => func()); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import logger from '../logger'; | |
import { httpPost } from '../util/http'; | |
import config from '../config'; | |
let refreshPromise; | |
/** | |
* getSFToken returns a promise itself, | |
* which can be .then()/await to obtain the token from it | |
*/ | |
function getSFToken() { | |
const { oauth2_url, username, password, security_token, client_id, client_secret } = config.salesforce; | |
const url = `${oauth2_url}?grant_type=password&username=${username}&password=${password}${security_token}&client_id=${client_id}&client_secret=${client_secret}`; | |
return httpPost(url, {}, { timeout: 15000}); | |
} | |
/** | |
* whenever a token is found expired/invalid | |
* this function is called to make the promise as undefined | |
* so that a new promise need to be created for new token | |
*/ | |
export function expireToken() { | |
refreshPromise = undefined; | |
} | |
/** | |
* getAccessToken returns a promise which when .then() will resolve into accessToken | |
* it will maintain a promise untill the token gets invalid | |
* and creates a new promise by calling getSFToken() to generate a new token promise | |
*/ | |
export function getAccessToken() { | |
if (refreshPromise) { | |
return refreshPromise; | |
} else { | |
refreshPromise = new Promise((resolve, reject) => { | |
getSFToken().then((accessToken) => { | |
logger.info(`Retrieved access token. Issued at: ${accessToken.issued_at}`); | |
resolve(accessToken); | |
}).catch((err) => { | |
logger.error(`Could not retrieve acccess token with error: ${JSON.stringify(err)}`); | |
expireToken(); | |
reject(err); | |
}); | |
}); | |
return refreshPromise; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment