Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce de-activation of authStatus for access_denied or inva… #2598

Merged
merged 11 commits into from
Sep 15, 2023
Merged
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