Skip to content

Commit 13fb2fd

Browse files
authored
Migrate analytics to component platform (#220)
* migrate analytics * minor analytics refactoring * interface merge * [AUTOMATED]: Prettier Code Styling * [AUTOMATED]: License Headers
1 parent db8e55d commit 13fb2fd

File tree

9 files changed

+144
-34
lines changed

9 files changed

+144
-34
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# @firebase/analytics-interop-types
2+
3+
**This package is not intended for direct usage, and should only be used via the officially supported [firebase](https://www.npmjs.com/package/firebase) package.**
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
export interface FirebaseAnalyticsInternal {
19+
/**
20+
* Sends analytics event with given `eventParams`. This method
21+
* automatically associates this logged event with this Firebase web
22+
* app instance on this device.
23+
* List of official event parameters can be found in
24+
* {@link https://developers.google.com/gtagjs/reference/event
25+
* the gtag.js reference documentation}.
26+
*/
27+
logEvent(
28+
eventName: string,
29+
eventParams?: { [key: string]: unknown },
30+
options?: AnalyticsCallOptions
31+
): void;
32+
}
33+
34+
export interface AnalyticsCallOptions {
35+
/**
36+
* If true, this config or event call applies globally to all
37+
* analytics properties on the page.
38+
*/
39+
global: boolean;
40+
}
41+
42+
declare module '@firebase/component' {
43+
interface ComponentContainer {
44+
getProvider(
45+
name: 'analytics-internal'
46+
): Provider<FirebaseAnalyticsInternal>;
47+
}
48+
49+
interface Provider<T> {}
50+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "@firebase/analytics-interop-types",
3+
"version": "0.1.0",
4+
"description": "@firebase/analytics Types",
5+
"author": "Firebase <[email protected]> (https://firebase.google.com/)",
6+
"license": "Apache-2.0",
7+
"scripts": {
8+
"test": "tsc"
9+
},
10+
"files": [
11+
"index.d.ts"
12+
],
13+
"repository": {
14+
"directory": "packages/analytics-interop-types",
15+
"type": "git",
16+
"url": "https://github.com/firebase/firebase-js-sdk.git"
17+
},
18+
"bugs": {
19+
"url": "https://github.com/firebase/firebase-js-sdk/issues"
20+
},
21+
"devDependencies": {
22+
"typescript": "3.6.4"
23+
}
24+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "../../config/tsconfig.base.json",
3+
"compilerOptions": {
4+
"noEmit": true
5+
},
6+
"exclude": [
7+
"dist/**/*"
8+
]
9+
}

packages/analytics/index.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ const customDataLayerName = 'customDataLayer';
4141
describe('FirebaseAnalytics instance tests', () => {
4242
it('Throws if no analyticsId in config', () => {
4343
const app = getFakeApp();
44-
expect(() => analyticsFactory(app, () => {})).to.throw('field is empty');
44+
expect(() => analyticsFactory(app)).to.throw('field is empty');
4545
});
4646
it('Throws if creating an instance with already-used analytics ID', () => {
4747
const app = getFakeApp(analyticsId);
4848
resetGlobalVars(false, { [analyticsId]: Promise.resolve() });
49-
expect(() => analyticsFactory(app, () => {})).to.throw('already exists');
49+
expect(() => analyticsFactory(app)).to.throw('already exists');
5050
});
5151
describe('Standard app, page already has user gtag script', () => {
5252
let app: FirebaseApp = {} as FirebaseApp;
@@ -55,7 +55,7 @@ describe('FirebaseAnalytics instance tests', () => {
5555
app = getFakeApp(analyticsId);
5656
window['gtag'] = gtagStub;
5757
window['dataLayer'] = [];
58-
analyticsInstance = analyticsFactory(app, () => {});
58+
analyticsInstance = analyticsFactory(app);
5959
});
6060
after(() => {
6161
delete window['gtag'];
@@ -121,7 +121,7 @@ describe('FirebaseAnalytics instance tests', () => {
121121
dataLayerName: customDataLayerName,
122122
gtagName: customGtagName
123123
});
124-
analyticsInstance = analyticsFactory(app, () => {});
124+
analyticsInstance = analyticsFactory(app);
125125
});
126126
after(() => {
127127
delete window[customGtagName];
@@ -164,7 +164,7 @@ describe('FirebaseAnalytics instance tests', () => {
164164
before(() => {
165165
resetGlobalVars();
166166
const app = getFakeApp(analyticsId);
167-
analyticsInstance = analyticsFactory(app, () => {});
167+
analyticsInstance = analyticsFactory(app);
168168
});
169169
after(() => {
170170
delete window['gtag'];

packages/analytics/index.ts

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@
1616
*/
1717
import firebase from '@firebase/app';
1818
import { FirebaseAnalytics } from '@firebase/analytics-types';
19-
import {
20-
FirebaseServiceFactory,
21-
_FirebaseNamespace
22-
} from '@firebase/app-types/private';
19+
import { _FirebaseNamespace } from '@firebase/app-types/private';
2320
import { factory, settings, resetGlobalVars } from './src/factory';
2421
import { EventName } from './src/constants';
22+
import { Component, ComponentType } from '@firebase/component';
23+
import { ERROR_FACTORY, AnalyticsError } from './src/errors';
2524

2625
declare global {
2726
interface Window {
@@ -35,17 +34,41 @@ declare global {
3534
const ANALYTICS_TYPE = 'analytics';
3635

3736
export function registerAnalytics(instance: _FirebaseNamespace): void {
38-
instance.INTERNAL.registerService(
39-
ANALYTICS_TYPE,
40-
factory as FirebaseServiceFactory,
41-
{
37+
instance.INTERNAL.registerComponent(
38+
new Component(
39+
ANALYTICS_TYPE,
40+
container => {
41+
// getImmediate for FirebaseApp will always succeed
42+
const app = container.getProvider('app').getImmediate()!;
43+
return factory(app);
44+
},
45+
ComponentType.PUBLIC
46+
).setServiceProps({
4247
settings,
4348
EventName
44-
},
45-
// We don't need to wait on any AppHooks.
46-
undefined,
47-
// Allow multiple analytics instances per app.
48-
false
49+
})
50+
);
51+
52+
instance.INTERNAL.registerComponent(
53+
new Component(
54+
'analytics-internal',
55+
container => {
56+
try {
57+
const analytics = container
58+
.getProvider(ANALYTICS_TYPE)
59+
.getImmediate();
60+
return {
61+
logEvent: analytics.logEvent
62+
};
63+
} catch (e) {
64+
throw ERROR_FACTORY.create(
65+
AnalyticsError.INTEROP_COMPONENT_REG_FAILED,
66+
{ reason: e }
67+
);
68+
}
69+
},
70+
ComponentType.PRIVATE
71+
)
4972
);
5073
}
5174

@@ -64,3 +87,11 @@ declare module '@firebase/app-types' {
6487
analytics(): FirebaseAnalytics;
6588
}
6689
}
90+
91+
declare module '@firebase/component' {
92+
interface ComponentContainer {
93+
getProvider(name: typeof ANALYTICS_TYPE): Provider<FirebaseAnalytics>;
94+
}
95+
96+
interface Provider<T> {}
97+
}

packages/analytics/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"dependencies": {
2626
"@firebase/analytics-types": "0.2.1",
2727
"@firebase/installations": "0.3.0",
28+
"@firebase/component": "0.1.0",
2829
"@firebase/util": "0.2.29",
2930
"tslib": "1.10.0"
3031
},

packages/analytics/src/errors.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import { ANALYTICS_ID_FIELD } from './constants';
2121
export const enum AnalyticsError {
2222
NO_GA_ID = 'no-ga-id',
2323
ALREADY_EXISTS = 'already-exists',
24-
ALREADY_INITIALIZED = 'already-initialized'
24+
ALREADY_INITIALIZED = 'already-initialized',
25+
INTEROP_COMPONENT_REG_FAILED = 'interop-component-reg-failed'
2526
}
2627

2728
const ERRORS: ErrorMap<AnalyticsError> = {
@@ -36,11 +37,14 @@ const ERRORS: ErrorMap<AnalyticsError> = {
3637
[AnalyticsError.ALREADY_INITIALIZED]:
3738
'Firebase Analytics has already been initialized.' +
3839
'settings() must be called before initializing any Analytics instance' +
39-
'or it will have no effect.'
40+
'or it will have no effect.',
41+
[AnalyticsError.INTEROP_COMPONENT_REG_FAILED]:
42+
'Firebase Analytics Interop Component failed to instantiate'
4043
};
4144

4245
interface ErrorParams {
4346
[AnalyticsError.ALREADY_EXISTS]: { id: string };
47+
[AnalyticsError.INTEROP_COMPONENT_REG_FAILED]: { reason: Error };
4448
}
4549

4650
export const ERROR_FACTORY = new ErrorFactory<AnalyticsError, ErrorParams>(

packages/analytics/src/factory.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { FirebaseApp } from '@firebase/app-types';
1918
import {
2019
FirebaseAnalytics,
2120
Gtag,
@@ -38,6 +37,7 @@ import {
3837
} from './helpers';
3938
import { ANALYTICS_ID_FIELD } from './constants';
4039
import { AnalyticsError, ERROR_FACTORY } from './errors';
40+
import { FirebaseApp } from '@firebase/app-types';
4141

4242
/**
4343
* Maps gaId to FID fetch promises.
@@ -102,11 +102,7 @@ export function settings(options: SettingsOptions): void {
102102
}
103103
}
104104

105-
export function factory(
106-
app: FirebaseApp,
107-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
108-
extendApp: (props: { [prop: string]: any }) => void
109-
): FirebaseAnalytics {
105+
export function factory(app: FirebaseApp): FirebaseAnalytics {
110106
if (!app.options[ANALYTICS_ID_FIELD]) {
111107
throw ERROR_FACTORY.create(AnalyticsError.NO_GA_ID);
112108
}
@@ -162,13 +158,5 @@ export function factory(
162158
setAnalyticsCollectionEnabled(app.options[ANALYTICS_ID_FIELD], enabled)
163159
};
164160

165-
extendApp({
166-
INTERNAL: {
167-
analytics: {
168-
logEvent: analyticsInstance.logEvent
169-
}
170-
}
171-
});
172-
173161
return analyticsInstance;
174162
}

0 commit comments

Comments
 (0)