Skip to content

Commit

Permalink
feat: introduce de-activation of authStatus for access_denied or inva… (
Browse files Browse the repository at this point in the history
#2598)

* feat: introduce de-activation of authStatus for access_denied or invalid_grant errors

Signed-off-by: Sai Sankeerth <[email protected]>

* fix: test-cases-1

Signed-off-by: Sai Sankeerth <[email protected]>

* chore: remove all references of disable_dest and remove the const disable_dest

Signed-off-by: Sai Sankeerth <[email protected]>

* chore: refactor to use util methods for getting auth error category and access-token

Signed-off-by: Sai Sankeerth <[email protected]>

* fix: pardot router component  test failure and remove unnecessary code

Signed-off-by: Sai Sankeerth <[email protected]>

* fix: remove unused code

---------

Signed-off-by: Sai Sankeerth <[email protected]>
Co-authored-by: Sai Sankeerth <[email protected]>
  • Loading branch information
sanpj2292 and Sai Sankeerth authored Sep 15, 2023
1 parent 0f3b39e commit 3b1fef6
Show file tree
Hide file tree
Showing 20 changed files with 112 additions and 240 deletions.
2 changes: 1 addition & 1 deletion src/adapters/networkhandler/authConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
* This class is used for handling Auth related errors
*/
module.exports = {
DISABLE_DEST: 'DISABLE_DESTINATION',
REFRESH_TOKEN: 'REFRESH_TOKEN',
AUTH_STATUS_INACTIVE: 'AUTH_STATUS_INACTIVE',
};
11 changes: 7 additions & 4 deletions src/v0/destinations/bqstream/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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 } = {}) => {
Expand Down
19 changes: 2 additions & 17 deletions src/v0/destinations/campaign_manager/networkHandler.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const { prepareProxyRequest, proxyRequest } = require('../../../adapters/network');
const { isHttpStatusSuccess } = require('../../util/index');
const { REFRESH_TOKEN } = require('../../../adapters/networkhandler/authConstants');
const { isHttpStatusSuccess, getAuthErrCategoryFromStCode } = require('../../util/index');

const {
processAxiosResponse,
Expand All @@ -9,20 +8,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;
}
return '';
};

function checkIfFailuresAreRetryable(response) {
try {
if (Array.isArray(response.status) && Array.isArray(response.status[0].errors)) {
Expand Down Expand Up @@ -73,7 +58,7 @@ const responseHandler = (destinationResponse) => {
[tags.TAG_NAMES.ERROR_TYPE]: getDynamicErrorType(status),
},
destinationResponse,
getAuthErrCategory(status),
getAuthErrCategoryFromStCode(status),
);
};

Expand Down
12 changes: 3 additions & 9 deletions src/v0/destinations/campaign_manager/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const {
removeUndefinedAndNullValues,
isDefinedAndNotNull,
simpleProcessRouterDest,
getAccessToken,
} = require('../../util');

const {
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +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 } = require('../../../adapters/networkhandler/authConstants');
const {
isHttpStatusSuccess,
getAuthErrCategoryFromErrDetailsAndStCode,
} = require('../../util/index');
const { CONVERSION_ACTION_ID_CACHE_TTL } = require('./config');
const Cache = require('../../util/cache');

Expand All @@ -15,18 +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;
return '';
};

/**
* This function is used for collecting the conversionActionId using the conversion name
Expand Down Expand Up @@ -68,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'),
),
Expand Down Expand Up @@ -134,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ const {
getValueFromMessage,
removeHyphens,
simpleProcessRouterDest,
getAccessToken,
} = require('../../util');

const {
InstrumentationError,
ConfigurationError,
OAuthSecretError,
} = require('../../util/errorTypes');

const { trackMapping, BASE_ENDPOINT } = require('./config');
Expand All @@ -36,34 +36,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ 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,
getAuthErrCategoryFromStCode,
} = require('../../util');
const { getConversionActionId } = require('./utils');
const Cache = require('../../util/cache');
Expand All @@ -24,21 +24,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(
Expand All @@ -57,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];
Expand All @@ -80,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;
Expand Down Expand Up @@ -131,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');
Expand Down Expand Up @@ -292,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),
);
};

Expand Down
37 changes: 4 additions & 33 deletions src/v0/destinations/google_adwords_offline_conversions/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ const {
getFieldValueFromMessage,
isDefinedAndNotNullAndNotEmpty,
isDefinedAndNotNull,
getAuthErrCategoryFromStCode,
getAccessToken,
} = require('../../util');
const { REFRESH_TOKEN } = require('../../../adapters/networkhandler/authConstants');
const {
SEARCH_STREAM,
CONVERSION_ACTION_ID_CACHE_TTL,
Expand All @@ -26,7 +27,6 @@ const { processAxiosResponse } = require('../../../adapters/utils/networkUtils')
const Cache = require('../../util/cache');
const {
AbortedError,
OAuthSecretError,
ConfigurationError,
InstrumentationError,
} = require('../../util/errorTypes');
Expand All @@ -43,34 +43,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 occured. 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 '';
};

/**
* get conversionAction using the conversion name using searchStream endpoint
* @param {*} customerId
Expand Down Expand Up @@ -100,7 +72,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(
Expand Down Expand Up @@ -194,7 +166,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'),
};
Expand Down Expand Up @@ -390,7 +362,6 @@ const getClickConversionPayloadAndEndpoint = (message, Config, filteredCustomerI
module.exports = {
validateDestinationConfig,
generateItemListFromProducts,
getAccessToken,
getConversionActionId,
removeHashToSha256TypeFromMappingJson,
getStoreConversionPayload,
Expand Down
Loading

0 comments on commit 3b1fef6

Please sign in to comment.