From a5a45b46fe44595962eb0135a7a75add752da0dc Mon Sep 17 00:00:00 2001 From: Hannes Petersen Date: Thu, 27 Jul 2017 07:57:22 +0200 Subject: [PATCH 1/6] fixed wrong type "Any" notation to "any". Furthermore changed all general types (Boolean, Number, String) to the primitive ones (boolean, number, string), see https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html --- typescript/analytics.d.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/typescript/analytics.d.ts b/typescript/analytics.d.ts index db9a2810..56049909 100644 --- a/typescript/analytics.d.ts +++ b/typescript/analytics.d.ts @@ -2,19 +2,19 @@ declare class UniversalAnalyticsPlugin { /** In your 'deviceready' handler, call this to set up your Analytics tracker, where id is your Google Analytics Mobile App property */ - public startTrackerWithId(id:String, dispatchPeriod?:Number, successCallback?:Function, errorCallback?:Function):void; + public startTrackerWithId(id:string, dispatchPeriod?:number, successCallback?:Function, errorCallback?:Function):void; /** Sets a UserId */ - public setUserId(id:String, successCallback?:Function, errorCallback?:Function):void; + public setUserId(id:string, successCallback?:Function, errorCallback?:Function):void; /** Sets a setAnonymizeIp */ - public setAnonymizeIp(anonymize:Boolean, successCallback?:Function, errorCallback?:Function):void; + public setAnonymizeIp(anonymize:boolean, successCallback?:Function, errorCallback?:Function):void; /** Sets a setOptOut */ - public setOptOut(optout:Boolean, successCallback?:Function, errorCallback?:Function): void; + public setOptOut(optout:boolean, successCallback?:Function, errorCallback?:Function): void; /** Sets a setAllowIDFACollection */ - public setAllowIDFACollection(enable:Boolean, successCallback?:Function, errorCallback?:Function):void; + public setAllowIDFACollection(enable:boolean, successCallback?:Function, errorCallback?:Function):void; /** Sets a AppVersion */ public setAppVersion(version:string, successCallback?:Function, errorCallback?:Function):void; @@ -35,37 +35,37 @@ declare class UniversalAnalyticsPlugin { public trackMetric(key:number, value?:number, successCallback?:Function, errorCallback?:Function):void; /** Track a Screen (PageView) */ - public trackView(screen:String, campaignUrl?:string, newSession?:boolean, successCallback?:Function, errorCallback?:Function):void; + public trackView(screen:string, campaignUrl?:string, newSession?:boolean, successCallback?:Function, errorCallback?:Function):void; /** Add a Custom Dimension */ - public addCustomDimension(key:number, value:String, successCallback?:Function, errorCallback?:Function):void; + public addCustomDimension(key:number, value:string, successCallback?:Function, errorCallback?:Function):void; /** Track an Event */ - public trackEvent(category:String, action:String, label?:String, value?:Number, newSession?:boolean, successCallback?:Function, errorCallback?:Function):void; + public trackEvent(category:string, action:string, label?:string, value?:number, newSession?:boolean, successCallback?:Function, errorCallback?:Function):void; /** Track an Exception https://developers.google.com/analytics/devguides/collection/android/v3/exceptions */ - public trackException(description:String, fatal:Boolean, successCallback?:Function, errorCallback?:Function):void; + public trackException(description:string, fatal:boolean, successCallback?:Function, errorCallback?:Function):void; /** Enable/disable automatic reporting of uncaught exceptions */ - public enableUncaughtExceptionReporting(enable:Boolean, successCallback?:Function, errorCallback?:Function):void; + public enableUncaughtExceptionReporting(enable:boolean, successCallback?:Function, errorCallback?:Function):void; /** Track User Timing (App Speed) */ - public trackTiming(category:String, intervalInMilliseconds?:Number, name?:String, label?:String, successCallback?:Function, errorCallback?:Function):void; + public trackTiming(category:string, intervalInMilliseconds?:number, name?:string, label?:string, successCallback?:Function, errorCallback?:Function):void; // Deprecated on 1.9.0 will be removed on next minor version (1.10.0). /** Add a Transaction (Google Analytics e-Ccommerce Tracking) https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce */ - public addTransaction(transactionId:String, affiliation:String, revenue:Number, tax:Number, shipping:Number, currencyCode:String, successCallback?:Function, errorCallback?:Function):void; + public addTransaction(transactionId:string, affiliation:string, revenue:number, tax:number, shipping:number, currencyCode:string, successCallback?:Function, errorCallback?:Function):void; // Deprecated on 1.9.0 will be removed on next minor version (1.10.0). /** Add a Transaction Item (Ecommerce) */ - public addTransactionItem(transactionId:String, name:String, sku:String, category:String, price:Number, quantity:Number, currencyCode:String, successCallback?:Function, errorCallback?:Function):void; + public addTransactionItem(transactionId:string, name:string, sku:string, category:string, price:number, quantity:number, currencyCode:string, successCallback?:Function, errorCallback?:Function):void; - public addImpression(screamName:String, product:Any, successCallback?:Function, errorCallback?:Function):void; + public addImpression(screamName:string, product:any, successCallback?:Function, errorCallback?:Function):void; - public productAction(screamName:String, product:Any, successCallback?:Function, errorCallback?:Function):void; + public productAction(screamName:string, product:any, successCallback?:Function, errorCallback?:Function):void; - public addPromotion(action:String, promotion:Any, label?:String, category?:String, successCallback?:Function, errorCallback?:Function):void; + public addPromotion(action:string, promotion:any, label?:string, category?:string, successCallback?:Function, errorCallback?:Function):void; } From f10b97bbec07c0da6202e1087eeeec7d16fc1daf Mon Sep 17 00:00:00 2001 From: Hannes Petersen Date: Thu, 27 Jul 2017 08:02:51 +0200 Subject: [PATCH 2/6] fixed parameter name from "screamName" to "screen"; added rethink note about parameter order. --- www/analytics.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/www/analytics.js b/www/analytics.js index 80af0707..f16357e9 100644 --- a/www/analytics.js +++ b/www/analytics.js @@ -193,14 +193,18 @@ promotion.name = "mypromo"; promotion.position = "position"; promotion.creative = "creative"; */ -UniversalAnalyticsPlugin.prototype.addImpression = function(screamName, product, success, error) { - cordova.exec(success, error, 'UniversalAnalytics', 'addImpression', [screamName, product]); + +UniversalAnalyticsPlugin.prototype.addImpression = function(screen, product, success, error) { + cordova.exec(success, error, 'UniversalAnalytics', 'addImpression', [screen, product]); }; -UniversalAnalyticsPlugin.prototype.productAction = function(screamName, product, productAction, success, error) { - cordova.exec(success, error, 'UniversalAnalytics', 'productAction', [screamName, product, productAction]); +UniversalAnalyticsPlugin.prototype.productAction = function(screen, product, productAction, success, error) { + cordova.exec(success, error, 'UniversalAnalytics', 'productAction', [screen, product, productAction]); }; +// RETHINK: swap label and category parameter positions? +// (as an event hit is formed under the hood, category is mandatory while label is optional) + UniversalAnalyticsPlugin.prototype.addPromotion = function(action, promotion, label, category, success, error) { cordova.exec(success, error, 'UniversalAnalytics', 'addPromotion', [action, promotion, label, category]); }; From be924a90f123d4d1257e2c93fe7fe4612fda3d61 Mon Sep 17 00:00:00 2001 From: Hannes Petersen Date: Thu, 27 Jul 2017 08:03:45 +0200 Subject: [PATCH 3/6] fixed parameter name "screamName" to "screen" --- typescript/analytics.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/analytics.d.ts b/typescript/analytics.d.ts index 56049909..a6c7ba13 100644 --- a/typescript/analytics.d.ts +++ b/typescript/analytics.d.ts @@ -62,9 +62,9 @@ declare class UniversalAnalyticsPlugin { /** Add a Transaction Item (Ecommerce) */ public addTransactionItem(transactionId:string, name:string, sku:string, category:string, price:number, quantity:number, currencyCode:string, successCallback?:Function, errorCallback?:Function):void; - public addImpression(screamName:string, product:any, successCallback?:Function, errorCallback?:Function):void; + public addImpression(screen:string, product:any, successCallback?:Function, errorCallback?:Function):void; - public productAction(screamName:string, product:any, successCallback?:Function, errorCallback?:Function):void; + public productAction(screen:string, product:any, successCallback?:Function, errorCallback?:Function):void; public addPromotion(action:string, promotion:any, label?:string, category?:string, successCallback?:Function, errorCallback?:Function):void; From 99681ffadb1482c4a2d08c564c58f095c9ef7c07 Mon Sep 17 00:00:00 2001 From: Hannes Petersen Date: Thu, 27 Jul 2017 08:04:55 +0200 Subject: [PATCH 4/6] fixed parameter name "screamName" to "screen"; added descriptions to ECommerce on Windows platform. --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0a75e3da..a9a5bc7c 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ window.ga.enableUncaughtExceptionReporting(Enable, success, error)// where Enabl Just to send product impressions */ -window.ga.addImpression(screamName, product) +window.ga.addImpression(screen, product) /** Product Action definitions: @@ -182,7 +182,7 @@ window.ga.addImpression(screamName, product) Enhanced Ecommerce Tracking https://developers.google.com/analytics/devguides/collection/android/v4/enhanced-ecommerce */ -window.ga.productAction(screamName, product, productAction) +window.ga.productAction(screen, product, productAction) /**Promotion definitions: action field: @@ -295,10 +295,17 @@ The following plugin methods are (currently) not supported by the UWP.SDKforGoog * `setAllowIDFACollection()` * `addTransaction()` * `addTransactionItem()` +* `addImpression()` Unexpected behaviour may occur on the following methods: * `trackView()`: campaign details are currently not supported and therefore not tracked. * `trackMetric()`: there is currently a bug in version 1.5.2 of the [UWP.SDKforGoogleAnalytics.Native package via NuGet](http://nuget.org/packages/UWP.SDKforGoogleAnalytics.Native), that the wrong data specifier `cd` is taken for metrics, whereas `cm` should be the correct specifier. -So as long as this bug is not fixed, trackMetrics will overwrite previous addCustomDimension with same index!! +So as long as this bug is not fixed, trackMetrics() will overwrite previous addCustomDimension with same index!! +* `productAction()`: there is currently a bug in version 1.5.2 of the [UWP.SDKforGoogleAnalytics.Native package via NuGet](http://nuget.org/packages/UWP.SDKforGoogleAnalytics.Native), +that the wrong data specifier `at` is taken for product quantity, whereas `qt` should be the correct specifier. +So as long as this bug is not fixed, productAction() will possibly not work correctly!! + +Pull Requests are already submitted. As long as these are not merged, you can clone the Fork from https://github.com/spacepope/windows-sdk-for-google-analytics and add the corresponding project reference to your UWP project instead of installing the current nuget package.. + From e8b54a3277fe6ea636174e06d3233c1f977150a4 Mon Sep 17 00:00:00 2001 From: Hannes Petersen Date: Thu, 27 Jul 2017 08:06:03 +0200 Subject: [PATCH 5/6] added enhanced ECommerce support for addPromotion() and productAction(); addImpression currently not supported. --- windows/GoogleAnalyticsProxy.js | 172 +++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 4 deletions(-) diff --git a/windows/GoogleAnalyticsProxy.js b/windows/GoogleAnalyticsProxy.js index 12a7bb93..157fb1c2 100644 --- a/windows/GoogleAnalyticsProxy.js +++ b/windows/GoogleAnalyticsProxy.js @@ -37,19 +37,21 @@ function getTracker() { // extended debug support function onHitMalformed(args) { - console.warn("**hit malformed** \n" + args.httpStatusCode, parseHit(args.hit)); + console.warn("**Google Analytics hit malformed**\n" + args.httpStatusCode); + console.warn("Hit data:\n", parseHit(args.hit)); } function onHitFailed(args) { - console.error("**hit failed**\n" + args.error.message, parseHit(args.hit)); + console.error("**Google Analytics hit failed**\n" + args.error.message); + console.error("Hit data:\n", parseHit(args.hit)); } function onHitSent(args) { - console.log("Analytics result: " + args.response, parseHit(args.hit)); + console.log("**Google Analytics hit succeeded**\n" + args.response); + console.log("Hit data:\n", parseHit(args.hit)); } function parseHit(hit) { - var pair; var result = ""; var iter = hit.data.first(); while (iter.hasCurrent) { @@ -285,6 +287,168 @@ module.exports = { fail("not supported on Windows platform"); }, + productAction: function (win, fail, args) { + if (!args || args.length === 0 || args[0] === "") { + fail("Expected non empty string argument"); + return; + } + if (args.length < 2 || typeof args[1] !== "object") { + fail("Expected non object argument"); + return; + } + if (args.length < 3 || typeof args[2] !== "object") { + fail("Expected non object argument"); + return; + } + + var product = new GoogleAnalytics.Ecommerce.Product(); + product.id = args[1].id; + product.name = args[1].name; + if (typeof args[1].position !== "undefined") { + product.position = args[1].position; + } + if (typeof args[1].price !== "undefined") { + product.price = args[1].price; + } + if (typeof args[1].quantity !== "undefined") { + product.quantity = args[1].quantity; + } + if (typeof args[1].variant !== "undefined") { + product.variant = args[1].variant; + } + if (typeof args[1].brand !== "undefined") { + product.brand = args[1].brand; + } + if (typeof args[1].category !== "undefined") { + product.category = args[1].category; + } + if (typeof args[1].couponCode !== "undefined") { + product.couponCode = args[1].couponCode; + } + // RETHINK! (should support multiple dimensions and metrics..) + if (typeof args[1].customDimension !== "undefined" && args[1].customDimension.length === 2) { + product.customDimensions.insert(args[1].customDimension[0], args[1].customDimension[1]); + } + if (typeof args[1].customMetric !== "undefined" && args[1].customMetric.length === 2) { + product.customMetrics.insert(args[1].customMetric[0], args[1].customMetric[1]); + } + + var action = ""; + switch (args[2].action) { + case "ACTION_ADD": + action = "Add"; + break; + case "ACTION_CHECKOUT": + action = "Checkout"; + break; + case "ACTION_CHECKOUT_OPTION": + case "ACTION_CHECKOUT_OPTIONS": + action = "CheckoutOption"; + break; + case "ACTION_CLICK": + action = "Click"; + break; + case "ACTION_DETAIL": + action = "Detail"; + break; + case "ACTION_PURCHASE": + action = "Purchase"; + break; + case "ACTION_REFUND": + action = "Refund"; + break; + case "ACTION_REMOVE": + action = "Remove"; + break; + } + var action = new GoogleAnalytics.Ecommerce.ProductAction(action); + if (typeof args[2].checkoutOptions !== "undefined") { + action.checkoutOptions = args[2].checkoutOptions; + } + if (typeof args[2].checkoutStep !== "undefined") { + action.checkoutStep = args[2].checkoutStep; + } + if (typeof args[2].transactionAffiliation !== "undefined") { + action.transactionAffiliation = args[2].transactionAffiliation; + } + if (typeof args[2].transactionCouponCode !== "undefined") { + action.transactionCouponCode = args[2].transactionCouponCode; + } + if (typeof args[2].transactionId !== "undefined") { + action.transactionId = args[2].transactionId; + } + if (typeof args[2].transactionRevenue !== "undefined") { + action.transactionRevenue = args[2].transactionRevenue; + } + if (typeof args[2].transactionShipping !== "undefined") { + action.transactionShipping = args[2].transactionShipping; + } + if (typeof args[2].transactionTax !== "undefined") { + action.transactionTax = args[2].transactionTax; + } + + var hit = GoogleAnalytics.HitBuilder.createScreenView(args[0]).addProduct(product).setProductAction(action); + + const data = hit.build(); + getTracker().send(data); + win(); + }, + + addPromotion: function (win, fail, args) { + if (!args || args.length === 0 || args[0] === "") { + fail("Expected non empty string argument"); + return; + } + if (args.length < 2 || typeof args[1] !== "object") { + fail("Expected non object argument"); + return; + } + if (args.length < 3 || args[2] === "") { + fail("Expected non empty string argument"); + return; + } + if (args.length < 4 || args[3] === "") { + fail("Expected non empty string argument"); + return; + } + + var hit = GoogleAnalytics.HitBuilder.createCustomEvent(args[3], args[0], args[2] || null, 0); + + var promotion = new GoogleAnalytics.Ecommerce.Promotion(); + promotion.id = args[1].id; + promotion.name = args[1].name; + if (typeof args[1].position !== "undefined") { + promotion.position = args[1].position; + } + if (typeof args[1].creative !== "undefined") { + promotion.creative = args[1].creative; + } + + hit = hit.addPromotion(promotion); + + var action = ""; + switch (args[0]) { + case "ACTION_CLICK": + action = "Click"; + break; + case "ACTION_VIEW": + action = "View"; + break; + } + if (action !== "") { + hit = hit.setPromotionAction(action); + } + + const data = hit.build(); + getTracker().send(data); + win(); + }, + + addImpression: function (win, fail, args) { + // not supported + fail("not supported on Windows platform"); + }, + trackView: function (win, fail, args) { if (!args || args.length === 0 || args[0] === "") { fail("Expected non empty string argument"); From c0b285a058d04d2c27ebed14e3f17af38fe724dc Mon Sep 17 00:00:00 2001 From: Hannes Petersen Date: Fri, 28 Jul 2017 17:32:07 +0200 Subject: [PATCH 6/6] Refactorings and additional Typescript definitions. Swapped addPromotion parameters category and label; Also added typescript interfaces for enhanced ecommerce. --- README.md | 2 +- android/UniversalAnalyticsPlugin.java | 4 +-- typescript/analytics.d.ts | 52 +++++++++++++++++++++++++-- windows/GoogleAnalyticsProxy.js | 6 +--- www/analytics.js | 7 ++-- 5 files changed, 55 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a9a5bc7c..12fcb120 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ window.ga.productAction(screen, product, productAction) promotion.position = "position"; promotion.creative = "creative"; */ -window.ga.addPromotion(action, promotion, label, category) +window.ga.addPromotion(action, promotion, category, label) //To add a Transaction (Ecommerce) -- Deprecated on 1.9.0 window.ga.addTransaction('ID', 'Affiliation', Revenue, Tax, Shipping, 'Currency Code')// where Revenue, Tax, and Shipping are numeric diff --git a/android/UniversalAnalyticsPlugin.java b/android/UniversalAnalyticsPlugin.java index 46d3ff24..b2c99c6b 100644 --- a/android/UniversalAnalyticsPlugin.java +++ b/android/UniversalAnalyticsPlugin.java @@ -132,7 +132,7 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo int length = args.length(); this.addPromotion(args.getString(0), args.getJSONObject(1), length > 2 ? args.getString(2) : "", - length > 3 ? args.getString(3) : "",callbackContext); + length > 3 ? args.getString(3) : "", callbackContext); return true; } else if (SET_ALLOW_IDFA_COLLECTION.equals(action)) { this.setAllowIDFACollection(args.getBoolean(0), callbackContext); @@ -588,7 +588,7 @@ private void productAction(String screenName, JSONObject productInput, JSONObjec callbackContext.success("product action executed"); } - private void addPromotion(String action, JSONObject promotionInput, String label, String category, CallbackContext callbackContext) { + private void addPromotion(String action, JSONObject promotionInput, String category, String label, CallbackContext callbackContext) { if (!trackerStarted) { callbackContext.error("Tracker not started"); return; diff --git a/typescript/analytics.d.ts b/typescript/analytics.d.ts index a6c7ba13..32c30b1b 100644 --- a/typescript/analytics.d.ts +++ b/typescript/analytics.d.ts @@ -1,3 +1,49 @@ +export interface Product { + id: string; + name: string; + category?: string; + brand?: string; + variant?: string; + position?: number; + customDimension?: any[]; + customMetric?: any[]; + price?: number; + quantity?: number; + couponCode?: string; +} + +export type ProductActionType = "ACTION_ADD" | + "ACTION_CHECKOUT" | + "ACTION_CHECKOUT_OPTION" | + "ACTION_CHECKOUT_OPTIONS" | + "ACTION_CLICK" | + "ACTION_DETAIL" | + "ACTION_PURCHASE" | + "ACTION_REFUND" | + "ACTION_REMOVE" + +export interface ProductAction { + action: ProductActionType; + transactionId?: string; + transactionRevenue?: number; + transactionCouponCode?: string; + transactionShipping?: number; + transactionTax?: number; + checkoutOptions?: string; + checkoutStep?: number; + transactionAffiliation?: string; +} + +export interface Promotion { + id: string; + name: string; + position?: string; + creative?: string; +} + +export type PromotionAction = "ACTION_CLICK" | + "ACTION_VIEW" + declare class UniversalAnalyticsPlugin { /** In your 'deviceready' handler, call this to set up your Analytics tracker, @@ -62,10 +108,10 @@ declare class UniversalAnalyticsPlugin { /** Add a Transaction Item (Ecommerce) */ public addTransactionItem(transactionId:string, name:string, sku:string, category:string, price:number, quantity:number, currencyCode:string, successCallback?:Function, errorCallback?:Function):void; - public addImpression(screen:string, product:any, successCallback?:Function, errorCallback?:Function):void; + public addImpression(screen:string, product:Product, successCallback?:Function, errorCallback?:Function):void; - public productAction(screen:string, product:any, successCallback?:Function, errorCallback?:Function):void; + public productAction(screen:string, product:Product, productAction: ProductAction, successCallback?:Function, errorCallback?:Function):void; - public addPromotion(action:string, promotion:any, label?:string, category?:string, successCallback?:Function, errorCallback?:Function):void; + public addPromotion(action:PromotionAction, promotion:Promotion, category:string, label?:string, successCallback?:Function, errorCallback?:Function):void; } diff --git a/windows/GoogleAnalyticsProxy.js b/windows/GoogleAnalyticsProxy.js index 157fb1c2..81efcba0 100644 --- a/windows/GoogleAnalyticsProxy.js +++ b/windows/GoogleAnalyticsProxy.js @@ -407,12 +407,8 @@ module.exports = { fail("Expected non empty string argument"); return; } - if (args.length < 4 || args[3] === "") { - fail("Expected non empty string argument"); - return; - } - var hit = GoogleAnalytics.HitBuilder.createCustomEvent(args[3], args[0], args[2] || null, 0); + var hit = GoogleAnalytics.HitBuilder.createCustomEvent(args[2], args[0], args[3] || null, 0); var promotion = new GoogleAnalytics.Ecommerce.Promotion(); promotion.id = args[1].id; diff --git a/www/analytics.js b/www/analytics.js index f16357e9..6b6f9260 100644 --- a/www/analytics.js +++ b/www/analytics.js @@ -202,11 +202,8 @@ UniversalAnalyticsPlugin.prototype.productAction = function(screen, product, pro cordova.exec(success, error, 'UniversalAnalytics', 'productAction', [screen, product, productAction]); }; -// RETHINK: swap label and category parameter positions? -// (as an event hit is formed under the hood, category is mandatory while label is optional) - -UniversalAnalyticsPlugin.prototype.addPromotion = function(action, promotion, label, category, success, error) { - cordova.exec(success, error, 'UniversalAnalytics', 'addPromotion', [action, promotion, label, category]); +UniversalAnalyticsPlugin.prototype.addPromotion = function(action, promotion, category, label, success, error) { + cordova.exec(success, error, 'UniversalAnalytics', 'addPromotion', [action, promotion, category, label]); }; //TODO add promotion impresion