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

refactor: zoho record processing #4054

Merged
merged 20 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
44d9121
refactor: zoho to not asssume same action in all events of a batch
koladilip Feb 6, 2025
8b90258
refactor: zoho to not asssume same action in all events of a batch
koladilip Feb 7, 2025
07c62c0
refactor: zoho to not asssume same action in all events of a batch
koladilip Feb 7, 2025
844150c
Merge branch 'develop' into refactor/zoho-record-processing
koladilip Feb 7, 2025
098fcb5
refactor: remove unused import in zoho tests
koladilip Feb 10, 2025
34f977b
refactor: remove unused import in zoho tests
koladilip Feb 10, 2025
560bef4
refactor: add missing tests
koladilip Feb 10, 2025
8a8ef5a
refactor: zoho utils using cursor
koladilip Feb 10, 2025
09905f8
refactor: zoho utils using cursor
koladilip Feb 10, 2025
741080a
chore: add more tests for zoho utils
koladilip Feb 11, 2025
208c375
chore: add more tests for zoho utils
koladilip Feb 11, 2025
e49ae2d
Merge branch 'develop' into refactor/zoho-record-processing
koladilip Feb 11, 2025
2b6ff09
Merge branch 'develop' into refactor/zoho-record-processing
koladilip Feb 12, 2025
75d68aa
refactor: update zoho search record id function
koladilip Feb 12, 2025
b18e138
refactor: update zoho format multi select fields function
koladilip Feb 12, 2025
210f71f
refactor: add more util tests for zoho
koladilip Feb 12, 2025
a0c09f5
Merge branch 'develop' into refactor/zoho-record-processing
koladilip Feb 12, 2025
342c195
refactor: use function to express condition
koladilip Feb 12, 2025
2ae02f1
chore: add more test for zoho
koladilip Feb 13, 2025
2f86476
chore: add more test for zoho
koladilip Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/cdk/v2/destinations/zoho/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const DATA_CENTRE_BASE_ENDPOINTS_MAP = {
};

const getBaseEndpoint = (dataServer) => DATA_CENTRE_BASE_ENDPOINTS_MAP[dataServer];
const COMMON_RECORD_ENDPOINT = (dataCenter = 'US') =>
`${getBaseEndpoint(dataCenter)}/crm/v6/moduleType`;
const COMMON_RECORD_ENDPOINT = (dataCenter) =>
`${getBaseEndpoint(dataCenter || 'US')}/crm/v6/moduleType`;

