Skip to content

Commit

Permalink
fix: ga4 allow zero (#1915)
Browse files Browse the repository at this point in the history
* fix: ga4 allow zero

* chore: add test case for prepareStandardEventParams

* chore: test cases added

* chore: improve coverage

---------

Co-authored-by: Utsab Chowdhury <[email protected]>
Co-authored-by: Sai Sankeerth <[email protected]>
  • Loading branch information
3 people authored Nov 15, 2024
1 parent 07909cf commit 9ebbab9
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
isReservedEventName,
getCustomParameters,
formatAndValidateEventName,
prepareStandardEventParams,
} from '../../../src/integrations/GA4/utils';

import {
Expand Down Expand Up @@ -338,43 +339,221 @@ describe('Google Analytics 4 utilities tests', () => {

describe('filterUserTraits function tests', () => {
it('Should update mentioned PII keys value to null', () => {
const piiPropertiesToIgnore = [{ piiProperty: 'email' }, { piiProperty: 'phone' }, { piiProperty: 'card_number' }];
const piiPropertiesToIgnore = [
{ piiProperty: 'email' },
{ piiProperty: 'phone' },
{ piiProperty: 'card_number' },
];

const userTraits = {
name: 'SDK Test',
email: '[email protected]',
card_number: '123456',
phone: '123456789',
country: 'usa'
}
country: 'usa',
};

const filteredUserTraits = {
name: 'SDK Test',
country: 'usa',
email: null,
card_number: null,
phone: null
phone: null,
};

const result = filterUserTraits(piiPropertiesToIgnore, userTraits);
expect(result).toEqual(filteredUserTraits);
});

it('Should override values of pii fields to null', () => {
const piiPropertiesToIgnore = [{ piiProperty: 'email' }, { piiProperty: 'name' }, { piiProperty: 'isPaid' }, { piiProperty: undefined }, { piiProperty: {} }];
const piiPropertiesToIgnore = [
{ piiProperty: 'email' },
{ piiProperty: 'name' },
{ piiProperty: 'isPaid' },
{ piiProperty: undefined },
{ piiProperty: {} },
];

const userTraits = {
name: 'SDK Test',
email: '[email protected]',
isPaid: false
}
isPaid: false,
};

const result = filterUserTraits(piiPropertiesToIgnore, userTraits);
expect(result).toEqual({
name: null,
email: null,
isPaid: null
isPaid: null,
});
});
});
});

