Skip to content

Commit

Permalink
fix: deleting access token cache for marketo bulk upload destination (#…
Browse files Browse the repository at this point in the history
…3029)

* fix: removing cache and adding debug logs

* fix: updating test case

* fix: removing console logs

* fix: removing console logs

* fix: removing console logs

* fix: adding more test cases

* Apply suggestions from code review

Co-authored-by: Utsab Chowdhury <[email protected]>

* fix: editing test case

* fix: small edit

* fix: code review address and move test cases in single file

---------

Co-authored-by: Utsab Chowdhury <[email protected]>
  • Loading branch information
shrouti1507 and utsabc authored Jan 25, 2024
1 parent a664a08 commit 78b75bf
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 91 deletions.
5 changes: 2 additions & 3 deletions src/v0/destinations/marketo_bulk_upload/fileUpload.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,11 @@ const getImportID = async (input, config, accessToken, csvHeader) => {
stats.counter('marketo_bulk_upload_upload_file_unsuccJobs', unsuccessfulJobs.length);
if (!isHttpStatusSuccess(resp.status)) {
throw new NetworkError(
`Unable to upload file due to error : ${resp.response}`,
`Unable to upload file due to error : ${JSON.stringify(resp.response)}`,
hydrateStatusForServer(resp.status, 'During uploading file'),
);
}
return handleFileUploadResponse(resp, successfulJobs, unsuccessfulJobs, requestTime, config);
return handleFileUploadResponse(resp, successfulJobs, unsuccessfulJobs, requestTime);
}
return { importId: null, successfulJobs, unsuccessfulJobs };
};
Expand Down Expand Up @@ -242,7 +242,6 @@ const responseHandler = async (input, config) => {
accessToken,
headerForCsv,
);

// if upload is successful
if (importId) {
const csvHeader = headerForCsv.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,49 @@ const {
handleCommonErrorResponse,
handlePollResponse,
handleFileUploadResponse,
getAccessToken,
} = require('./util');

const { AbortedError, RetryableError } = require('@rudderstack/integrations-lib');
const {
AbortedError,
RetryableError,
NetworkError,
TransformationError,
} = require('@rudderstack/integrations-lib');
const util = require('./util.js');
const networkAdapter = require('../../../adapters/network');
const { handleHttpRequest } = networkAdapter;

// Mock the handleHttpRequest function
jest.mock('../../../adapters/network');

const successfulResponse = {
status: 200,
response: {
access_token: '<dummy-access-token>',
token_type: 'bearer',
expires_in: 3600,
scope: '[email protected]',
success: true,
},
};

const unsuccessfulResponse = {
status: 400,
response: '[ENOTFOUND] :: DNS lookup failed',
};

const emptyResponse = {
response: '',
};

const invalidClientErrorResponse = {
status: 401,
response: {
error: 'invalid_client',
error_description: 'Bad client credentials',
},
};

describe('handleCommonErrorResponse', () => {
test('should throw AbortedError for abortable error codes', () => {
Expand All @@ -13,7 +53,7 @@ describe('handleCommonErrorResponse', () => {
errors: [{ code: 1003, message: 'Aborted' }],
},
};
expect(() => handleCommonErrorResponse(resp, 'OpErrorMessage', 'OpActivity')).toThrow(
expect(() => handleCommonErrorResponse(resp, 'opErrorMessage', 'opActivity')).toThrow(
AbortedError,
);
});
Expand All @@ -24,7 +64,7 @@ describe('handleCommonErrorResponse', () => {
errors: [{ code: 615, message: 'Throttled' }],
},
};
expect(() => handleCommonErrorResponse(resp, 'OpErrorMessage', 'OpActivity')).toThrow(
expect(() => handleCommonErrorResponse(resp, 'opErrorMessage', 'opActivity')).toThrow(
RetryableError,
);
});
Expand All @@ -35,7 +75,7 @@ describe('handleCommonErrorResponse', () => {
errors: [{ code: 2000, message: 'Retryable' }],
},
};
expect(() => handleCommonErrorResponse(resp, 'OpErrorMessage', 'OpActivity')).toThrow(
expect(() => handleCommonErrorResponse(resp, 'opErrorMessage', 'opActivity')).toThrow(
RetryableError,
);
});
Expand All @@ -46,7 +86,7 @@ describe('handleCommonErrorResponse', () => {
errors: [],
},
};
expect(() => handleCommonErrorResponse(resp, 'OpErrorMessage', 'OpActivity')).toThrow(
expect(() => handleCommonErrorResponse(resp, 'opErrorMessage', 'opActivity')).toThrow(
RetryableError,
);
});
Expand Down Expand Up @@ -228,3 +268,88 @@ describe('handleFileUploadResponse', () => {
}).toThrow(AbortedError);
});
});

