Skip to content

Commit

Permalink
chore: adding salesforce oauth sandbox support for closed testing (#3778
Browse files Browse the repository at this point in the history
)

* chore: adding salesforce oauth sandbox support for closed testing

* chore: adding test log

* fix: throw error for empty secret

* fix: processor last test-case

* chore: add logical changes for passing test-cases

* fix: adding dynamic destination name for rETL connection

* fix: adding test case

* fix: remove log

---------

Co-authored-by: Sai Sankeerth <[email protected]>
  • Loading branch information
shrouti1507 and Sai Sankeerth authored Oct 8, 2024
1 parent f3046f0 commit 6d9976c
Show file tree
Hide file tree
Showing 10 changed files with 399 additions and 11 deletions.
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 {
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

0 comments on commit 6d9976c

Please sign in to comment.