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

Set user #52

Merged
merged 6 commits into from
Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 1 addition & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ module.exports = {
'no-useless-call': 'error',
'no-useless-concat': 'error',
'no-void': 'error',
'no-warning-comments': 'error',
'no-with': 'error',
'radix': 'error',
'vars-on-top': 'off',
Expand Down Expand Up @@ -420,7 +419,7 @@ module.exports = {
'import/exports-last': 'off',
'import/group-exports': 'off',
'import/no-cycle': 'error',
'import/no-default-export': 'error',
'import/no-default-export': 'off',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We were already ignoring this rule elsewhere.

'import/no-self-import': 'error',
'import/no-useless-path-segments': 'error',

Expand Down
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.*/node_modules/npm
.*/node_modules/jsonlint
.*/dist/module
.*/test/tracker/set-user.test.js
[untyped]
.*/src/lib/jetlore
[include]
Expand Down
2 changes: 1 addition & 1 deletion src/declarations.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* @flow */

import type { MuseGlobalType } from './types';
import type { MuseGlobalType } from './lib/types';
Copy link
Contributor

Choose a reason for hiding this comment

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

would it make sense to create a types folder and put MuseGlobalType in index.js for now until we have more types? When I think about lib usually I think helper libraries and things like that. I think types should be in a folder of its own.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, but I'd prefer to do that + additional refactoring in another PR if possible.


declare var __muse__ : MuseGlobalType;
43 changes: 43 additions & 0 deletions src/lib/compose-cart.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
/* @flow */
import { getCookie } from './cookie-utils';
import constants from './constants';

const {
storage,
sevenDays
} = constants;
// $FlowFixMe
export const removeFromCart = (items, currentItems = []) => {
return items.reduce((accumulator, item) => {
Expand Down Expand Up @@ -26,3 +33,39 @@ export const removeFromCart = (items, currentItems = []) => {
export const addToCart = (items, currentItems = []) => {
return [ ...currentItems, ...items ];
};
// $FlowFixMe
export const composeCart = (type, data) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This method now uses 'constants' imported above. Otherwise unchanged.

// Copy the data so we don't modify it outside the scope of this method.
let _data = { ...data };

// Devnote: Checking for cookie for backwards compatibility (the cookie check can be removed
// a couple weeks after deploy because any cart cookie storage will be moved to localStorage
// in this function).
const storedCart = window.localStorage.getItem(storage.paypalCrCart) || getCookie(storage.paypalCrCart) || '{}';
const expiry = window.localStorage.getItem(storage.paypalCrCartExpiry);
const cart = JSON.parse(storedCart);
const currentItems = cart ? cart.items : [];

if (!expiry) {
window.localStorage.setItem(storage.paypalCrCartExpiry, Date.now() + sevenDays);
}

switch (type) {
case 'add':
_data.items = addToCart(data.items, currentItems);
break;
case 'set':
_data.items = data.items;
break;
case 'remove':
_data = { ...cart, ...data };
_data.items = removeFromCart(data.items, currentItems);
break;
default:
throw new Error('invalid cart action');
}

window.localStorage.setItem(storage.paypalCrCart, JSON.stringify(_data));

return _data;
};
16 changes: 16 additions & 0 deletions src/lib/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* @flow */
export default {
'sevenDays': 6.048e+8,
Copy link
Contributor

@songz songz Aug 29, 2019

Choose a reason for hiding this comment

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

Personally, (nitpick) I think its easier to understand if you wrote it like this: sevenDays: 7*24*60*60*1000

😆

'accessTokenUrl': 'https://www.paypal.com/muse/api/partner-token',
'storage': {
'paypalCrCart': 'paypal-cr-cart',
'paypalCrCartExpiry': 'paypal-cr-cart-expiry'
},
'defaultTrackerConfig': {
'user': {
'id': null,
'email': null,
'name': null
}
}
};
10 changes: 10 additions & 0 deletions src/lib/cookie-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* @flow */
import generate from './generate-id';

export const getCookie = (cookieName : string) : string => {
const name = `${ cookieName }=`;
Expand All @@ -21,3 +22,12 @@ export const setCookie = (cookieName : string, cookieValue : string, expirationM
const expires = `expires=${ d.toUTCString() }`;
document.cookie = `${ cookieName }=${ cookieValue }; Path=/; ${ expires }`;
};

export const getUserIdCookie = () : ?string => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These methods were removed from 'tracker-component'. They have not been altered. (note: ONE_MONTH_IN_MILLISECONDS should probably be in constants)

return getCookie('paypal-user-id') || null;
};

export const setRandomUserIdCookie = () : void => {
const ONE_MONTH_IN_MILLISECONDS = 30 * 24 * 60 * 60 * 1000;
setCookie('paypal-user-id', generate.generateId(), ONE_MONTH_IN_MILLISECONDS);
};
1 change: 0 additions & 1 deletion src/generate-id.js → src/lib/generate-id.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { uniqueID } from 'belter/src';
** - flow and eslint have to be disabled on whatever line this is used.
*/

// eslint-disable-next-line import/no-default-export
export default {
generateId: uniqueID
};
40 changes: 40 additions & 0 deletions src/lib/get-property-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* @flow */
import { getClientID, getMerchantID } from '@paypal/sdk-client/src';

import type {
Config
} from './types';

export const getPropertyId = ({ paramsToPropertyIdUrl } : Config) => {
// $FlowFixMe
return new Promise(resolve => {
const clientId = getClientID();
const merchantId = getMerchantID()[0];
const propertyIdKey = `property-id-${ clientId }-${ merchantId }`;
const savedPropertyId = window.localStorage.getItem(propertyIdKey);
const currentUrl = `${ window.location.protocol }//${ window.location.host }`;
if (savedPropertyId) {
return resolve(savedPropertyId);
}
let url;
// $FlowFixMe
if (paramsToPropertyIdUrl) {
url = paramsToPropertyIdUrl();
} else {
url = 'https://paypal.com/tagmanager/containers/xo';
}
return window.fetch(`${ url }?mrid=${ merchantId }&url=${ encodeURIComponent(currentUrl) }`)
.then(res => {
if (res.status === 200) {
return res;
}
})
.then(r => r.json()).then(container => {
window.localStorage.setItem(propertyIdKey, container.id);
resolve(container.id);
})
.catch(() => {
// doing nothing for now since there's no logging
});
});
};
49 changes: 49 additions & 0 deletions src/lib/track.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* @flow */
import { getClientID, getMerchantID } from '@paypal/sdk-client/src';

import { getUserIdCookie, setRandomUserIdCookie } from './cookie-utils';
import { getDeviceInfo } from './get-device-info';
import type {
Config,
TrackingType
} from './types';

export const track = <T>(config : Config, trackingType : TrackingType, trackingData : T) => {
const encodeData = data => encodeURIComponent(btoa(JSON.stringify(data)));

const img = document.createElement('img');
img.style.display = 'none';
if (!getUserIdCookie()) {
setRandomUserIdCookie();
}
const user = {
...config.user,
id: getUserIdCookie()
};

const deviceInfo = getDeviceInfo();
const data = {
...trackingData,
user,
propertyId: config.propertyId,
trackingType,
clientId: getClientID(),
merchantId: getMerchantID().join(','),
deviceInfo
};

// paramsToBeaconUrl is a function that gives you the ability to override the beacon url
// to whatever you want it to be based on the trackingType string and data object.
// This can be useful for testing purposes, this feature won't be used by merchants.
if (config.paramsToBeaconUrl) {
img.src = config.paramsToBeaconUrl({ trackingType, data });
} else {
img.src = `https://www.paypal.com/targeting/track/${ trackingType }?data=${ encodeData(data) }`;
}

// TODO: this will add a new image EVERY time the 'track' method is called. There's no reason
// to clutter the DOM like this. We should replace the old image.
if (document.body) {
document.body.appendChild(img);
songz marked this conversation as resolved.
Show resolved Hide resolved
}
};
99 changes: 99 additions & 0 deletions src/lib/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* @flow */

export const TYPES = true;

export type MuseServerConfigType = {|
assetsUrl : string
|};

export type MuseGlobalType = {|
serverConfig : MuseServerConfigType
|};

export type TrackingType = 'view' | 'cartEvent' | 'purchase' | 'setUser' | 'cancelCart';

export type CartEventType = 'addToCart' | 'setCart' | 'removeFromCart';

export type Product = {|
id : string,
title? : string,
url? : string,
description? : string,
imgUrl? : string,
otherImages? : $ReadOnlyArray<string>,
keywords? : $ReadOnlyArray<string>,
price? : string,
quantity? : string
|};

export type ViewData = {| page : string, title? : string |};

export type CartData = {|
cartId? : string,
items : $ReadOnlyArray<Product>,
emailCampaignId? : string,
total? : string,
currencyCode? : string
|};

export type CancelCartData = {|
cartId? : string
|};

export type RemoveCartData = {|
cartId? : string,
items : $ReadOnlyArray<{ id : string }>
|};

export type PurchaseData = {| cartId : string |};

export type UserData = {|
user : {|
id? : string,
email? : string,
name? : string
|}
|};

export type IdentityData = {|
mrid : string,
onIdentification : Function,
onError? : Function
|};

export type ParamsToBeaconUrl = ({
trackingType : TrackingType,
data : ViewData | CartData | RemoveCartData | PurchaseData | CancelCartData
}) => string;

export type ParamsToTokenUrl = () => string;

export type ParamsToPropertyIdUrl = () => string;

export type JetloreConfig = {|
user_id : string,
cid : string,
feed_id : string,
div? : string,
lang? : string
|};

export type Config = {|
user? : {|
id? : string,
email? : string, // mandatory if unbranded cart recovery
name? : string
|},
propertyId? : string,
paramsToBeaconUrl? : ParamsToBeaconUrl,
paramsToTokenUrl? : ParamsToTokenUrl,
jetlore? : {|
user_id : string,
access_token : string,
feed_id : string,
div? : string,
lang? : string
|},
paramsToPropertyIdUrl? : ParamsToPropertyIdUrl
|};

Loading