describe('prepareStandardEventParams function tests', () => {
it('Should not fail when values are 0', () => {
const eventConfig = {
event: 'UserSignup',
mapping: [
{ sourceKeys: ['properties.total'], destKey: 'order_total', required: true },
{ sourceKeys: ['properties.value'], destKey: 'order_value', required: true },
{ sourceKeys: ['properties.revenue'], destKey: 'order_revenue', required: true },
{ sourceKeys: ['properties.price'], destKey: 'order_price', required: true },
],
};
const message = {
properties: {
total: 0, // Total is 0 but should pass without failure
value: 0, // Value is 0 but should pass without failure
revenue: 0, // Revenue is 0 but should pass without failure
price: 0, // Price is 0 but should pass without failure
},
event: 'UserSignup',
};
const result = prepareStandardEventParams(message, eventConfig);
expect(result).toEqual({
order_total: 0,
order_value: 0,
order_revenue: 0,
order_price: 0,
});
});

it('Should handle mixed types (string, number, array, boolean) in the same payload', () => {
const eventConfig = {
event: 'UserSignup',
mapping: [
{ sourceKeys: ['properties.total'], destKey: 'order_total', required: true },
{ sourceKeys: ['properties.value'], destKey: 'order_value', required: true },
{ sourceKeys: ['properties.revenue'], destKey: 'order_revenue', required: true },
{ sourceKeys: ['properties.price'], destKey: 'order_price', required: true },
],
};
const message = {
properties: {
total: '100', // String
value: 0, // Number
revenue: [50, 60], // Array
price: true, // Boolean
},
event: 'UserSignup',
};
const result = prepareStandardEventParams(message, eventConfig);
expect(result).toEqual({
order_total: '100',
order_value: 0,
order_revenue: [50, 60],
order_price: true,
});
});

it('should handle undefined and null values appropriately', () => {
const eventConfig = {
event: 'UserSignup',
mapping: [
{ sourceKeys: ['properties.total'], destKey: 'order_total', required: true },
{ sourceKeys: ['properties.optional'], destKey: 'optional_field', required: false },
],
};

const message = {
properties: {
total: 0,
optional: undefined,
},
event: 'UserSignup',
};

const result = prepareStandardEventParams(message, eventConfig);
expect(result).toEqual({
order_total: 0,
});
});

it('should handle negative values correctly', () => {
const eventConfig = {
event: 'Refund',
mapping: [{ sourceKeys: ['properties.amount'], destKey: 'refund_amount', required: true }],
};

const message = {
properties: {
amount: -50,
},
event: 'Refund',
};

const result = prepareStandardEventParams(message, eventConfig);
expect(result).toEqual({
refund_amount: -50,
});
});

it('Should correctly handle a mix of data types (string, number, array, boolean) and metadata', () => {
const eventConfig = {
event: 'UserSignup',
mapping: [
{ sourceKeys: ['properties.total'], destKey: 'order_total', required: true },
{ sourceKeys: ['properties.value'], destKey: 'order_value', required: true },
{ sourceKeys: ['properties.revenue'], destKey: 'order_revenue', required: true },
{ sourceKeys: ['properties.price'], destKey: 'order_price', required: true },
{ sourceKeys: ['properties.isActive'], destKey: 'user_isActive', required: true },
{ sourceKeys: ['properties.items'], destKey: 'order_items', required: false }, // Optional field
],
};

const message = {
properties: {
total: 0, // Number
value: '200', // String
revenue: [300, 400], // Array
price: 12, // Number
isActive: false, // Boolean
items: ['item1', 'item2'], // Array (optional)
},
event: 'UserSignup',
};

const metadata = {
order_total: 0,
order_value: 0,
order_revenue: 0,
order_price: 12,
user_isActive: true,
order_items: ['item3', 'item4'],
};

const result = prepareStandardEventParams(message, eventConfig, metadata);

// Using toEqual to check if the result matches the expected object
expect(result).toEqual({
order_total: 0,
order_value: '200',
order_revenue: [300, 400],
order_price: 12,
user_isActive: false,
order_items: ['item1', 'item2'],
});
});

it('should return null when required parameters are missing', () => {
const eventConfig = {
event: 'Purchase',
mapping: [
{ sourceKeys: ['properties.total'], destKey: 'order_total', required: true },
{ sourceKeys: ['properties.currency'], destKey: 'currency', required: true }
],
};

const message = {
properties: {
// total is missing
currency: 'USD'
},
event: 'Purchase',
};

const result = prepareStandardEventParams(message, eventConfig);
expect(result).toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
customParametersExclusion,
rootLevelProductsSupportedEventsList,
} from './config';
import { isBlank, flattenJson } from '../../utils/commonUtils';
import { isBlank, flattenJson, isDefinedAndNotNull } from '../../utils/commonUtils';
import { constructPayload, extractCustomFields } from '../../utils/utils';

const logger = new Logger(DISPLAY_NAME);
Expand Down Expand Up @@ -297,7 +297,7 @@ const prepareStandardEventParams = (message, eventConfig) => {
// Validation for required params
if (Array.isArray(mapping) && mapping.length > 0) {
const hasMissingRequiredValue = mapping.some(mappingItem => {
if (!payload[mappingItem.destKey] && mappingItem.required) {
if (!isDefinedAndNotNull(payload[mappingItem.destKey]) && mappingItem.required) {
logger.error(`Missing required value from ${JSON.stringify(mappingItem.sourceKeys)}`);
return true;
}
Expand Down Expand Up @@ -370,5 +370,6 @@ export {
getCustomParameters,
removeInvalidParams,
prepareParamsAndEventName,
prepareStandardEventParams,
formatAndValidateEventName,
};

0 comments on commit 9ebbab9

Please sign in to comment.