// ref: https://www.zoho.com/crm/developer/docs/api/v6/insert-records.html#:~:text=%2DX%20POST-,System%2Ddefined%20mandatory%20fields%20for%20each%20module,-While%20inserting%20records
const MODULE_MANDATORY_FIELD_CONFIG = {
Expand Down
11 changes: 9 additions & 2 deletions src/cdk/v2/destinations/zoho/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ const formatMultiSelectFields = (config, fields) => {
const formattedFields = { ...fields };

Object.keys(formattedFields).forEach((eachFieldKey) => {
if (multiSelectFields.hasOwnProperty(eachFieldKey)) {
if (
multiSelectFields.hasOwnProperty(eachFieldKey) &&
isDefinedAndNotNull(formattedFields[eachFieldKey])
) {
formattedFields[eachFieldKey] = [formattedFields[eachFieldKey]];
}
});
Expand Down Expand Up @@ -126,7 +129,11 @@ const searchRecordId = async (fields, metadata, Config) => {
};
}

if (searchResult.processedResponse.status === 204) {
if (
searchResult.processedResponse.status === 204 ||
!Array.isArray(searchResult.processedResponse.response?.data) ||
searchResult.processedResponse.response?.data?.length === 0
) {
koladilip marked this conversation as resolved.
Show resolved Hide resolved
return {
erroneous: true,
message: 'No contact is found with record details',
Expand Down
202 changes: 154 additions & 48 deletions src/cdk/v2/destinations/zoho/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ describe('formatMultiSelectFields', () => {
},
expected: { tags: ['value'] },
},
{
name: 'should leave fields unchanged if mapping fields exists but null',
input: {
config: {
multiSelectFieldLevelDecision: [{ from: 'tags', to: 'tagsArray' }],
},
fields: { tags: null, other: 'val' },
},
expected: { tags: null, other: 'val' },
},
{
name: 'should leave fields unchanged if no mapping exists',
input: {
Expand Down Expand Up @@ -150,82 +160,136 @@ describe('calculateTrigger', () => {
});

describe('searchRecordId', () => {
beforeEach(() => {
jest.resetAllMocks();
});
const mockFields = { Email: '[email protected]' };
const mockMetadata = { secret: { accessToken: 'mock-token' } };
const mockConfig = { region: 'us' };

afterEach(() => {
beforeEach(() => {
jest.clearAllMocks();
});

const testCases = [
{
name: 'should return valid record IDs when HTTP response is successful with data',
input: {
fields: { email: '[email protected]' },
metadata: { secret: { accessToken: 'token' } },
config: { region: 'US' },
mockResponse: {
processedResponse: {
status: 200,
response: {
data: [{ id: 'rec1' }, { id: 'rec2' }],
},
name: 'should handle non-array response data',
response: {
processedResponse: {
status: 200,
response: {
data: 'not-an-array',
},
},
},
expected: { erroneous: false, message: ['rec1', 'rec2'] },
expected: {
erroneous: true,
message: 'No contact is found with record details',
},
},
{
name: 'should return error if HTTP status indicates failure',
input: {
fields: { email: '[email protected]' },
metadata: { secret: { accessToken: 'token' } },
config: { region: 'US' },
mockResponse: {
processedResponse: {
status: 400,
response: 'Bad Request',
name: 'should handle missing response data property',
response: {
processedResponse: {
status: 200,
response: {},
},
},
expected: {
erroneous: true,
message: 'No contact is found with record details',
},
},
{
name: 'should handle null response data',
response: {
processedResponse: {
status: 200,
response: {
data: null,
},
},
},
expected: { erroneous: true, message: 'Bad Request' },
expected: {
erroneous: true,
message: 'No contact is found with record details',
},
},
{
name: 'should return error message when HTTP status is 204 (no content)',
input: {
fields: { email: '[email protected]' },
metadata: { secret: { accessToken: 'token' } },
config: { region: 'US' },
mockResponse: {
processedResponse: {
status: 204,
response: null,
name: 'should handle empty array response data',
response: {
processedResponse: {
status: 200,
response: {
data: [],
},
},
},
expected: { erroneous: true, message: 'No contact is found with record details' },
expected: {
erroneous: true,
message: 'No contact is found with record details',
},
},
{
name: 'should handle network errors gracefully',
input: {
fields: { email: '[email protected]' },
metadata: { secret: { accessToken: 'token' } },
config: { region: 'US' },
mockError: new Error('Network timeout'),
name: 'should handle valid array response data with single record',
response: {
processedResponse: {
status: 200,
response: {
data: [{ id: '123' }],
},
},
},
expected: {
erroneous: false,
message: ['123'],
},
},
{
name: 'should handle valid array response data with multiple records',
response: {
processedResponse: {
status: 200,
response: {
data: [{ id: '123' }, { id: '456' }],
},
},
},
expected: {
erroneous: false,
message: ['123', '456'],
},
},
{
name: 'should handle non-success HTTP status code',
response: {
processedResponse: {
status: 400,
response: 'Bad Request Error',
},
},
expected: {
erroneous: true,
message: 'Bad Request Error',
},
},
{
name: 'should handle HTTP request error',
error: new Error('Network Error'),
expected: {
erroneous: true,
message: 'Network Error',
},
expected: { erroneous: true, message: 'Network timeout' },
},
];

testCases.forEach(({ name, input, expected }) => {
testCases.forEach(({ name, response, error, expected }) => {
it(name, async () => {
if (input.mockError) {
handleHttpRequest.mockRejectedValue(input.mockError);
if (error) {
handleHttpRequest.mockRejectedValueOnce(error);
} else {
handleHttpRequest.mockResolvedValue(input.mockResponse);
handleHttpRequest.mockResolvedValueOnce(response);
}
const result = await searchRecordId(input.fields, input.metadata, input.config);

const result = await searchRecordId(mockFields, mockMetadata, mockConfig);

expect(result).toEqual(expected);
});
});
Expand Down Expand Up @@ -315,6 +379,48 @@ describe('deduceModuleInfo', () => {
},
expected: {},
},
{
name: 'should use default US region when config.region is null',
input: {
inputs: [
{
message: {
context: {
externalId: [{ type: 'ZOHO-Leads', id: '12345', identifierType: 'Email' }],
mappedToDestination: true,
},
},
},
],
config: { region: null },
},
expected: {
operationModuleType: 'Leads',
upsertEndPoint: 'https://www.zohoapis.com/crm/v6/Leads',
identifierType: 'Email',
},
},
{
name: 'should use default US region when config.region is undefined',
input: {
inputs: [
{
message: {
context: {
externalId: [{ type: 'ZOHO-Leads', id: '12345', identifierType: 'Email' }],
mappedToDestination: true,
},
},
},
],
config: {}, // region is undefined
},
expected: {
operationModuleType: 'Leads',
upsertEndPoint: 'https://www.zohoapis.com/crm/v6/Leads',
identifierType: 'Email',
},
},
];

testCases.forEach(({ name, input, expected }) => {
Expand Down
Loading