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

chore: adding salesforce oauth sandbox support for closed testing #3778

Merged
merged 9 commits into from
Oct 8, 2024
1 change: 1 addition & 0 deletions src/constants/destinationCanonicalNames.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const DestHandlerMap = {
ga360: 'ga',
salesforce_oauth: 'salesforce',
salesforce_oauth_sandbox: 'salesforce',
};

const DestCanonicalNames = {
Expand Down
1 change: 1 addition & 0 deletions src/features.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"PROFITWELL": true,
"SALESFORCE": true,
"SALESFORCE_OAUTH": true,
"SALESFORCE_OAUTH_SANDBOX": true,
"SFMC": true,
"SNAPCHAT_CONVERSION": true,
"TIKTOK_ADS": true,
Expand Down
2 changes: 2 additions & 0 deletions src/v0/destinations/salesforce/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const SF_TOKEN_REQUEST_URL = 'https://login.salesforce.com/services/oauth2/token
const SF_TOKEN_REQUEST_URL_SANDBOX = 'https://test.salesforce.com/services/oauth2/token';

const DESTINATION = 'Salesforce';
const SALESFORCE_OAUTH_SANDBOX = 'salesforce_oauth_sandbox';
const OAUTH = 'oauth';
const LEGACY = 'legacy';

Expand All @@ -41,4 +42,5 @@ module.exports = {
DESTINATION,
OAUTH,
LEGACY,
SALESFORCE_OAUTH_SANDBOX,
};
3 changes: 2 additions & 1 deletion src/v0/destinations/salesforce/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ async function processIdentify(
authorizationData,
authorizationFlow,
) {
const { Name } = destination.DestinationDefinition;
const mapProperty =
destination.Config.mapProperty === undefined ? true : destination.Config.mapProperty;
// check the traits before hand
Expand All @@ -304,7 +305,7 @@ async function processIdentify(
// Append external ID to traits if event is mapped to destination and only if identifier type is not id
// If identifier type is id, then it should not be added to traits, else saleforce will throw an error
const mappedToDestination = get(message, MappedToDestinationKey);
const externalId = getDestinationExternalIDObjectForRetl(message, 'SALESFORCE');
const externalId = getDestinationExternalIDObjectForRetl(message, Name);
if (mappedToDestination && externalId?.identifierType?.toLowerCase() !== 'id') {
addExternalIdToTraits(message);
}
Expand Down
25 changes: 19 additions & 6 deletions src/v0/destinations/salesforce/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
const { RetryableError, ThrottledError, AbortedError } = require('@rudderstack/integrations-lib');
const {
RetryableError,
ThrottledError,
AbortedError,
OAuthSecretError,
} = require('@rudderstack/integrations-lib');
const { handleHttpRequest } = require('../../../adapters/network');
const {
isHttpStatusSuccess,
Expand All @@ -13,6 +18,7 @@ const {
DESTINATION,
LEGACY,
OAUTH,
SALESFORCE_OAUTH_SANDBOX,
} = require('./config');

const ACCESS_TOKEN_CACHE = new Cache(ACCESS_TOKEN_CACHE_TTL);
Expand Down Expand Up @@ -104,10 +110,15 @@ const salesforceResponseHandler = (destResponse, sourceMessage, authKey, authori
* @param {destination: Record<string, any>, metadata: Record<string, object>}
* @returns
*/
const getAccessTokenOauth = (metadata) => ({
token: metadata.secret?.access_token,
instanceUrl: metadata.secret?.instance_url,
});
const getAccessTokenOauth = (metadata) => {
if (!isDefinedAndNotNull(metadata?.secret)) {
throw new OAuthSecretError('secret is undefined/null');
}
return {
token: metadata.secret?.access_token,
instanceUrl: metadata.secret?.instance_url,
};
};

const getAccessToken = async ({ destination, metadata }) => {
const accessTokenKey = destination.ID;
Expand Down Expand Up @@ -169,7 +180,9 @@ const getAccessToken = async ({ destination, metadata }) => {
const collectAuthorizationInfo = async (event) => {
let authorizationFlow;
let authorizationData;
if (isDefinedAndNotNull(event.metadata?.secret)) {
const { Name } = event.destination.DestinationDefinition;
const lowerCaseName = Name?.toLowerCase?.();
if (isDefinedAndNotNull(event?.metadata?.secret) || lowerCaseName === SALESFORCE_OAUTH_SANDBOX) {
authorizationFlow = OAUTH;
authorizationData = getAccessTokenOauth(event.metadata);
} else {
Expand Down
34 changes: 34 additions & 0 deletions src/v0/destinations/salesforce_oauth_sandbox/networkHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { proxyRequest, prepareProxyRequest } = require('../../../adapters/network');
const { processAxiosResponse } = require('../../../adapters/utils/networkUtils');
const { OAUTH } = require('../salesforce/config');
const { salesforceResponseHandler } = require('../salesforce/utils');

const responseHandler = (responseParams) => {
const { destinationResponse, destType, rudderJobMetadata } = responseParams;
const message = `Request for destination: ${destType} Processed Successfully`;

salesforceResponseHandler(
destinationResponse,
'during Salesforce Response Handling',
rudderJobMetadata?.destInfo?.authKey,
OAUTH,
);

// else successfully return status as 200, message and original destination response
return {

Check warning on line 18 in src/v0/destinations/salesforce_oauth_sandbox/networkHandler.js

View check run for this annotation

Codecov / codecov/patch

src/v0/destinations/salesforce_oauth_sandbox/networkHandler.js#L18

Added line #L18 was not covered by tests
status: 200,
message,
destinationResponse,
};
};

function networkHandler() {
this.responseHandler = responseHandler;
this.proxy = proxyRequest;
this.prepareProxy = prepareProxyRequest;
this.processAxiosResponse = processAxiosResponse;
}

module.exports = {
networkHandler,
};
116 changes: 112 additions & 4 deletions test/integrations/destinations/salesforce/processor/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1373,7 +1373,7 @@ export const data = [
},
{
name: 'salesforce',
description: 'Test 10',
description: 'Test 11',
feature: 'processor',
module: 'destination',
version: 'v0',
Expand All @@ -1389,9 +1389,9 @@ export const data = [
sandbox: true,
},
DestinationDefinition: {
DisplayName: 'Salesforce',
DisplayName: 'Salesforce Sandbox',
ID: '1T96GHZ0YZ1qQSLULHCoJkow9KC',
Name: 'SALESFORCE',
Name: 'SALESFORCE_OAUTH_SANDBOX',
},
Enabled: true,
ID: '1ut7LcVW1QC56y2EoTNo7ZwBWSY',
Expand All @@ -1412,7 +1412,7 @@ export const data = [
externalId: [
{
id: 'a005g0000383kmUAAQ',
type: 'SALESFORCE-custom_object__c',
type: 'SALESFORCE_OAUTH_SANDBOX-custom_object__c',
identifierType: 'Id',
},
],
Expand Down Expand Up @@ -1499,4 +1499,112 @@ export const data = [
},
},
},
{
name: 'salesforce',
description: 'Test 12 : Retry happens when no secret information is found',
feature: 'processor',
module: 'destination',
version: 'v0',
input: {
request: {
body: [
{
destination: {
Config: {
initialAccessToken: '7fiy1FKcO9sohsxq1v6J88sg',
password: 'dummyPassword2',
userName: '[email protected]',
sandbox: true,
},
DestinationDefinition: {
DisplayName: 'Salesforce Sandbox',
ID: '1T96GHZ0YZ1qQSLULHCoJkow9KC',
Name: 'SALESFORCE_OAUTH_SANDBOX',
},
Enabled: true,
ID: '1ut7LcVW1QC56y2EoTNo7ZwBWSY',
Name: 'Test SF',
Transformations: [],
},
metadata: {
jobId: 1,
},
message: {
anonymousId: '1e7673da-9473-49c6-97f7-da848ecafa76',
channel: 'web',
context: {
mappedToDestination: true,
externalId: [
{
id: 'a005g0000383kmUAAQ',
type: 'SALESFORCE_OAUTH_SANDBOX-custom_object__c',
identifierType: 'Id',
},
],
app: {
build: '1.0.0',
name: 'RudderLabs JavaScript SDK',
namespace: 'com.rudderlabs.javascript',
version: '1.0.0',
},
ip: '0.0.0.0',
library: {
name: 'RudderLabs JavaScript SDK',
version: '1.0.0',
},
locale: 'en-US',
os: {
name: '',
version: '',
},
screen: {
density: 2,
},
traits: {
email: '[email protected]',
firstname: 'john doe',
Id: 'some-id',
},
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36',
},
integrations: {
All: true,
},
messageId: 'f19c35da-e9de-4c6e-b6e5-9e60cccc12c8',
originalTimestamp: '2020-01-27T12:20:55.301Z',
receivedAt: '2020-01-27T17:50:58.657+05:30',
request_ip: '14.98.244.60',
sentAt: '2020-01-27T12:20:56.849Z',
timestamp: '2020-01-27T17:50:57.109+05:30',
type: 'identify',
userId: '1e7673da-9473-49c6-97f7-da848ecafa76',
},
},
],
},
},
output: {
response: {
status: 200,
body: [
{
statusCode: 500,
error: 'secret is undefined/null',
metadata: {
jobId: 1,
},
statTags: {
errorCategory: 'platform',
errorType: 'oAuthSecret',
destType: 'SALESFORCE',
module: 'destination',
implementation: 'native',
feature: 'processor',
},
},
],
},
},
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { testScenariosForV1API } from './oauth';

export const data = [...testScenariosForV1API];
Loading
Loading