From fcabfbc6c2de25b54123f427f7d070ed266419bb Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Tue, 6 Aug 2024 13:05:14 -0700 Subject: [PATCH 01/25] Partial initialization draft --- package-lock.json | 8 + package.json | 1 + src/initialization.js | 68 +- test/end-to-end-testapp/build/compilation.js | 750 +++++++++++++++++++ 4 files changed, 811 insertions(+), 16 deletions(-) create mode 100644 test/end-to-end-testapp/build/compilation.js diff --git a/package-lock.json b/package-lock.json index a796b9b..e6bf746 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@semantic-release/exec": "^5.0.0", "@semantic-release/git": "^9.0.0", "chai": "^4.2.0", + "crypto-js": "^4.2.0", "eslint": "^7.25.0", "eslint-config-prettier": "9.1.0", "karma": "^5.1.0", @@ -2821,6 +2822,13 @@ "node": "*" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://nexus.ops-shared.mparticle.com/repository/npm-group/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "dev": true, + "license": "MIT" + }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", diff --git a/package.json b/package.json index 5b18d4b..0675828 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@semantic-release/exec": "^5.0.0", "@semantic-release/git": "^9.0.0", "chai": "^4.2.0", + "crypto-js": "^4.2.0", "eslint": "^7.25.0", "eslint-config-prettier": "9.1.0", "karma": "^5.1.0", diff --git a/src/initialization.js b/src/initialization.js index e107aac..f3df742 100644 --- a/src/initialization.js +++ b/src/initialization.js @@ -1,5 +1,9 @@ +import SHA256 from 'crypto-js/sha256'; + var initialization = { name: 'ID5', + + //To-Do: add Module id after establishing in QA moduleId: '', /* ****** Fill out initForwarder to load your SDK ****** Note that not all arguments may apply to your SDK initialization. @@ -17,22 +21,23 @@ var initialization = { Generally, our integrations create script tags and append them to the . Please follow the following format as a guide: */ - // var clientScript = document.createElement('script'); - // clientScript.type = 'text/javascript'; - // clientScript.async = true; - // clientScript.src = 'https://www.clientscript.com/static/clientSDK.js'; // <---- Update this to be your script - // (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(clientScript); - // clientScript.onload = function() { - // if (clientSDKObject && eventQueue.length > 0) { - // // Process any events that may have been queued up while forwarder was being initialized. - // for (var i = 0; i < eventQueue.length; i++) { - // processEvent(eventQueue[i]); - // } - // // now that each queued event is processed, we empty the eventQueue - // eventQueue = []; - // } - // clientSDKObject.initialize(forwarderSettings.apiKey); - // }; + var clientScript = document.createElement('script'); + clientScript.type = 'text/javascript'; + clientScript.async = true; + clientScript.src = 'https://cdn.id5-sync.com/api/1.0/id5-api.js'; // <---- Update this to be your script + (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(clientScript); + clientScript.onload = function() { + var partnerData = buildPartnerData(userIdentities); + + //To-Do: PartnerUserId to be added to the init options once received from id5 + window.ID5.init({partnerId: forwarderSettings.partnerId, pd: partnerData}).onAvailable(function(status) { + var id5Id = status.getID5Id(); + var idType = forwarderSettings.id5IdType; + var identities = {}; + identities[idType] = id5Id; + window.mParticle.Identity.modify({userIdentities: identities}, identityCallback) + }); + }; } else { // For testing, you should fill out this section in order to ensure any required initialization calls are made, // clientSDKObject.initialize(forwarderSettings.apiKey) @@ -40,4 +45,35 @@ var initialization = { } }; + +function buildPartnerData(userIdentities) { + // To-Do: finalize which PD values we are utilizing + var email = userIdentities.userIdentities['email']; + var cleansedEmail = normalizeEmail(email); + var hashedEmail = SHA256(cleansedEmail); + + var phone = userIdentities.userIdentities['mobile_number']; + var cleansedPhone = normalizePhone(phone); + var hashedPhoneNumber = SHA256(cleansedPhone); + + var fullUrl; + var deviceIPv4; + var userAgentString; + var idfv; + + var pdKeys = { + 1: hashedEmail, + 2: hashedPhoneNumber, + 8: encodeURIComponent(fullUrl), + 10: encodeURIComponent(deviceIPv4), + 12: encodeURIComponent(userAgentString), + 14: encodeURIComponent(idfv), + } + + var pdRaw = Object.keys(pdKeys).map(function(key){ + return key + '=' + pdKeys[key] + }).join('&'); + + return btoa(pdRaw); +} module.exports = initialization; diff --git a/test/end-to-end-testapp/build/compilation.js b/test/end-to-end-testapp/build/compilation.js new file mode 100644 index 0000000..af8bce9 --- /dev/null +++ b/test/end-to-end-testapp/build/compilation.js @@ -0,0 +1,750 @@ +var ID5Kit = (function (exports) { + 'use strict'; + + function Common() {} + + Common.prototype.exampleMethod = function () { + return 'I am an example'; + }; + + var common = Common; + + function CommerceHandler(common) { + this.common = common || {}; + } + + CommerceHandler.prototype.logCommerceEvent = function(event) { + /* + Sample ecommerce event schema: + { + CurrencyCode: 'USD', + DeviceId:'a80eea1c-57f5-4f84-815e-06fe971b6ef2', // MP generated + EventAttributes: { key1: 'value1', key2: 'value2' }, + EventType: 16, + EventCategory: 10, // (This is an add product to cart event, see below for additional ecommerce EventCategories) + EventName: "eCommerce - AddToCart", + MPID: "8278431810143183490", + ProductAction: { + Affiliation: 'aff1', + CouponCode: 'coupon', + ProductActionType: 7, + ProductList: [ + { + Attributes: { prodKey1: 'prodValue1', prodKey2: 'prodValue2' }, + Brand: 'Apple', + Category: 'phones', + CouponCode: 'coupon1', + Name: 'iPhone', + Price: '600', + Quantity: 2, + Sku: "SKU123", + TotalAmount: 1200, + Variant: '64GB' + } + ], + TransactionId: "tid1", + ShippingAmount: 10, + TaxAmount: 5, + TotalAmount: 1215, + }, + UserAttributes: { userKey1: 'userValue1', userKey2: 'userValue2' } + UserIdentities: [ + { + Identity: 'test@gmail.com', Type: 7 + } + ] + } + + If your SDK has specific ways to log different eCommerce events, see below for + mParticle's additional ecommerce EventCategory types: + + 10: ProductAddToCart, (as shown above) + 11: ProductRemoveFromCart, + 12: ProductCheckout, + 13: ProductCheckoutOption, + 14: ProductClick, + 15: ProductViewDetail, + 16: ProductPurchase, + 17: ProductRefund, + 18: PromotionView, + 19: PromotionClick, + 20: ProductAddToWishlist, + 21: ProductRemoveFromWishlist, + 22: ProductImpression + */ + }; + + var commerceHandler = CommerceHandler; + + /* + A non-ecommerce event has the following schema: + + { + DeviceId: "a80eea1c-57f5-4f84-815e-06fe971b6ef2", + EventAttributes: {test: "Error", t: 'stack trace in string form'}, + EventName: "Error", + MPID: "123123123123", + UserAttributes: {userAttr1: 'value1', userAttr2: 'value2'}, + UserIdentities: [{Identity: 'email@gmail.com', Type: 7}] + User Identity Types can be found here: + } + + */ + + function EventHandler(common) { + this.common = common || {}; + } + EventHandler.prototype.logEvent = function(event) {}; + EventHandler.prototype.logError = function(event) { + // The schema for a logError event is the same, but noteworthy differences are as follows: + // { + // EventAttributes: {m: 'name of error passed into MP', s: "Error", t: 'stack trace in string form if applicable'}, + // EventName: "Error" + // } + }; + EventHandler.prototype.logPageView = function(event) { + /* The schema for a logPagView event is the same, but noteworthy differences are as follows: + { + EventAttributes: {hostname: "www.google.com", title: 'Test Page'}, // These are event attributes only if no additional event attributes are explicitly provided to mParticle.logPageView(...) + } + */ + }; + + var eventHandler = EventHandler; + + /* + The 'mParticleUser' is an object with methods get user Identities and set/get user attributes + Partners can determine what userIds are available to use in their SDK + Call mParticleUser.getUserIdentities() to return an object of userIdentities --> { userIdentities: {customerid: '1234', email: 'email@gmail.com'} } + For more identity types, see https://docs.mparticle.com/developers/sdk/web/idsync/#supported-identity-types + Call mParticleUser.getMPID() to get mParticle ID + For any additional methods, see https://docs.mparticle.com/developers/sdk/web/core-apidocs/classes/mParticle.Identity.getCurrentUser().html + */ + + /* + identityApiRequest has the schema: + { + userIdentities: { + customerid: '123', + email: 'abc' + } + } + For more userIdentity types, see https://docs.mparticle.com/developers/sdk/web/idsync/#supported-identity-types + */ + + function IdentityHandler(common) { + this.common = common || {}; + } + IdentityHandler.prototype.onUserIdentified = function(mParticleUser) {}; + IdentityHandler.prototype.onIdentifyComplete = function( + mParticleUser, + identityApiRequest + ) {}; + IdentityHandler.prototype.onLoginComplete = function( + mParticleUser, + identityApiRequest + ) {}; + IdentityHandler.prototype.onLogoutComplete = function( + mParticleUser, + identityApiRequest + ) {}; + IdentityHandler.prototype.onModifyComplete = function( + mParticleUser, + identityApiRequest + ) {}; + + /* In previous versions of the mParticle web SDK, setting user identities on + kits is only reachable via the onSetUserIdentity method below. We recommend + filling out `onSetUserIdentity` for maximum compatibility + */ + IdentityHandler.prototype.onSetUserIdentity = function( + forwarderSettings, + id, + type + ) {}; + + var identityHandler = IdentityHandler; + + var initialization = { + name: 'ID5', + moduleId: '', + /* ****** Fill out initForwarder to load your SDK ****** + Note that not all arguments may apply to your SDK initialization. + These are passed from mParticle, but leave them even if they are not being used. + forwarderSettings contain settings that your SDK requires in order to initialize + userAttributes example: {gender: 'male', age: 25} + userIdentities example: { 1: 'customerId', 2: 'facebookId', 7: 'emailid@email.com' } + additional identityTypes can be found at https://github.com/mParticle/mparticle-sdk-javascript/blob/master-v2/src/types.js#L88-L101 + */ + initForwarder: function(forwarderSettings, testMode, userAttributes, userIdentities, processEvent, eventQueue, isInitialized, common, appVersion, appName, customFlags, clientId) { + } + }; + + var initialization_1 = initialization; + + var sessionHandler = { + onSessionStart: function(event) { + + }, + onSessionEnd: function(event) { + + } + }; + + var sessionHandler_1 = sessionHandler; + + /* + The 'mParticleUser' is an object with methods on it to get user Identities and set/get user attributes + Partners can determine what userIds are available to use in their SDK + Call mParticleUser.getUserIdentities() to return an object of userIdentities --> { userIdentities: {customerid: '1234', email: 'email@gmail.com'} } + For more identity types, see http://docs.mparticle.com/developers/sdk/javascript/identity#allowed-identity-types + Call mParticleUser.getMPID() to get mParticle ID + For any additional methods, see http://docs.mparticle.com/developers/sdk/javascript/apidocs/classes/mParticle.Identity.getCurrentUser().html + */ + + function UserAttributeHandler(common) { + this.common = common || {}; + } + UserAttributeHandler.prototype.onRemoveUserAttribute = function( + key, + mParticleUser + ) {}; + UserAttributeHandler.prototype.onSetUserAttribute = function( + key, + value, + mParticleUser + ) {}; + UserAttributeHandler.prototype.onConsentStateUpdated = function( + oldState, + newState, + mParticleUser + ) {}; + + var userAttributeHandler = UserAttributeHandler; + + // =============== REACH OUT TO MPARTICLE IF YOU HAVE ANY QUESTIONS =============== + // + // Copyright 2018 mParticle, Inc. + // + // Licensed under the Apache License, Version 2.0 (the "License"); + // you may not use this file except in compliance with the License. + // You may obtain a copy of the License at + // + // http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, software + // distributed under the License is distributed on an "AS IS" BASIS, + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + // See the License for the specific language governing permissions and + // limitations under the License. + + + + + + + + + + var name = initialization_1.name, + moduleId = initialization_1.moduleId, + MessageType = { + SessionStart: 1, + SessionEnd: 2, + PageView: 3, + PageEvent: 4, + CrashReport: 5, + OptOut: 6, + Commerce: 16, + Media: 20, + }; + + var constructor = function() { + var self = this, + isInitialized = false, + forwarderSettings, + reportingService, + eventQueue = []; + + self.name = initialization_1.name; + self.moduleId = initialization_1.moduleId; + self.common = new common(); + + function initForwarder( + settings, + service, + testMode, + trackerId, + userAttributes, + userIdentities, + appVersion, + appName, + customFlags, + clientId + ) { + forwarderSettings = settings; + + if ( + typeof window !== 'undefined' && + window.mParticle.isTestEnvironment + ) { + reportingService = function() {}; + } else { + reportingService = service; + } + + try { + initialization_1.initForwarder( + settings, + testMode, + userAttributes, + userIdentities, + processEvent, + eventQueue, + isInitialized, + self.common, + appVersion, + appName, + customFlags, + clientId + ); + self.eventHandler = new eventHandler(self.common); + self.identityHandler = new identityHandler(self.common); + self.userAttributeHandler = new userAttributeHandler(self.common); + self.commerceHandler = new commerceHandler(self.common); + + isInitialized = true; + } catch (e) { + console.log('Failed to initialize ' + name + ' - ' + e); + } + } + + function processEvent(event) { + var reportEvent = false; + if (isInitialized) { + try { + if (event.EventDataType === MessageType.SessionStart) { + reportEvent = logSessionStart(event); + } else if (event.EventDataType === MessageType.SessionEnd) { + reportEvent = logSessionEnd(event); + } else if (event.EventDataType === MessageType.CrashReport) { + reportEvent = logError(event); + } else if (event.EventDataType === MessageType.PageView) { + reportEvent = logPageView(event); + } else if (event.EventDataType === MessageType.Commerce) { + reportEvent = logEcommerceEvent(event); + } else if (event.EventDataType === MessageType.PageEvent) { + reportEvent = logEvent(event); + } else if (event.EventDataType === MessageType.Media) { + // Kits should just treat Media Events as generic Events + reportEvent = logEvent(event); + } + if (reportEvent === true && reportingService) { + reportingService(self, event); + return 'Successfully sent to ' + name; + } else { + return ( + 'Error logging event or event type not supported on forwarder ' + + name + ); + } + } catch (e) { + return 'Failed to send to ' + name + ' ' + e; + } + } else { + eventQueue.push(event); + return ( + "Can't send to forwarder " + + name + + ', not initialized. Event added to queue.' + ); + } + } + + function logSessionStart(event) { + try { + return sessionHandler_1.onSessionStart(event); + } catch (e) { + return { + error: 'Error starting session on forwarder ' + name + '; ' + e, + }; + } + } + + function logSessionEnd(event) { + try { + return sessionHandler_1.onSessionEnd(event); + } catch (e) { + return { + error: 'Error ending session on forwarder ' + name + '; ' + e, + }; + } + } + + function logError(event) { + try { + return self.eventHandler.logError(event); + } catch (e) { + return { + error: 'Error logging error on forwarder ' + name + '; ' + e, + }; + } + } + + function logPageView(event) { + try { + return self.eventHandler.logPageView(event); + } catch (e) { + return { + error: + 'Error logging page view on forwarder ' + name + '; ' + e, + }; + } + } + + function logEvent(event) { + try { + return self.eventHandler.logEvent(event); + } catch (e) { + return { + error: 'Error logging event on forwarder ' + name + '; ' + e, + }; + } + } + + function logEcommerceEvent(event) { + try { + return self.commerceHandler.logCommerceEvent(event); + } catch (e) { + return { + error: + 'Error logging purchase event on forwarder ' + + name + + '; ' + + e, + }; + } + } + + function setUserAttribute(key, value) { + if (isInitialized) { + try { + self.userAttributeHandler.onSetUserAttribute( + key, + value, + forwarderSettings + ); + return 'Successfully set user attribute on forwarder ' + name; + } catch (e) { + return ( + 'Error setting user attribute on forwarder ' + + name + + '; ' + + e + ); + } + } else { + return ( + "Can't set user attribute on forwarder " + + name + + ', not initialized' + ); + } + } + + function removeUserAttribute(key) { + if (isInitialized) { + try { + self.userAttributeHandler.onRemoveUserAttribute( + key, + forwarderSettings + ); + return ( + 'Successfully removed user attribute on forwarder ' + name + ); + } catch (e) { + return ( + 'Error removing user attribute on forwarder ' + + name + + '; ' + + e + ); + } + } else { + return ( + "Can't remove user attribute on forwarder " + + name + + ', not initialized' + ); + } + } + + function setUserIdentity(id, type) { + if (isInitialized) { + try { + self.identityHandler.onSetUserIdentity( + forwarderSettings, + id, + type + ); + return 'Successfully set user Identity on forwarder ' + name; + } catch (e) { + return ( + 'Error removing user attribute on forwarder ' + + name + + '; ' + + e + ); + } + } else { + return ( + "Can't call setUserIdentity on forwarder " + + name + + ', not initialized' + ); + } + } + + function onUserIdentified(user) { + if (isInitialized) { + try { + self.identityHandler.onUserIdentified(user); + + return ( + 'Successfully called onUserIdentified on forwarder ' + name + ); + } catch (e) { + return { + error: + 'Error calling onUserIdentified on forwarder ' + + name + + '; ' + + e, + }; + } + } else { + return ( + "Can't set new user identities on forwader " + + name + + ', not initialized' + ); + } + } + + function onIdentifyComplete(user, filteredIdentityRequest) { + if (isInitialized) { + try { + self.identityHandler.onIdentifyComplete( + user, + filteredIdentityRequest + ); + + return ( + 'Successfully called onIdentifyComplete on forwarder ' + + name + ); + } catch (e) { + return { + error: + 'Error calling onIdentifyComplete on forwarder ' + + name + + '; ' + + e, + }; + } + } else { + return ( + "Can't call onIdentifyCompleted on forwader " + + name + + ', not initialized' + ); + } + } + + function onLoginComplete(user, filteredIdentityRequest) { + if (isInitialized) { + try { + self.identityHandler.onLoginComplete( + user, + filteredIdentityRequest + ); + + return ( + 'Successfully called onLoginComplete on forwarder ' + name + ); + } catch (e) { + return { + error: + 'Error calling onLoginComplete on forwarder ' + + name + + '; ' + + e, + }; + } + } else { + return ( + "Can't call onLoginComplete on forwader " + + name + + ', not initialized' + ); + } + } + + function onLogoutComplete(user, filteredIdentityRequest) { + if (isInitialized) { + try { + self.identityHandler.onLogoutComplete( + user, + filteredIdentityRequest + ); + + return ( + 'Successfully called onLogoutComplete on forwarder ' + name + ); + } catch (e) { + return { + error: + 'Error calling onLogoutComplete on forwarder ' + + name + + '; ' + + e, + }; + } + } else { + return ( + "Can't call onLogoutComplete on forwader " + + name + + ', not initialized' + ); + } + } + + function onModifyComplete(user, filteredIdentityRequest) { + if (isInitialized) { + try { + self.identityHandler.onModifyComplete( + user, + filteredIdentityRequest + ); + + return ( + 'Successfully called onModifyComplete on forwarder ' + name + ); + } catch (e) { + return { + error: + 'Error calling onModifyComplete on forwarder ' + + name + + '; ' + + e, + }; + } + } else { + return ( + "Can't call onModifyComplete on forwader " + + name + + ', not initialized' + ); + } + } + + function setOptOut(isOptingOutBoolean) { + if (isInitialized) { + try { + self.initialization.setOptOut(isOptingOutBoolean); + + return 'Successfully called setOptOut on forwarder ' + name; + } catch (e) { + return { + error: + 'Error calling setOptOut on forwarder ' + + name + + '; ' + + e, + }; + } + } else { + return ( + "Can't call setOptOut on forwader " + + name + + ', not initialized' + ); + } + } + + this.init = initForwarder; + this.process = processEvent; + this.setUserAttribute = setUserAttribute; + this.removeUserAttribute = removeUserAttribute; + this.onUserIdentified = onUserIdentified; + this.setUserIdentity = setUserIdentity; + this.onIdentifyComplete = onIdentifyComplete; + this.onLoginComplete = onLoginComplete; + this.onLogoutComplete = onLogoutComplete; + this.onModifyComplete = onModifyComplete; + this.setOptOut = setOptOut; + }; + + function getId() { + return moduleId; + } + + if (typeof window !== 'undefined') { + if (window && window.mParticle && window.mParticle.addForwarder) { + window.mParticle.addForwarder({ + name: name, + constructor: constructor, + getId: getId, + }); + } + } + + var SDKsettings = { + apiKey: 'testAPIKey' + /* fill in SDKsettings with any particular settings or options your sdk requires in order to + initialize, this may be apiKey, projectId, primaryCustomerType, etc. These are passed + into the src/initialization.js file as the + */ + }; + + // Do not edit below: + var settings = SDKsettings; + + var name$1 = initialization_1.name; + + var config = { + name: name$1, + moduleId: 100, // when published, you will receive a new moduleID + isDebug: true, + isSandbox: true, + settings: settings, + userIdentityFilters: [], + hasDebugString: [], + isVisible: [], + eventNameFilters: [], + eventTypeFilters: [], + attributeFilters: [], + screenNameFilters: [], + pageViewAttributeFilters: [], + userAttributeFilters: [], + filteringEventAttributeValue: 'null', + filteringUserAttributeValue: 'null', + eventSubscriptionId: 123, + filteringConsentRuleValues: 'null', + excludeAnonymousUser: false + }; + + window.mParticle.config = window.mParticle.config || {}; + window.mParticle.config.workspaceToken = 'testkit'; + window.mParticle.config.requestConfig = false; + window.mParticle.config.kitConfigs = [config]; + + var endToEndTestapp = { + + }; + + exports.default = endToEndTestapp; + + return exports; + +}({})); From 3a7ac9e743694f5b744cc78a9bfa782ed6e8d92d Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:17:06 -0700 Subject: [PATCH 02/25] Add function to normalize phone and email --- src/initialization.js | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/initialization.js b/src/initialization.js index f3df742..0b6dcf5 100644 --- a/src/initialization.js +++ b/src/initialization.js @@ -1,4 +1,4 @@ -import SHA256 from 'crypto-js/sha256'; +// import SHA256 from 'crypto-js/sha256'; var initialization = { name: 'ID5', @@ -35,6 +35,7 @@ var initialization = { var idType = forwarderSettings.id5IdType; var identities = {}; identities[idType] = id5Id; + window.mParticle.Identity.modify({userIdentities: identities}, identityCallback) }); }; @@ -47,6 +48,7 @@ var initialization = { function buildPartnerData(userIdentities) { + var SHA256 = require('crypto-js/sha256'); // To-Do: finalize which PD values we are utilizing var email = userIdentities.userIdentities['email']; var cleansedEmail = normalizeEmail(email); @@ -56,7 +58,7 @@ function buildPartnerData(userIdentities) { var cleansedPhone = normalizePhone(phone); var hashedPhoneNumber = SHA256(cleansedPhone); - var fullUrl; + var fullUrl = window.location.href; var deviceIPv4; var userAgentString; var idfv; @@ -76,4 +78,27 @@ function buildPartnerData(userIdentities) { return btoa(pdRaw); } + +function normalizeEmail(email) { + var parts = email.split("@") + var charactersToRemove = ['+', '.'] + + if (parts[1] != 'gmail.com') { + return email; + } + + charactersToRemove.forEach(function(character) { + parts[0] = parts[0].replaceAll(character, '').toLowerCase(); + }) + + return parts.join('@'); +} + +function normalizePhone(phone) { + var charactersToRemove = [' ', '-', '(', ')'] + charactersToRemove.forEach(function(character) { + phone = phone.replaceAll(character, ''); + }) + return phone; +} module.exports = initialization; From 9e438a2e1b572380048658fd5e7bde72ddbe262e Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:21:18 -0700 Subject: [PATCH 03/25] Update spacing --- src/initialization.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/initialization.js b/src/initialization.js index 0b6dcf5..214d4a5 100644 --- a/src/initialization.js +++ b/src/initialization.js @@ -101,4 +101,5 @@ function normalizePhone(phone) { }) return phone; } + module.exports = initialization; From d7ed10bdf00ef2855ff79b39925fb321ea2d9197 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:26:29 -0700 Subject: [PATCH 04/25] Remove compilation file --- .gitignore | 2 +- test/end-to-end-testapp/build/compilation.js | 750 ------------------- 2 files changed, 1 insertion(+), 751 deletions(-) delete mode 100644 test/end-to-end-testapp/build/compilation.js diff --git a/.gitignore b/.gitignore index 8a57a19..2343d7e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ node_modules/ test/test-bundle.js .DS_Store -test/end-to-end-testapp/compilation.js +test/end-to-end-testapp/build/compilation.js dist/ diff --git a/test/end-to-end-testapp/build/compilation.js b/test/end-to-end-testapp/build/compilation.js deleted file mode 100644 index af8bce9..0000000 --- a/test/end-to-end-testapp/build/compilation.js +++ /dev/null @@ -1,750 +0,0 @@ -var ID5Kit = (function (exports) { - 'use strict'; - - function Common() {} - - Common.prototype.exampleMethod = function () { - return 'I am an example'; - }; - - var common = Common; - - function CommerceHandler(common) { - this.common = common || {}; - } - - CommerceHandler.prototype.logCommerceEvent = function(event) { - /* - Sample ecommerce event schema: - { - CurrencyCode: 'USD', - DeviceId:'a80eea1c-57f5-4f84-815e-06fe971b6ef2', // MP generated - EventAttributes: { key1: 'value1', key2: 'value2' }, - EventType: 16, - EventCategory: 10, // (This is an add product to cart event, see below for additional ecommerce EventCategories) - EventName: "eCommerce - AddToCart", - MPID: "8278431810143183490", - ProductAction: { - Affiliation: 'aff1', - CouponCode: 'coupon', - ProductActionType: 7, - ProductList: [ - { - Attributes: { prodKey1: 'prodValue1', prodKey2: 'prodValue2' }, - Brand: 'Apple', - Category: 'phones', - CouponCode: 'coupon1', - Name: 'iPhone', - Price: '600', - Quantity: 2, - Sku: "SKU123", - TotalAmount: 1200, - Variant: '64GB' - } - ], - TransactionId: "tid1", - ShippingAmount: 10, - TaxAmount: 5, - TotalAmount: 1215, - }, - UserAttributes: { userKey1: 'userValue1', userKey2: 'userValue2' } - UserIdentities: [ - { - Identity: 'test@gmail.com', Type: 7 - } - ] - } - - If your SDK has specific ways to log different eCommerce events, see below for - mParticle's additional ecommerce EventCategory types: - - 10: ProductAddToCart, (as shown above) - 11: ProductRemoveFromCart, - 12: ProductCheckout, - 13: ProductCheckoutOption, - 14: ProductClick, - 15: ProductViewDetail, - 16: ProductPurchase, - 17: ProductRefund, - 18: PromotionView, - 19: PromotionClick, - 20: ProductAddToWishlist, - 21: ProductRemoveFromWishlist, - 22: ProductImpression - */ - }; - - var commerceHandler = CommerceHandler; - - /* - A non-ecommerce event has the following schema: - - { - DeviceId: "a80eea1c-57f5-4f84-815e-06fe971b6ef2", - EventAttributes: {test: "Error", t: 'stack trace in string form'}, - EventName: "Error", - MPID: "123123123123", - UserAttributes: {userAttr1: 'value1', userAttr2: 'value2'}, - UserIdentities: [{Identity: 'email@gmail.com', Type: 7}] - User Identity Types can be found here: - } - - */ - - function EventHandler(common) { - this.common = common || {}; - } - EventHandler.prototype.logEvent = function(event) {}; - EventHandler.prototype.logError = function(event) { - // The schema for a logError event is the same, but noteworthy differences are as follows: - // { - // EventAttributes: {m: 'name of error passed into MP', s: "Error", t: 'stack trace in string form if applicable'}, - // EventName: "Error" - // } - }; - EventHandler.prototype.logPageView = function(event) { - /* The schema for a logPagView event is the same, but noteworthy differences are as follows: - { - EventAttributes: {hostname: "www.google.com", title: 'Test Page'}, // These are event attributes only if no additional event attributes are explicitly provided to mParticle.logPageView(...) - } - */ - }; - - var eventHandler = EventHandler; - - /* - The 'mParticleUser' is an object with methods get user Identities and set/get user attributes - Partners can determine what userIds are available to use in their SDK - Call mParticleUser.getUserIdentities() to return an object of userIdentities --> { userIdentities: {customerid: '1234', email: 'email@gmail.com'} } - For more identity types, see https://docs.mparticle.com/developers/sdk/web/idsync/#supported-identity-types - Call mParticleUser.getMPID() to get mParticle ID - For any additional methods, see https://docs.mparticle.com/developers/sdk/web/core-apidocs/classes/mParticle.Identity.getCurrentUser().html - */ - - /* - identityApiRequest has the schema: - { - userIdentities: { - customerid: '123', - email: 'abc' - } - } - For more userIdentity types, see https://docs.mparticle.com/developers/sdk/web/idsync/#supported-identity-types - */ - - function IdentityHandler(common) { - this.common = common || {}; - } - IdentityHandler.prototype.onUserIdentified = function(mParticleUser) {}; - IdentityHandler.prototype.onIdentifyComplete = function( - mParticleUser, - identityApiRequest - ) {}; - IdentityHandler.prototype.onLoginComplete = function( - mParticleUser, - identityApiRequest - ) {}; - IdentityHandler.prototype.onLogoutComplete = function( - mParticleUser, - identityApiRequest - ) {}; - IdentityHandler.prototype.onModifyComplete = function( - mParticleUser, - identityApiRequest - ) {}; - - /* In previous versions of the mParticle web SDK, setting user identities on - kits is only reachable via the onSetUserIdentity method below. We recommend - filling out `onSetUserIdentity` for maximum compatibility - */ - IdentityHandler.prototype.onSetUserIdentity = function( - forwarderSettings, - id, - type - ) {}; - - var identityHandler = IdentityHandler; - - var initialization = { - name: 'ID5', - moduleId: '', - /* ****** Fill out initForwarder to load your SDK ****** - Note that not all arguments may apply to your SDK initialization. - These are passed from mParticle, but leave them even if they are not being used. - forwarderSettings contain settings that your SDK requires in order to initialize - userAttributes example: {gender: 'male', age: 25} - userIdentities example: { 1: 'customerId', 2: 'facebookId', 7: 'emailid@email.com' } - additional identityTypes can be found at https://github.com/mParticle/mparticle-sdk-javascript/blob/master-v2/src/types.js#L88-L101 - */ - initForwarder: function(forwarderSettings, testMode, userAttributes, userIdentities, processEvent, eventQueue, isInitialized, common, appVersion, appName, customFlags, clientId) { - } - }; - - var initialization_1 = initialization; - - var sessionHandler = { - onSessionStart: function(event) { - - }, - onSessionEnd: function(event) { - - } - }; - - var sessionHandler_1 = sessionHandler; - - /* - The 'mParticleUser' is an object with methods on it to get user Identities and set/get user attributes - Partners can determine what userIds are available to use in their SDK - Call mParticleUser.getUserIdentities() to return an object of userIdentities --> { userIdentities: {customerid: '1234', email: 'email@gmail.com'} } - For more identity types, see http://docs.mparticle.com/developers/sdk/javascript/identity#allowed-identity-types - Call mParticleUser.getMPID() to get mParticle ID - For any additional methods, see http://docs.mparticle.com/developers/sdk/javascript/apidocs/classes/mParticle.Identity.getCurrentUser().html - */ - - function UserAttributeHandler(common) { - this.common = common || {}; - } - UserAttributeHandler.prototype.onRemoveUserAttribute = function( - key, - mParticleUser - ) {}; - UserAttributeHandler.prototype.onSetUserAttribute = function( - key, - value, - mParticleUser - ) {}; - UserAttributeHandler.prototype.onConsentStateUpdated = function( - oldState, - newState, - mParticleUser - ) {}; - - var userAttributeHandler = UserAttributeHandler; - - // =============== REACH OUT TO MPARTICLE IF YOU HAVE ANY QUESTIONS =============== - // - // Copyright 2018 mParticle, Inc. - // - // Licensed under the Apache License, Version 2.0 (the "License"); - // you may not use this file except in compliance with the License. - // You may obtain a copy of the License at - // - // http://www.apache.org/licenses/LICENSE-2.0 - // - // Unless required by applicable law or agreed to in writing, software - // distributed under the License is distributed on an "AS IS" BASIS, - // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - // See the License for the specific language governing permissions and - // limitations under the License. - - - - - - - - - - var name = initialization_1.name, - moduleId = initialization_1.moduleId, - MessageType = { - SessionStart: 1, - SessionEnd: 2, - PageView: 3, - PageEvent: 4, - CrashReport: 5, - OptOut: 6, - Commerce: 16, - Media: 20, - }; - - var constructor = function() { - var self = this, - isInitialized = false, - forwarderSettings, - reportingService, - eventQueue = []; - - self.name = initialization_1.name; - self.moduleId = initialization_1.moduleId; - self.common = new common(); - - function initForwarder( - settings, - service, - testMode, - trackerId, - userAttributes, - userIdentities, - appVersion, - appName, - customFlags, - clientId - ) { - forwarderSettings = settings; - - if ( - typeof window !== 'undefined' && - window.mParticle.isTestEnvironment - ) { - reportingService = function() {}; - } else { - reportingService = service; - } - - try { - initialization_1.initForwarder( - settings, - testMode, - userAttributes, - userIdentities, - processEvent, - eventQueue, - isInitialized, - self.common, - appVersion, - appName, - customFlags, - clientId - ); - self.eventHandler = new eventHandler(self.common); - self.identityHandler = new identityHandler(self.common); - self.userAttributeHandler = new userAttributeHandler(self.common); - self.commerceHandler = new commerceHandler(self.common); - - isInitialized = true; - } catch (e) { - console.log('Failed to initialize ' + name + ' - ' + e); - } - } - - function processEvent(event) { - var reportEvent = false; - if (isInitialized) { - try { - if (event.EventDataType === MessageType.SessionStart) { - reportEvent = logSessionStart(event); - } else if (event.EventDataType === MessageType.SessionEnd) { - reportEvent = logSessionEnd(event); - } else if (event.EventDataType === MessageType.CrashReport) { - reportEvent = logError(event); - } else if (event.EventDataType === MessageType.PageView) { - reportEvent = logPageView(event); - } else if (event.EventDataType === MessageType.Commerce) { - reportEvent = logEcommerceEvent(event); - } else if (event.EventDataType === MessageType.PageEvent) { - reportEvent = logEvent(event); - } else if (event.EventDataType === MessageType.Media) { - // Kits should just treat Media Events as generic Events - reportEvent = logEvent(event); - } - if (reportEvent === true && reportingService) { - reportingService(self, event); - return 'Successfully sent to ' + name; - } else { - return ( - 'Error logging event or event type not supported on forwarder ' + - name - ); - } - } catch (e) { - return 'Failed to send to ' + name + ' ' + e; - } - } else { - eventQueue.push(event); - return ( - "Can't send to forwarder " + - name + - ', not initialized. Event added to queue.' - ); - } - } - - function logSessionStart(event) { - try { - return sessionHandler_1.onSessionStart(event); - } catch (e) { - return { - error: 'Error starting session on forwarder ' + name + '; ' + e, - }; - } - } - - function logSessionEnd(event) { - try { - return sessionHandler_1.onSessionEnd(event); - } catch (e) { - return { - error: 'Error ending session on forwarder ' + name + '; ' + e, - }; - } - } - - function logError(event) { - try { - return self.eventHandler.logError(event); - } catch (e) { - return { - error: 'Error logging error on forwarder ' + name + '; ' + e, - }; - } - } - - function logPageView(event) { - try { - return self.eventHandler.logPageView(event); - } catch (e) { - return { - error: - 'Error logging page view on forwarder ' + name + '; ' + e, - }; - } - } - - function logEvent(event) { - try { - return self.eventHandler.logEvent(event); - } catch (e) { - return { - error: 'Error logging event on forwarder ' + name + '; ' + e, - }; - } - } - - function logEcommerceEvent(event) { - try { - return self.commerceHandler.logCommerceEvent(event); - } catch (e) { - return { - error: - 'Error logging purchase event on forwarder ' + - name + - '; ' + - e, - }; - } - } - - function setUserAttribute(key, value) { - if (isInitialized) { - try { - self.userAttributeHandler.onSetUserAttribute( - key, - value, - forwarderSettings - ); - return 'Successfully set user attribute on forwarder ' + name; - } catch (e) { - return ( - 'Error setting user attribute on forwarder ' + - name + - '; ' + - e - ); - } - } else { - return ( - "Can't set user attribute on forwarder " + - name + - ', not initialized' - ); - } - } - - function removeUserAttribute(key) { - if (isInitialized) { - try { - self.userAttributeHandler.onRemoveUserAttribute( - key, - forwarderSettings - ); - return ( - 'Successfully removed user attribute on forwarder ' + name - ); - } catch (e) { - return ( - 'Error removing user attribute on forwarder ' + - name + - '; ' + - e - ); - } - } else { - return ( - "Can't remove user attribute on forwarder " + - name + - ', not initialized' - ); - } - } - - function setUserIdentity(id, type) { - if (isInitialized) { - try { - self.identityHandler.onSetUserIdentity( - forwarderSettings, - id, - type - ); - return 'Successfully set user Identity on forwarder ' + name; - } catch (e) { - return ( - 'Error removing user attribute on forwarder ' + - name + - '; ' + - e - ); - } - } else { - return ( - "Can't call setUserIdentity on forwarder " + - name + - ', not initialized' - ); - } - } - - function onUserIdentified(user) { - if (isInitialized) { - try { - self.identityHandler.onUserIdentified(user); - - return ( - 'Successfully called onUserIdentified on forwarder ' + name - ); - } catch (e) { - return { - error: - 'Error calling onUserIdentified on forwarder ' + - name + - '; ' + - e, - }; - } - } else { - return ( - "Can't set new user identities on forwader " + - name + - ', not initialized' - ); - } - } - - function onIdentifyComplete(user, filteredIdentityRequest) { - if (isInitialized) { - try { - self.identityHandler.onIdentifyComplete( - user, - filteredIdentityRequest - ); - - return ( - 'Successfully called onIdentifyComplete on forwarder ' + - name - ); - } catch (e) { - return { - error: - 'Error calling onIdentifyComplete on forwarder ' + - name + - '; ' + - e, - }; - } - } else { - return ( - "Can't call onIdentifyCompleted on forwader " + - name + - ', not initialized' - ); - } - } - - function onLoginComplete(user, filteredIdentityRequest) { - if (isInitialized) { - try { - self.identityHandler.onLoginComplete( - user, - filteredIdentityRequest - ); - - return ( - 'Successfully called onLoginComplete on forwarder ' + name - ); - } catch (e) { - return { - error: - 'Error calling onLoginComplete on forwarder ' + - name + - '; ' + - e, - }; - } - } else { - return ( - "Can't call onLoginComplete on forwader " + - name + - ', not initialized' - ); - } - } - - function onLogoutComplete(user, filteredIdentityRequest) { - if (isInitialized) { - try { - self.identityHandler.onLogoutComplete( - user, - filteredIdentityRequest - ); - - return ( - 'Successfully called onLogoutComplete on forwarder ' + name - ); - } catch (e) { - return { - error: - 'Error calling onLogoutComplete on forwarder ' + - name + - '; ' + - e, - }; - } - } else { - return ( - "Can't call onLogoutComplete on forwader " + - name + - ', not initialized' - ); - } - } - - function onModifyComplete(user, filteredIdentityRequest) { - if (isInitialized) { - try { - self.identityHandler.onModifyComplete( - user, - filteredIdentityRequest - ); - - return ( - 'Successfully called onModifyComplete on forwarder ' + name - ); - } catch (e) { - return { - error: - 'Error calling onModifyComplete on forwarder ' + - name + - '; ' + - e, - }; - } - } else { - return ( - "Can't call onModifyComplete on forwader " + - name + - ', not initialized' - ); - } - } - - function setOptOut(isOptingOutBoolean) { - if (isInitialized) { - try { - self.initialization.setOptOut(isOptingOutBoolean); - - return 'Successfully called setOptOut on forwarder ' + name; - } catch (e) { - return { - error: - 'Error calling setOptOut on forwarder ' + - name + - '; ' + - e, - }; - } - } else { - return ( - "Can't call setOptOut on forwader " + - name + - ', not initialized' - ); - } - } - - this.init = initForwarder; - this.process = processEvent; - this.setUserAttribute = setUserAttribute; - this.removeUserAttribute = removeUserAttribute; - this.onUserIdentified = onUserIdentified; - this.setUserIdentity = setUserIdentity; - this.onIdentifyComplete = onIdentifyComplete; - this.onLoginComplete = onLoginComplete; - this.onLogoutComplete = onLogoutComplete; - this.onModifyComplete = onModifyComplete; - this.setOptOut = setOptOut; - }; - - function getId() { - return moduleId; - } - - if (typeof window !== 'undefined') { - if (window && window.mParticle && window.mParticle.addForwarder) { - window.mParticle.addForwarder({ - name: name, - constructor: constructor, - getId: getId, - }); - } - } - - var SDKsettings = { - apiKey: 'testAPIKey' - /* fill in SDKsettings with any particular settings or options your sdk requires in order to - initialize, this may be apiKey, projectId, primaryCustomerType, etc. These are passed - into the src/initialization.js file as the - */ - }; - - // Do not edit below: - var settings = SDKsettings; - - var name$1 = initialization_1.name; - - var config = { - name: name$1, - moduleId: 100, // when published, you will receive a new moduleID - isDebug: true, - isSandbox: true, - settings: settings, - userIdentityFilters: [], - hasDebugString: [], - isVisible: [], - eventNameFilters: [], - eventTypeFilters: [], - attributeFilters: [], - screenNameFilters: [], - pageViewAttributeFilters: [], - userAttributeFilters: [], - filteringEventAttributeValue: 'null', - filteringUserAttributeValue: 'null', - eventSubscriptionId: 123, - filteringConsentRuleValues: 'null', - excludeAnonymousUser: false - }; - - window.mParticle.config = window.mParticle.config || {}; - window.mParticle.config.workspaceToken = 'testkit'; - window.mParticle.config.requestConfig = false; - window.mParticle.config.kitConfigs = [config]; - - var endToEndTestapp = { - - }; - - exports.default = endToEndTestapp; - - return exports; - -}({})); From 402b2aa3393cab168497864cc516830d4bb51918 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:50:28 -0700 Subject: [PATCH 05/25] Adjust spacing --- src/initialization.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/initialization.js b/src/initialization.js index 214d4a5..be817d4 100644 --- a/src/initialization.js +++ b/src/initialization.js @@ -49,6 +49,7 @@ var initialization = { function buildPartnerData(userIdentities) { var SHA256 = require('crypto-js/sha256'); + // To-Do: finalize which PD values we are utilizing var email = userIdentities.userIdentities['email']; var cleansedEmail = normalizeEmail(email); From 89e7fb50122acfb63cb7e405226f7569735cbbd6 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:09:58 -0700 Subject: [PATCH 06/25] Upload outline of tests --- test/src/tests.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/test/src/tests.js b/test/src/tests.js index 991cc2a..0e0d5a8 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -70,9 +70,9 @@ describe('ID5 Forwarder', function () { }, reportService = new ReportingService(); - // -------------------DO NOT EDIT ANYTHING ABOVE THIS LINE----------------------- - // -------------------START EDITING BELOW:----------------------- - // -------------------mParticle stubs - Add any additional stubbing to our methods as needed----------------------- +// -------------------DO NOT EDIT ANYTHING ABOVE THIS LINE----------------------- +// -------------------START EDITING BELOW:----------------------- +// -------------------mParticle stubs - Add any additional stubbing to our methods as needed----------------------- mParticle.Identity = { getCurrentUser: function() { return { @@ -83,7 +83,7 @@ describe('ID5 Forwarder', function () { }; } }; - // -------------------START EDITING BELOW:----------------------- +// -------------------START EDITING BELOW:----------------------- var MockID5Forwarder = function() { var self = this; @@ -143,7 +143,7 @@ describe('ID5 Forwarder', function () { }); beforeEach(function() { - window.MockID5Forwarder = new MockID5Forwarder(); + window.MockXYZForwarder = new MockID5Forwarder(); // Include any specific settings that is required for initializing your SDK here var sdkSettings = { clientKey: '123456', @@ -169,6 +169,22 @@ describe('ID5 Forwarder', function () { mParticle.forwarder.init(sdkSettings, reportService.cb, true, null, userAttributes, userIdentities); }); + it ('should initialize ID5 with a PartnerID', function(done) { + done(); + }); + + it ('should not initialize ID5 without a PartnerID', function(done){ + done(); + }); + + it ('should build PD based on user identities and device IDs', function(done) { + done(); + }); + + it ('should only utilize known identities and device IDs to build PDs', function(done) { + done(); + }); + it('should log event', function(done) { // mParticle.forwarder.process({ // EventDataType: MessageType.PageEvent, From 63bc11c9eeedf0ef0aa1ab7be024d839ef884aa6 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:13:58 -0700 Subject: [PATCH 07/25] Add additional test cases --- test/src/tests.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/src/tests.js b/test/src/tests.js index 0e0d5a8..3b3b8a6 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -185,6 +185,20 @@ describe('ID5 Forwarder', function () { done(); }); + it ('should normalize emails ending in "@gmail"', function(done){ + done(); + }); + + it ('should not normalize emails not ending in @gmail', function(done){ + done(); + }); + + it ('should normalize phone numbers to ') + it ('should assign the ID5 ID to the selected identity in the forwarder settings', function(done){ + done(); + }); + + it('should log event', function(done) { // mParticle.forwarder.process({ // EventDataType: MessageType.PageEvent, From 7ef6fbb6ba434a2d46c0705634d09fd96ab16def Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Thu, 8 Aug 2024 09:32:18 -0700 Subject: [PATCH 08/25] Add additional test cases --- test/src/tests.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/test/src/tests.js b/test/src/tests.js index 3b3b8a6..e0ee10e 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -173,7 +173,7 @@ describe('ID5 Forwarder', function () { done(); }); - it ('should not initialize ID5 without a PartnerID', function(done){ + it ('should not initialize ID5 without a PartnerID', function(done) { done(); }); @@ -185,19 +185,29 @@ describe('ID5 Forwarder', function () { done(); }); - it ('should normalize emails ending in "@gmail"', function(done){ + it ('should normalize emails ending in "@gmail"', function(done) { done(); }); - it ('should not normalize emails not ending in @gmail', function(done){ + it ('should not normalize emails not ending in @gmail', function(done) { done(); }); - it ('should normalize phone numbers to ') + it ('should normalize phone numbers by removing certain special characters', function(done) { + done(); + }); + + it ('should not remove special characters beyond the specified list', function(done){ + done() + }); + it ('should assign the ID5 ID to the selected identity in the forwarder settings', function(done){ done(); }); + it ('should not assign the ID5 ID if a selected identity is missing in teh forwarder settings', function(done) { + done(); + }); it('should log event', function(done) { // mParticle.forwarder.process({ From e29ba5738d364edc30fa6b2ee0b98d055be65cb2 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:15:07 -0700 Subject: [PATCH 09/25] Update npm registry --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index e6bf746..76aa927 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2824,7 +2824,7 @@ }, "node_modules/crypto-js": { "version": "4.2.0", - "resolved": "https://nexus.ops-shared.mparticle.com/repository/npm-group/crypto-js/-/crypto-js-4.2.0.tgz", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", "dev": true, "license": "MIT" From 0ec13d4c35968fb4c8b5957eef88fd51382b88d1 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:34:25 -0700 Subject: [PATCH 10/25] Spelling in comments --- test/src/tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/tests.js b/test/src/tests.js index e0ee10e..4a76392 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -205,7 +205,7 @@ describe('ID5 Forwarder', function () { done(); }); - it ('should not assign the ID5 ID if a selected identity is missing in teh forwarder settings', function(done) { + it ('should not assign the ID5 ID if a selected identity is missing in the forwarder settings', function(done) { done(); }); From 9767e25761335bc80dab9d95811869680aaae0a5 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Mon, 12 Aug 2024 15:56:44 -0700 Subject: [PATCH 11/25] Adjust processing around available pdKeys --- src/initialization.js | 46 +++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/initialization.js b/src/initialization.js index be817d4..32ebe7c 100644 --- a/src/initialization.js +++ b/src/initialization.js @@ -2,9 +2,8 @@ var initialization = { name: 'ID5', + moduleId: '248', - //To-Do: add Module id after establishing in QA - moduleId: '', /* ****** Fill out initForwarder to load your SDK ****** Note that not all arguments may apply to your SDK initialization. These are passed from mParticle, but leave them even if they are not being used. @@ -48,30 +47,31 @@ var initialization = { function buildPartnerData(userIdentities) { - var SHA256 = require('crypto-js/sha256'); + var pdKeys = {}; - // To-Do: finalize which PD values we are utilizing var email = userIdentities.userIdentities['email']; - var cleansedEmail = normalizeEmail(email); - var hashedEmail = SHA256(cleansedEmail); + var processedEmail = normalizeAndHashEmail(email); + if (processedEmail) pdKeys[1] = processedEmail; var phone = userIdentities.userIdentities['mobile_number']; - var cleansedPhone = normalizePhone(phone); - var hashedPhoneNumber = SHA256(cleansedPhone); + var processedPhone = normalizeAndHashPhone(phone); + if (processedPhone) pdKeys[2] = processedPhone; var fullUrl = window.location.href; + if (fullUrl) pdKeys[8] = encodeURIComponent(fullUrl); + + var domain = window.location.host; + if (domain) pdKeys[9] = domain; + + // Below may not be accessible from kit var deviceIPv4; + if (deviceIPv4) pdKeys[10] = encodeURIComponent(deviceIPv4); + var userAgentString; - var idfv; + if (userAgentString) pdKeys[12] = encodeURIComponent(userAgentString); - var pdKeys = { - 1: hashedEmail, - 2: hashedPhoneNumber, - 8: encodeURIComponent(fullUrl), - 10: encodeURIComponent(deviceIPv4), - 12: encodeURIComponent(userAgentString), - 14: encodeURIComponent(idfv), - } + var idfv; + if (idfv) pdKeys[14] = encodeURIComponent(idfv); var pdRaw = Object.keys(pdKeys).map(function(key){ return key + '=' + pdKeys[key] @@ -80,7 +80,9 @@ function buildPartnerData(userIdentities) { return btoa(pdRaw); } -function normalizeEmail(email) { +function normalizeAndHashEmail(email) { + var SHA256 = require('crypto-js/sha256'); + var parts = email.split("@") var charactersToRemove = ['+', '.'] @@ -92,15 +94,17 @@ function normalizeEmail(email) { parts[0] = parts[0].replaceAll(character, '').toLowerCase(); }) - return parts.join('@'); + return SHA256(parts.join('@')); } -function normalizePhone(phone) { +function normalizeAndHashPhone(phone) { + var SHA256 = require('crypto-js/sha256'); + var charactersToRemove = [' ', '-', '(', ')'] charactersToRemove.forEach(function(character) { phone = phone.replaceAll(character, ''); }) - return phone; + return SHA256(phone); } module.exports = initialization; From d2b7d516837e6bf282add35cab236d93ad63951e Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Tue, 13 Aug 2024 09:07:28 -0700 Subject: [PATCH 12/25] Update PD's sent based on availability --- src/initialization.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/initialization.js b/src/initialization.js index 32ebe7c..1876f35 100644 --- a/src/initialization.js +++ b/src/initialization.js @@ -64,15 +64,9 @@ function buildPartnerData(userIdentities) { if (domain) pdKeys[9] = domain; // Below may not be accessible from kit - var deviceIPv4; - if (deviceIPv4) pdKeys[10] = encodeURIComponent(deviceIPv4); - var userAgentString; if (userAgentString) pdKeys[12] = encodeURIComponent(userAgentString); - var idfv; - if (idfv) pdKeys[14] = encodeURIComponent(idfv); - var pdRaw = Object.keys(pdKeys).map(function(key){ return key + '=' + pdKeys[key] }).join('&'); From 36c72fe7b53bb292308881412482fc282002dafe Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:52:50 -0600 Subject: [PATCH 13/25] Draft update --- src/common.js | 79 ++++++++++++++++++++++- src/event-handler.js | 5 +- src/identity-handler.js | 15 ++++- src/initialization.js | 88 ++++--------------------- test/end-to-end-testapp/settings.js | 2 +- test/src/tests.js | 99 ++++++++++++++--------------- 6 files changed, 154 insertions(+), 134 deletions(-) diff --git a/src/common.js b/src/common.js index 11f4d4e..e247f20 100644 --- a/src/common.js +++ b/src/common.js @@ -1,7 +1,84 @@ +var SHA256 = require('crypto-js/sha256'); function Common() {} Common.prototype.exampleMethod = function () { return 'I am an example'; } -module.exports = Common; \ No newline at end of file +Common.prototype.logId5 = function () { + var id5Id = this.id5Instance.getUserId(); + debugger; + + //Checks and saves ID5 ID if it is new + if (id5Id != this.id5Id) { + this.id5Id = id5Id; + this.id5IdSent = false + } + + //Only sends the ID5 ID Custom Event if unsent + if (this.id5IdSent == false){ + var currentUser = mParticle.Identity.getCurrentUser(); + currentUser.setUserAttribute('ID5ID', id5Id); + this.id5IdSent = true; + } +}; + +Common.prototype.buildPartnerData = function (mParticleUser) { + var pdKeys = {}; + var userIdentities = mParticleUser.getUserIdentities(); + + var email = userIdentities.userIdentities['email']; + if (email) { + var processedEmail = normalizeAndHashEmail(email); + pdKeys[1] = processedEmail; + } + + var phone = userIdentities.userIdentities['mobile_number']; + if (phone) { + var processedPhone = normalizeAndHashPhone(phone); + pdKeys[2] = processedPhone; + } + + //Candidates to be removed + var fullUrl = window.location.href; + if (fullUrl) pdKeys[8] = encodeURIComponent(fullUrl); + + var domain = window.location.host; + if (domain) pdKeys[9] = domain; + + var userAgentString = navigator.userAgent; + if (userAgentString) pdKeys[12] = encodeURIComponent(userAgentString); + // above may be pulled from PD as they are un needed for the ID5 ID generation as a basic level + + + var pdRaw = Object.keys(pdKeys).map(function(key){ + return key + '=' + pdKeys[key] + }).join('&'); + + return btoa(pdRaw); +} + +function normalizeAndHashEmail(email) { + var parts = email.split("@") + var charactersToRemove = ['+', '.'] + + if (parts[1] != 'gmail.com') { + return email; + } + + charactersToRemove.forEach(function(character) { + parts[0] = parts[0].replaceAll(character, '').toLowerCase(); + }) + + return SHA256(parts.join('@')); +} + +function normalizeAndHashPhone(phone) { + var charactersToRemove = [' ', '-', '(', ')'] + charactersToRemove.forEach(function(character) { + phone = phone.replaceAll(character, ''); + }) + return SHA256(phone); +} + +module.exports = Common; diff --git a/src/event-handler.js b/src/event-handler.js index e6bb018..70e087c 100644 --- a/src/event-handler.js +++ b/src/event-handler.js @@ -16,7 +16,10 @@ A non-ecommerce event has the following schema: function EventHandler(common) { this.common = common || {}; } -EventHandler.prototype.logEvent = function(event) {}; +EventHandler.prototype.logEvent = function() { + this.common.logId5(); +}; + EventHandler.prototype.logError = function(event) { // The schema for a logError event is the same, but noteworthy differences are as follows: // { diff --git a/src/identity-handler.js b/src/identity-handler.js index b8e29ff..ed563a7 100644 --- a/src/identity-handler.js +++ b/src/identity-handler.js @@ -25,7 +25,10 @@ IdentityHandler.prototype.onUserIdentified = function(mParticleUser) {}; IdentityHandler.prototype.onIdentifyComplete = function( mParticleUser, identityApiRequest -) {}; +) { + var partnerData = this.common.buildPartnerData(mParticleUser); + this.common.id5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) +}; IdentityHandler.prototype.onLoginComplete = function( mParticleUser, identityApiRequest @@ -33,11 +36,17 @@ IdentityHandler.prototype.onLoginComplete = function( IdentityHandler.prototype.onLogoutComplete = function( mParticleUser, identityApiRequest -) {}; +) { + var partnerData = this.common.buildPartnerData(mParticleUser); + this.common.id5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) +}; IdentityHandler.prototype.onModifyComplete = function( mParticleUser, identityApiRequest -) {}; +) { + var partnerData = this.common.buildPartnerData(mParticleUser); + this.common.id5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) +}; /* In previous versions of the mParticle web SDK, setting user identities on kits is only reachable via the onSetUserIdentity method below. We recommend diff --git a/src/initialization.js b/src/initialization.js index 3c43dca..84f69c0 100644 --- a/src/initialization.js +++ b/src/initialization.js @@ -1,5 +1,3 @@ -// import SHA256 from 'crypto-js/sha256'; - var initialization = { name: 'ID5', moduleId: '248', @@ -11,7 +9,7 @@ var initialization = { userIdentities example: { 1: 'customerId', 2: 'facebookId', 7: 'emailid@email.com' } additional identityTypes can be found at https://github.com/mParticle/mparticle-sdk-javascript/blob/master-v2/src/types.js#L88-L101 */ - initForwarder: function(forwarderSettings, testMode, userAttributes, userIdentities, processEvent, eventQueue, isInitialized, common, appVersion, appName, customFlags, clientId) { + initForwarder: function(forwarderSettings, testMode, userAttributes, userIdentities, processEvent, eventQueue, isInitialized, common) { /* `forwarderSettings` contains your SDK specific settings such as apiKey that your customer needs in order to initialize your SDK properly */ if (!testMode) { @@ -19,23 +17,17 @@ var initialization = { Generally, our integrations create script tags and append them to the . Please follow the following format as a guide: */ - var clientScript = document.createElement('script'); - clientScript.type = 'text/javascript'; - clientScript.async = true; - clientScript.src = 'https://cdn.id5-sync.com/api/1.0/id5-api.js'; // <---- Update this to be your script - (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(clientScript); - clientScript.onload = function() { - var partnerData = buildPartnerData(userIdentities); - - //To-Do: PartnerUserId to be added to the init options once received from id5 - window.ID5.init({partnerId: forwarderSettings.partnerId, pd: partnerData}).onAvailable(function(status) { - var id5Id = status.getID5Id(); - var idType = forwarderSettings.id5IdType; - var identities = {}; - identities[idType] = id5Id; - - window.mParticle.Identity.modify({userIdentities: identities}, identityCallback) - }); + var id5Script = document.createElement('script'); + id5Script.src = 'https://cdn.id5-sync.com/api/1.0/id5-api.js'; // <---- Update this to be your script + (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(id5Script); + debugger; + common.id5Id = null; + common.id5IdSent = false; + common.parterId = forwarderSettings.partnerId; + + id5Script.onload = function() { + isInitialized = true; + common.id5Instance = window.ID5.init({partnerId: forwarderSettings.partnerId}) }; } else { // For testing, you should fill out this section in order to ensure any required initialization calls are made, @@ -44,60 +36,4 @@ var initialization = { } }; - -function buildPartnerData(userIdentities) { - var pdKeys = {}; - - var email = userIdentities.userIdentities['email']; - var processedEmail = normalizeAndHashEmail(email); - if (processedEmail) pdKeys[1] = processedEmail; - - var phone = userIdentities.userIdentities['mobile_number']; - var processedPhone = normalizeAndHashPhone(phone); - if (processedPhone) pdKeys[2] = processedPhone; - - var fullUrl = window.location.href; - if (fullUrl) pdKeys[8] = encodeURIComponent(fullUrl); - - var domain = window.location.host; - if (domain) pdKeys[9] = domain; - - // Below may not be accessible from kit - var userAgentString; - if (userAgentString) pdKeys[12] = encodeURIComponent(userAgentString); - - var pdRaw = Object.keys(pdKeys).map(function(key){ - return key + '=' + pdKeys[key] - }).join('&'); - - return btoa(pdRaw); -} - -function normalizeAndHashEmail(email) { - var SHA256 = require('crypto-js/sha256'); - - var parts = email.split("@") - var charactersToRemove = ['+', '.'] - - if (parts[1] != 'gmail.com') { - return email; - } - - charactersToRemove.forEach(function(character) { - parts[0] = parts[0].replaceAll(character, '').toLowerCase(); - }) - - return SHA256(parts.join('@')); -} - -function normalizeAndHashPhone(phone) { - var SHA256 = require('crypto-js/sha256'); - - var charactersToRemove = [' ', '-', '(', ')'] - charactersToRemove.forEach(function(character) { - phone = phone.replaceAll(character, ''); - }) - return SHA256(phone); -} - module.exports = initialization; diff --git a/test/end-to-end-testapp/settings.js b/test/end-to-end-testapp/settings.js index 7dd4831..f8405c2 100644 --- a/test/end-to-end-testapp/settings.js +++ b/test/end-to-end-testapp/settings.js @@ -1,5 +1,5 @@ var SDKsettings = { - apiKey: 'testAPIKey' + partnerId: '1234' /* fill in SDKsettings with any particular settings or options your sdk requires in order to initialize, this may be apiKey, projectId, primaryCustomerType, etc. These are passed into the src/initialization.js file as the diff --git a/test/src/tests.js b/test/src/tests.js index 4a76392..5d7f4cf 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -10,7 +10,10 @@ describe('ID5 Forwarder', function () { OptOut: 6, AppStateTransition: 10, Profile: 14, - Commerce: 16 + Commerce: 16, + Media: 20, + UserAttributeChange: 17, + UserIdentityChange: 18, }, EventType = { Unknown: 0, @@ -49,8 +52,19 @@ describe('ID5 Forwarder', function () { Microsoft: 5, Yahoo: 6, Email: 7, - Alias: 8, FacebookCustomAudienceId: 9, + Other2: 10, + Other3: 11, + Other4: 12, + Other5: 13, + Other6: 14, + Other7: 15, + Other8: 16, + Other9: 17, + Other10: 18, + MobileNumber: 19, + PhoneNumber2: 20, + PhoneNumber3: 21, }, ReportingService = function () { var self = this; @@ -83,59 +97,32 @@ describe('ID5 Forwarder', function () { }; } }; -// -------------------START EDITING BELOW:----------------------- + // -------------------START EDITING BELOW:----------------------- var MockID5Forwarder = function() { var self = this; // create properties for each type of event you want tracked, see below for examples this.trackCustomEventCalled = false; this.logPurchaseEventCalled = false; - this.initializeCalled = false; - - this.trackCustomName = null; - this.logPurchaseName = null; - this.apiKey = null; - this.appId = null; - this.userId = null; - this.userAttributes = {}; - this.userIdField = null; + this.isInitialized = false; + this.initCalled = false; + this.getUserIdCalled = false; - this.eventProperties = []; - this.purchaseEventProperties = []; + this.pd = null; + this.partnerId = null; + this.configurationOptions = null; // stub your different methods to ensure they are being called properly - this.initialize = function(appId, apiKey) { - self.initializeCalled = true; - self.apiKey = apiKey; - self.appId = appId; - }; - - this.stubbedTrackingMethod = function(name, eventProperties){ - self.trackCustomEventCalled = true; - self.trackCustomName = name; - self.eventProperties.push(eventProperties); - // Return true to indicate event should be reported - return true; + this.init = function(id5Options) { + self.initCalled = true; + self.partnerId = id5Options.partnerId; + self.pd = id5Options.pd }; - this.stubbedUserAttributeSettingMethod = function(userAttributes) { - self.userId = id; - userAttributes = userAttributes || {}; - if (Object.keys(userAttributes).length) { - for (var key in userAttributes) { - if (userAttributes[key] === null) { - delete self.userAttributes[key]; - } - else { - self.userAttributes[key] = userAttributes[key]; - } - } - } - }; - - this.stubbedUserLoginMethod = function(id) { - self.userId = id; - }; + this.getUserId = function() { + self.getUserIdCalled = true; + return 'ID5*testtesttesttest' + } }; before(function () { @@ -143,17 +130,16 @@ describe('ID5 Forwarder', function () { }); beforeEach(function() { - window.MockXYZForwarder = new MockID5Forwarder(); + window.ID5 = new MockID5Forwarder(); // Include any specific settings that is required for initializing your SDK here var sdkSettings = { - clientKey: '123456', - appId: 'abcde', - userIdField: 'customerId' + partnerId: '1234', }; // You may require userAttributes or userIdentities to be passed into initialization var userAttributes = { color: 'green' }; + var userIdentities = [{ Identity: 'customerId', Type: IdentityType.CustomerId @@ -161,19 +147,28 @@ describe('ID5 Forwarder', function () { Identity: 'email', Type: IdentityType.Email }, { - Identity: 'facebook', - Type: IdentityType.Facebook + Identity: 'mobile_number', + Type: IdentityType.MobileNumber }]; + // The third argument here is a boolean to indicate that the integration is in test mode to avoid loading any third party scripts. Do not change this value. mParticle.forwarder.init(sdkSettings, reportService.cb, true, null, userAttributes, userIdentities); }); - it ('should initialize ID5 with a PartnerID', function(done) { + + it ('Initialization should load the script into the document', function(done) { + mParticle.forwarder.init({ + partnerId: '1234', + }); + document.scripts[0].src.should.equal('https://cdn.id5-sync.com/api/1.0/id5-api.js'); done(); }); - it ('should not initialize ID5 without a PartnerID', function(done) { + it ('should call ID5.init', function(done) { + mParticle.forwarder.init({ + partnerId: '1234', + }); done(); }); From 68fe871d47890810b2d005f43f6eeffef617355a Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:01:30 -0700 Subject: [PATCH 14/25] updates unit tests to cover all common functions --- src/common.js | 36 ++--- src/event-handler.js | 5 +- src/identity-handler.js | 16 ++- src/initialization.js | 10 +- test/end-to-end-testapp/settings.js | 2 +- test/src/tests.js | 199 +++++++++------------------- 6 files changed, 93 insertions(+), 175 deletions(-) diff --git a/src/common.js b/src/common.js index e247f20..355f722 100644 --- a/src/common.js +++ b/src/common.js @@ -5,17 +5,16 @@ Common.prototype.exampleMethod = function () { return 'I am an example'; } -Common.prototype.logId5 = function () { - var id5Id = this.id5Instance.getUserId(); - debugger; - +Common.prototype.logId5Id = function (id5Id) { //Checks and saves ID5 ID if it is new if (id5Id != this.id5Id) { this.id5Id = id5Id; this.id5IdSent = false } - //Only sends the ID5 ID Custom Event if unsent + //Sets user attribute if ID is unsent. + //This function will be updated once the decryption architecture is finalized. + //The ID may need to be sent as custom event. if (this.id5IdSent == false){ var currentUser = mParticle.Identity.getCurrentUser(); currentUser.setUserAttribute('ID5ID', id5Id); @@ -29,28 +28,16 @@ Common.prototype.buildPartnerData = function (mParticleUser) { var email = userIdentities.userIdentities['email']; if (email) { - var processedEmail = normalizeAndHashEmail(email); + var processedEmail = SHA256(this.normalizeEmail(email)); pdKeys[1] = processedEmail; } var phone = userIdentities.userIdentities['mobile_number']; if (phone) { - var processedPhone = normalizeAndHashPhone(phone); + var processedPhone = SHA256(this.normalizePhone(phone)); pdKeys[2] = processedPhone; } - //Candidates to be removed - var fullUrl = window.location.href; - if (fullUrl) pdKeys[8] = encodeURIComponent(fullUrl); - - var domain = window.location.host; - if (domain) pdKeys[9] = domain; - - var userAgentString = navigator.userAgent; - if (userAgentString) pdKeys[12] = encodeURIComponent(userAgentString); - // above may be pulled from PD as they are un needed for the ID5 ID generation as a basic level - - var pdRaw = Object.keys(pdKeys).map(function(key){ return key + '=' + pdKeys[key] }).join('&'); @@ -58,7 +45,8 @@ Common.prototype.buildPartnerData = function (mParticleUser) { return btoa(pdRaw); } -function normalizeAndHashEmail(email) { +Common.prototype.normalizeEmail = function(email) { + debugger; var parts = email.split("@") var charactersToRemove = ['+', '.'] @@ -70,15 +58,17 @@ function normalizeAndHashEmail(email) { parts[0] = parts[0].replaceAll(character, '').toLowerCase(); }) - return SHA256(parts.join('@')); + return parts.join('@'); } -function normalizeAndHashPhone(phone) { +Common.prototype.normalizePhone = function(phone) { var charactersToRemove = [' ', '-', '(', ')'] + charactersToRemove.forEach(function(character) { phone = phone.replaceAll(character, ''); }) - return SHA256(phone); + + return phone; } module.exports = Common; diff --git a/src/event-handler.js b/src/event-handler.js index 70e087c..cf83833 100644 --- a/src/event-handler.js +++ b/src/event-handler.js @@ -17,17 +17,16 @@ function EventHandler(common) { this.common = common || {}; } EventHandler.prototype.logEvent = function() { - this.common.logId5(); }; -EventHandler.prototype.logError = function(event) { +EventHandler.prototype.logError = function() { // The schema for a logError event is the same, but noteworthy differences are as follows: // { // EventAttributes: {m: 'name of error passed into MP', s: "Error", t: 'stack trace in string form if applicable'}, // EventName: "Error" // } }; -EventHandler.prototype.logPageView = function(event) { +EventHandler.prototype.logPageView = function() { /* The schema for a logPagView event is the same, but noteworthy differences are as follows: { EventAttributes: {hostname: "www.google.com", title: 'Test Page'}, // These are event attributes only if no additional event attributes are explicitly provided to mParticle.logPageView(...) diff --git a/src/identity-handler.js b/src/identity-handler.js index ed563a7..5358e44 100644 --- a/src/identity-handler.js +++ b/src/identity-handler.js @@ -26,26 +26,28 @@ IdentityHandler.prototype.onIdentifyComplete = function( mParticleUser, identityApiRequest ) { - var partnerData = this.common.buildPartnerData(mParticleUser); - this.common.id5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) }; IdentityHandler.prototype.onLoginComplete = function( mParticleUser, identityApiRequest -) {}; +) { + var logId5Id = this.common.logId5Id; + var partnerData = this.common.buildPartnerData(mParticleUser); + var newId5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) + + newId5Instance.onAvailable(function(status){ + logId5Id(status.getUserId()); + }.bind(logId5Id)); +}; IdentityHandler.prototype.onLogoutComplete = function( mParticleUser, identityApiRequest ) { - var partnerData = this.common.buildPartnerData(mParticleUser); - this.common.id5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) }; IdentityHandler.prototype.onModifyComplete = function( mParticleUser, identityApiRequest ) { - var partnerData = this.common.buildPartnerData(mParticleUser); - this.common.id5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) }; /* In previous versions of the mParticle web SDK, setting user identities on diff --git a/src/initialization.js b/src/initialization.js index 84f69c0..6690e6f 100644 --- a/src/initialization.js +++ b/src/initialization.js @@ -20,14 +20,18 @@ var initialization = { var id5Script = document.createElement('script'); id5Script.src = 'https://cdn.id5-sync.com/api/1.0/id5-api.js'; // <---- Update this to be your script (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(id5Script); - debugger; + common.id5Id = null; common.id5IdSent = false; - common.parterId = forwarderSettings.partnerId; + common.partnerId = forwarderSettings.partnerId; id5Script.onload = function() { isInitialized = true; - common.id5Instance = window.ID5.init({partnerId: forwarderSettings.partnerId}) + var id5Instance = window.ID5.init({partnerId: common.partnerId}) + + id5Instance.onAvailable(function(status){ + common.logId5Id(status.getUserId()); + }.bind(common)); }; } else { // For testing, you should fill out this section in order to ensure any required initialization calls are made, diff --git a/test/end-to-end-testapp/settings.js b/test/end-to-end-testapp/settings.js index f8405c2..11cc53f 100644 --- a/test/end-to-end-testapp/settings.js +++ b/test/end-to-end-testapp/settings.js @@ -1,5 +1,5 @@ var SDKsettings = { - partnerId: '1234' + partnerId: 1234 /* fill in SDKsettings with any particular settings or options your sdk requires in order to initialize, this may be apiKey, projectId, primaryCustomerType, etc. These are passed into the src/initialization.js file as the diff --git a/test/src/tests.js b/test/src/tests.js index 5d7f4cf..d186105 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -87,12 +87,20 @@ describe('ID5 Forwarder', function () { // -------------------DO NOT EDIT ANYTHING ABOVE THIS LINE----------------------- // -------------------START EDITING BELOW:----------------------- // -------------------mParticle stubs - Add any additional stubbing to our methods as needed----------------------- + var userAttributes = {}; + mParticle.Identity = { getCurrentUser: function() { return { getMPID: function() { return '123'; - } + }, + setUserAttribute: function(key, value){ + userAttributes[key]= value + }, + getAllUserAttributes: function() { + return userAttributes; + }, }; } @@ -117,6 +125,11 @@ describe('ID5 Forwarder', function () { self.initCalled = true; self.partnerId = id5Options.partnerId; self.pd = id5Options.pd + return this; + }; + + this.onAvailable = function(callback) { + return callback }; this.getUserId = function() { @@ -133,7 +146,7 @@ describe('ID5 Forwarder', function () { window.ID5 = new MockID5Forwarder(); // Include any specific settings that is required for initializing your SDK here var sdkSettings = { - partnerId: '1234', + partnerId: 1234, }; // You may require userAttributes or userIdentities to be passed into initialization var userAttributes = { @@ -157,157 +170,67 @@ describe('ID5 Forwarder', function () { }); - it ('Initialization should load the script into the document', function(done) { + it ('should load the script into the document during initialization', function(done) { + window.ID5 = new MockID5Forwarder(); mParticle.forwarder.init({ - partnerId: '1234', + partnerId: 1234, }); document.scripts[0].src.should.equal('https://cdn.id5-sync.com/api/1.0/id5-api.js'); done(); }); - it ('should call ID5.init', function(done) { - mParticle.forwarder.init({ - partnerId: '1234', - }); - done(); - }); - - it ('should build PD based on user identities and device IDs', function(done) { - done(); - }); - - it ('should only utilize known identities and device IDs to build PDs', function(done) { - done(); - }); - - it ('should normalize emails ending in "@gmail"', function(done) { - done(); - }); - - it ('should not normalize emails not ending in @gmail', function(done) { - done(); - }); - - it ('should normalize phone numbers by removing certain special characters', function(done) { - done(); - }); - - it ('should not remove special characters beyond the specified list', function(done){ - done() - }); + describe('Common Functions', function() { + it ('should log a user attribute when logId5 is called', function(done) { + mParticle.forwarder.common.logId5Id("testId"); - it ('should assign the ID5 ID to the selected identity in the forwarder settings', function(done){ - done(); - }); + var attributes = mParticle.Identity.getCurrentUser().getAllUserAttributes() - it ('should not assign the ID5 ID if a selected identity is missing in the forwarder settings', function(done) { - done(); - }); + attributes.ID5ID.should.exist; + attributes.ID5ID.should.equal("testId") + done(); + }); - it('should log event', function(done) { - // mParticle.forwarder.process({ - // EventDataType: MessageType.PageEvent, - // EventName: 'Test Event', - // EventAttributes: { - // label: 'label', - // value: 200, - // category: 'category' - // } - // }); + it ('should build pd when buildPartnerData is called with a user', function(done) { + var user = { + getUserIdentities: function() { + return { + userIdentities: { + email: 'test@email.com', + phone: '123-456-7890', + } + } + }, + }; - // window.MockID5Forwarder.eventProperties[0].label.should.equal('label'); - // window.MockID5Forwarder.eventProperties[0].value.should.equal(200); + var pd = mParticle.forwarder.common.buildPartnerData(user) - done(); - }); + pd.should.exist; + pd.should.equal('MT03MzA2MmQ4NzI5MjZjMmE1NTZmMTdiMzZmNTBlMzI4ZGRmOWJmZjlkNDAzOTM5YmQxNGI2YzNiN2Y1YTMzZmMy') + done(); + }); - it('should log page view', function(done) { - // mParticle.forwarder.process({ - // EventDataType: MessageType.PageView, - // EventName: 'test name', - // EventAttributes: { - // attr1: 'test1', - // attr2: 'test2' - // } - // }); - // - // window.MockID5Forwarder.trackCustomEventCalled.should.equal(true); - // window.MockID5Forwarder.trackCustomName.should.equal('test name'); - // window.MockID5Forwarder.eventProperties[0].attr1.should.equal('test1'); - // window.MockID5Forwarder.eventProperties[0].attr2.should.equal('test2'); + it ('should normalize an gmail when normalizeEmail is called', function(done) { + var normalizedGmail = mParticle.forwarder.common.normalizeEmail('test+test.2@gmail.com'); - done(); - }); + normalizedGmail.should.exist; + normalizedGmail.should.equal('testtest2@gmail.com') + done(); + }); - it('should log a product purchase commerce event', function(done) { - // mParticle.forwarder.process({ - // EventName: 'Test Purchase Event', - // EventDataType: MessageType.Commerce, - // EventCategory: EventType.ProductPurchase, - // ProductAction: { - // ProductActionType: ProductActionType.Purchase, - // ProductList: [ - // { - // Sku: '12345', - // Name: 'iPhone 6', - // Category: 'Phones', - // Brand: 'iPhone', - // Variant: '6', - // Price: 400, - // TotalAmount: 400, - // CouponCode: 'coupon-code', - // Quantity: 1 - // } - // ], - // TransactionId: 123, - // Affiliation: 'my-affiliation', - // TotalAmount: 450, - // TaxAmount: 40, - // ShippingAmount: 10, - // CouponCode: null - // } - // }); - // - // window.MockID5Forwarder.trackCustomEventCalled.should.equal(true); - // window.MockID5Forwarder.trackCustomName.should.equal('Purchase'); - // - // window.MockID5Forwarder.eventProperties[0].Sku.should.equal('12345'); - // window.MockID5Forwarder.eventProperties[0].Name.should.equal('iPhone 6'); - // window.MockID5Forwarder.eventProperties[0].Category.should.equal('Phones'); - // window.MockID5Forwarder.eventProperties[0].Brand.should.equal('iPhone'); - // window.MockID5Forwarder.eventProperties[0].Variant.should.equal('6'); - // window.MockID5Forwarder.eventProperties[0].Price.should.equal(400); - // window.MockID5Forwarder.eventProperties[0].TotalAmount.should.equal(400); - // window.MockID5Forwarder.eventProperties[0].CouponCode.should.equal('coupon-code'); - // window.MockID5Forwarder.eventProperties[0].Quantity.should.equal(1); + it ('should not normalize an non-gmail when normalizeEmail is called', function(done) { + var normalizedOther = mParticle.forwarder.common.normalizeEmail('test+test.2@test.com'); - done(); - }); + normalizedOther.should.exist; + normalizedOther.should.equal('test+test.2@test.com'); + done(); + }); - it('should set customer id user identity on user identity change', function(done) { - // var fakeUserStub = { - // getUserIdentities: function() { - // return { - // userIdentities: { - // customerid: '123' - // } - // }; - // }, - // getMPID: function() { - // return 'testMPID'; - // }, - // setUserAttribute: function() { - // - // }, - // removeUserAttribute: function() { - // - // } - // }; - // - // mParticle.forwarder.onUserIdentified(fakeUserStub); - // - // window.MockID5Forwarder.userId.should.equal('123'); + it ('should normalize phone numbers when normalizePhone is called', function(done) { + var normalizedPhone = mParticle.forwarder.common.normalizePhone('(123) 456-7890'); - done(); - }); + normalizedPhone.should.exist; + normalizedPhone.should.equal('1234567890'); + done(); + }) + }) }); From b186e54a96a5affaf740ad6f25734b45077e8ea9 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:04:48 -0700 Subject: [PATCH 15/25] Restore identity handler --- src/identity-handler.js | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/identity-handler.js b/src/identity-handler.js index 5358e44..b8e29ff 100644 --- a/src/identity-handler.js +++ b/src/identity-handler.js @@ -25,30 +25,19 @@ IdentityHandler.prototype.onUserIdentified = function(mParticleUser) {}; IdentityHandler.prototype.onIdentifyComplete = function( mParticleUser, identityApiRequest -) { -}; +) {}; IdentityHandler.prototype.onLoginComplete = function( mParticleUser, identityApiRequest -) { - var logId5Id = this.common.logId5Id; - var partnerData = this.common.buildPartnerData(mParticleUser); - var newId5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) - - newId5Instance.onAvailable(function(status){ - logId5Id(status.getUserId()); - }.bind(logId5Id)); -}; +) {}; IdentityHandler.prototype.onLogoutComplete = function( mParticleUser, identityApiRequest -) { -}; +) {}; IdentityHandler.prototype.onModifyComplete = function( mParticleUser, identityApiRequest -) { -}; +) {}; /* In previous versions of the mParticle web SDK, setting user identities on kits is only reachable via the onSetUserIdentity method below. We recommend From d5d450308a21015a9c323f3222624ea633c83a5a Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Fri, 4 Oct 2024 12:28:28 -0700 Subject: [PATCH 16/25] Update unit tests and implement identity handling --- src/common.js | 4 +- src/identity-handler.js | 22 ++++-- src/initialization.js | 10 ++- test/src/tests.js | 149 +++++++++++++++------------------------- 4 files changed, 83 insertions(+), 102 deletions(-) diff --git a/src/common.js b/src/common.js index 355f722..d814d11 100644 --- a/src/common.js +++ b/src/common.js @@ -6,6 +6,7 @@ Common.prototype.exampleMethod = function () { } Common.prototype.logId5Id = function (id5Id) { + debugger; //Checks and saves ID5 ID if it is new if (id5Id != this.id5Id) { this.id5Id = id5Id; @@ -20,7 +21,7 @@ Common.prototype.logId5Id = function (id5Id) { currentUser.setUserAttribute('ID5ID', id5Id); this.id5IdSent = true; } -}; +}.bind(this); Common.prototype.buildPartnerData = function (mParticleUser) { var pdKeys = {}; @@ -46,7 +47,6 @@ Common.prototype.buildPartnerData = function (mParticleUser) { } Common.prototype.normalizeEmail = function(email) { - debugger; var parts = email.split("@") var charactersToRemove = ['+', '.'] diff --git a/src/identity-handler.js b/src/identity-handler.js index b8e29ff..6051819 100644 --- a/src/identity-handler.js +++ b/src/identity-handler.js @@ -27,13 +27,27 @@ IdentityHandler.prototype.onIdentifyComplete = function( identityApiRequest ) {}; IdentityHandler.prototype.onLoginComplete = function( - mParticleUser, - identityApiRequest -) {}; + mParticleUser +) { + var partnerData = this.common.buildPartnerData(mParticleUser); + var id5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) + var logId5Id = this.common.logId5Id; + + id5Instance.onAvailable(function(status){ + logId5Id(status.getUserId()); + }.bind(logId5Id)); +}; IdentityHandler.prototype.onLogoutComplete = function( mParticleUser, identityApiRequest -) {}; +) { + var id5Instance = window.ID5.init({partnerId: this.common.partnerId}) + var logId5Id = this.common.logId5Id; + + id5Instance.onAvailable(function(status){ + logId5Id(status.getUserId()); + }.bind(logId5Id)); +}; IdentityHandler.prototype.onModifyComplete = function( mParticleUser, identityApiRequest diff --git a/src/initialization.js b/src/initialization.js index 6690e6f..bc54d91 100644 --- a/src/initialization.js +++ b/src/initialization.js @@ -27,6 +27,7 @@ var initialization = { id5Script.onload = function() { isInitialized = true; + var id5Instance = window.ID5.init({partnerId: common.partnerId}) id5Instance.onAvailable(function(status){ @@ -34,8 +35,13 @@ var initialization = { }.bind(common)); }; } else { - // For testing, you should fill out this section in order to ensure any required initialization calls are made, - // clientSDKObject.initialize(forwarderSettings.apiKey) + isInitialized = true; + + var id5Instance = window.ID5.init({partnerId: common.partnerId}) + + id5Instance.onAvailable(function(status){ + common.logId5Id(status.getUserId()); + }.bind(common)); } } }; diff --git a/test/src/tests.js b/test/src/tests.js index d186105..5b85e20 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -1,72 +1,7 @@ /* eslint-disable no-undef*/ describe('ID5 Forwarder', function () { // -------------------DO NOT EDIT ANYTHING BELOW THIS LINE----------------------- - var MessageType = { - SessionStart: 1, - SessionEnd: 2, - PageView: 3, - PageEvent: 4, - CrashReport: 5, - OptOut: 6, - AppStateTransition: 10, - Profile: 14, - Commerce: 16, - Media: 20, - UserAttributeChange: 17, - UserIdentityChange: 18, - }, - EventType = { - Unknown: 0, - Navigation: 1, - Location: 2, - Search: 3, - Transaction: 4, - UserContent: 5, - UserPreference: 6, - Social: 7, - Other: 8, - Media: 9, - getName: function() { - return 'blahblah'; - } - }, - ProductActionType = { - Unknown: 0, - AddToCart: 1, - RemoveFromCart: 2, - Checkout: 3, - CheckoutOption: 4, - Click: 5, - ViewDetail: 6, - Purchase: 7, - Refund: 8, - AddToWishlist: 9, - RemoveFromWishlist: 10 - }, - IdentityType = { - Other: 0, - CustomerId: 1, - Facebook: 2, - Twitter: 3, - Google: 4, - Microsoft: 5, - Yahoo: 6, - Email: 7, - FacebookCustomAudienceId: 9, - Other2: 10, - Other3: 11, - Other4: 12, - Other5: 13, - Other6: 14, - Other7: 15, - Other8: 16, - Other9: 17, - Other10: 18, - MobileNumber: 19, - PhoneNumber2: 20, - PhoneNumber3: 21, - }, - ReportingService = function () { + var ReportingService = function () { var self = this; this.id = null; @@ -84,7 +19,7 @@ describe('ID5 Forwarder', function () { }, reportService = new ReportingService(); -// -------------------DO NOT EDIT ANYTHING ABOVE THIS LINE----------------------- + // -------------------DO NOT EDIT ANYTHING ABOVE THIS LINE----------------------- // -------------------START EDITING BELOW:----------------------- // -------------------mParticle stubs - Add any additional stubbing to our methods as needed----------------------- var userAttributes = {}; @@ -106,7 +41,7 @@ describe('ID5 Forwarder', function () { } }; // -------------------START EDITING BELOW:----------------------- - var MockID5Forwarder = function() { + var MockID5 = function() { var self = this; // create properties for each type of event you want tracked, see below for examples @@ -114,6 +49,7 @@ describe('ID5 Forwarder', function () { this.logPurchaseEventCalled = false; this.isInitialized = false; this.initCalled = false; + this.numberOfInitsCalled = 0; this.getUserIdCalled = false; this.pd = null; @@ -123,19 +59,22 @@ describe('ID5 Forwarder', function () { // stub your different methods to ensure they are being called properly this.init = function(id5Options) { self.initCalled = true; + self.numberOfInitsCalled += 1; self.partnerId = id5Options.partnerId; self.pd = id5Options.pd return this; }; this.onAvailable = function(callback) { - return callback + return callback(self) }; this.getUserId = function() { self.getUserIdCalled = true; return 'ID5*testtesttesttest' } + + return self; }; before(function () { @@ -143,41 +82,63 @@ describe('ID5 Forwarder', function () { }); beforeEach(function() { - window.ID5 = new MockID5Forwarder(); + window.ID5 = new MockID5(); // Include any specific settings that is required for initializing your SDK here var sdkSettings = { partnerId: 1234, }; - // You may require userAttributes or userIdentities to be passed into initialization - var userAttributes = { - color: 'green' - }; - - var userIdentities = [{ - Identity: 'customerId', - Type: IdentityType.CustomerId - }, { - Identity: 'email', - Type: IdentityType.Email - }, { - Identity: 'mobile_number', - Type: IdentityType.MobileNumber - }]; - // The third argument here is a boolean to indicate that the integration is in test mode to avoid loading any third party scripts. Do not change this value. - mParticle.forwarder.init(sdkSettings, reportService.cb, true, null, userAttributes, userIdentities); + mParticle.forwarder.init(sdkSettings, reportService.cb, true); }); - it ('should load the script into the document during initialization', function(done) { - window.ID5 = new MockID5Forwarder(); - mParticle.forwarder.init({ - partnerId: 1234, + describe('Initialization', function() { + it('should call ID5.init', function(done) { + window.ID5.initCalled.should.equal(true); + done(); }); - document.scripts[0].src.should.equal('https://cdn.id5-sync.com/api/1.0/id5-api.js'); - done(); - }); + + it('should call getUserId', function(done) { + window.ID5.getUserIdCalled.should.equal(true); + done(); + }); + }) + + describe('Identity Handling', function() { + it('should call ID5.init twice on userLoginComplete', function(done){ + var user = { + getUserIdentities: function () { + return { + userIdentities: { + email: 'test@email.com', + }, + }; + }, + }; + mParticle.forwarder.onLoginComplete(user); + + window.ID5.numberOfInitsCalled.should.equal(2); + done(); + }); + + it('should call ID5.init twice on userLogoutComplete', function(done){ + var user = { + getUserIdentities: function () { + return { + userIdentities: { + email: 'test@email.com', + }, + }; + }, + }; + mParticle.forwarder.onLoginComplete(user); + + window.ID5.numberOfInitsCalled.should.equal(2); + done(); + }); + }) + describe('Common Functions', function() { it ('should log a user attribute when logId5 is called', function(done) { From e4b6789361f1578b7368a89191a65b02d23d695c Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:13:35 -0700 Subject: [PATCH 17/25] Finalize logout complete functionality --- test/src/tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/tests.js b/test/src/tests.js index 5b85e20..0da60e0 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -132,7 +132,7 @@ describe('ID5 Forwarder', function () { }; }, }; - mParticle.forwarder.onLoginComplete(user); + mParticle.forwarder.onLogoutComplete(user); window.ID5.numberOfInitsCalled.should.equal(2); done(); From 891bf5642a54ef7448054244877cbc8e7a1ec304 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Tue, 8 Oct 2024 09:37:04 -0700 Subject: [PATCH 18/25] Refactor code for to remove uneccesary variable and update workflows --- .github/workflows/reusable-workflows.yml | 23 +++++++++++++---------- src/common.js | 13 +++++-------- src/event-handler.js | 3 +-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/.github/workflows/reusable-workflows.yml b/.github/workflows/reusable-workflows.yml index a733811..95183a5 100644 --- a/.github/workflows/reusable-workflows.yml +++ b/.github/workflows/reusable-workflows.yml @@ -1,15 +1,18 @@ name: Reusable Workflows on: - pull_request: + pull_request: jobs: - pr-branch-check-name: - name: Check PR for semantic branch name - uses: mParticle/mparticle-workflows/.github/workflows/pr-branch-check-name.yml@stable - pr-title-check: - name: Check PR for semantic title - uses: mParticle/mparticle-workflows/.github/workflows/pr-title-check.yml@stable - pr-branch-target-gitflow: - name: Check PR for semantic target branch - uses: mParticle/mparticle-workflows/.github/workflows/pr-branch-target-gitflow.yml@stable + web-kit-pull-request: + name: Run Web Kit PR Workflow + uses: mParticle/mparticle-workflows/.github/workflows/web-kit-pull-request.yml@stable + pr-branch-check-name: + name: Check PR for semantic branch name + uses: mParticle/mparticle-workflows/.github/workflows/pr-branch-check-name.yml@stable + pr-title-check: + name: Check PR for semantic title + uses: mParticle/mparticle-workflows/.github/workflows/pr-title-check.yml@stable + pr-branch-target-gitflow: + name: Check PR for semantic target branch + uses: mParticle/mparticle-workflows/.github/workflows/pr-branch-target-gitflow.yml@stable diff --git a/src/common.js b/src/common.js index d814d11..4f4da62 100644 --- a/src/common.js +++ b/src/common.js @@ -6,9 +6,8 @@ Common.prototype.exampleMethod = function () { } Common.prototype.logId5Id = function (id5Id) { - debugger; //Checks and saves ID5 ID if it is new - if (id5Id != this.id5Id) { + if (id5Id !== this.id5Id) { this.id5Id = id5Id; this.id5IdSent = false } @@ -16,12 +15,12 @@ Common.prototype.logId5Id = function (id5Id) { //Sets user attribute if ID is unsent. //This function will be updated once the decryption architecture is finalized. //The ID may need to be sent as custom event. - if (this.id5IdSent == false){ + if (this.id5IdSent === false){ var currentUser = mParticle.Identity.getCurrentUser(); currentUser.setUserAttribute('ID5ID', id5Id); this.id5IdSent = true; } -}.bind(this); +}; Common.prototype.buildPartnerData = function (mParticleUser) { var pdKeys = {}; @@ -29,14 +28,12 @@ Common.prototype.buildPartnerData = function (mParticleUser) { var email = userIdentities.userIdentities['email']; if (email) { - var processedEmail = SHA256(this.normalizeEmail(email)); - pdKeys[1] = processedEmail; + pdKeys[1] = SHA256(this.normalizeEmail(email)); } var phone = userIdentities.userIdentities['mobile_number']; if (phone) { - var processedPhone = SHA256(this.normalizePhone(phone)); - pdKeys[2] = processedPhone; + pdKeys[2]= SHA256(this.normalizePhone(phone)); } var pdRaw = Object.keys(pdKeys).map(function(key){ diff --git a/src/event-handler.js b/src/event-handler.js index cf83833..484eca3 100644 --- a/src/event-handler.js +++ b/src/event-handler.js @@ -16,8 +16,7 @@ A non-ecommerce event has the following schema: function EventHandler(common) { this.common = common || {}; } -EventHandler.prototype.logEvent = function() { -}; +EventHandler.prototype.logEvent = function() {}; EventHandler.prototype.logError = function() { // The schema for a logError event is the same, but noteworthy differences are as follows: From 6f42b7eeed5ff4c323c633122b24ab8c1c53271d Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Tue, 8 Oct 2024 09:42:23 -0700 Subject: [PATCH 19/25] Remove unused variables --- src/identity-handler.js | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/identity-handler.js b/src/identity-handler.js index 6051819..dd70ea1 100644 --- a/src/identity-handler.js +++ b/src/identity-handler.js @@ -21,11 +21,9 @@ For more userIdentity types, see https://docs.mparticle.com/developers/sdk/web/i function IdentityHandler(common) { this.common = common || {}; } -IdentityHandler.prototype.onUserIdentified = function(mParticleUser) {}; -IdentityHandler.prototype.onIdentifyComplete = function( - mParticleUser, - identityApiRequest -) {}; +IdentityHandler.prototype.onUserIdentified = function() {}; +IdentityHandler.prototype.onIdentifyComplete = function() {}; + IdentityHandler.prototype.onLoginComplete = function( mParticleUser ) { @@ -37,9 +35,8 @@ IdentityHandler.prototype.onLoginComplete = function( logId5Id(status.getUserId()); }.bind(logId5Id)); }; + IdentityHandler.prototype.onLogoutComplete = function( - mParticleUser, - identityApiRequest ) { var id5Instance = window.ID5.init({partnerId: this.common.partnerId}) var logId5Id = this.common.logId5Id; @@ -48,19 +45,13 @@ IdentityHandler.prototype.onLogoutComplete = function( logId5Id(status.getUserId()); }.bind(logId5Id)); }; -IdentityHandler.prototype.onModifyComplete = function( - mParticleUser, - identityApiRequest -) {}; + +IdentityHandler.prototype.onModifyComplete = function() {}; /* In previous versions of the mParticle web SDK, setting user identities on kits is only reachable via the onSetUserIdentity method below. We recommend filling out `onSetUserIdentity` for maximum compatibility */ -IdentityHandler.prototype.onSetUserIdentity = function( - forwarderSettings, - id, - type -) {}; +IdentityHandler.prototype.onSetUserIdentity = function() {}; module.exports = IdentityHandler; From 69869ddd42b4751be5c402d7e1708cf1ce604860 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Tue, 8 Oct 2024 09:43:35 -0700 Subject: [PATCH 20/25] remove unused variables --- src/commerce-handler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commerce-handler.js b/src/commerce-handler.js index d0a092e..f225d27 100644 --- a/src/commerce-handler.js +++ b/src/commerce-handler.js @@ -2,7 +2,7 @@ function CommerceHandler(common) { this.common = common || {}; } -CommerceHandler.prototype.logCommerceEvent = function(event) { +CommerceHandler.prototype.logCommerceEvent = function() { /* Sample ecommerce event schema: { From b5302b746b189b82159465c7b33bb070b0cd6f5e Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Tue, 8 Oct 2024 09:48:52 -0700 Subject: [PATCH 21/25] Remove unused variables and adjust spacing --- src/session-handler.js | 6 +++--- src/user-attribute-handler.js | 17 +++-------------- test/src/tests.js | 4 ++-- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/session-handler.js b/src/session-handler.js index 96d5bf4..c66ef86 100644 --- a/src/session-handler.js +++ b/src/session-handler.js @@ -1,8 +1,8 @@ var sessionHandler = { - onSessionStart: function(event) { - + onSessionStart: function() { + }, - onSessionEnd: function(event) { + onSessionEnd: function() { } }; diff --git a/src/user-attribute-handler.js b/src/user-attribute-handler.js index 59c55f3..efb221b 100644 --- a/src/user-attribute-handler.js +++ b/src/user-attribute-handler.js @@ -10,19 +10,8 @@ For any additional methods, see http://docs.mparticle.com/developers/sdk/javascr function UserAttributeHandler(common) { this.common = common || {}; } -UserAttributeHandler.prototype.onRemoveUserAttribute = function( - key, - mParticleUser -) {}; -UserAttributeHandler.prototype.onSetUserAttribute = function( - key, - value, - mParticleUser -) {}; -UserAttributeHandler.prototype.onConsentStateUpdated = function( - oldState, - newState, - mParticleUser -) {}; +UserAttributeHandler.prototype.onRemoveUserAttribute = function() {}; +UserAttributeHandler.prototype.onSetUserAttribute = function() {}; +UserAttributeHandler.prototype.onConsentStateUpdated = function() {}; module.exports = UserAttributeHandler; diff --git a/test/src/tests.js b/test/src/tests.js index 0da60e0..d6db125 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -20,8 +20,8 @@ describe('ID5 Forwarder', function () { reportService = new ReportingService(); // -------------------DO NOT EDIT ANYTHING ABOVE THIS LINE----------------------- -// -------------------START EDITING BELOW:----------------------- -// -------------------mParticle stubs - Add any additional stubbing to our methods as needed----------------------- + // -------------------START EDITING BELOW:----------------------- + // -------------------mParticle stubs - Add any additional stubbing to our methods as needed----------------------- var userAttributes = {}; mParticle.Identity = { From 07ddbb398df3c7cd015aa1eeb74e5643badcde7a Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:08:11 -0700 Subject: [PATCH 22/25] Remove replaceAll --- src/common.js | 21 +++++++++++++-------- src/event-handler.js | 1 - 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/common.js b/src/common.js index 4f4da62..f7bbee4 100644 --- a/src/common.js +++ b/src/common.js @@ -51,9 +51,7 @@ Common.prototype.normalizeEmail = function(email) { return email; } - charactersToRemove.forEach(function(character) { - parts[0] = parts[0].replaceAll(character, '').toLowerCase(); - }) + parts[0]= replace(parts[0], charactersToRemove); return parts.join('@'); } @@ -61,11 +59,18 @@ Common.prototype.normalizeEmail = function(email) { Common.prototype.normalizePhone = function(phone) { var charactersToRemove = [' ', '-', '(', ')'] - charactersToRemove.forEach(function(character) { - phone = phone.replaceAll(character, ''); - }) - - return phone; + return replace(phone, charactersToRemove); } +function replace(string, targets) { + debugger; + var newString = ''; + for(var i = 0; i < string.length; i++){ + var char = string[i]; + if (!targets.includes(char)){ + newString += char + } + } + return newString.toLowerCase(); +} module.exports = Common; diff --git a/src/event-handler.js b/src/event-handler.js index 484eca3..e6cb645 100644 --- a/src/event-handler.js +++ b/src/event-handler.js @@ -17,7 +17,6 @@ function EventHandler(common) { this.common = common || {}; } EventHandler.prototype.logEvent = function() {}; - EventHandler.prototype.logError = function() { // The schema for a logError event is the same, but noteworthy differences are as follows: // { From edb93c32543e358330231c21f3c7b83194a11a9e Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:29:46 -0700 Subject: [PATCH 23/25] Refactor and increase testing --- src/common.js | 51 ++++++++++++--- src/identity-handler.js | 15 +++-- src/initialization.js | 4 +- test/src/tests.js | 136 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 188 insertions(+), 18 deletions(-) diff --git a/src/common.js b/src/common.js index f7bbee4..bf292fb 100644 --- a/src/common.js +++ b/src/common.js @@ -26,14 +26,19 @@ Common.prototype.buildPartnerData = function (mParticleUser) { var pdKeys = {}; var userIdentities = mParticleUser.getUserIdentities(); - var email = userIdentities.userIdentities['email']; + var email = this.normalizeEmail(userIdentities.userIdentities['email']); + var phone = this.normalizePhone(userIdentities.userIdentities['mobile_number']); + + if (!email && !phone) { + return null; + } + if (email) { - pdKeys[1] = SHA256(this.normalizeEmail(email)); + pdKeys[1] = SHA256(email); } - var phone = userIdentities.userIdentities['mobile_number']; if (phone) { - pdKeys[2]= SHA256(this.normalizePhone(phone)); + pdKeys[2]= SHA256(phone); } var pdRaw = Object.keys(pdKeys).map(function(key){ @@ -44,6 +49,9 @@ Common.prototype.buildPartnerData = function (mParticleUser) { } Common.prototype.normalizeEmail = function(email) { + if (!email || !this.validateEmail(email)) { + return null; + } var parts = email.split("@") var charactersToRemove = ['+', '.'] @@ -51,19 +59,29 @@ Common.prototype.normalizeEmail = function(email) { return email; } - parts[0]= replace(parts[0], charactersToRemove); + parts[0]= this.replace(parts[0], charactersToRemove); return parts.join('@'); } Common.prototype.normalizePhone = function(phone) { + if (!phone) { + return null; + } var charactersToRemove = [' ', '-', '(', ')'] + var normalizedPhone = this.replace(phone, charactersToRemove); + + if (normalizedPhone[0] !== '+') { + normalizedPhone = '+' + normalizedPhone + } - return replace(phone, charactersToRemove); + if (!this.validatePhone(normalizedPhone)) { + return null; + } + return normalizedPhone; } -function replace(string, targets) { - debugger; +Common.prototype.replace = function(string, targets) { var newString = ''; for(var i = 0; i < string.length; i++){ var char = string[i]; @@ -73,4 +91,21 @@ function replace(string, targets) { } return newString.toLowerCase(); } + +Common.prototype.validateEmail = function(email){ + if (!email) { + return false; + } + var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); +} + +Common.prototype.validatePhone = function(phone) { + if (!phone){ + return false; + } + var e164Regex = /^\+?[1-9]\d{1,14}$/; + + return e164Regex.test(phone); +} module.exports = Common; diff --git a/src/identity-handler.js b/src/identity-handler.js index dd70ea1..21cbb8c 100644 --- a/src/identity-handler.js +++ b/src/identity-handler.js @@ -24,18 +24,23 @@ function IdentityHandler(common) { IdentityHandler.prototype.onUserIdentified = function() {}; IdentityHandler.prototype.onIdentifyComplete = function() {}; +//Must re-initialize ID5 with partner identities(pd) in the config file on login IdentityHandler.prototype.onLoginComplete = function( mParticleUser ) { var partnerData = this.common.buildPartnerData(mParticleUser); - var id5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) - var logId5Id = this.common.logId5Id; - id5Instance.onAvailable(function(status){ - logId5Id(status.getUserId()); - }.bind(logId5Id)); + if (partnerData) { + var id5Instance = window.ID5.init({partnerId: this.common.partnerId, pd: partnerData}) + var logId5Id = this.common.logId5Id; + + id5Instance.onAvailable(function(status){ + logId5Id(status.getUserId()); + }.bind(logId5Id)); + } }; +//Must re-initialize ID5 without partner identities (pd) in the config on logout complete IdentityHandler.prototype.onLogoutComplete = function( ) { var id5Instance = window.ID5.init({partnerId: this.common.partnerId}) diff --git a/src/initialization.js b/src/initialization.js index bc54d91..aedb3fc 100644 --- a/src/initialization.js +++ b/src/initialization.js @@ -16,9 +16,9 @@ var initialization = { /* Load your Web SDK here using a variant of your snippet from your readme that your customers would generally put into their tags Generally, our integrations create script tags and append them to the . Please follow the following format as a guide: */ - + //ID5 docs on initialization can be found here: https://github.com/id5io/id5-api.js/blob/master/README.md var id5Script = document.createElement('script'); - id5Script.src = 'https://cdn.id5-sync.com/api/1.0/id5-api.js'; // <---- Update this to be your script + id5Script.src = 'https://cdn.id5-sync.com/api/1.0/id5-api.js'; (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(id5Script); common.id5Id = null; diff --git a/test/src/tests.js b/test/src/tests.js index d6db125..ecbf72b 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -122,6 +122,22 @@ describe('ID5 Forwarder', function () { done(); }); + it('should call ID5.init only once on userLoginComplete with a user without email or phone', function(done){ + var user = { + getUserIdentities: function () { + return { + userIdentities: { + customerId: 'testId1234', + }, + }; + }, + }; + mParticle.forwarder.onLoginComplete(user); + + window.ID5.numberOfInitsCalled.should.equal(1); + done(); + }) + it('should call ID5.init twice on userLogoutComplete', function(done){ var user = { getUserIdentities: function () { @@ -157,7 +173,7 @@ describe('ID5 Forwarder', function () { return { userIdentities: { email: 'test@email.com', - phone: '123-456-7890', + mobile_number: '123-456-7890', } } }, @@ -166,10 +182,82 @@ describe('ID5 Forwarder', function () { var pd = mParticle.forwarder.common.buildPartnerData(user) pd.should.exist; - pd.should.equal('MT03MzA2MmQ4NzI5MjZjMmE1NTZmMTdiMzZmNTBlMzI4ZGRmOWJmZjlkNDAzOTM5YmQxNGI2YzNiN2Y1YTMzZmMy') done(); }); + it ('should build null pd when build PartnerData is called with an empty users', function(done){ + var user = { + getUserIdentities: function() { + return { + userIdentities: {} + } + }, + }; + var pd = mParticle.forwarder.common.buildPartnerData(user) + debugger; + expect(pd).to.be.null; + done(); + }); + + it ('should return null pd when buildPartnerData is called with a user only having an invalid phone number', function(done) { + var user = { + getUserIdentities: function() { + return { + userIdentities: { + mobile_number: '1234567890112233' + } + } + }, + } + var pd = mParticle.forwarder.common.buildPartnerData(user) + + expect(pd).to.be.null; + done(); + }); + + it ('should return null pd when buildPartnerData is called with a user only having an invalid email', function(done) { + var user = { + getUserIdentities: function() { + return { + userIdentities: { + email: 'test@test@test.com' + } + } + }, + } + var pd = mParticle.forwarder.common.buildPartnerData(user) + + expect(pd).to.be.null; + done(); + }); + + it ('should omit invalid identities when building PD when buildPartnerData is called', function(done) { + var user1 = { + getUserIdentities: function() { + return { + userIdentities: { + email: 'test@test@test.com', //note: invalid email address + mobile_number: '123-456-7890', + } + } + }, + } + var user2 = { + getUserIdentities: function() { + return { + userIdentities: { + mobile_number: '123-456-7890', + } + } + }, + } + var pd1 = mParticle.forwarder.common.buildPartnerData(user1); + var pd2 = mParticle.forwarder.common.buildPartnerData(user2); + + pd1.should.equal(pd2); + done(); + }) + it ('should normalize an gmail when normalizeEmail is called', function(done) { var normalizedGmail = mParticle.forwarder.common.normalizeEmail('test+test.2@gmail.com'); @@ -186,11 +274,53 @@ describe('ID5 Forwarder', function () { done(); }); + it ('should return null when normalizeEmail is called with a null value', function(done){ + var normalized = mParticle.forwarder.common.normalizeEmail() + + expect(normalized).to.be.null; + done(); + }); + it ('should normalize phone numbers when normalizePhone is called', function(done) { var normalizedPhone = mParticle.forwarder.common.normalizePhone('(123) 456-7890'); normalizedPhone.should.exist; - normalizedPhone.should.equal('1234567890'); + normalizedPhone.should.equal('+1234567890'); + done(); + }); + + it ('should return null when normalizePhone is called with a null value', function(done){ + var normalizedPhone = mParticle.forwarder.common.normalizePhone(); + + expect(normalizedPhone).to.be.null; + done(); + }); + + it ('should return true when a valid e.164 phone number is passed to validatePhone', function(done) { + var validated = mParticle.forwarder.common.validatePhone('+1234567890'); + + validated.should.equal(true); + done(); + }); + + it ('should return false when an invalid e.164 phone number is passed to validatePhone', function(done) { + var validated = mParticle.forwarder.common.validatePhone('1234567890112233'); + + validated.should.equal(false); + done(); + }); + + it ('should return true when a valid email is passed to validateEmail', function(done) { + var validated = mParticle.forwarder.common.validateEmail('test@test.com'); + + validated.should.equal(true); + done(); + }) + + it ('should return false when an invalid email is passed to validateEmail', function(done) { + var validated = mParticle.forwarder.common.validateEmail('test@test@test.com') + + validated.should.equal(false); done(); }) }) From 8d0c3293014f59439ca57ab85fb1cac60b2e244e Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:34:42 -0700 Subject: [PATCH 24/25] Update comments for clarity --- src/identity-handler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/identity-handler.js b/src/identity-handler.js index 21cbb8c..477025f 100644 --- a/src/identity-handler.js +++ b/src/identity-handler.js @@ -24,7 +24,7 @@ function IdentityHandler(common) { IdentityHandler.prototype.onUserIdentified = function() {}; IdentityHandler.prototype.onIdentifyComplete = function() {}; -//Must re-initialize ID5 with partner identities(pd) in the config file on login +//Must re-initialize ID5 with partner identities(pd) in the config to collect an updated ID5 ID IdentityHandler.prototype.onLoginComplete = function( mParticleUser ) { @@ -40,7 +40,7 @@ IdentityHandler.prototype.onLoginComplete = function( } }; -//Must re-initialize ID5 without partner identities (pd) in the config on logout complete +//Must re-initialize ID5 without partner identities (pd) in the config to revert to an anonymous ID5 ID IdentityHandler.prototype.onLogoutComplete = function( ) { var id5Instance = window.ID5.init({partnerId: this.common.partnerId}) From a49ce15b6cf10c610e8f0c925f25001dd2c1c368 Mon Sep 17 00:00:00 2001 From: SbDove <100377552+SbDove@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:54:49 -0700 Subject: [PATCH 25/25] Removing logging of the ID5 ID untill architecuture finalized --- src/common.js | 8 ++------ test/src/tests.js | 10 ---------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/common.js b/src/common.js index bf292fb..a84a4f2 100644 --- a/src/common.js +++ b/src/common.js @@ -14,12 +14,8 @@ Common.prototype.logId5Id = function (id5Id) { //Sets user attribute if ID is unsent. //This function will be updated once the decryption architecture is finalized. - //The ID may need to be sent as custom event. - if (this.id5IdSent === false){ - var currentUser = mParticle.Identity.getCurrentUser(); - currentUser.setUserAttribute('ID5ID', id5Id); - this.id5IdSent = true; - } + //TO-DO: Log the ID5 ID to correct location + }; Common.prototype.buildPartnerData = function (mParticleUser) { diff --git a/test/src/tests.js b/test/src/tests.js index ecbf72b..fde1021 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -157,16 +157,6 @@ describe('ID5 Forwarder', function () { describe('Common Functions', function() { - it ('should log a user attribute when logId5 is called', function(done) { - mParticle.forwarder.common.logId5Id("testId"); - - var attributes = mParticle.Identity.getCurrentUser().getAllUserAttributes() - - attributes.ID5ID.should.exist; - attributes.ID5ID.should.equal("testId") - done(); - }); - it ('should build pd when buildPartnerData is called with a user', function(done) { var user = { getUserIdentities: function() {