From 17cd44d85a0f51ce2bcefacf954f27ff44199707 Mon Sep 17 00:00:00 2001 From: Sai Sankeerth Date: Tue, 12 Sep 2023 14:12:26 +0530 Subject: [PATCH 1/6] feat: introduce de-activation of authStatus for access_denied or invalid_grant errors Signed-off-by: Sai Sankeerth --- src/adapters/networkhandler/authConstants.js | 2 ++ .../campaign_manager/networkHandler.js | 8 +++++++- .../networkHandler.js | 6 +++++- .../networkHandler.js | 18 +----------------- .../utils.js | 14 +++++++++++--- .../networkHandler.js | 6 +++++- .../snapchat_custom_audience/networkHandler.js | 8 +++++++- 7 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/adapters/networkhandler/authConstants.js b/src/adapters/networkhandler/authConstants.js index c9a6a57835..b5c2f8ac88 100644 --- a/src/adapters/networkhandler/authConstants.js +++ b/src/adapters/networkhandler/authConstants.js @@ -2,6 +2,8 @@ * This class is used for handling Auth related errors */ module.exports = { + // Deprecated: This constant would be deprecated soon DISABLE_DEST: 'DISABLE_DESTINATION', REFRESH_TOKEN: 'REFRESH_TOKEN', + AUTH_STATUS_INACTIVE: 'AUTH_STATUS_INACTIVE', }; diff --git a/src/v0/destinations/campaign_manager/networkHandler.js b/src/v0/destinations/campaign_manager/networkHandler.js index d324c6300b..5f1562cff3 100644 --- a/src/v0/destinations/campaign_manager/networkHandler.js +++ b/src/v0/destinations/campaign_manager/networkHandler.js @@ -1,6 +1,9 @@ const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess } = require('../../util/index'); -const { REFRESH_TOKEN } = require('../../../adapters/networkhandler/authConstants'); +const { + REFRESH_TOKEN, + AUTH_STATUS_INACTIVE, +} = require('../../../adapters/networkhandler/authConstants'); const { processAxiosResponse, @@ -20,6 +23,9 @@ const getAuthErrCategory = (code) => { if (code === 401) { return REFRESH_TOKEN; } + if (code === 403) { + return AUTH_STATUS_INACTIVE; + } return ''; }; diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js index 5349b74178..829617c866 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js @@ -2,7 +2,10 @@ const { get, set } = require('lodash'); const sha256 = require('sha256'); const { prepareProxyRequest, handleHttpRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess } = require('../../util/index'); -const { REFRESH_TOKEN } = require('../../../adapters/networkhandler/authConstants'); +const { + REFRESH_TOKEN, + AUTH_STATUS_INACTIVE, +} = require('../../../adapters/networkhandler/authConstants'); const { CONVERSION_ACTION_ID_CACHE_TTL } = require('./config'); const Cache = require('../../util/cache'); @@ -25,6 +28,7 @@ const tags = require('../../util/tags'); */ const getAuthErrCategory = (code, response) => { if (code === 401 && !get(response, 'error.details')) return REFRESH_TOKEN; + if (code === 403 && !get(response, 'error.details')) return AUTH_STATUS_INACTIVE; return ''; }; diff --git a/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js b/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js index 963721f024..1b1d7d4b05 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js @@ -2,13 +2,12 @@ const set = require('set-value'); const get = require('get-value'); const sha256 = require('sha256'); const { prepareProxyRequest, httpSend, httpPOST } = require('../../../adapters/network'); -const { REFRESH_TOKEN } = require('../../../adapters/networkhandler/authConstants'); const { isHttpStatusSuccess, getHashFromArray, isDefinedAndNotNullAndNotEmpty, } = require('../../util'); -const { getConversionActionId } = require('./utils'); +const { getConversionActionId, getAuthErrCategory } = require('./utils'); const Cache = require('../../util/cache'); const { CONVERSION_CUSTOM_VARIABLE_CACHE_TTL, SEARCH_STREAM } = require('./config'); const { @@ -24,21 +23,6 @@ const tags = require('../../util/tags'); const conversionCustomVariableCache = new Cache(CONVERSION_CUSTOM_VARIABLE_CACHE_TTL); -/** - * This function helps to determine the type of error occurred. We set the authErrorCategory - * as per the destination response that is received and take the decision whether - * to refresh the access_token or disable the destination. - * @param {*} status - * @returns - */ -const getAuthErrCategory = (status) => { - if (status === 401) { - // UNAUTHORIZED - return REFRESH_TOKEN; - } - return ''; -}; - const createJob = async (endpoint, headers, payload) => { const endPoint = `${endpoint}:create`; let createJobResponse = await httpPOST( diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.js b/src/v0/destinations/google_adwords_offline_conversions/utils.js index 6131b5dde7..5d2d93de3c 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.js @@ -12,7 +12,10 @@ const { isDefinedAndNotNullAndNotEmpty, isDefinedAndNotNull, } = require('../../util'); -const { REFRESH_TOKEN } = require('../../../adapters/networkhandler/authConstants'); +const { + REFRESH_TOKEN, + AUTH_STATUS_INACTIVE, +} = require('../../../adapters/networkhandler/authConstants'); const { SEARCH_STREAM, CONVERSION_ACTION_ID_CACHE_TTL, @@ -57,9 +60,9 @@ const getAccessToken = ({ secret }) => { }; /** - * This function helps to determine the type of error occured. We set the authErrorCategory + * This function helps to determine the type of error occurred. We set the authErrorCategory * as per the destination response that is received and take the decision whether - * to refresh the access_token or disable the destination. + * to refresh the access_token or de-activate authStatus. * @param {*} status * @returns */ @@ -68,6 +71,10 @@ const getAuthErrCategory = (status) => { // UNAUTHORIZED return REFRESH_TOKEN; } + if (status === 403) { + // ACCESS_DENIED + return AUTH_STATUS_INACTIVE; + } return ''; }; @@ -398,4 +405,5 @@ module.exports = { buildAndGetAddress, getClickConversionPayloadAndEndpoint, getExisitingUserIdentifier, + getAuthErrCategory, }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js b/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js index ceea51f6a2..309063968f 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js @@ -1,7 +1,10 @@ const { httpSend, prepareProxyRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess } = require('../../util/index'); -const { REFRESH_TOKEN } = require('../../../adapters/networkhandler/authConstants'); +const { + REFRESH_TOKEN, + AUTH_STATUS_INACTIVE, +} = require('../../../adapters/networkhandler/authConstants'); const { processAxiosResponse, @@ -127,6 +130,7 @@ const gaAudienceProxyRequest = async (request) => { */ const getAuthErrCategory = (code, response) => { if (code === 401 && !response.error.details) return REFRESH_TOKEN; + if (code === 403 && !response.error.details) return AUTH_STATUS_INACTIVE; return ''; }; diff --git a/src/v0/destinations/snapchat_custom_audience/networkHandler.js b/src/v0/destinations/snapchat_custom_audience/networkHandler.js index 0f75d36a56..b0ba7d487d 100644 --- a/src/v0/destinations/snapchat_custom_audience/networkHandler.js +++ b/src/v0/destinations/snapchat_custom_audience/networkHandler.js @@ -1,7 +1,10 @@ const { removeUndefinedValues } = require('../../util'); const { prepareProxyRequest, getPayloadData, httpSend } = require('../../../adapters/network'); const { isHttpStatusSuccess } = require('../../util/index'); -const { REFRESH_TOKEN } = require('../../../adapters/networkhandler/authConstants'); +const { + REFRESH_TOKEN, + AUTH_STATUS_INACTIVE, +} = require('../../../adapters/networkhandler/authConstants'); const tags = require('../../util/tags'); const { getDynamicErrorType, @@ -43,6 +46,9 @@ const getAuthErrCategory = (code, response) => { if (code === 401) { authErrCategory = !response.error?.details ? REFRESH_TOKEN : ''; } + if (code === 403) { + authErrCategory = !response.error?.details ? AUTH_STATUS_INACTIVE : ''; + } return authErrCategory; }; From 3fbcba513e17b17b483c13d902b2262af2b5a698 Mon Sep 17 00:00:00 2001 From: Sai Sankeerth Date: Tue, 12 Sep 2023 14:26:50 +0530 Subject: [PATCH 2/6] fix: test-cases-1 Signed-off-by: Sai Sankeerth --- test/__tests__/data/snapchat_custom_audience_proxy_output.json | 1 + 1 file changed, 1 insertion(+) diff --git a/test/__tests__/data/snapchat_custom_audience_proxy_output.json b/test/__tests__/data/snapchat_custom_audience_proxy_output.json index 654d94c45c..e5bcc4faa8 100644 --- a/test/__tests__/data/snapchat_custom_audience_proxy_output.json +++ b/test/__tests__/data/snapchat_custom_audience_proxy_output.json @@ -43,6 +43,7 @@ }, { "output": { + "authErrorCategory": "AUTH_STATUS_INACTIVE", "status": 400, "destinationResponse": { "response": { From 4997b5b9cf80a6d1c3c34cefc54078df2c2157ec Mon Sep 17 00:00:00 2001 From: Sai Sankeerth Date: Tue, 12 Sep 2023 16:22:46 +0530 Subject: [PATCH 3/6] chore: remove all references of disable_dest and remove the const disable_dest Signed-off-by: Sai Sankeerth --- src/adapters/networkhandler/authConstants.js | 2 -- src/v0/destinations/bqstream/util.js | 11 +++++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/adapters/networkhandler/authConstants.js b/src/adapters/networkhandler/authConstants.js index b5c2f8ac88..f88361107a 100644 --- a/src/adapters/networkhandler/authConstants.js +++ b/src/adapters/networkhandler/authConstants.js @@ -2,8 +2,6 @@ * This class is used for handling Auth related errors */ module.exports = { - // Deprecated: This constant would be deprecated soon - DISABLE_DEST: 'DISABLE_DESTINATION', REFRESH_TOKEN: 'REFRESH_TOKEN', AUTH_STATUS_INACTIVE: 'AUTH_STATUS_INACTIVE', }; diff --git a/src/v0/destinations/bqstream/util.js b/src/v0/destinations/bqstream/util.js index cc3aba78b2..2448d72d76 100644 --- a/src/v0/destinations/bqstream/util.js +++ b/src/v0/destinations/bqstream/util.js @@ -4,7 +4,10 @@ const { getDynamicErrorType, processAxiosResponse, } = require('../../../adapters/utils/networkUtils'); -const { DISABLE_DEST, REFRESH_TOKEN } = require('../../../adapters/networkhandler/authConstants'); +const { + REFRESH_TOKEN, + AUTH_STATUS_INACTIVE, +} = require('../../../adapters/networkhandler/authConstants'); const { isHttpStatusSuccess } = require('../../util'); const { proxyRequest } = require('../../../adapters/network'); const { UnhandledStatusCodeError, NetworkError, AbortedError } = require('../../util/errorTypes'); @@ -24,7 +27,7 @@ const trimBqStreamResponse = (response) => ({ * Obtains the Destination OAuth Error Category based on the error code obtained from destination * * - If an error code is such that the user will not be allowed inside the destination, - * such error codes fall under DISABLE_DESTINATION + * such error codes fall under AUTH_STATUS_INACTIVE * - If an error code is such that upon refresh we can get a new token which can be used to send event, * such error codes fall under REFRESH_TOKEN category * - If an error code doesn't fall under both categories, we can return an empty string @@ -34,7 +37,7 @@ const trimBqStreamResponse = (response) => ({ const getDestAuthCategory = (errorCategory) => { switch (errorCategory) { case 'PERMISSION_DENIED': - return DISABLE_DEST; + return AUTH_STATUS_INACTIVE; case 'UNAUTHENTICATED': return REFRESH_TOKEN; default: @@ -88,7 +91,7 @@ const getStatusAndCategory = (dresponse, status) => { * Retryable -> 5[0-9][02-9], 401(UNAUTHENTICATED) * "Special Cases": * status=200, resp.insertErrors.length > 0 === Failure - * 403 => AccessDenied -> DISABLE_DEST, other 403 => Just abort + * 403 => AccessDenied -> AUTH_STATUS_INACTIVE, other 403 => Just abort * */ const processResponse = ({ dresponse, status } = {}) => { From ae49c98662bb3171346e65f0f26fefce2f3c6a62 Mon Sep 17 00:00:00 2001 From: Sai Sankeerth Date: Thu, 14 Sep 2023 15:57:58 +0530 Subject: [PATCH 4/6] chore: refactor to use util methods for getting auth error category and access-token Signed-off-by: Sai Sankeerth --- .../campaign_manager/networkHandler.js | 21 +------ .../campaign_manager/transform.js | 12 +--- .../networkHandler.js | 24 ++------ .../transform.js | 24 +------- .../networkHandler.js | 11 ++-- .../utils.js | 44 ++------------- .../networkHandler.js | 24 ++------ .../transform.js | 37 +++---------- src/v0/destinations/pardot/transform.js | 23 +------- .../networkHandler.js | 39 ++++++------- .../snapchat_custom_audience/transform.js | 26 +-------- src/v0/util/index.js | 55 ++++++++++++++++++- ...e_adwords_enhanced_conversions_output.json | 2 +- ...ds_enhanced_conversions_router_output.json | 2 +- ...ogle_adwords_remarketing_lists_output.json | 2 +- test/__tests__/data/pardot_router_output.json | 2 +- 16 files changed, 116 insertions(+), 232 deletions(-) diff --git a/src/v0/destinations/campaign_manager/networkHandler.js b/src/v0/destinations/campaign_manager/networkHandler.js index 5f1562cff3..7d4f401dc0 100644 --- a/src/v0/destinations/campaign_manager/networkHandler.js +++ b/src/v0/destinations/campaign_manager/networkHandler.js @@ -1,5 +1,5 @@ const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); -const { isHttpStatusSuccess } = require('../../util/index'); +const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../util/index'); const { REFRESH_TOKEN, AUTH_STATUS_INACTIVE, @@ -12,23 +12,6 @@ const { const { AbortedError, RetryableError, NetworkError } = require('../../util/errorTypes'); const tags = require('../../util/tags'); -/** - * This function helps to detarmine type of error occured. According to the response - * we set authErrorCategory to take decision if we need to refresh the access_token - * or need to disable the destination. - * @param {*} code - * @returns - */ -const getAuthErrCategory = (code) => { - if (code === 401) { - return REFRESH_TOKEN; - } - if (code === 403) { - return AUTH_STATUS_INACTIVE; - } - return ''; -}; - function checkIfFailuresAreRetryable(response) { try { if (Array.isArray(response.status) && Array.isArray(response.status[0].errors)) { @@ -79,7 +62,7 @@ const responseHandler = (destinationResponse) => { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, destinationResponse, - getAuthErrCategory(status), + getAuthErrCategoryFromStCode(status), ); }; diff --git a/src/v0/destinations/campaign_manager/transform.js b/src/v0/destinations/campaign_manager/transform.js index 93ec3e8ea3..99e5fe9c57 100644 --- a/src/v0/destinations/campaign_manager/transform.js +++ b/src/v0/destinations/campaign_manager/transform.js @@ -7,6 +7,7 @@ const { removeUndefinedAndNullValues, isDefinedAndNotNull, simpleProcessRouterDest, + getAccessToken, } = require('../../util'); const { @@ -17,16 +18,9 @@ const { EncryptionSource, } = require('./config'); -const { InstrumentationError, OAuthSecretError } = require('../../util/errorTypes'); +const { InstrumentationError } = require('../../util/errorTypes'); const { JSON_MIME_TYPE } = require('../../util/constant'); -const getAccessToken = ({ secret }) => { - if (!secret) { - throw new OAuthSecretError('[CAMPAIGN MANAGER (DCM)]:: OAuth - access token not found'); - } - return secret.access_token; -}; - function isEmptyObject(obj) { return Object.keys(obj).length === 0 && obj.constructor === Object; } @@ -36,7 +30,7 @@ function buildResponse(requestJson, metadata, endpointUrl, requestType, encrypti const response = defaultRequestConfig(); response.endpoint = endpointUrl; response.headers = { - Authorization: `Bearer ${getAccessToken(metadata)}`, + Authorization: `Bearer ${getAccessToken(metadata, 'access_token')}`, 'Content-Type': JSON_MIME_TYPE, }; response.method = defaultPostRequestConfig.requestMethod; diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js index 829617c866..e79c568238 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/networkHandler.js @@ -1,11 +1,10 @@ const { get, set } = require('lodash'); const sha256 = require('sha256'); const { prepareProxyRequest, handleHttpRequest } = require('../../../adapters/network'); -const { isHttpStatusSuccess } = require('../../util/index'); const { - REFRESH_TOKEN, - AUTH_STATUS_INACTIVE, -} = require('../../../adapters/networkhandler/authConstants'); + isHttpStatusSuccess, + getAuthErrCategoryFromErrDetailsAndStCode, +} = require('../../util/index'); const { CONVERSION_ACTION_ID_CACHE_TTL } = require('./config'); const Cache = require('../../util/cache'); @@ -18,19 +17,6 @@ const { const { BASE_ENDPOINT } = require('./config'); const { NetworkError, NetworkInstrumentationError } = require('../../util/errorTypes'); const tags = require('../../util/tags'); -/** - * This function helps to detarmine type of error occured. According to the response - * we set authErrorCategory to take decision if we need to refresh the access_token - * or need to disable the destination. - * @param {*} code - * @param {*} response - * @returns - */ -const getAuthErrCategory = (code, response) => { - if (code === 401 && !get(response, 'error.details')) return REFRESH_TOKEN; - if (code === 403 && !get(response, 'error.details')) return AUTH_STATUS_INACTIVE; - return ''; -}; /** * This function is used for collecting the conversionActionId using the conversion name @@ -72,7 +58,7 @@ const getConversionActionId = async (method, headers, params) => { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(gaecConversionActionIdResponse.status), }, gaecConversionActionIdResponse.response, - getAuthErrCategory( + getAuthErrCategoryFromErrDetailsAndStCode( get(gaecConversionActionIdResponse, 'status'), get(gaecConversionActionIdResponse, 'response[0].error.message'), ), @@ -138,7 +124,7 @@ const responseHandler = (destinationResponse) => { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, response, - getAuthErrCategory(status, response), + getAuthErrCategoryFromErrDetailsAndStCode(status, response), ); }; // eslint-disable-next-line func-names diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/transform.js b/src/v0/destinations/google_adwords_enhanced_conversions/transform.js index 96ef089001..350fe4032c 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/transform.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/transform.js @@ -8,6 +8,7 @@ const { getValueFromMessage, removeHyphens, simpleProcessRouterDest, + getAccessToken, } = require('../../util'); const { @@ -36,34 +37,13 @@ const updateMappingJson = (mapping) => { return newMapping; }; -/** - * Get access token to be bound to the event req headers - * - * Note: - * This method needs to be implemented particular to the destination - * As the schema that we'd get in `metadata.secret` can be different - * for different destinations - * - * @param {Object} metadata - * @returns - */ -const getAccessToken = (metadata) => { - // OAuth for this destination - const { secret } = metadata; - // we would need to verify if secret is present and also if the access token field is present in secret - if (!secret || !secret.access_token) { - throw new OAuthSecretError('Empty/Invalid access token'); - } - return secret.access_token; -}; - const responseBuilder = async (metadata, message, { Config }, payload) => { const response = defaultRequestConfig(); const { event } = message; const filteredCustomerId = removeHyphens(Config.customerId); response.endpoint = `${BASE_ENDPOINT}/${filteredCustomerId}:uploadConversionAdjustments`; response.body.JSON = payload; - const accessToken = getAccessToken(metadata); + const accessToken = getAccessToken(metadata, 'access_token'); response.headers = { Authorization: `Bearer ${accessToken}`, 'Content-Type': JSON_MIME_TYPE, diff --git a/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js b/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js index 1b1d7d4b05..71fdccff20 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js +++ b/src/v0/destinations/google_adwords_offline_conversions/networkHandler.js @@ -6,8 +6,9 @@ const { isHttpStatusSuccess, getHashFromArray, isDefinedAndNotNullAndNotEmpty, + getAuthErrCategoryFromStCode, } = require('../../util'); -const { getConversionActionId, getAuthErrCategory } = require('./utils'); +const { getConversionActionId } = require('./utils'); const Cache = require('../../util/cache'); const { CONVERSION_CUSTOM_VARIABLE_CACHE_TTL, SEARCH_STREAM } = require('./config'); const { @@ -41,7 +42,7 @@ const createJob = async (endpoint, headers, payload) => { `[Google Ads Offline Conversions]:: ${response?.error?.message} during google_ads_offline_store_conversions Job Creation`, status, response, - getAuthErrCategory(status), + getAuthErrCategoryFromStCode(status), ); } return response.resourceName.split('/')[3]; @@ -64,7 +65,7 @@ const addConversionToJob = async (endpoint, headers, jobId, payload) => { `[Google Ads Offline Conversions]:: ${addConversionToJobResponse.response?.error?.message} during google_ads_offline_store_conversions Add Conversion`, addConversionToJobResponse.status, addConversionToJobResponse.response, - getAuthErrCategory(get(addConversionToJobResponse, 'status')), + getAuthErrCategoryFromStCode(get(addConversionToJobResponse, 'status')), ); } return true; @@ -115,7 +116,7 @@ const getConversionCustomVariable = async (headers, params) => { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(searchStreamResponse.status), }, searchStreamResponse?.response || searchStreamResponse, - getAuthErrCategory(searchStreamResponse.status), + getAuthErrCategoryFromStCode(searchStreamResponse.status), ); } const conversionCustomVariable = get(searchStreamResponse, 'response.0.results'); @@ -276,7 +277,7 @@ const responseHandler = (destinationResponse) => { `[Google Ads Offline Conversions]:: ${response?.error?.message} during google_ads_offline_conversions response transformation`, status, response, - getAuthErrCategory(status), + getAuthErrCategoryFromStCode(status), ); }; diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.js b/src/v0/destinations/google_adwords_offline_conversions/utils.js index 5d2d93de3c..71d70632bb 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.js @@ -11,11 +11,9 @@ const { getFieldValueFromMessage, isDefinedAndNotNullAndNotEmpty, isDefinedAndNotNull, + getAuthErrCategoryFromStCode, + getAccessToken, } = require('../../util'); -const { - REFRESH_TOKEN, - AUTH_STATUS_INACTIVE, -} = require('../../../adapters/networkhandler/authConstants'); const { SEARCH_STREAM, CONVERSION_ACTION_ID_CACHE_TTL, @@ -46,38 +44,6 @@ const validateDestinationConfig = ({ Config }) => { } }; -/** - * for OAuth destination - * get access_token from metadata.secret{ ... } - * @param {*} param0 - * @returns - */ -const getAccessToken = ({ secret }) => { - if (!secret) { - throw new OAuthSecretError('OAuth - access token not found'); - } - return secret.access_token; -}; - -/** - * This function helps to determine the type of error occurred. We set the authErrorCategory - * as per the destination response that is received and take the decision whether - * to refresh the access_token or de-activate authStatus. - * @param {*} status - * @returns - */ -const getAuthErrCategory = (status) => { - if (status === 401) { - // UNAUTHORIZED - return REFRESH_TOKEN; - } - if (status === 403) { - // ACCESS_DENIED - return AUTH_STATUS_INACTIVE; - } - return ''; -}; - /** * get conversionAction using the conversion name using searchStream endpoint * @param {*} customerId @@ -107,7 +73,7 @@ const getConversionActionId = async (headers, params) => { )} during google_ads_offline_conversions response transformation`, searchStreamResponse.status, searchStreamResponse.response, - getAuthErrCategory(get(searchStreamResponse, 'status')), + getAuthErrCategoryFromStCode(get(searchStreamResponse, 'status')), ); } const conversionAction = get( @@ -201,7 +167,7 @@ const requestBuilder = ( } response.body.JSON = payload; response.headers = { - Authorization: `Bearer ${getAccessToken(metadata)}`, + Authorization: `Bearer ${getAccessToken(metadata, 'access_token')}`, 'Content-Type': 'application/json', 'developer-token': get(metadata, 'secret.developer_token'), }; @@ -397,7 +363,6 @@ const getClickConversionPayloadAndEndpoint = (message, Config, filteredCustomerI module.exports = { validateDestinationConfig, generateItemListFromProducts, - getAccessToken, getConversionActionId, removeHashToSha256TypeFromMappingJson, getStoreConversionPayload, @@ -405,5 +370,4 @@ module.exports = { buildAndGetAddress, getClickConversionPayloadAndEndpoint, getExisitingUserIdentifier, - getAuthErrCategory, }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js b/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js index 309063968f..979f263fcc 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/networkHandler.js @@ -1,10 +1,8 @@ const { httpSend, prepareProxyRequest } = require('../../../adapters/network'); -const { isHttpStatusSuccess } = require('../../util/index'); - const { - REFRESH_TOKEN, - AUTH_STATUS_INACTIVE, -} = require('../../../adapters/networkhandler/authConstants'); + isHttpStatusSuccess, + getAuthErrCategoryFromErrDetailsAndStCode, +} = require('../../util/index'); const { processAxiosResponse, @@ -120,20 +118,6 @@ const gaAudienceProxyRequest = async (request) => { return thirdResponse; }; -/** - * This function helps to detarmine type of error occured. According to the response - * we set authErrorCategory to take decision if we need to refresh the access_token - * or need to disable the destination. - * @param {*} code - * @param {*} response - * @returns - */ -const getAuthErrCategory = (code, response) => { - if (code === 401 && !response.error.details) return REFRESH_TOKEN; - if (code === 403 && !response.error.details) return AUTH_STATUS_INACTIVE; - return ''; -}; - const gaAudienceRespHandler = (destResponse, stageMsg) => { const { status, response } = destResponse; // const respAttributes = response["@attributes"] || null; @@ -146,7 +130,7 @@ const gaAudienceRespHandler = (destResponse, stageMsg) => { [tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status), }, response, - getAuthErrCategory(status, response), + getAuthErrCategoryFromErrDetailsAndStCode(status, response), ); }; diff --git a/src/v0/destinations/google_adwords_remarketing_lists/transform.js b/src/v0/destinations/google_adwords_remarketing_lists/transform.js index e847e9f72a..f8cd7c7037 100644 --- a/src/v0/destinations/google_adwords_remarketing_lists/transform.js +++ b/src/v0/destinations/google_adwords_remarketing_lists/transform.js @@ -1,6 +1,6 @@ const sha256 = require('sha256'); -const logger = require('../../../logger'); const get = require('get-value'); +const logger = require('../../../logger'); const { isDefinedAndNotNullAndNotEmpty, returnArrayOfSubarrays, @@ -11,13 +11,10 @@ const { removeHyphens, simpleProcessRouterDest, getDestinationExternalIDInfoForRetl, + getAccessToken, } = require('../../util'); -const { - InstrumentationError, - ConfigurationError, - OAuthSecretError, -} = require('../../util/errorTypes'); +const { InstrumentationError, ConfigurationError } = require('../../util/errorTypes'); const { offlineDataJobsMapping, addressInfoMapping, @@ -38,27 +35,6 @@ const hashEncrypt = (object) => { }); }; -/** - * Get access token to be bound to the event req headers - * - * Note: - * This method needs to be implemented particular to the destination - * As the schema that we'd get in `metadata.secret` can be different - * for different destinations - * - * @param {Object} metadata - * @returns - */ -const getAccessToken = (metadata) => { - // OAuth for this destination - const { secret } = metadata; - // we would need to verify if secret is present and also if the access token field is present in secret - if (!secret || !secret.access_token) { - throw new OAuthSecretError('Empty/Invalid access token'); - } - return secret.access_token; -}; - /** * This function is used for building the response. It create a default rudder response * and populate headers, params and body.JSON @@ -73,11 +49,14 @@ const responseBuilder = (metadata, body, { Config }, message) => { const filteredCustomerId = removeHyphens(Config.customerId); response.endpoint = `${BASE_ENDPOINT}/${filteredCustomerId}/offlineUserDataJobs`; response.body.JSON = removeUndefinedAndNullValues(payload); - const accessToken = getAccessToken(metadata); + const accessToken = getAccessToken(metadata, 'access_token'); let operationAudienceId = Config.audienceId || Config.listId; const mappedToDestination = get(message, MappedToDestinationKey); if (!operationAudienceId && mappedToDestination) { - const { objectType } = getDestinationExternalIDInfoForRetl(message, 'GOOGLE_ADWORDS_REMARKETING_LISTS'); + const { objectType } = getDestinationExternalIDInfoForRetl( + message, + 'GOOGLE_ADWORDS_REMARKETING_LISTS', + ); operationAudienceId = objectType; } if (!isDefinedAndNotNullAndNotEmpty(operationAudienceId)) { diff --git a/src/v0/destinations/pardot/transform.js b/src/v0/destinations/pardot/transform.js index 128d72cb14..b166416a5d 100644 --- a/src/v0/destinations/pardot/transform.js +++ b/src/v0/destinations/pardot/transform.js @@ -45,6 +45,7 @@ const { getSuccessRespEvents, checkInvalidRtTfEvents, handleRtTfSingleEventError, + getAccessToken, } = require('../../util'); const { CONFIG_CATEGORIES } = require('./config'); const { @@ -53,26 +54,6 @@ const { InstrumentationError, } = require('../../util/errorTypes'); -/** - * Get access token to be bound to the event req headers - * - * Note: - * This method needs to be implemented particular to the destination - * As the schema that we'd get in `metadata.secret` can be different - * for different destinations - * - * @param {Object} metadata - * @returns - */ -const getAccessToken = (metadata) => { - // OAuth for this destination - const { secret } = metadata; - if (!secret) { - throw new OAuthSecretError('Empty/Invalid access token'); - } - return secret.access_token; -}; - const buildResponse = (payload, url, destination, token) => { const responseBody = removeUndefinedValues(payload); const response = defaultRequestConfig(); @@ -122,7 +103,7 @@ const getUrl = (urlParams) => { const processIdentify = ({ message, metadata }, destination, category) => { const { campaignId } = destination.Config; - const accessToken = getAccessToken(metadata); + const accessToken = getAccessToken(metadata, 'access_token'); let extId = get(message, 'context.externalId'); extId = extId && extId.length > 0 ? extId[0] : null; diff --git a/src/v0/destinations/snapchat_custom_audience/networkHandler.js b/src/v0/destinations/snapchat_custom_audience/networkHandler.js index b0ba7d487d..870cc16aae 100644 --- a/src/v0/destinations/snapchat_custom_audience/networkHandler.js +++ b/src/v0/destinations/snapchat_custom_audience/networkHandler.js @@ -1,4 +1,4 @@ -const { removeUndefinedValues } = require('../../util'); +const { removeUndefinedValues, getAuthErrCategoryFromErrDetailsAndStCode } = require('../../util'); const { prepareProxyRequest, getPayloadData, httpSend } = require('../../../adapters/network'); const { isHttpStatusSuccess } = require('../../util/index'); const { @@ -33,6 +33,23 @@ const prepareProxyReq = (request) => { }); }; +const scAudienceProxyRequest = async (request) => { + const { endpoint, data, method, params, headers } = prepareProxyReq(request); + + const requestOptions = { + url: endpoint, + data, + params, + headers, + method, + }; + const response = await httpSend(requestOptions, { + feature: 'proxy', + destType: 'snapchat_custom_audience', + }); + return response; +}; + /** * This function helps to determine type of error occurred. According to the response * we set authErrorCategory to take decision if we need to refresh the access_token @@ -52,26 +69,10 @@ const getAuthErrCategory = (code, response) => { return authErrCategory; }; -const scAudienceProxyRequest = async (request) => { - const { endpoint, data, method, params, headers } = prepareProxyReq(request); - - const requestOptions = { - url: endpoint, - data, - params, - headers, - method, - }; - const response = await httpSend(requestOptions, { - feature: 'proxy', - destType: 'snapchat_custom_audience', - }); - return response; -}; - const scaAudienceRespHandler = (destResponse, stageMsg) => { const { status, response } = destResponse; - const authErrCategory = getAuthErrCategory(status, response); + const authErrCategory = getAuthErrCategoryFromErrDetailsAndStCode(status, response); + // const authErrCategory = getAuthErrCategory(status, response); if (authErrCategory === REFRESH_TOKEN) { throw new RetryableError( diff --git a/src/v0/destinations/snapchat_custom_audience/transform.js b/src/v0/destinations/snapchat_custom_audience/transform.js index 1c16115494..7938236594 100644 --- a/src/v0/destinations/snapchat_custom_audience/transform.js +++ b/src/v0/destinations/snapchat_custom_audience/transform.js @@ -4,33 +4,13 @@ const { removeUndefinedAndNullValues, simpleProcessRouterDest, isDefinedAndNotNullAndNotEmpty, + getAccessToken, } = require('../../util'); -const { ConfigurationError, OAuthSecretError } = require('../../util/errorTypes'); +const { ConfigurationError } = require('../../util/errorTypes'); const { BASE_URL, schemaType } = require('./config'); const { validatePayload, validateFields } = require('./utils'); const { JSON_MIME_TYPE } = require('../../util/constant'); -/** - * Get access token to be bound to the event req headers - * - * Note: - * This method needs to be implemented particular to the destination - * As the schema that we'd get in `metadata.secret` can be different - * for different destinations - * - * @param {Object} metadata - * @returns - */ -const getAccessToken = (metadata) => { - // OAuth for this destination - const { secret } = metadata; - // we would need to verify if secret is present and also if the access token field is present in secret - if (!secret || !secret.access_token) { - throw new OAuthSecretError('Empty/Invalid access token'); - } - return secret.access_token; -}; - const generateResponse = (groupedData, schema, segmentId, metadata, type) => { const payload = { users: [] }; const userPayload = { schema: [schema], data: groupedData }; @@ -43,7 +23,7 @@ const generateResponse = (groupedData, schema, segmentId, metadata, type) => { response.endpoint = `${BASE_URL}/segments/${segmentId}/users`; response.body.JSON = removeUndefinedAndNullValues(payload); - const accessToken = getAccessToken(metadata); + const accessToken = getAccessToken(metadata, 'access_token'); response.headers = { Authorization: `Bearer ${accessToken}`, 'Content-Type': JSON_MIME_TYPE, diff --git a/src/v0/util/index.js b/src/v0/util/index.js index b04d64deb6..8a20104dd0 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -28,6 +28,10 @@ const { } = require('./errorTypes'); const { client: errNotificationClient } = require('../../util/errorNotifier'); const { HTTP_STATUS_CODES } = require('./constant'); +const { + REFRESH_TOKEN, + AUTH_STATUS_INACTIVE, +} = require('../../adapters/networkhandler/authConstants'); // ======================================================================== // INLINERS // ======================================================================== @@ -1829,8 +1833,8 @@ const getAccessToken = (metadata, accessTokenKey) => { // OAuth for this destination const { secret } = metadata; // we would need to verify if secret is present and also if the access token field is present in secret - if (!secret || !secret[accessTokenKey]) { - throw new OAuthSecretError('Empty/Invalid access token'); + if (!secret?.[accessTokenKey]) { + throw new OAuthSecretError('OAuth - access token not found'); } return secret[accessTokenKey]; }; @@ -1912,6 +1916,51 @@ const batchMultiplexedEvents = (transformedEventsList, maxBatchSize) => { return batchedEvents; }; +/** + * This function helps to detarmine type of error occured. According to the response + * we set authErrorCategory to take decision if we need to refresh the access_token + * or need to de-activate authStatus for the destination. + * + * **Scenarios**: + * - statusCode=401, response.error.details !== "" -> REFRESH_TOKEN + * - statusCode=403, response.error.details !== "" -> AUTH_STATUS_INACTIVE + * + * @param {*} code + * @param {*} response + * @returns + */ +const getAuthErrCategoryFromErrDetailsAndStCode = (code, response) => { + if (code === 401 && (!get(response, 'error.details') || typeof response === 'string')) + return REFRESH_TOKEN; + if (code === 403 && (!get(response, 'error.details') || typeof response === 'string')) + return AUTH_STATUS_INACTIVE; + return ''; +}; + +/** + * This function helps to determine the type of error occurred. We set the authErrorCategory + * as per the destination response that is received and take the decision whether + * to refresh the access_token or de-activate authStatus. + * + * **Scenarios**: + * - statusCode=401 -> REFRESH_TOKEN + * - statusCode=403 -> AUTH_STATUS_INACTIVE + * + * @param {*} status + * @returns + */ +const getAuthErrCategoryFromStCode = (status) => { + if (status === 401) { + // UNAUTHORIZED + return REFRESH_TOKEN; + } + if (status === 403) { + // ACCESS_DENIED + return AUTH_STATUS_INACTIVE; + } + return ''; +}; + // ======================================================================== // EXPORTS // ======================================================================== @@ -2012,4 +2061,6 @@ module.exports = { checkAndCorrectUserId, getAccessToken, formatValues, + getAuthErrCategoryFromErrDetailsAndStCode, + getAuthErrCategoryFromStCode, }; diff --git a/test/__tests__/data/google_adwords_enhanced_conversions_output.json b/test/__tests__/data/google_adwords_enhanced_conversions_output.json index f90bcd5901..a0b1f1216d 100644 --- a/test/__tests__/data/google_adwords_enhanced_conversions_output.json +++ b/test/__tests__/data/google_adwords_enhanced_conversions_output.json @@ -177,7 +177,7 @@ "secret": null }, "statusCode": 400, - "error": "Empty/Invalid access token", + "error": "OAuth - access token not found", "statTags": { "destination": "google_adwords_enhanced_conversions", "stage": "transform", diff --git a/test/__tests__/data/google_adwords_enhanced_conversions_router_output.json b/test/__tests__/data/google_adwords_enhanced_conversions_router_output.json index 6ddec019e3..18e816b33c 100644 --- a/test/__tests__/data/google_adwords_enhanced_conversions_router_output.json +++ b/test/__tests__/data/google_adwords_enhanced_conversions_router_output.json @@ -137,7 +137,7 @@ }, "batched": false, "statusCode": 500, - "error": "Empty/Invalid access token", + "error": "OAuth - access token not found", "statTags": { "errorCategory": "platform", "errorType": "oAuthSecret" diff --git a/test/__tests__/data/google_adwords_remarketing_lists_output.json b/test/__tests__/data/google_adwords_remarketing_lists_output.json index 8c938bc3d3..389ec26e25 100644 --- a/test/__tests__/data/google_adwords_remarketing_lists_output.json +++ b/test/__tests__/data/google_adwords_remarketing_lists_output.json @@ -5779,7 +5779,7 @@ "secret": null }, "statusCode": 500, - "error": "Empty/Invalid access token", + "error": "OAuth - access token not found", "statTags": { "destination": "google_adwords_remarketing_lists", "stage": "transform", diff --git a/test/__tests__/data/pardot_router_output.json b/test/__tests__/data/pardot_router_output.json index 913e35ce94..889cc8fd96 100644 --- a/test/__tests__/data/pardot_router_output.json +++ b/test/__tests__/data/pardot_router_output.json @@ -376,7 +376,7 @@ ], "batched": false, "statusCode": 500, - "error": "Empty/Invalid access token", + "error": "OAuth - access token not found", "statTags": { "errorCategory": "platform", "errorType": "oAuthSecret" From 1f0962966fdded7afa156f933e25b78ef243650b Mon Sep 17 00:00:00 2001 From: Sai Sankeerth Date: Thu, 14 Sep 2023 16:22:21 +0530 Subject: [PATCH 5/6] fix: pardot router component test failure and remove unnecessary code Signed-off-by: Sai Sankeerth --- .../networkHandler.js | 20 ------------------- .../destinations/pardot/router/data.ts | 2 +- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/v0/destinations/snapchat_custom_audience/networkHandler.js b/src/v0/destinations/snapchat_custom_audience/networkHandler.js index 870cc16aae..29018ee4b9 100644 --- a/src/v0/destinations/snapchat_custom_audience/networkHandler.js +++ b/src/v0/destinations/snapchat_custom_audience/networkHandler.js @@ -50,29 +50,9 @@ const scAudienceProxyRequest = async (request) => { return response; }; -/** - * This function helps to determine type of error occurred. According to the response - * we set authErrorCategory to take decision if we need to refresh the access_token - * or need to disable the destination. - * @param {*} code - * @param {*} response - * @returns - */ -const getAuthErrCategory = (code, response) => { - let authErrCategory = ''; - if (code === 401) { - authErrCategory = !response.error?.details ? REFRESH_TOKEN : ''; - } - if (code === 403) { - authErrCategory = !response.error?.details ? AUTH_STATUS_INACTIVE : ''; - } - return authErrCategory; -}; - const scaAudienceRespHandler = (destResponse, stageMsg) => { const { status, response } = destResponse; const authErrCategory = getAuthErrCategoryFromErrDetailsAndStCode(status, response); - // const authErrCategory = getAuthErrCategory(status, response); if (authErrCategory === REFRESH_TOKEN) { throw new RetryableError( diff --git a/test/integrations/destinations/pardot/router/data.ts b/test/integrations/destinations/pardot/router/data.ts index 573f9192b5..28680deafc 100644 --- a/test/integrations/destinations/pardot/router/data.ts +++ b/test/integrations/destinations/pardot/router/data.ts @@ -999,7 +999,7 @@ export const data = [ ], batched: false, statusCode: 500, - error: 'Empty/Invalid access token', + error: 'OAuth - access token not found', statTags: { destType: 'PARDOT', feature: 'router', From c4a287f4dde9826f02cccdf2a4200adf4360de50 Mon Sep 17 00:00:00 2001 From: Sai Sankeerth Date: Thu, 14 Sep 2023 16:40:20 +0530 Subject: [PATCH 6/6] fix: remove unused code --- src/v0/destinations/campaign_manager/networkHandler.js | 4 ---- .../google_adwords_enhanced_conversions/transform.js | 1 - .../destinations/google_adwords_offline_conversions/utils.js | 1 - src/v0/destinations/pardot/transform.js | 1 - .../destinations/snapchat_custom_audience/networkHandler.js | 1 - 5 files changed, 8 deletions(-) diff --git a/src/v0/destinations/campaign_manager/networkHandler.js b/src/v0/destinations/campaign_manager/networkHandler.js index 7d4f401dc0..a80fff6fe4 100644 --- a/src/v0/destinations/campaign_manager/networkHandler.js +++ b/src/v0/destinations/campaign_manager/networkHandler.js @@ -1,9 +1,5 @@ const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network'); const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../util/index'); -const { - REFRESH_TOKEN, - AUTH_STATUS_INACTIVE, -} = require('../../../adapters/networkhandler/authConstants'); const { processAxiosResponse, diff --git a/src/v0/destinations/google_adwords_enhanced_conversions/transform.js b/src/v0/destinations/google_adwords_enhanced_conversions/transform.js index 350fe4032c..24993a3006 100644 --- a/src/v0/destinations/google_adwords_enhanced_conversions/transform.js +++ b/src/v0/destinations/google_adwords_enhanced_conversions/transform.js @@ -14,7 +14,6 @@ const { const { InstrumentationError, ConfigurationError, - OAuthSecretError, } = require('../../util/errorTypes'); const { trackMapping, BASE_ENDPOINT } = require('./config'); diff --git a/src/v0/destinations/google_adwords_offline_conversions/utils.js b/src/v0/destinations/google_adwords_offline_conversions/utils.js index 71d70632bb..37f17c9fd0 100644 --- a/src/v0/destinations/google_adwords_offline_conversions/utils.js +++ b/src/v0/destinations/google_adwords_offline_conversions/utils.js @@ -27,7 +27,6 @@ const { processAxiosResponse } = require('../../../adapters/utils/networkUtils') const Cache = require('../../util/cache'); const { AbortedError, - OAuthSecretError, ConfigurationError, InstrumentationError, } = require('../../util/errorTypes'); diff --git a/src/v0/destinations/pardot/transform.js b/src/v0/destinations/pardot/transform.js index b166416a5d..b9f78a6c34 100644 --- a/src/v0/destinations/pardot/transform.js +++ b/src/v0/destinations/pardot/transform.js @@ -49,7 +49,6 @@ const { } = require('../../util'); const { CONFIG_CATEGORIES } = require('./config'); const { - OAuthSecretError, ConfigurationError, InstrumentationError, } = require('../../util/errorTypes'); diff --git a/src/v0/destinations/snapchat_custom_audience/networkHandler.js b/src/v0/destinations/snapchat_custom_audience/networkHandler.js index 29018ee4b9..49ea452550 100644 --- a/src/v0/destinations/snapchat_custom_audience/networkHandler.js +++ b/src/v0/destinations/snapchat_custom_audience/networkHandler.js @@ -3,7 +3,6 @@ const { prepareProxyRequest, getPayloadData, httpSend } = require('../../../adap const { isHttpStatusSuccess } = require('../../util/index'); const { REFRESH_TOKEN, - AUTH_STATUS_INACTIVE, } = require('../../../adapters/networkhandler/authConstants'); const tags = require('../../util/tags'); const {