From 210fd66a757a36244d6f0bd90440ab0804165986 Mon Sep 17 00:00:00 2001 From: Thorben Primke Date: Wed, 27 May 2020 09:58:33 -0700 Subject: [PATCH 1/6] [expo-firebase-analytics] Updates Regex To Allow Numeric Chars --- packages/expo-firebase-analytics/package.json | 3 +++ .../src/FirebaseAnalyticsJS.ts | 3 ++- .../src/__tests__/FirebaseAnalyticsJS-test.ts | 27 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts diff --git a/packages/expo-firebase-analytics/package.json b/packages/expo-firebase-analytics/package.json index eb0c722b63ecf..b2f645fc526e8 100644 --- a/packages/expo-firebase-analytics/package.json +++ b/packages/expo-firebase-analytics/package.json @@ -32,6 +32,9 @@ "author": "650 Industries, Inc.", "license": "MIT", "homepage": "https://github.com/expo/expo/tree/master/packages/expo-firebase-analytics", + "jest": { + "preset": "expo-module-scripts/ios" + }, "unimodulePeerDependencies": { "@unimodules/core": "*", "expo-constants": "*" diff --git a/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts b/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts index cfa63d627b329..f5349ba73412a 100644 --- a/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts +++ b/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts @@ -189,7 +189,8 @@ class FirebaseAnalyticsJS { !eventName.length || eventName.length > 40 || eventName[0] === '_' || - !eventName.match(/^[A-Za-z_]+$/) || + eventName[0].match(/^\d$/) || + !eventName.match(/^[A-Za-z_0-9]+$/) || eventName.startsWith('firebase_') || eventName.startsWith('google_') || eventName.startsWith('ga_') diff --git a/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts b/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts new file mode 100644 index 0000000000000..5252b9d5efdc2 --- /dev/null +++ b/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts @@ -0,0 +1,27 @@ +import FirebaseAnalyticsJS from '../FirebaseAnalyticsJS'; + +it(`Verfies parseEvent eventName validation`, async () => { + expect.assertions(2); + const expectedErrorMessage = + 'Invalid event-name specified. Should contain 1 to 40 alphanumeric characters or underscores. The name must start with an alphabetic character.'; + const options = { + clientId: '0', + sessionId: '0', + }; + + FirebaseAnalyticsJS.parseEvent(options, 'MyAnalyticsEvent'); + + try { + FirebaseAnalyticsJS.parseEvent(options, '_MyAnalyticsEvent'); + } catch (error) { + expect(error.message).toBe(expectedErrorMessage); + } + + try { + FirebaseAnalyticsJS.parseEvent(options, '0MyAnalyticsEvent'); + } catch (error) { + expect(error.message).toBe(expectedErrorMessage); + } + + FirebaseAnalyticsJS.parseEvent(options, 'MyAnalyticsEvent0'); +}); From 3af83650d938f4627891cf4b56e6b1c58f78d83d Mon Sep 17 00:00:00 2001 From: Thorben Primke Date: Wed, 27 May 2020 10:04:47 -0700 Subject: [PATCH 2/6] Adds changelog --- packages/expo-firebase-analytics/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/expo-firebase-analytics/CHANGELOG.md b/packages/expo-firebase-analytics/CHANGELOG.md index 82dd45ad4ce0d..cab4789759957 100644 --- a/packages/expo-firebase-analytics/CHANGELOG.md +++ b/packages/expo-firebase-analytics/CHANGELOG.md @@ -8,6 +8,8 @@ ### 🐛 Bug fixes +- Fixes `parseEvent` to allow numeric characters as part of the `eventName` since the name can be alphanumeric. ([#8516](https://github.com/expo/expo/pull/8516) by [@thorbenprimke](https://github.com/thorbenprimke)) + ## 2.4.0 — 2020-05-27 ### 🎉 New features From 843c3bd4ba342ca4dd712a4046bdb5cc7406176b Mon Sep 17 00:00:00 2001 From: Thorben Primke Date: Thu, 28 May 2020 07:19:53 -0700 Subject: [PATCH 3/6] Updated per feedback - pulled out logic into isValidName - added more tests --- packages/expo-firebase-analytics/package.json | 2 +- .../src/FirebaseAnalyticsJS.ts | 40 +++++------ .../src/__tests__/FirebaseAnalyticsJS-test.ts | 66 +++++++++++++++---- 3 files changed, 72 insertions(+), 36 deletions(-) diff --git a/packages/expo-firebase-analytics/package.json b/packages/expo-firebase-analytics/package.json index b2f645fc526e8..2cecf465f3253 100644 --- a/packages/expo-firebase-analytics/package.json +++ b/packages/expo-firebase-analytics/package.json @@ -33,7 +33,7 @@ "license": "MIT", "homepage": "https://github.com/expo/expo/tree/master/packages/expo-firebase-analytics", "jest": { - "preset": "expo-module-scripts/ios" + "preset": "expo-module-scripts" }, "unimodulePeerDependencies": { "@unimodules/core": "*", diff --git a/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts b/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts index f5349ba73412a..ad1b309b96473 100644 --- a/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts +++ b/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts @@ -172,6 +172,19 @@ class FirebaseAnalyticsJS { } } + private static isValidName(name: string, maxLength: number): boolean { + return !!( + name && + name.length && + name.length <= maxLength && + name.match(/^[A-Za-z][A-Za-z_\d]*$/) && + name !== 'user_id' && + !name.startsWith('firebase_') && + !name.startsWith('google_') && + !name.startsWith('ga_') + ); + } + /** * Parses an event (as passed to logEvent) and throws an error when the * event-name or parameters are invalid. @@ -184,19 +197,9 @@ class FirebaseAnalyticsJS { eventName: string, eventParams?: { [key: string]: any } ): FirebaseAnalyticsJSCodedEvent { - if ( - !eventName || - !eventName.length || - eventName.length > 40 || - eventName[0] === '_' || - eventName[0].match(/^\d$/) || - !eventName.match(/^[A-Za-z_0-9]+$/) || - eventName.startsWith('firebase_') || - eventName.startsWith('google_') || - eventName.startsWith('ga_') - ) { + if (!FirebaseAnalyticsJS.isValidName(eventName, 40)) { throw new Error( - 'Invalid event-name specified. Should contain 1 to 40 alphanumeric characters or underscores. The name must start with an alphabetic character.' + `Invalid event-name (${eventName}) specified. Should contain 1 to 40 alphanumeric characters or underscores. The name must start with an alphabetic character.` ); } const params: FirebaseAnalyticsJSCodedEvent = { @@ -227,18 +230,9 @@ class FirebaseAnalyticsJS { userPropertyName: string, userPropertyValue: any ): string { - if ( - !userPropertyName.length || - userPropertyName.length > 24 || - userPropertyName[0] === '_' || - !userPropertyName.match(/^[A-Za-z_]+$/) || - userPropertyName === 'user_id' || - userPropertyName.startsWith('firebase_') || - userPropertyName.startsWith('google_') || - userPropertyName.startsWith('ga_') - ) { + if (!FirebaseAnalyticsJS.isValidName(userPropertyName, 24)) { throw new Error( - 'Invalid user-property name specified. Should contain 1 to 24 alphanumeric characters or underscores. The name must start with an alphabetic character.' + `Invalid user-property name (${userPropertyName}) specified. Should contain 1 to 24 alphanumeric characters or underscores. The name must start with an alphabetic character.` ); } if ( diff --git a/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts b/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts index 5252b9d5efdc2..74cf33d2dd16f 100644 --- a/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts +++ b/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts @@ -1,27 +1,69 @@ import FirebaseAnalyticsJS from '../FirebaseAnalyticsJS'; -it(`Verfies parseEvent eventName validation`, async () => { - expect.assertions(2); - const expectedErrorMessage = - 'Invalid event-name specified. Should contain 1 to 40 alphanumeric characters or underscores. The name must start with an alphabetic character.'; - const options = { - clientId: '0', - sessionId: '0', - }; +let eventName = ''; +const expectedErrorMessage = (name, eventName, maxLength) => + `Invalid ${name} (${eventName}) specified. Should contain 1 to ${maxLength} alphanumeric characters or underscores. The name must start with an alphabetic character.`; +const options = { + clientId: '0', + sessionId: '0', +}; +it(`Verfies parseEvent eventName validation`, async () => { + const name = 'event-name'; + expect.assertions(3); FirebaseAnalyticsJS.parseEvent(options, 'MyAnalyticsEvent'); try { - FirebaseAnalyticsJS.parseEvent(options, '_MyAnalyticsEvent'); + eventName = '_MyAnalyticsEvent'; + FirebaseAnalyticsJS.parseEvent(options, eventName); } catch (error) { - expect(error.message).toBe(expectedErrorMessage); + expect(error.message).toBe(expectedErrorMessage(name, eventName, 40)); } try { - FirebaseAnalyticsJS.parseEvent(options, '0MyAnalyticsEvent'); + eventName = '0MyAnalyticsEvent'; + FirebaseAnalyticsJS.parseEvent(options, eventName); } catch (error) { - expect(error.message).toBe(expectedErrorMessage); + expect(error.message).toBe(expectedErrorMessage(name, eventName, 40)); } FirebaseAnalyticsJS.parseEvent(options, 'MyAnalyticsEvent0'); + + try { + eventName = '0SuperLongNameThatIsMoreThan40CharactersInLength'; + FirebaseAnalyticsJS.parseEvent(options, eventName); + } catch (error) { + expect(error.message).toBe(expectedErrorMessage(name, eventName, 40)); + } +}); + +it(`Verfies parseUserProperty userPropertyName validation`, async () => { + const name = 'user-property name'; + expect.assertions(3); + const value = 'value'; + + FirebaseAnalyticsJS.parseUserProperty(options, 'MyAnalyticsEvent', value); + + try { + eventName = '_MyAnalyticsEvent'; + FirebaseAnalyticsJS.parseUserProperty(options, eventName, value); + } catch (error) { + expect(error.message).toBe(expectedErrorMessage(name, eventName, 24)); + } + + try { + eventName = '0MyAnalyticsEvent'; + FirebaseAnalyticsJS.parseUserProperty(options, eventName, value); + } catch (error) { + expect(error.message).toBe(expectedErrorMessage(name, eventName, 24)); + } + + FirebaseAnalyticsJS.parseUserProperty(options, 'MyAnalyticsEvent0', value); + + try { + eventName = '0LongNameThatIsMoreThan24ButLessThan40'; + FirebaseAnalyticsJS.parseUserProperty(options, eventName, value); + } catch (error) { + expect(error.message).toBe(expectedErrorMessage(name, eventName, 24)); + } }); From ef56f7a0798a40e203c03d29f18ddc4a3ecc5381 Mon Sep 17 00:00:00 2001 From: Thorben Primke Date: Thu, 28 May 2020 07:20:19 -0700 Subject: [PATCH 4/6] rebuild files --- .../build/FirebaseAnalyticsJS.d.ts | 1 + .../build/FirebaseAnalyticsJS.js | 32 ++++++++----------- .../build/FirebaseAnalyticsJS.js.map | 2 +- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.d.ts b/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.d.ts index 1e5982545321c..84ce0505d3a14 100644 --- a/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.d.ts +++ b/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.d.ts @@ -39,6 +39,7 @@ declare class FirebaseAnalyticsJS { * Clears any queued events and cancels the flush timer. */ clearEvents(): void; + private static isValidName; /** * Parses an event (as passed to logEvent) and throws an error when the * event-name or parameters are invalid. diff --git a/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js b/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js index 440dd55108bb0..b5f1069bf9407 100644 --- a/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js +++ b/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js @@ -152,6 +152,16 @@ class FirebaseAnalyticsJS { this.flushEventsTimer = 0; } } + static isValidName(name, maxLength) { + return !!(name && + name.length && + name.length <= maxLength && + name.match(/^[A-Za-z][A-Za-z_\d]*$/) && + name !== 'user_id' && + !name.startsWith('firebase_') && + !name.startsWith('google_') && + !name.startsWith('ga_')); + } /** * Parses an event (as passed to logEvent) and throws an error when the * event-name or parameters are invalid. @@ -160,15 +170,8 @@ class FirebaseAnalyticsJS { * through the Google Measurement API v2. */ static parseEvent(options, eventName, eventParams) { - if (!eventName || - !eventName.length || - eventName.length > 40 || - eventName[0] === '_' || - !eventName.match(/^[A-Za-z_]+$/) || - eventName.startsWith('firebase_') || - eventName.startsWith('google_') || - eventName.startsWith('ga_')) { - throw new Error('Invalid event-name specified. Should contain 1 to 40 alphanumeric characters or underscores. The name must start with an alphabetic character.'); + if (!FirebaseAnalyticsJS.isValidName(eventName, 40)) { + throw new Error(`Invalid event-name (${eventName}) specified. Should contain 1 to 40 alphanumeric characters or underscores. The name must start with an alphabetic character.`); } const params = { en: eventName, @@ -192,15 +195,8 @@ class FirebaseAnalyticsJS { * through the Google Measurement API v2. */ static parseUserProperty(options, userPropertyName, userPropertyValue) { - if (!userPropertyName.length || - userPropertyName.length > 24 || - userPropertyName[0] === '_' || - !userPropertyName.match(/^[A-Za-z_]+$/) || - userPropertyName === 'user_id' || - userPropertyName.startsWith('firebase_') || - userPropertyName.startsWith('google_') || - userPropertyName.startsWith('ga_')) { - throw new Error('Invalid user-property name specified. Should contain 1 to 24 alphanumeric characters or underscores. The name must start with an alphabetic character.'); + if (!FirebaseAnalyticsJS.isValidName(userPropertyName, 24)) { + throw new Error(`Invalid user-property name (${userPropertyName}) specified. Should contain 1 to 24 alphanumeric characters or underscores. The name must start with an alphabetic character.`); } if (userPropertyValue !== undefined && userPropertyValue !== null && diff --git a/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js.map b/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js.map index a7f39b1b8b83a..cdbd82a87f7b4 100644 --- a/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js.map +++ b/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js.map @@ -1 +1 @@ -{"version":3,"file":"FirebaseAnalyticsJS.js","sourceRoot":"","sources":["../src/FirebaseAnalyticsJS.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FirebaseAnalyticsJSCodedEvent,\n FirebaseAnalyticsJSConfig,\n FirebaseAnalyticsJSOptions,\n} from './FirebaseAnalyticsJS.types';\n\n/**\n * A pure JavaScript Google Firebase Analytics implementation that uses\n * the HTTPS Measurement API 2 to send events to Google Analytics.\n *\n * This class provides an alternative for the Firebase Analytics module\n * shipped with the Firebase JS SDK. That library uses the gtag.js dependency\n * and requires certain browser features. This prevents the use\n * analytics on other platforms, such as Node-js and react-native.\n *\n * FirebaseAnalyticsJS provides a bare-bone implementation of the new\n * HTTPS Measurement API 2 protocol (which is undocumented), with an API\n * that follows the Firebase Analytics JS SDK.\n */\nclass FirebaseAnalyticsJS {\n public readonly url: string;\n private enabled: boolean;\n public readonly config: FirebaseAnalyticsJSConfig;\n private userId?: string;\n private userProperties?: { [key: string]: any };\n private screenName?: string;\n private eventQueue = new Set();\n private options: FirebaseAnalyticsJSOptions;\n private flushEventsPromise: Promise = Promise.resolve();\n private flushEventsTimer: any;\n private lastTime: number = -1;\n private sequenceNr: number = 1;\n\n constructor(config: FirebaseAnalyticsJSConfig, options: FirebaseAnalyticsJSOptions) {\n // Verify the measurement- & client Ids\n if (!config.measurementId)\n throw new Error(\n 'No valid measurementId. Make sure to provide a valid measurementId with a G-XXXXXXXXXX format.'\n );\n if (!options.clientId)\n throw new Error(\n 'No valid clientId. Make sure to provide a valid clientId with a UUID (v4) format.'\n );\n\n // Initialize\n this.url = 'https://www.google-analytics.com/g/collect';\n this.enabled = true;\n this.config = config;\n this.options = {\n customArgs: {},\n maxCacheTime: 5000,\n strictNativeEmulation: false,\n origin: 'firebase',\n ...options,\n };\n }\n\n /**\n * Sends 1 or more coded-events to the back-end.\n * When only 1 event is provided, it is send inside the query URL.\n * When more than 1 event is provided, the event-data is send in\n * the body of the POST request.\n */\n private async send(events: Set): Promise {\n const { config, options } = this;\n let queryArgs: any = {\n ...options.customArgs,\n v: 2,\n tid: config.measurementId,\n cid: options.clientId,\n sid: options.sessionId,\n _s: this.sequenceNr++,\n seg: 1,\n };\n if (options.sessionNumber) queryArgs.sct = options.sessionNumber;\n if (options.userLanguage) queryArgs.ul = options.userLanguage;\n if (options.appName) queryArgs.an = options.appName;\n if (options.appVersion) queryArgs.av = options.appVersion;\n if (options.docTitle) queryArgs.dt = options.docTitle;\n if (options.docLocation) queryArgs.dl = options.docLocation;\n if (options.screenRes) queryArgs.sr = options.screenRes;\n if (options.debug) queryArgs._dbg = 1;\n if (this.sequenceNr === 2) queryArgs._ss = 1; // Session start\n let body;\n\n const lastTime = this.lastTime;\n if (events.size > 1) {\n body = '';\n events.forEach(event => {\n body += encodeQueryArgs(event, this.lastTime) + '\\n';\n this.lastTime = event._et;\n });\n } else if (events.size === 1) {\n const event = events.values().next().value;\n this.lastTime = event._et;\n queryArgs = {\n ...event,\n ...queryArgs,\n };\n }\n const args = encodeQueryArgs(queryArgs, lastTime);\n const url = `${this.url}?${args}`;\n await fetch(url, {\n method: 'POST',\n mode: 'no-cors',\n cache: 'no-cache',\n headers: {\n 'Content-Type': 'text/plain;charset=UTF-8',\n },\n ...(options.headers\n ? {\n headers: options.headers,\n }\n : {}),\n body,\n });\n }\n\n private async addEvent(event: FirebaseAnalyticsJSCodedEvent) {\n const { userId, userProperties, screenName, options } = this;\n\n // Extend the event with the currently set User-id\n if (userId) event.uid = userId;\n if (screenName) event['ep.screen_name'] = screenName;\n\n // Add user-properties\n if (userProperties) {\n for (const name in userProperties) {\n event[name] = userProperties[name];\n }\n\n // Reset user-properties after the first event. This is what gtag.js seems\n // to do as well, although I couldn't find any docs explaining this behavior.\n this.userProperties = undefined;\n }\n\n // Add the event to the queue\n this.eventQueue.add(event);\n\n // Start debounce timer\n if (!this.flushEventsTimer) {\n this.flushEventsTimer = setTimeout(\n async () => {\n this.flushEventsTimer = undefined;\n try {\n await this.flushEventsPromise;\n } catch (err) {\n // nop\n }\n this.flushEventsPromise = this.flushEvents();\n },\n options.debug ? 10 : options.maxCacheTime\n );\n }\n }\n\n private async flushEvents() {\n if (!this.eventQueue.size) return;\n const events = new Set(this.eventQueue);\n await this.send(events);\n events.forEach(event => this.eventQueue.delete(event));\n }\n\n /**\n * Clears any queued events and cancels the flush timer.\n */\n clearEvents() {\n this.eventQueue.clear();\n if (this.flushEventsTimer) {\n clearTimeout(this.flushEventsTimer);\n this.flushEventsTimer = 0;\n }\n }\n\n /**\n * Parses an event (as passed to logEvent) and throws an error when the\n * event-name or parameters are invalid.\n *\n * Upon success, returns the event in encoded format, ready to be send\n * through the Google Measurement API v2.\n */\n static parseEvent(\n options: FirebaseAnalyticsJSOptions,\n eventName: string,\n eventParams?: { [key: string]: any }\n ): FirebaseAnalyticsJSCodedEvent {\n if (\n !eventName ||\n !eventName.length ||\n eventName.length > 40 ||\n eventName[0] === '_' ||\n !eventName.match(/^[A-Za-z_]+$/) ||\n eventName.startsWith('firebase_') ||\n eventName.startsWith('google_') ||\n eventName.startsWith('ga_')\n ) {\n throw new Error(\n 'Invalid event-name specified. Should contain 1 to 40 alphanumeric characters or underscores. The name must start with an alphabetic character.'\n );\n }\n const params: FirebaseAnalyticsJSCodedEvent = {\n en: eventName,\n _et: Date.now(),\n 'ep.origin': options.origin,\n };\n if (eventParams) {\n for (const key in eventParams) {\n const paramKey =\n SHORT_EVENT_PARAMS[key] ||\n (typeof eventParams[key] === 'number' ? `epn.${key}` : `ep.${key}`);\n params[paramKey] = eventParams[key];\n }\n }\n return params;\n }\n\n /**\n * Parses user-properties (as passed to setUserProperties) and throws an error when\n * one of the user properties is invalid.\n *\n * Upon success, returns the user-properties in encoded format, ready to be send\n * through the Google Measurement API v2.\n */\n static parseUserProperty(\n options: FirebaseAnalyticsJSOptions,\n userPropertyName: string,\n userPropertyValue: any\n ): string {\n if (\n !userPropertyName.length ||\n userPropertyName.length > 24 ||\n userPropertyName[0] === '_' ||\n !userPropertyName.match(/^[A-Za-z_]+$/) ||\n userPropertyName === 'user_id' ||\n userPropertyName.startsWith('firebase_') ||\n userPropertyName.startsWith('google_') ||\n userPropertyName.startsWith('ga_')\n ) {\n throw new Error(\n 'Invalid user-property name specified. Should contain 1 to 24 alphanumeric characters or underscores. The name must start with an alphabetic character.'\n );\n }\n if (\n userPropertyValue !== undefined &&\n userPropertyValue !== null &&\n options.strictNativeEmulation &&\n (typeof userPropertyValue !== 'string' || userPropertyValue.length > 36)\n ) {\n throw new Error(\n 'Invalid user-property value specified. Value should be a string of up to 36 characters long.'\n );\n }\n return typeof userPropertyValue === 'number'\n ? `upn.${userPropertyName}`\n : `up.${userPropertyName}`;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#log-event\n */\n async logEvent(eventName: string, eventParams?: { [key: string]: any }): Promise {\n const event = FirebaseAnalyticsJS.parseEvent(this.options, eventName, eventParams);\n if (!this.enabled) return;\n if (this.options.debug) {\n console.log(\n `FirebaseAnalytics event: \"${eventName}\", params: ${JSON.stringify(\n eventParams,\n undefined,\n 2\n )}`\n );\n }\n return this.addEvent(event);\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-analytics-collection-enabled\n */\n async setAnalyticsCollectionEnabled(isEnabled: boolean): Promise {\n this.enabled = isEnabled;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-current-screen\n */\n async setCurrentScreen(screenName?: string, screenClassOverride?: string): Promise {\n if (screenName && screenName.length > 100) {\n throw new Error(\n 'Invalid screen-name specified. Should contain 1 to 100 characters. Set to undefined to clear the current screen name.'\n );\n }\n if (!this.enabled) return;\n this.screenName = screenName || undefined;\n\n // On native, calling `setCurrentScreen` automatically records a screen_view event.\n // Mimimic that behavior when native emulation is enabled.\n // https://firebase.google.com/docs/analytics/screenviews#manually_track_screens\n if (screenName && this.options.strictNativeEmulation) {\n await this.logEvent('screen_view', {\n screen_name: screenName,\n });\n }\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-user-id\n */\n async setUserId(userId: string | null): Promise {\n if (!this.enabled) return;\n this.userId = userId || undefined;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-user-properties\n */\n async setUserProperties(userProperties: { [key: string]: any }): Promise {\n if (!this.enabled) return;\n for (const name in userProperties) {\n const val = userProperties[name];\n const key = FirebaseAnalyticsJS.parseUserProperty(this.options, name, val);\n if (val === null || val === undefined) {\n if (this.userProperties) {\n delete this.userProperties[key];\n }\n } else {\n this.userProperties = this.userProperties || {};\n this.userProperties[key] = val;\n }\n }\n }\n\n /**\n * Clears all analytics data for this instance.\n */\n async resetAnalyticsData() {\n this.clearEvents();\n this.screenName = undefined;\n this.userId = undefined;\n this.userProperties = undefined;\n }\n\n /**\n * Enables or disabled debug mode.\n */\n async setDebugModeEnabled(isEnabled: boolean): Promise {\n this.options.debug = isEnabled;\n }\n}\n\nfunction encodeQueryArgs(queryArgs: FirebaseAnalyticsJSCodedEvent, lastTime: number): string {\n let keys = Object.keys(queryArgs);\n if (lastTime < 0) {\n keys = keys.filter(key => key !== '_et');\n }\n return keys\n .map(key => {\n return `${key}=${encodeURIComponent(\n key === '_et' ? Math.max(queryArgs[key] - lastTime, 0) : queryArgs[key]\n )}`;\n })\n .join('&');\n}\n\nconst SHORT_EVENT_PARAMS = {\n currency: 'cu',\n};\n\nexport default FirebaseAnalyticsJS;\n"]} \ No newline at end of file +{"version":3,"file":"FirebaseAnalyticsJS.js","sourceRoot":"","sources":["../src/FirebaseAnalyticsJS.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FirebaseAnalyticsJSCodedEvent,\n FirebaseAnalyticsJSConfig,\n FirebaseAnalyticsJSOptions,\n} from './FirebaseAnalyticsJS.types';\n\n/**\n * A pure JavaScript Google Firebase Analytics implementation that uses\n * the HTTPS Measurement API 2 to send events to Google Analytics.\n *\n * This class provides an alternative for the Firebase Analytics module\n * shipped with the Firebase JS SDK. That library uses the gtag.js dependency\n * and requires certain browser features. This prevents the use\n * analytics on other platforms, such as Node-js and react-native.\n *\n * FirebaseAnalyticsJS provides a bare-bone implementation of the new\n * HTTPS Measurement API 2 protocol (which is undocumented), with an API\n * that follows the Firebase Analytics JS SDK.\n */\nclass FirebaseAnalyticsJS {\n public readonly url: string;\n private enabled: boolean;\n public readonly config: FirebaseAnalyticsJSConfig;\n private userId?: string;\n private userProperties?: { [key: string]: any };\n private screenName?: string;\n private eventQueue = new Set();\n private options: FirebaseAnalyticsJSOptions;\n private flushEventsPromise: Promise = Promise.resolve();\n private flushEventsTimer: any;\n private lastTime: number = -1;\n private sequenceNr: number = 1;\n\n constructor(config: FirebaseAnalyticsJSConfig, options: FirebaseAnalyticsJSOptions) {\n // Verify the measurement- & client Ids\n if (!config.measurementId)\n throw new Error(\n 'No valid measurementId. Make sure to provide a valid measurementId with a G-XXXXXXXXXX format.'\n );\n if (!options.clientId)\n throw new Error(\n 'No valid clientId. Make sure to provide a valid clientId with a UUID (v4) format.'\n );\n\n // Initialize\n this.url = 'https://www.google-analytics.com/g/collect';\n this.enabled = true;\n this.config = config;\n this.options = {\n customArgs: {},\n maxCacheTime: 5000,\n strictNativeEmulation: false,\n origin: 'firebase',\n ...options,\n };\n }\n\n /**\n * Sends 1 or more coded-events to the back-end.\n * When only 1 event is provided, it is send inside the query URL.\n * When more than 1 event is provided, the event-data is send in\n * the body of the POST request.\n */\n private async send(events: Set): Promise {\n const { config, options } = this;\n let queryArgs: any = {\n ...options.customArgs,\n v: 2,\n tid: config.measurementId,\n cid: options.clientId,\n sid: options.sessionId,\n _s: this.sequenceNr++,\n seg: 1,\n };\n if (options.sessionNumber) queryArgs.sct = options.sessionNumber;\n if (options.userLanguage) queryArgs.ul = options.userLanguage;\n if (options.appName) queryArgs.an = options.appName;\n if (options.appVersion) queryArgs.av = options.appVersion;\n if (options.docTitle) queryArgs.dt = options.docTitle;\n if (options.docLocation) queryArgs.dl = options.docLocation;\n if (options.screenRes) queryArgs.sr = options.screenRes;\n if (options.debug) queryArgs._dbg = 1;\n if (this.sequenceNr === 2) queryArgs._ss = 1; // Session start\n let body;\n\n const lastTime = this.lastTime;\n if (events.size > 1) {\n body = '';\n events.forEach(event => {\n body += encodeQueryArgs(event, this.lastTime) + '\\n';\n this.lastTime = event._et;\n });\n } else if (events.size === 1) {\n const event = events.values().next().value;\n this.lastTime = event._et;\n queryArgs = {\n ...event,\n ...queryArgs,\n };\n }\n const args = encodeQueryArgs(queryArgs, lastTime);\n const url = `${this.url}?${args}`;\n await fetch(url, {\n method: 'POST',\n mode: 'no-cors',\n cache: 'no-cache',\n headers: {\n 'Content-Type': 'text/plain;charset=UTF-8',\n },\n ...(options.headers\n ? {\n headers: options.headers,\n }\n : {}),\n body,\n });\n }\n\n private async addEvent(event: FirebaseAnalyticsJSCodedEvent) {\n const { userId, userProperties, screenName, options } = this;\n\n // Extend the event with the currently set User-id\n if (userId) event.uid = userId;\n if (screenName) event['ep.screen_name'] = screenName;\n\n // Add user-properties\n if (userProperties) {\n for (const name in userProperties) {\n event[name] = userProperties[name];\n }\n\n // Reset user-properties after the first event. This is what gtag.js seems\n // to do as well, although I couldn't find any docs explaining this behavior.\n this.userProperties = undefined;\n }\n\n // Add the event to the queue\n this.eventQueue.add(event);\n\n // Start debounce timer\n if (!this.flushEventsTimer) {\n this.flushEventsTimer = setTimeout(\n async () => {\n this.flushEventsTimer = undefined;\n try {\n await this.flushEventsPromise;\n } catch (err) {\n // nop\n }\n this.flushEventsPromise = this.flushEvents();\n },\n options.debug ? 10 : options.maxCacheTime\n );\n }\n }\n\n private async flushEvents() {\n if (!this.eventQueue.size) return;\n const events = new Set(this.eventQueue);\n await this.send(events);\n events.forEach(event => this.eventQueue.delete(event));\n }\n\n /**\n * Clears any queued events and cancels the flush timer.\n */\n clearEvents() {\n this.eventQueue.clear();\n if (this.flushEventsTimer) {\n clearTimeout(this.flushEventsTimer);\n this.flushEventsTimer = 0;\n }\n }\n\n private static isValidName(name: string, maxLength: number): boolean {\n return !!(\n name &&\n name.length &&\n name.length <= maxLength &&\n name.match(/^[A-Za-z][A-Za-z_\\d]*$/) &&\n name !== 'user_id' &&\n !name.startsWith('firebase_') &&\n !name.startsWith('google_') &&\n !name.startsWith('ga_')\n );\n }\n\n /**\n * Parses an event (as passed to logEvent) and throws an error when the\n * event-name or parameters are invalid.\n *\n * Upon success, returns the event in encoded format, ready to be send\n * through the Google Measurement API v2.\n */\n static parseEvent(\n options: FirebaseAnalyticsJSOptions,\n eventName: string,\n eventParams?: { [key: string]: any }\n ): FirebaseAnalyticsJSCodedEvent {\n if (!FirebaseAnalyticsJS.isValidName(eventName, 40)) {\n throw new Error(\n `Invalid event-name (${eventName}) specified. Should contain 1 to 40 alphanumeric characters or underscores. The name must start with an alphabetic character.`\n );\n }\n const params: FirebaseAnalyticsJSCodedEvent = {\n en: eventName,\n _et: Date.now(),\n 'ep.origin': options.origin,\n };\n if (eventParams) {\n for (const key in eventParams) {\n const paramKey =\n SHORT_EVENT_PARAMS[key] ||\n (typeof eventParams[key] === 'number' ? `epn.${key}` : `ep.${key}`);\n params[paramKey] = eventParams[key];\n }\n }\n return params;\n }\n\n /**\n * Parses user-properties (as passed to setUserProperties) and throws an error when\n * one of the user properties is invalid.\n *\n * Upon success, returns the user-properties in encoded format, ready to be send\n * through the Google Measurement API v2.\n */\n static parseUserProperty(\n options: FirebaseAnalyticsJSOptions,\n userPropertyName: string,\n userPropertyValue: any\n ): string {\n if (!FirebaseAnalyticsJS.isValidName(userPropertyName, 24)) {\n throw new Error(\n `Invalid user-property name (${userPropertyName}) specified. Should contain 1 to 24 alphanumeric characters or underscores. The name must start with an alphabetic character.`\n );\n }\n if (\n userPropertyValue !== undefined &&\n userPropertyValue !== null &&\n options.strictNativeEmulation &&\n (typeof userPropertyValue !== 'string' || userPropertyValue.length > 36)\n ) {\n throw new Error(\n 'Invalid user-property value specified. Value should be a string of up to 36 characters long.'\n );\n }\n return typeof userPropertyValue === 'number'\n ? `upn.${userPropertyName}`\n : `up.${userPropertyName}`;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#log-event\n */\n async logEvent(eventName: string, eventParams?: { [key: string]: any }): Promise {\n const event = FirebaseAnalyticsJS.parseEvent(this.options, eventName, eventParams);\n if (!this.enabled) return;\n if (this.options.debug) {\n console.log(\n `FirebaseAnalytics event: \"${eventName}\", params: ${JSON.stringify(\n eventParams,\n undefined,\n 2\n )}`\n );\n }\n return this.addEvent(event);\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-analytics-collection-enabled\n */\n async setAnalyticsCollectionEnabled(isEnabled: boolean): Promise {\n this.enabled = isEnabled;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-current-screen\n */\n async setCurrentScreen(screenName?: string, screenClassOverride?: string): Promise {\n if (screenName && screenName.length > 100) {\n throw new Error(\n 'Invalid screen-name specified. Should contain 1 to 100 characters. Set to undefined to clear the current screen name.'\n );\n }\n if (!this.enabled) return;\n this.screenName = screenName || undefined;\n\n // On native, calling `setCurrentScreen` automatically records a screen_view event.\n // Mimimic that behavior when native emulation is enabled.\n // https://firebase.google.com/docs/analytics/screenviews#manually_track_screens\n if (screenName && this.options.strictNativeEmulation) {\n await this.logEvent('screen_view', {\n screen_name: screenName,\n });\n }\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-user-id\n */\n async setUserId(userId: string | null): Promise {\n if (!this.enabled) return;\n this.userId = userId || undefined;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-user-properties\n */\n async setUserProperties(userProperties: { [key: string]: any }): Promise {\n if (!this.enabled) return;\n for (const name in userProperties) {\n const val = userProperties[name];\n const key = FirebaseAnalyticsJS.parseUserProperty(this.options, name, val);\n if (val === null || val === undefined) {\n if (this.userProperties) {\n delete this.userProperties[key];\n }\n } else {\n this.userProperties = this.userProperties || {};\n this.userProperties[key] = val;\n }\n }\n }\n\n /**\n * Clears all analytics data for this instance.\n */\n async resetAnalyticsData() {\n this.clearEvents();\n this.screenName = undefined;\n this.userId = undefined;\n this.userProperties = undefined;\n }\n\n /**\n * Enables or disabled debug mode.\n */\n async setDebugModeEnabled(isEnabled: boolean): Promise {\n this.options.debug = isEnabled;\n }\n}\n\nfunction encodeQueryArgs(queryArgs: FirebaseAnalyticsJSCodedEvent, lastTime: number): string {\n let keys = Object.keys(queryArgs);\n if (lastTime < 0) {\n keys = keys.filter(key => key !== '_et');\n }\n return keys\n .map(key => {\n return `${key}=${encodeURIComponent(\n key === '_et' ? Math.max(queryArgs[key] - lastTime, 0) : queryArgs[key]\n )}`;\n })\n .join('&');\n}\n\nconst SHORT_EVENT_PARAMS = {\n currency: 'cu',\n};\n\nexport default FirebaseAnalyticsJS;\n"]} \ No newline at end of file From 874dc646309a00e1ba14c7ceb7b7cb0908ffd432 Mon Sep 17 00:00:00 2001 From: Thorben Primke Date: Thu, 28 May 2020 07:24:01 -0700 Subject: [PATCH 5/6] updates changelog --- packages/expo-firebase-analytics/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/expo-firebase-analytics/CHANGELOG.md b/packages/expo-firebase-analytics/CHANGELOG.md index cab4789759957..7145d29c2560c 100644 --- a/packages/expo-firebase-analytics/CHANGELOG.md +++ b/packages/expo-firebase-analytics/CHANGELOG.md @@ -8,7 +8,7 @@ ### 🐛 Bug fixes -- Fixes `parseEvent` to allow numeric characters as part of the `eventName` since the name can be alphanumeric. ([#8516](https://github.com/expo/expo/pull/8516) by [@thorbenprimke](https://github.com/thorbenprimke)) +- Fixes `parseEvent` and `parseUserProperty` to allow numeric characters in the name parameter. ([#8516](https://github.com/expo/expo/pull/8516) by [@thorbenprimke](https://github.com/thorbenprimke)) ## 2.4.0 — 2020-05-27 From c76f860cff72fdd550a26a2644e3bf46e48d0312 Mon Sep 17 00:00:00 2001 From: Thorben Primke Date: Thu, 28 May 2020 08:27:02 -0700 Subject: [PATCH 6/6] Fixed user_id case --- .../build/FirebaseAnalyticsJS.js | 3 +-- .../build/FirebaseAnalyticsJS.js.map | 2 +- .../src/FirebaseAnalyticsJS.ts | 3 +-- .../src/__tests__/FirebaseAnalyticsJS-test.ts | 11 ++++++++++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js b/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js index b5f1069bf9407..5957fd4d79fe2 100644 --- a/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js +++ b/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js @@ -157,7 +157,6 @@ class FirebaseAnalyticsJS { name.length && name.length <= maxLength && name.match(/^[A-Za-z][A-Za-z_\d]*$/) && - name !== 'user_id' && !name.startsWith('firebase_') && !name.startsWith('google_') && !name.startsWith('ga_')); @@ -195,7 +194,7 @@ class FirebaseAnalyticsJS { * through the Google Measurement API v2. */ static parseUserProperty(options, userPropertyName, userPropertyValue) { - if (!FirebaseAnalyticsJS.isValidName(userPropertyName, 24)) { + if (!FirebaseAnalyticsJS.isValidName(userPropertyName, 24) || userPropertyName === 'user_id') { throw new Error(`Invalid user-property name (${userPropertyName}) specified. Should contain 1 to 24 alphanumeric characters or underscores. The name must start with an alphabetic character.`); } if (userPropertyValue !== undefined && diff --git a/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js.map b/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js.map index cdbd82a87f7b4..05df3d51598dc 100644 --- a/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js.map +++ b/packages/expo-firebase-analytics/build/FirebaseAnalyticsJS.js.map @@ -1 +1 @@ -{"version":3,"file":"FirebaseAnalyticsJS.js","sourceRoot":"","sources":["../src/FirebaseAnalyticsJS.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FirebaseAnalyticsJSCodedEvent,\n FirebaseAnalyticsJSConfig,\n FirebaseAnalyticsJSOptions,\n} from './FirebaseAnalyticsJS.types';\n\n/**\n * A pure JavaScript Google Firebase Analytics implementation that uses\n * the HTTPS Measurement API 2 to send events to Google Analytics.\n *\n * This class provides an alternative for the Firebase Analytics module\n * shipped with the Firebase JS SDK. That library uses the gtag.js dependency\n * and requires certain browser features. This prevents the use\n * analytics on other platforms, such as Node-js and react-native.\n *\n * FirebaseAnalyticsJS provides a bare-bone implementation of the new\n * HTTPS Measurement API 2 protocol (which is undocumented), with an API\n * that follows the Firebase Analytics JS SDK.\n */\nclass FirebaseAnalyticsJS {\n public readonly url: string;\n private enabled: boolean;\n public readonly config: FirebaseAnalyticsJSConfig;\n private userId?: string;\n private userProperties?: { [key: string]: any };\n private screenName?: string;\n private eventQueue = new Set();\n private options: FirebaseAnalyticsJSOptions;\n private flushEventsPromise: Promise = Promise.resolve();\n private flushEventsTimer: any;\n private lastTime: number = -1;\n private sequenceNr: number = 1;\n\n constructor(config: FirebaseAnalyticsJSConfig, options: FirebaseAnalyticsJSOptions) {\n // Verify the measurement- & client Ids\n if (!config.measurementId)\n throw new Error(\n 'No valid measurementId. Make sure to provide a valid measurementId with a G-XXXXXXXXXX format.'\n );\n if (!options.clientId)\n throw new Error(\n 'No valid clientId. Make sure to provide a valid clientId with a UUID (v4) format.'\n );\n\n // Initialize\n this.url = 'https://www.google-analytics.com/g/collect';\n this.enabled = true;\n this.config = config;\n this.options = {\n customArgs: {},\n maxCacheTime: 5000,\n strictNativeEmulation: false,\n origin: 'firebase',\n ...options,\n };\n }\n\n /**\n * Sends 1 or more coded-events to the back-end.\n * When only 1 event is provided, it is send inside the query URL.\n * When more than 1 event is provided, the event-data is send in\n * the body of the POST request.\n */\n private async send(events: Set): Promise {\n const { config, options } = this;\n let queryArgs: any = {\n ...options.customArgs,\n v: 2,\n tid: config.measurementId,\n cid: options.clientId,\n sid: options.sessionId,\n _s: this.sequenceNr++,\n seg: 1,\n };\n if (options.sessionNumber) queryArgs.sct = options.sessionNumber;\n if (options.userLanguage) queryArgs.ul = options.userLanguage;\n if (options.appName) queryArgs.an = options.appName;\n if (options.appVersion) queryArgs.av = options.appVersion;\n if (options.docTitle) queryArgs.dt = options.docTitle;\n if (options.docLocation) queryArgs.dl = options.docLocation;\n if (options.screenRes) queryArgs.sr = options.screenRes;\n if (options.debug) queryArgs._dbg = 1;\n if (this.sequenceNr === 2) queryArgs._ss = 1; // Session start\n let body;\n\n const lastTime = this.lastTime;\n if (events.size > 1) {\n body = '';\n events.forEach(event => {\n body += encodeQueryArgs(event, this.lastTime) + '\\n';\n this.lastTime = event._et;\n });\n } else if (events.size === 1) {\n const event = events.values().next().value;\n this.lastTime = event._et;\n queryArgs = {\n ...event,\n ...queryArgs,\n };\n }\n const args = encodeQueryArgs(queryArgs, lastTime);\n const url = `${this.url}?${args}`;\n await fetch(url, {\n method: 'POST',\n mode: 'no-cors',\n cache: 'no-cache',\n headers: {\n 'Content-Type': 'text/plain;charset=UTF-8',\n },\n ...(options.headers\n ? {\n headers: options.headers,\n }\n : {}),\n body,\n });\n }\n\n private async addEvent(event: FirebaseAnalyticsJSCodedEvent) {\n const { userId, userProperties, screenName, options } = this;\n\n // Extend the event with the currently set User-id\n if (userId) event.uid = userId;\n if (screenName) event['ep.screen_name'] = screenName;\n\n // Add user-properties\n if (userProperties) {\n for (const name in userProperties) {\n event[name] = userProperties[name];\n }\n\n // Reset user-properties after the first event. This is what gtag.js seems\n // to do as well, although I couldn't find any docs explaining this behavior.\n this.userProperties = undefined;\n }\n\n // Add the event to the queue\n this.eventQueue.add(event);\n\n // Start debounce timer\n if (!this.flushEventsTimer) {\n this.flushEventsTimer = setTimeout(\n async () => {\n this.flushEventsTimer = undefined;\n try {\n await this.flushEventsPromise;\n } catch (err) {\n // nop\n }\n this.flushEventsPromise = this.flushEvents();\n },\n options.debug ? 10 : options.maxCacheTime\n );\n }\n }\n\n private async flushEvents() {\n if (!this.eventQueue.size) return;\n const events = new Set(this.eventQueue);\n await this.send(events);\n events.forEach(event => this.eventQueue.delete(event));\n }\n\n /**\n * Clears any queued events and cancels the flush timer.\n */\n clearEvents() {\n this.eventQueue.clear();\n if (this.flushEventsTimer) {\n clearTimeout(this.flushEventsTimer);\n this.flushEventsTimer = 0;\n }\n }\n\n private static isValidName(name: string, maxLength: number): boolean {\n return !!(\n name &&\n name.length &&\n name.length <= maxLength &&\n name.match(/^[A-Za-z][A-Za-z_\\d]*$/) &&\n name !== 'user_id' &&\n !name.startsWith('firebase_') &&\n !name.startsWith('google_') &&\n !name.startsWith('ga_')\n );\n }\n\n /**\n * Parses an event (as passed to logEvent) and throws an error when the\n * event-name or parameters are invalid.\n *\n * Upon success, returns the event in encoded format, ready to be send\n * through the Google Measurement API v2.\n */\n static parseEvent(\n options: FirebaseAnalyticsJSOptions,\n eventName: string,\n eventParams?: { [key: string]: any }\n ): FirebaseAnalyticsJSCodedEvent {\n if (!FirebaseAnalyticsJS.isValidName(eventName, 40)) {\n throw new Error(\n `Invalid event-name (${eventName}) specified. Should contain 1 to 40 alphanumeric characters or underscores. The name must start with an alphabetic character.`\n );\n }\n const params: FirebaseAnalyticsJSCodedEvent = {\n en: eventName,\n _et: Date.now(),\n 'ep.origin': options.origin,\n };\n if (eventParams) {\n for (const key in eventParams) {\n const paramKey =\n SHORT_EVENT_PARAMS[key] ||\n (typeof eventParams[key] === 'number' ? `epn.${key}` : `ep.${key}`);\n params[paramKey] = eventParams[key];\n }\n }\n return params;\n }\n\n /**\n * Parses user-properties (as passed to setUserProperties) and throws an error when\n * one of the user properties is invalid.\n *\n * Upon success, returns the user-properties in encoded format, ready to be send\n * through the Google Measurement API v2.\n */\n static parseUserProperty(\n options: FirebaseAnalyticsJSOptions,\n userPropertyName: string,\n userPropertyValue: any\n ): string {\n if (!FirebaseAnalyticsJS.isValidName(userPropertyName, 24)) {\n throw new Error(\n `Invalid user-property name (${userPropertyName}) specified. Should contain 1 to 24 alphanumeric characters or underscores. The name must start with an alphabetic character.`\n );\n }\n if (\n userPropertyValue !== undefined &&\n userPropertyValue !== null &&\n options.strictNativeEmulation &&\n (typeof userPropertyValue !== 'string' || userPropertyValue.length > 36)\n ) {\n throw new Error(\n 'Invalid user-property value specified. Value should be a string of up to 36 characters long.'\n );\n }\n return typeof userPropertyValue === 'number'\n ? `upn.${userPropertyName}`\n : `up.${userPropertyName}`;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#log-event\n */\n async logEvent(eventName: string, eventParams?: { [key: string]: any }): Promise {\n const event = FirebaseAnalyticsJS.parseEvent(this.options, eventName, eventParams);\n if (!this.enabled) return;\n if (this.options.debug) {\n console.log(\n `FirebaseAnalytics event: \"${eventName}\", params: ${JSON.stringify(\n eventParams,\n undefined,\n 2\n )}`\n );\n }\n return this.addEvent(event);\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-analytics-collection-enabled\n */\n async setAnalyticsCollectionEnabled(isEnabled: boolean): Promise {\n this.enabled = isEnabled;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-current-screen\n */\n async setCurrentScreen(screenName?: string, screenClassOverride?: string): Promise {\n if (screenName && screenName.length > 100) {\n throw new Error(\n 'Invalid screen-name specified. Should contain 1 to 100 characters. Set to undefined to clear the current screen name.'\n );\n }\n if (!this.enabled) return;\n this.screenName = screenName || undefined;\n\n // On native, calling `setCurrentScreen` automatically records a screen_view event.\n // Mimimic that behavior when native emulation is enabled.\n // https://firebase.google.com/docs/analytics/screenviews#manually_track_screens\n if (screenName && this.options.strictNativeEmulation) {\n await this.logEvent('screen_view', {\n screen_name: screenName,\n });\n }\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-user-id\n */\n async setUserId(userId: string | null): Promise {\n if (!this.enabled) return;\n this.userId = userId || undefined;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-user-properties\n */\n async setUserProperties(userProperties: { [key: string]: any }): Promise {\n if (!this.enabled) return;\n for (const name in userProperties) {\n const val = userProperties[name];\n const key = FirebaseAnalyticsJS.parseUserProperty(this.options, name, val);\n if (val === null || val === undefined) {\n if (this.userProperties) {\n delete this.userProperties[key];\n }\n } else {\n this.userProperties = this.userProperties || {};\n this.userProperties[key] = val;\n }\n }\n }\n\n /**\n * Clears all analytics data for this instance.\n */\n async resetAnalyticsData() {\n this.clearEvents();\n this.screenName = undefined;\n this.userId = undefined;\n this.userProperties = undefined;\n }\n\n /**\n * Enables or disabled debug mode.\n */\n async setDebugModeEnabled(isEnabled: boolean): Promise {\n this.options.debug = isEnabled;\n }\n}\n\nfunction encodeQueryArgs(queryArgs: FirebaseAnalyticsJSCodedEvent, lastTime: number): string {\n let keys = Object.keys(queryArgs);\n if (lastTime < 0) {\n keys = keys.filter(key => key !== '_et');\n }\n return keys\n .map(key => {\n return `${key}=${encodeURIComponent(\n key === '_et' ? Math.max(queryArgs[key] - lastTime, 0) : queryArgs[key]\n )}`;\n })\n .join('&');\n}\n\nconst SHORT_EVENT_PARAMS = {\n currency: 'cu',\n};\n\nexport default FirebaseAnalyticsJS;\n"]} \ No newline at end of file +{"version":3,"file":"FirebaseAnalyticsJS.js","sourceRoot":"","sources":["../src/FirebaseAnalyticsJS.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FirebaseAnalyticsJSCodedEvent,\n FirebaseAnalyticsJSConfig,\n FirebaseAnalyticsJSOptions,\n} from './FirebaseAnalyticsJS.types';\n\n/**\n * A pure JavaScript Google Firebase Analytics implementation that uses\n * the HTTPS Measurement API 2 to send events to Google Analytics.\n *\n * This class provides an alternative for the Firebase Analytics module\n * shipped with the Firebase JS SDK. That library uses the gtag.js dependency\n * and requires certain browser features. This prevents the use\n * analytics on other platforms, such as Node-js and react-native.\n *\n * FirebaseAnalyticsJS provides a bare-bone implementation of the new\n * HTTPS Measurement API 2 protocol (which is undocumented), with an API\n * that follows the Firebase Analytics JS SDK.\n */\nclass FirebaseAnalyticsJS {\n public readonly url: string;\n private enabled: boolean;\n public readonly config: FirebaseAnalyticsJSConfig;\n private userId?: string;\n private userProperties?: { [key: string]: any };\n private screenName?: string;\n private eventQueue = new Set();\n private options: FirebaseAnalyticsJSOptions;\n private flushEventsPromise: Promise = Promise.resolve();\n private flushEventsTimer: any;\n private lastTime: number = -1;\n private sequenceNr: number = 1;\n\n constructor(config: FirebaseAnalyticsJSConfig, options: FirebaseAnalyticsJSOptions) {\n // Verify the measurement- & client Ids\n if (!config.measurementId)\n throw new Error(\n 'No valid measurementId. Make sure to provide a valid measurementId with a G-XXXXXXXXXX format.'\n );\n if (!options.clientId)\n throw new Error(\n 'No valid clientId. Make sure to provide a valid clientId with a UUID (v4) format.'\n );\n\n // Initialize\n this.url = 'https://www.google-analytics.com/g/collect';\n this.enabled = true;\n this.config = config;\n this.options = {\n customArgs: {},\n maxCacheTime: 5000,\n strictNativeEmulation: false,\n origin: 'firebase',\n ...options,\n };\n }\n\n /**\n * Sends 1 or more coded-events to the back-end.\n * When only 1 event is provided, it is send inside the query URL.\n * When more than 1 event is provided, the event-data is send in\n * the body of the POST request.\n */\n private async send(events: Set): Promise {\n const { config, options } = this;\n let queryArgs: any = {\n ...options.customArgs,\n v: 2,\n tid: config.measurementId,\n cid: options.clientId,\n sid: options.sessionId,\n _s: this.sequenceNr++,\n seg: 1,\n };\n if (options.sessionNumber) queryArgs.sct = options.sessionNumber;\n if (options.userLanguage) queryArgs.ul = options.userLanguage;\n if (options.appName) queryArgs.an = options.appName;\n if (options.appVersion) queryArgs.av = options.appVersion;\n if (options.docTitle) queryArgs.dt = options.docTitle;\n if (options.docLocation) queryArgs.dl = options.docLocation;\n if (options.screenRes) queryArgs.sr = options.screenRes;\n if (options.debug) queryArgs._dbg = 1;\n if (this.sequenceNr === 2) queryArgs._ss = 1; // Session start\n let body;\n\n const lastTime = this.lastTime;\n if (events.size > 1) {\n body = '';\n events.forEach(event => {\n body += encodeQueryArgs(event, this.lastTime) + '\\n';\n this.lastTime = event._et;\n });\n } else if (events.size === 1) {\n const event = events.values().next().value;\n this.lastTime = event._et;\n queryArgs = {\n ...event,\n ...queryArgs,\n };\n }\n const args = encodeQueryArgs(queryArgs, lastTime);\n const url = `${this.url}?${args}`;\n await fetch(url, {\n method: 'POST',\n mode: 'no-cors',\n cache: 'no-cache',\n headers: {\n 'Content-Type': 'text/plain;charset=UTF-8',\n },\n ...(options.headers\n ? {\n headers: options.headers,\n }\n : {}),\n body,\n });\n }\n\n private async addEvent(event: FirebaseAnalyticsJSCodedEvent) {\n const { userId, userProperties, screenName, options } = this;\n\n // Extend the event with the currently set User-id\n if (userId) event.uid = userId;\n if (screenName) event['ep.screen_name'] = screenName;\n\n // Add user-properties\n if (userProperties) {\n for (const name in userProperties) {\n event[name] = userProperties[name];\n }\n\n // Reset user-properties after the first event. This is what gtag.js seems\n // to do as well, although I couldn't find any docs explaining this behavior.\n this.userProperties = undefined;\n }\n\n // Add the event to the queue\n this.eventQueue.add(event);\n\n // Start debounce timer\n if (!this.flushEventsTimer) {\n this.flushEventsTimer = setTimeout(\n async () => {\n this.flushEventsTimer = undefined;\n try {\n await this.flushEventsPromise;\n } catch (err) {\n // nop\n }\n this.flushEventsPromise = this.flushEvents();\n },\n options.debug ? 10 : options.maxCacheTime\n );\n }\n }\n\n private async flushEvents() {\n if (!this.eventQueue.size) return;\n const events = new Set(this.eventQueue);\n await this.send(events);\n events.forEach(event => this.eventQueue.delete(event));\n }\n\n /**\n * Clears any queued events and cancels the flush timer.\n */\n clearEvents() {\n this.eventQueue.clear();\n if (this.flushEventsTimer) {\n clearTimeout(this.flushEventsTimer);\n this.flushEventsTimer = 0;\n }\n }\n\n private static isValidName(name: string, maxLength: number): boolean {\n return !!(\n name &&\n name.length &&\n name.length <= maxLength &&\n name.match(/^[A-Za-z][A-Za-z_\\d]*$/) &&\n !name.startsWith('firebase_') &&\n !name.startsWith('google_') &&\n !name.startsWith('ga_')\n );\n }\n\n /**\n * Parses an event (as passed to logEvent) and throws an error when the\n * event-name or parameters are invalid.\n *\n * Upon success, returns the event in encoded format, ready to be send\n * through the Google Measurement API v2.\n */\n static parseEvent(\n options: FirebaseAnalyticsJSOptions,\n eventName: string,\n eventParams?: { [key: string]: any }\n ): FirebaseAnalyticsJSCodedEvent {\n if (!FirebaseAnalyticsJS.isValidName(eventName, 40)) {\n throw new Error(\n `Invalid event-name (${eventName}) specified. Should contain 1 to 40 alphanumeric characters or underscores. The name must start with an alphabetic character.`\n );\n }\n const params: FirebaseAnalyticsJSCodedEvent = {\n en: eventName,\n _et: Date.now(),\n 'ep.origin': options.origin,\n };\n if (eventParams) {\n for (const key in eventParams) {\n const paramKey =\n SHORT_EVENT_PARAMS[key] ||\n (typeof eventParams[key] === 'number' ? `epn.${key}` : `ep.${key}`);\n params[paramKey] = eventParams[key];\n }\n }\n return params;\n }\n\n /**\n * Parses user-properties (as passed to setUserProperties) and throws an error when\n * one of the user properties is invalid.\n *\n * Upon success, returns the user-properties in encoded format, ready to be send\n * through the Google Measurement API v2.\n */\n static parseUserProperty(\n options: FirebaseAnalyticsJSOptions,\n userPropertyName: string,\n userPropertyValue: any\n ): string {\n if (!FirebaseAnalyticsJS.isValidName(userPropertyName, 24) || userPropertyName === 'user_id') {\n throw new Error(\n `Invalid user-property name (${userPropertyName}) specified. Should contain 1 to 24 alphanumeric characters or underscores. The name must start with an alphabetic character.`\n );\n }\n if (\n userPropertyValue !== undefined &&\n userPropertyValue !== null &&\n options.strictNativeEmulation &&\n (typeof userPropertyValue !== 'string' || userPropertyValue.length > 36)\n ) {\n throw new Error(\n 'Invalid user-property value specified. Value should be a string of up to 36 characters long.'\n );\n }\n return typeof userPropertyValue === 'number'\n ? `upn.${userPropertyName}`\n : `up.${userPropertyName}`;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#log-event\n */\n async logEvent(eventName: string, eventParams?: { [key: string]: any }): Promise {\n const event = FirebaseAnalyticsJS.parseEvent(this.options, eventName, eventParams);\n if (!this.enabled) return;\n if (this.options.debug) {\n console.log(\n `FirebaseAnalytics event: \"${eventName}\", params: ${JSON.stringify(\n eventParams,\n undefined,\n 2\n )}`\n );\n }\n return this.addEvent(event);\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-analytics-collection-enabled\n */\n async setAnalyticsCollectionEnabled(isEnabled: boolean): Promise {\n this.enabled = isEnabled;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-current-screen\n */\n async setCurrentScreen(screenName?: string, screenClassOverride?: string): Promise {\n if (screenName && screenName.length > 100) {\n throw new Error(\n 'Invalid screen-name specified. Should contain 1 to 100 characters. Set to undefined to clear the current screen name.'\n );\n }\n if (!this.enabled) return;\n this.screenName = screenName || undefined;\n\n // On native, calling `setCurrentScreen` automatically records a screen_view event.\n // Mimimic that behavior when native emulation is enabled.\n // https://firebase.google.com/docs/analytics/screenviews#manually_track_screens\n if (screenName && this.options.strictNativeEmulation) {\n await this.logEvent('screen_view', {\n screen_name: screenName,\n });\n }\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-user-id\n */\n async setUserId(userId: string | null): Promise {\n if (!this.enabled) return;\n this.userId = userId || undefined;\n }\n\n /**\n * https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics#set-user-properties\n */\n async setUserProperties(userProperties: { [key: string]: any }): Promise {\n if (!this.enabled) return;\n for (const name in userProperties) {\n const val = userProperties[name];\n const key = FirebaseAnalyticsJS.parseUserProperty(this.options, name, val);\n if (val === null || val === undefined) {\n if (this.userProperties) {\n delete this.userProperties[key];\n }\n } else {\n this.userProperties = this.userProperties || {};\n this.userProperties[key] = val;\n }\n }\n }\n\n /**\n * Clears all analytics data for this instance.\n */\n async resetAnalyticsData() {\n this.clearEvents();\n this.screenName = undefined;\n this.userId = undefined;\n this.userProperties = undefined;\n }\n\n /**\n * Enables or disabled debug mode.\n */\n async setDebugModeEnabled(isEnabled: boolean): Promise {\n this.options.debug = isEnabled;\n }\n}\n\nfunction encodeQueryArgs(queryArgs: FirebaseAnalyticsJSCodedEvent, lastTime: number): string {\n let keys = Object.keys(queryArgs);\n if (lastTime < 0) {\n keys = keys.filter(key => key !== '_et');\n }\n return keys\n .map(key => {\n return `${key}=${encodeURIComponent(\n key === '_et' ? Math.max(queryArgs[key] - lastTime, 0) : queryArgs[key]\n )}`;\n })\n .join('&');\n}\n\nconst SHORT_EVENT_PARAMS = {\n currency: 'cu',\n};\n\nexport default FirebaseAnalyticsJS;\n"]} \ No newline at end of file diff --git a/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts b/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts index ad1b309b96473..6561e9541bb6f 100644 --- a/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts +++ b/packages/expo-firebase-analytics/src/FirebaseAnalyticsJS.ts @@ -178,7 +178,6 @@ class FirebaseAnalyticsJS { name.length && name.length <= maxLength && name.match(/^[A-Za-z][A-Za-z_\d]*$/) && - name !== 'user_id' && !name.startsWith('firebase_') && !name.startsWith('google_') && !name.startsWith('ga_') @@ -230,7 +229,7 @@ class FirebaseAnalyticsJS { userPropertyName: string, userPropertyValue: any ): string { - if (!FirebaseAnalyticsJS.isValidName(userPropertyName, 24)) { + if (!FirebaseAnalyticsJS.isValidName(userPropertyName, 24) || userPropertyName === 'user_id') { throw new Error( `Invalid user-property name (${userPropertyName}) specified. Should contain 1 to 24 alphanumeric characters or underscores. The name must start with an alphabetic character.` ); diff --git a/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts b/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts index 74cf33d2dd16f..b0900624a7310 100644 --- a/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts +++ b/packages/expo-firebase-analytics/src/__tests__/FirebaseAnalyticsJS-test.ts @@ -35,11 +35,13 @@ it(`Verfies parseEvent eventName validation`, async () => { } catch (error) { expect(error.message).toBe(expectedErrorMessage(name, eventName, 40)); } + + FirebaseAnalyticsJS.parseEvent(options, 'user_id'); }); it(`Verfies parseUserProperty userPropertyName validation`, async () => { const name = 'user-property name'; - expect.assertions(3); + expect.assertions(4); const value = 'value'; FirebaseAnalyticsJS.parseUserProperty(options, 'MyAnalyticsEvent', value); @@ -66,4 +68,11 @@ it(`Verfies parseUserProperty userPropertyName validation`, async () => { } catch (error) { expect(error.message).toBe(expectedErrorMessage(name, eventName, 24)); } + + try { + eventName = 'user_id'; + FirebaseAnalyticsJS.parseUserProperty(options, eventName, value); + } catch (error) { + expect(error.message).toBe(expectedErrorMessage(name, eventName, 24)); + } });