Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
41 changes: 34 additions & 7 deletions src/persistence.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ export default function _Persistence(mpInstance) {

// https://go.mparticle.com/work/SQDSDKS-6048
try {
if (mpInstance._Store.isLocalStorageAvailable) {
if (
mpInstance._Store.isLocalStorageAvailable &&
!mpInstance._Store.getPrivacyFlag('Products')
) {
var encodedProducts = localStorage.getItem(
mpInstance._Store.prodStorageName
);
Expand Down Expand Up @@ -269,7 +272,10 @@ export default function _Persistence(mpInstance) {
};

this.getUserProductsFromLS = function(mpid) {
if (!mpInstance._Store.isLocalStorageAvailable) {
if (
mpInstance._Store.getPrivacyFlag('Products') ||
!mpInstance._Store.isLocalStorageAvailable
) {
return [];
}

Expand Down Expand Up @@ -308,6 +314,15 @@ export default function _Persistence(mpInstance) {
};

this.getAllUserProductsFromLS = function() {
if (mpInstance._Store.getPrivacyFlag('Products')) {
var currentUser = mpInstance.Identity.getCurrentUser();
var mpid = currentUser ? currentUser.getMPID() : null;
var result = {};
if (mpid) {
result[mpid] = { cp: [] };
}
return result;
Comment thread
rmi22186 marked this conversation as resolved.
}
var decodedProducts,
encodedProducts = localStorage.getItem(
mpInstance._Store.prodStorageName
Expand Down Expand Up @@ -342,14 +357,20 @@ export default function _Persistence(mpInstance) {
? allLocalStorageProducts[mpid].cp
: [],
};

if (mpInstance._Store.getPrivacyFlag('Products')) {
return;
}
if (mpid) {
allLocalStorageProducts = allLocalStorageProducts || {};
allLocalStorageProducts[mpid] = currentUserProducts;
try {
window.localStorage.setItem(
encodeURIComponent(mpInstance._Store.prodStorageName),
Base64.encode(JSON.stringify(allLocalStorageProducts))
);
if (!mpInstance._Store.getPrivacyFlag('Products')) {
window.localStorage.setItem(
encodeURIComponent(mpInstance._Store.prodStorageName),
Base64.encode(JSON.stringify(allLocalStorageProducts))
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also clean up the original code a bit so that i's more readable:

Suggested change
window.localStorage.setItem(
encodeURIComponent(mpInstance._Store.prodStorageName),
Base64.encode(JSON.stringify(allLocalStorageProducts))
);
const encodedKey = encodeURIComponent(mpInstance._Store.prodStorageName);
const encodedValue = Base64.encode(JSON.stringify(allLocalStorageProducts));
window.localStorage.setItem(encodedKey, encodedValue);

}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the if check for 368 necessary here? Since you have 361-363 already returning based on the Products privacy flag, I don't think you need to check for the inverse of 361 on line 368.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, its not needed here since we're already returning at line 362

} catch (e) {
mpInstance.Logger.error(
'Error with setting products on localStorage.'
Expand Down Expand Up @@ -894,6 +915,9 @@ export default function _Persistence(mpInstance) {
};

this.getCartProducts = function(mpid) {
if (mpInstance._Store.getPrivacyFlag('Products')) {
return [];
}
var allCartProducts,
cartProductsString = localStorage.getItem(
mpInstance._Store.prodStorageName
Expand All @@ -913,7 +937,10 @@ export default function _Persistence(mpInstance) {
};

this.setCartProducts = function(allProducts) {
if (!mpInstance._Store.isLocalStorageAvailable) {
if (
!mpInstance._Store.isLocalStorageAvailable ||
mpInstance._Store.getPrivacyFlag('Products')
) {
return;
}

Expand Down
34 changes: 34 additions & 0 deletions test/jest/persistence.products.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
mParticle,
MPConfig,
testMPID,
} from '../src/config/constants';
import { Product } from '@mparticle/web-sdk';

describe('Products Persistence', () => {
let mpInstance = mParticle.getInstance();
beforeEach(() => {
mParticle._resetForTests(MPConfig);
mpInstance._Persistence.resetPersistence();
});

it('should save products to localStorage when noTargeting is false by default', () => {
const product = { Name: 'iphone', Sku: 'iphonesku', Price: 599, Quantity: 1 };
mpInstance._Persistence.setCartProducts({ [testMPID]: { cp: [product] } } as unknown as Product[]);
expect(mpInstance._Persistence.getCartProducts(testMPID).length).toBe(1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think instead of trying to coerce the Type, let's see if we can use a Partial so we can maintain type safety.

Suggested change
const product = { Name: 'iphone', Sku: 'iphonesku', Price: 599, Quantity: 1 };
mpInstance._Persistence.setCartProducts({ [testMPID]: { cp: [product] } } as unknown as Product[]);
expect(mpInstance._Persistence.getCartProducts(testMPID).length).toBe(1);
const product: Partial<Product> = { Name: 'iphone', Sku: 'iphonesku', Price: 599, Quantity: 1 };
mpInstance._Persistence.setCartProducts({ [testMPID]: { cp: [product] } });
expect(mpInstance._Persistence.getCartProducts(testMPID).length).toBe(1);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexs-mparticle I’ve incorporated Partial<Product> for the item, but I’m still hitting the TypeScript error since setCartProducts is typed as Product[] while the runtime path expects a per‑MPID map. That’s why I had to coerce the call to match the actual shape earlier as well. If there’s a better approach or a preferred workaround to avoid the cast while keeping the test on the real write path, please advise and I’ll update.

});

it('should NOT save products to localStorage when noTargeting is true', () => {
mpInstance._Store.setNoTargeting(true);
const product = { Name: 'iphone', Sku: 'iphonesku', Price: 599, Quantity: 1 };
mpInstance._Persistence.setCartProducts({ [testMPID]: { cp: [product] } } as unknown as Product[]);
expect(mpInstance._Persistence.getCartProducts(testMPID).length).toBe(0);
});

it('should save products to localStorage when noTargeting is false', () => {
mpInstance._Store.setNoTargeting(false);
const product = { Name: 'iphone', Sku: 'iphonesku', Price: 599, Quantity: 1 };
mpInstance._Persistence.setCartProducts({ [testMPID]: { cp: [product] } } as unknown as Product[]);
expect(mpInstance._Persistence.getCartProducts(testMPID).length).toBe(1);
});
});
47 changes: 47 additions & 0 deletions test/src/tests-persistence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,53 @@ describe('persistence', () => {
parsedProductsAfter['testMPID'].cp.length.should.equal(0);
});

describe('products persistence with noTargeting privacy flag', () => {
beforeEach(() => {
mParticle._resetForTests(MPConfig);
});

it('should NOT save products to LS when noTargeting is true', async () => {
mParticle.config.launcherOptions = { noTargeting: true };

mParticle.init(apiKey, mParticle.config);
await waitForCondition(hasIdentifyReturned);
const localStorageProducts = localStorage.getItem(LocalStorageProductsV4WithWorkSpaceName);
expect(localStorageProducts).to.not.be.ok;
});

it('should save products to LS when noTargeting is false', async () => {
mParticle.config.launcherOptions = { noTargeting: false };

mParticle.init(apiKey, mParticle.config);
await waitForCondition(hasIdentifyReturned);
const iphone = mParticle.eCommerce.createProduct(
'iphone',
'iphonesku',
599,
1
);
mParticle.eCommerce.Cart.add(iphone, true);

const localStorageProducts = localStorage.getItem(LocalStorageProductsV4WithWorkSpaceName);
expect(localStorageProducts).to.be.ok;
});

it('should save products to LS when noTargeting is false by default', async () => {
mParticle.init(apiKey, mParticle.config);
await waitForCondition(hasIdentifyReturned);
const iphone = mParticle.eCommerce.createProduct(
'iphone',
'iphonesku',
599,
1
);
mParticle.eCommerce.Cart.add(iphone, true);

const localStorageProducts = localStorage.getItem(LocalStorageProductsV4WithWorkSpaceName);
expect(localStorageProducts).to.be.ok;
});
});

it('should only set setFirstSeenTime() once', async () => {
const cookies = JSON.stringify({
gs: {
Expand Down
Loading