11import { createHash } from "crypto" ;
22import * as fs from "fs" ;
33import * as path from "path" ;
4- import * as qs from "querystring" ;
54import { GoogleAnalyticsParameters } from "../types" ;
65import { App } from "./App" ;
76import { ProjectConfig } from "./ProjectConfig" ;
@@ -14,10 +13,12 @@ class GoogleAnalytics {
1413 protected static userSettings : string = "user-settings.json" ;
1514 protected static appVersion : string ;
1615 protected static npmVersion : string ;
17- protected static trackingID = "UA-392932-23" ;
16+ // GA4 Measurement ID and API Secret
17+ protected static measurementID = "G-XXXXXXXXXX" ; // TODO: Replace with actual GA4 Measurement ID
18+ protected static apiSecret = "XXXXXXXXXXXXXXXXXXXXXXXX" ; // TODO: Replace with actual GA4 API Secret
1819
1920 /**
20- * Generates http post request with provided parameters and sends it to GA
21+ * Generates http post request with provided parameters and sends it to GA4
2122 * @param parameters Object containing all the parameters to send
2223 */
2324 public static post ( parameters : GoogleAnalyticsParameters ) {
@@ -26,46 +27,102 @@ class GoogleAnalytics {
2627 return ;
2728 }
2829
29- // set GA protocol version. This should be 1
30- parameters . v = 1 ;
31-
32- // set the Tracking ID
33- parameters . tid = this . trackingID ;
34-
35- // set application version if not set beforehand
36- if ( ! parameters . av ) {
37- if ( ! this . appVersion ) {
38- this . appVersion = Util . version ( ) ;
39- }
40-
41- parameters . av = this . appVersion ;
30+ // Get application version if not set
31+ if ( ! this . appVersion ) {
32+ this . appVersion = Util . version ( ) ;
4233 }
4334
44- // set application name
45- parameters . an = App . appName ;
46-
47- // set user agent string. We are using this for detecting the user's OS.
48- // as well as node version. The latest is set as browser version.
35+ // Get user agent info
4936 const nodeVersion = process . version ;
5037 const os = this . getOsForUserAgent ( ) ;
5138 const npmVersion = this . getNpmVersion ( ) ;
52- parameters . ua = `node/${ nodeVersion } (${ os } ) npm/${ npmVersion } ` ;
53-
54- // set user ID
55- parameters . uid = this . getUUID ( ) ;
39+ const userAgent = `node/${ nodeVersion } (${ os } ) npm/${ npmVersion } ` ;
40+
41+ // Get client ID (user ID)
42+ const clientId = this . getUUID ( ) ;
43+
44+ // Build GA4 event payload
45+ const eventName = this . mapHitTypeToEventName ( parameters . t || "event" ) ;
46+ const eventParams : any = {
47+ app_name : App . appName ,
48+ app_version : parameters . av || this . appVersion ,
49+ engagement_time_msec : "100" // Required for events
50+ } ;
51+
52+ // Map custom dimensions to event parameters
53+ if ( parameters . cd ) eventParams . screen_name = parameters . cd ;
54+ if ( parameters . ec ) eventParams . event_category = parameters . ec ;
55+ if ( parameters . ea ) eventParams . event_action = parameters . ea ;
56+ if ( parameters . el ) eventParams . event_label = parameters . el ;
57+ if ( parameters . exd ) eventParams . exception_description = parameters . exd ;
58+
59+ // Map custom dimensions
60+ if ( parameters . cd1 ) eventParams . framework = parameters . cd1 ;
61+ if ( parameters . cd2 ) eventParams . project_type = parameters . cd2 ;
62+ if ( parameters . cd3 ) eventParams . project_name = parameters . cd3 ;
63+ if ( parameters . cd4 ) eventParams . action = parameters . cd4 ;
64+ if ( parameters . cd5 ) eventParams . component_group = parameters . cd5 ;
65+ if ( parameters . cd6 ) eventParams . component_name = parameters . cd6 ;
66+ if ( parameters . cd7 ) eventParams . template_name = parameters . cd7 ;
67+ if ( parameters . cd8 ) eventParams . custom_view_name = parameters . cd8 ;
68+ if ( parameters . cd9 ) eventParams . extra_config = parameters . cd9 ;
69+ if ( parameters . cd10 !== undefined ) eventParams . skip_config = parameters . cd10 ;
70+ if ( parameters . cd11 !== undefined ) eventParams . skip_git = parameters . cd11 ;
71+ if ( parameters . cd12 !== undefined ) eventParams . global = parameters . cd12 ;
72+ if ( parameters . cd13 ) eventParams . search_term = parameters . cd13 ;
73+ if ( parameters . cd14 ) eventParams . theme = parameters . cd14 ;
74+
75+ // Build GA4 request payload
76+ const payload = {
77+ client_id : clientId ,
78+ user_properties : {
79+ user_agent : {
80+ value : userAgent
81+ }
82+ } ,
83+ events : [ {
84+ name : eventName ,
85+ params : eventParams
86+ } ]
87+ } ;
88+
89+ const payloadString = JSON . stringify ( payload ) ;
90+ const fullPath = `/mp/collect?measurement_id=${ this . measurementID } &api_secret=${ this . apiSecret } ` ;
91+ const options = {
92+ host : "www.google-analytics.com" ,
93+ path : fullPath ,
94+ method : "POST" ,
95+ headers : {
96+ "Content-Type" : "application/json" ,
97+ "Content-Length" : Buffer . byteLength ( payloadString )
98+ }
99+ } ;
56100
57- // generate http request and sent it to GA
58- const queryString = qs . stringify ( parameters as { } ) ;
59- const fullPath = "/collect?" + queryString ;
60- const options = { host : "www.google-analytics.com" , path : fullPath , method : "POST" } ;
61101 const https = require ( "https" ) ;
62102 const req = https . request ( options ) ;
63103 req . on ( "error" , e => {
64104 // TODO: save all the logs and send them later
65105 } ) ;
106+ req . write ( payloadString ) ;
66107 req . end ( ) ;
67108 }
68109
110+ /**
111+ * Maps Universal Analytics hit types to GA4 event names
112+ */
113+ protected static mapHitTypeToEventName ( hitType : string ) : string {
114+ switch ( hitType ) {
115+ case "screenview" :
116+ return "screen_view" ;
117+ case "event" :
118+ return "cli_event" ;
119+ case "exception" :
120+ return "exception" ;
121+ default :
122+ return "cli_event" ;
123+ }
124+ }
125+
69126 protected static getUUID ( ) : string {
70127 const absolutePath = path . join ( this . userDataFolder , this . appFolder , this . userSettings ) ;
71128 let UUID = "" ;
@@ -74,7 +131,7 @@ class GoogleAnalytics {
74131 } else {
75132 const dirName = path . dirname ( absolutePath ) ;
76133 if ( ! fs . existsSync ( dirName ) ) {
77- fs . mkdirSync ( dirName ) ;
134+ fs . mkdirSync ( dirName , { recursive : true } ) ;
78135 }
79136
80137 UUID = this . getUserID ( ) ;
0 commit comments