Skip to content

Commit 358e9f5

Browse files
committed
Migrate messaging to component framework
1 parent af4fdc4 commit 358e9f5

21 files changed

+571
-187
lines changed

packages/app-types/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Provider } from '@firebase/component';
2+
13
/**
24
* @license
35
* Copyright 2017 Google Inc.

packages/messaging/index.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,41 +16,53 @@
1616
*/
1717

1818
import firebase from '@firebase/app';
19+
import '@firebase/installations';
1920
import {
2021
_FirebaseNamespace,
21-
FirebaseServiceFactory
22+
FirebaseService
2223
} from '@firebase/app-types/private';
2324
import { FirebaseMessaging } from '@firebase/messaging-types';
24-
2525
import { SwController } from './src/controllers/sw-controller';
2626
import { WindowController } from './src/controllers/window-controller';
2727
import { ErrorCode, errorFactory } from './src/models/errors';
28+
import {
29+
Component,
30+
ComponentType,
31+
ComponentContainer
32+
} from '@firebase/component';
2833

2934
export function registerMessaging(instance: _FirebaseNamespace): void {
3035
const messagingName = 'messaging';
3136

32-
const factoryMethod: FirebaseServiceFactory = app => {
37+
const factoryMethod = (container: ComponentContainer): FirebaseService => {
38+
/* Dependencies */
39+
const app = container.getProvider('app').getImmediate();
40+
const installations = container.getProvider('installations').getImmediate();
41+
const analyticsProvider = container.getProvider('analytics-internal');
42+
3343
if (!isSupported()) {
3444
throw errorFactory.create(ErrorCode.UNSUPPORTED_BROWSER);
3545
}
3646

3747
if (self && 'ServiceWorkerGlobalScope' in self) {
3848
// Running in ServiceWorker context
39-
return new SwController(app);
49+
return new SwController(app, installations);
4050
} else {
4151
// Assume we are in the window context.
42-
return new WindowController(app);
52+
return new WindowController(app, installations, analyticsProvider);
4353
}
4454
};
4555

4656
const namespaceExports = {
4757
isSupported
4858
};
4959

50-
instance.INTERNAL.registerService(
51-
messagingName,
52-
factoryMethod,
53-
namespaceExports
60+
instance.INTERNAL.registerComponent(
61+
new Component(
62+
messagingName,
63+
factoryMethod,
64+
ComponentType.PUBLIC
65+
).setServiceProps(namespaceExports)
5466
);
5567
}
5668

packages/messaging/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@firebase/installations": "0.3.2",
3030
"@firebase/messaging-types": "0.3.4",
3131
"@firebase/util": "0.2.31",
32+
"@firebase/component": "0.1.0",
3233
"tslib": "1.10.0"
3334
},
3435
"devDependencies": {

packages/messaging/src/controllers/base-controller.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { ErrorCode, errorFactory } from '../models/errors';
3333
import { SubscriptionManager } from '../models/subscription-manager';
3434
import { TokenDetailsModel } from '../models/token-details-model';
3535
import { VapidDetailsModel } from '../models/vapid-details-model';
36+
import { FirebaseInstallations } from '@firebase/installations-types';
3637

3738
export type BgMessageHandler = (
3839
payload: MessagePayload
@@ -47,7 +48,10 @@ export abstract class BaseController implements FirebaseMessaging {
4748
private readonly vapidDetailsModel = new VapidDetailsModel();
4849
private readonly subscriptionManager = new SubscriptionManager();
4950

50-
constructor(readonly app: FirebaseApp) {
51+
constructor(
52+
readonly app: FirebaseApp,
53+
private readonly installations: FirebaseInstallations
54+
) {
5155
if (
5256
!app.options.messagingSenderId ||
5357
typeof app.options.messagingSenderId !== 'string'
@@ -59,7 +63,7 @@ export abstract class BaseController implements FirebaseMessaging {
5963
delete: () => this.delete()
6064
};
6165

62-
this.tokenDetailsModel = new TokenDetailsModel(app);
66+
this.tokenDetailsModel = new TokenDetailsModel(app, installations);
6367
}
6468

6569
async getToken(): Promise<string | null> {
@@ -148,6 +152,7 @@ export abstract class BaseController implements FirebaseMessaging {
148152
const updatedToken = await this.subscriptionManager.updateToken(
149153
tokenDetails,
150154
this.app,
155+
this.installations,
151156
pushSubscription,
152157
publicVapidKey
153158
);
@@ -182,6 +187,7 @@ export abstract class BaseController implements FirebaseMessaging {
182187
): Promise<string> {
183188
const newToken = await this.subscriptionManager.getToken(
184189
this.app,
190+
this.installations,
185191
pushSubscription,
186192
publicVapidKey
187193
);
@@ -228,7 +234,11 @@ export abstract class BaseController implements FirebaseMessaging {
228234
*/
229235
private async deleteTokenFromDB(token: string): Promise<void> {
230236
const tokenDetails = await this.tokenDetailsModel.deleteToken(token);
231-
await this.subscriptionManager.deleteToken(this.app, tokenDetails);
237+
await this.subscriptionManager.deleteToken(
238+
this.app,
239+
this.installations,
240+
tokenDetails
241+
);
232242
}
233243

234244
// Visible for testing

packages/messaging/src/controllers/sw-controller.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from '../models/fcm-details';
3131
import { InternalMessage, MessageType } from '../models/worker-page-message';
3232
import { BaseController, BgMessageHandler } from './base-controller';
33+
import { FirebaseInstallations } from '@firebase/installations-types';
3334

3435
// Let TS know that this is a service worker
3536
declare const self: ServiceWorkerGlobalScope;
@@ -39,8 +40,8 @@ const FCM_MSG = 'FCM_MSG';
3940
export class SwController extends BaseController {
4041
private bgMessageHandler: BgMessageHandler | null = null;
4142

42-
constructor(app: FirebaseApp) {
43-
super(app);
43+
constructor(app: FirebaseApp, installations: FirebaseInstallations) {
44+
super(app, installations);
4445

4546
self.addEventListener('push', e => {
4647
this.onPush(e);

packages/messaging/src/controllers/window-controller.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ import {
3939
} from '../models/fcm-details';
4040
import { InternalMessage, MessageType } from '../models/worker-page-message';
4141
import { BaseController } from './base-controller';
42+
import { FirebaseAnalyticsInternal } from '@firebase/analytics-interop-types';
43+
import { Provider } from '@firebase/component';
44+
import { FirebaseInstallations } from '@firebase/installations-types';
4245

4346
export class WindowController extends BaseController {
4447
private registrationToUse: ServiceWorkerRegistration | null = null;
@@ -63,8 +66,12 @@ export class WindowController extends BaseController {
6366
/**
6467
* A service that provides a MessagingService instance.
6568
*/
66-
constructor(app: FirebaseApp) {
67-
super(app);
69+
constructor(
70+
app: FirebaseApp,
71+
installations: FirebaseInstallations,
72+
readonly analyticsProvider: Provider<FirebaseAnalyticsInternal>
73+
) {
74+
super(app, installations);
6875

6976
this.setupSWMessageListener_();
7077
}
@@ -308,16 +315,23 @@ export class WindowController extends BaseController {
308315
// This message has a campaign id, meaning it was sent using the FN Console.
309316
// Analytics is enabled on this message, so we should log it.
310317
const eventType = getEventType(firebaseMessagingType);
311-
(this.app as _FirebaseApp).INTERNAL.analytics.logEvent(
312-
eventType,
313-
/* eslint-disable camelcase */
314-
{
315-
message_name: data[FN_CAMPAIGN_NAME],
316-
message_id: data[FN_CAMPAIGN_ID],
317-
message_time: data[FN_CAMPAIGN_TIME],
318-
message_device_time: Math.floor(Date.now() / 1000)
318+
this.analyticsProvider.get().then(
319+
analytics => {
320+
analytics.logEvent(
321+
eventType,
322+
/* eslint-disable camelcase */
323+
{
324+
message_name: data[FN_CAMPAIGN_NAME],
325+
message_id: data[FN_CAMPAIGN_ID],
326+
message_time: data[FN_CAMPAIGN_TIME],
327+
message_device_time: Math.floor(Date.now() / 1000)
328+
}
329+
/* eslint-enable camelcase */
330+
);
331+
},
332+
() => {
333+
/* it will never reject */
319334
}
320-
/* eslint-enable camelcase */
321335
);
322336
}
323337
},

packages/messaging/src/models/clean-v1-undefined.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,16 @@
2929

3030
import { SubscriptionManager } from './subscription-manager';
3131
import { FirebaseApp } from '@firebase/app-types';
32+
import { FirebaseInstallations } from '@firebase/installations-types';
3233

3334
const OLD_DB_NAME = 'undefined';
3435
const OLD_OBJECT_STORE_NAME = 'fcm_token_object_Store';
3536

36-
function handleDb(db: IDBDatabase, app: FirebaseApp): void {
37+
function handleDb(
38+
db: IDBDatabase,
39+
app: FirebaseApp,
40+
installations: FirebaseInstallations
41+
): void {
3742
if (!db.objectStoreNames.contains(OLD_OBJECT_STORE_NAME)) {
3843
// We found a database with the name 'undefined', but our expected object
3944
// store isn't defined.
@@ -59,7 +64,7 @@ function handleDb(db: IDBDatabase, app: FirebaseApp): void {
5964
const tokenDetails = cursor.value;
6065

6166
// eslint-disable-next-line @typescript-eslint/no-floating-promises
62-
subscriptionManager.deleteToken(app, tokenDetails);
67+
subscriptionManager.deleteToken(app, installations, tokenDetails);
6368

6469
cursor.continue();
6570
} else {
@@ -69,13 +74,16 @@ function handleDb(db: IDBDatabase, app: FirebaseApp): void {
6974
};
7075
}
7176

72-
export function cleanV1(app: FirebaseApp): void {
77+
export function cleanV1(
78+
app: FirebaseApp,
79+
installations: FirebaseInstallations
80+
): void {
7381
const request: IDBOpenDBRequest = indexedDB.open(OLD_DB_NAME);
7482
request.onerror = _event => {
7583
// NOOP - Nothing we can do.
7684
};
7785
request.onsuccess = _event => {
7886
const db = request.result;
79-
handleDb(db, app);
87+
handleDb(db, app, installations);
8088
};
8189
}

packages/messaging/src/models/subscription-manager.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import { isArrayBufferEqual } from '../helpers/is-array-buffer-equal';
2020
import { ErrorCode, errorFactory } from './errors';
2121
import { DEFAULT_PUBLIC_VAPID_KEY, ENDPOINT } from './fcm-details';
2222
import { FirebaseApp } from '@firebase/app-types';
23-
import '@firebase/installations';
2423
import { TokenDetails } from '../interfaces/token-details';
24+
import { FirebaseInstallations } from '@firebase/installations-types';
2525

2626
interface ApiResponse {
2727
token?: string;
@@ -40,10 +40,11 @@ interface TokenRequestBody {
4040
export class SubscriptionManager {
4141
async getToken(
4242
app: FirebaseApp,
43+
installations: FirebaseInstallations,
4344
subscription: PushSubscription,
4445
vapidKey: Uint8Array
4546
): Promise<string> {
46-
const headers = await getHeaders(app);
47+
const headers = await getHeaders(app, installations);
4748
const body = getBody(subscription, vapidKey);
4849

4950
const subscribeOptions = {
@@ -82,10 +83,11 @@ export class SubscriptionManager {
8283
async updateToken(
8384
tokenDetails: TokenDetails,
8485
app: FirebaseApp,
86+
installations: FirebaseInstallations,
8587
subscription: PushSubscription,
8688
vapidKey: Uint8Array
8789
): Promise<string> {
88-
const headers = await getHeaders(app);
90+
const headers = await getHeaders(app, installations);
8991
const body = getBody(subscription, vapidKey);
9092

9193
const updateOptions = {
@@ -123,10 +125,11 @@ export class SubscriptionManager {
123125

124126
async deleteToken(
125127
app: FirebaseApp,
128+
installations: FirebaseInstallations,
126129
tokenDetails: TokenDetails
127130
): Promise<void> {
128131
// TODO: Add FIS header
129-
const headers = await getHeaders(app);
132+
const headers = await getHeaders(app, installations);
130133

131134
const unsubscribeOptions = {
132135
method: 'DELETE',
@@ -157,8 +160,10 @@ function getEndpoint(app: FirebaseApp): string {
157160
return `${ENDPOINT}/projects/${app.options.projectId!}/registrations`;
158161
}
159162

160-
async function getHeaders(app: FirebaseApp): Promise<Headers> {
161-
const installations = app.installations();
163+
async function getHeaders(
164+
app: FirebaseApp,
165+
installations: FirebaseInstallations
166+
): Promise<Headers> {
162167
const authToken = await installations.getToken();
163168

164169
return new Headers({

packages/messaging/src/models/token-details-model.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@ import { cleanV1 } from './clean-v1-undefined';
2121
import { DbInterface } from './db-interface';
2222
import { ErrorCode, errorFactory } from './errors';
2323
import { FirebaseApp } from '@firebase/app-types';
24+
import { FirebaseInstallations } from '@firebase/installations-types';
2425

2526
export class TokenDetailsModel extends DbInterface {
2627
protected readonly dbName: string = 'fcm_token_details_db';
2728
protected readonly dbVersion: number = 4;
2829
protected readonly objectStoreName: string = 'fcm_token_object_Store';
2930

30-
constructor(private readonly app: FirebaseApp) {
31+
constructor(
32+
private readonly app: FirebaseApp,
33+
private readonly installations: FirebaseInstallations
34+
) {
3135
super();
3236
}
3337

@@ -57,7 +61,7 @@ export class TokenDetailsModel extends DbInterface {
5761
// Prior to version 2, we were using either 'fcm_token_details_db'
5862
// or 'undefined' as the database name due to bug in the SDK
5963
// So remove the old tokens and databases.
60-
cleanV1(this.app);
64+
cleanV1(this.app, this.installations);
6165
}
6266

6367
case 2: {

packages/messaging/test/constructor.test.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,15 @@ import { SwController } from '../src/controllers/sw-controller';
2222
import { WindowController } from '../src/controllers/window-controller';
2323
import { ErrorCode } from '../src/models/errors';
2424

25-
import { makeFakeApp } from './testing-utils/make-fake-app';
25+
import {
26+
makeFakeApp,
27+
makeFakeInstallations
28+
} from './testing-utils/make-fake-app';
29+
import { makeFakeAnalyticsProvider } from './testing-utils/make-fake-providers';
2630

2731
describe('Firebase Messaging > new *Controller()', () => {
32+
const analyticsProvider = makeFakeAnalyticsProvider();
33+
const installations = makeFakeInstallations();
2834
it('should handle bad input', () => {
2935
const badInputs = [
3036
makeFakeApp({
@@ -45,8 +51,8 @@ describe('Firebase Messaging > new *Controller()', () => {
4551
];
4652
badInputs.forEach(badInput => {
4753
try {
48-
new WindowController(badInput);
49-
new SwController(badInput);
54+
new WindowController(badInput, installations, analyticsProvider);
55+
new SwController(badInput, installations);
5056

5157
assert.fail(
5258
`Bad Input should have thrown: ${JSON.stringify(badInput)}`
@@ -60,7 +66,7 @@ describe('Firebase Messaging > new *Controller()', () => {
6066

6167
it('should be able to handle good input', () => {
6268
const app = makeFakeApp();
63-
new WindowController(app);
64-
new SwController(app);
69+
new WindowController(app, installations, analyticsProvider);
70+
new SwController(app, installations);
6571
});
6672
});

0 commit comments

Comments
 (0)