describe('getAccessToken', () => {
beforeEach(() => {
handleHttpRequest.mockClear();
});

it('should retrieve and return access token on successful response', async () => {
const url =
'https://dummyMunchkinId.mktorest.com/identity/oauth/token?client_id=dummyClientId&client_secret=dummyClientSecret&grant_type=client_credentials';

handleHttpRequest.mockResolvedValueOnce({
processedResponse: successfulResponse,
});

const config = {
clientId: 'dummyClientId',
clientSecret: 'dummyClientSecret',
munchkinId: 'dummyMunchkinId',
};

const result = await getAccessToken(config);
expect(result).toBe('<dummy-access-token>');
expect(handleHttpRequest).toHaveBeenCalledTimes(1);
// Ensure your mock response structure is consistent with the actual behavior
expect(handleHttpRequest).toHaveBeenCalledWith('get', url, {
destType: 'marketo_bulk_upload',
feature: 'transformation',
});
});

it('should throw a NetworkError on unsuccessful HTTP status', async () => {
handleHttpRequest.mockResolvedValueOnce({
processedResponse: unsuccessfulResponse,
});

const config = {
clientId: 'dummyClientId',
clientSecret: 'dummyClientSecret',
munchkinId: 'dummyMunchkinId',
};

await expect(getAccessToken(config)).rejects.toThrow(NetworkError);
});

it('should throw a RetryableError when expires_in is 0', async () => {
handleHttpRequest.mockResolvedValueOnce({
processedResponse: {
...successfulResponse,
response: { ...successfulResponse.response, expires_in: 0 },
},
});

const config = {
clientId: 'dummyClientId',
clientSecret: 'dummyClientSecret',
munchkinId: 'dummyMunchkinId',
};

await expect(getAccessToken(config)).rejects.toThrow(RetryableError);
});

it('should throw an AbortedError on unsuccessful response', async () => {
handleHttpRequest.mockResolvedValueOnce({ processedResponse: invalidClientErrorResponse });

const config = {
clientId: 'invalidClientID',
clientSecret: 'dummyClientSecret',
munchkinId: 'dummyMunchkinId',
};

await expect(getAccessToken(config)).rejects.toThrow(NetworkError);
});

it('should throw transformation error response', async () => {
handleHttpRequest.mockResolvedValueOnce({ processedResponse: emptyResponse });

const config = {
clientId: 'dummyClientId',
clientSecret: 'dummyClientSecret',
munchkinId: 'dummyMunchkinId',
};

await expect(getAccessToken(config)).rejects.toThrow(TransformationError);
});
});
4 changes: 2 additions & 2 deletions src/v0/destinations/marketo_bulk_upload/poll.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ const getPollStatus = async (event) => {
state: 'Retryable',
});
throw new NetworkError(
`Could not poll status: due to error ${pollStatus.response}`,
`Could not poll status: due to error ${JSON.stringify(pollStatus.response)}`,
hydrateStatusForServer(pollStatus.status, 'During fetching poll status'),
);
}
return handlePollResponse(pollStatus, event.config);
return handlePollResponse(pollStatus);
};

const responseHandler = async (event) => {
Expand Down
Loading

0 comments on commit 78b75bf

Please sign in to comment.