Skip to content

Commit ddeff83

Browse files
authored
Installations heartbeat implementation (#5966)
1 parent 2d672ce commit ddeff83

15 files changed

+141
-85
lines changed

.changeset/proud-otters-tap.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@firebase/installations': patch
3+
---
4+
5+
Update platform logging code to send to new endpoint.

packages/installations/src/api/get-id.test.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,14 @@ import {
2626
import { getFakeInstallations } from '../testing/fake-generators';
2727
import '../testing/setup';
2828
import { getId } from './get-id';
29-
import {
30-
FirebaseInstallationsImpl,
31-
AppConfig
32-
} from '../interfaces/installation-impl';
29+
import { FirebaseInstallationsImpl } from '../interfaces/installation-impl';
3330

3431
const FID = 'disciples-of-the-watch';
3532

3633
describe('getId', () => {
3734
let installations: FirebaseInstallationsImpl;
3835
let getInstallationEntrySpy: SinonStub<
39-
[AppConfig],
36+
[FirebaseInstallationsImpl],
4037
Promise<getInstallationEntryModule.InstallationEntryWithRegistrationPromise>
4138
>;
4239

packages/installations/src/api/get-id.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { Installations } from '../interfaces/public-types';
3030
export async function getId(installations: Installations): Promise<string> {
3131
const installationsImpl = installations as FirebaseInstallationsImpl;
3232
const { installationEntry, registrationPromise } = await getInstallationEntry(
33-
installationsImpl.appConfig
33+
installationsImpl
3434
);
3535

3636
if (registrationPromise) {

packages/installations/src/api/get-token.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ const setupInstallationEntryMap: Map<
175175
describe('getToken', () => {
176176
let installations: FirebaseInstallationsImpl;
177177
let createInstallationRequestSpy: SinonStub<
178-
[AppConfig, InProgressInstallationEntry],
178+
[FirebaseInstallationsImpl, InProgressInstallationEntry],
179179
Promise<RegisteredInstallationEntry>
180180
>;
181181
let generateAuthTokenRequestSpy: SinonStub<

packages/installations/src/api/get-token.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@
1717

1818
import { getInstallationEntry } from '../helpers/get-installation-entry';
1919
import { refreshAuthToken } from '../helpers/refresh-auth-token';
20-
import {
21-
FirebaseInstallationsImpl,
22-
AppConfig
23-
} from '../interfaces/installation-impl';
20+
import { FirebaseInstallationsImpl } from '../interfaces/installation-impl';
2421
import { Installations } from '../interfaces/public-types';
2522

2623
/**
@@ -36,7 +33,7 @@ export async function getToken(
3633
forceRefresh = false
3734
): Promise<string> {
3835
const installationsImpl = installations as FirebaseInstallationsImpl;
39-
await completeInstallationRegistration(installationsImpl.appConfig);
36+
await completeInstallationRegistration(installationsImpl);
4037

4138
// At this point we either have a Registered Installation in the DB, or we've
4239
// already thrown an error.
@@ -45,9 +42,9 @@ export async function getToken(
4542
}
4643

4744
async function completeInstallationRegistration(
48-
appConfig: AppConfig
45+
installations: FirebaseInstallationsImpl
4946
): Promise<void> {
50-
const { registrationPromise } = await getInstallationEntry(appConfig);
47+
const { registrationPromise } = await getInstallationEntry(installations);
5148

5249
if (registrationPromise) {
5350
// A createInstallation request is in progress. Wait until it finishes.

packages/installations/src/api/on-id-change.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { Installations } from '../interfaces/public-types';
2626
*/
2727
export type IdChangeCallbackFn = (installationId: string) => void;
2828
/**
29-
* Unsubscribe a callback function previously added via {@link #IdChangeCallbackFn}.
29+
* Unsubscribe a callback function previously added via {@link IdChangeCallbackFn}.
3030
*
3131
* @public
3232
*/

packages/installations/src/functions/config.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ const publicFactory: InstanceFactory<'installations'> = (
3636
const app = container.getProvider('app').getImmediate();
3737
// Throws if app isn't configured properly.
3838
const appConfig = extractAppConfig(app);
39-
const platformLoggerProvider = _getProvider(app, 'platform-logger');
39+
const heartbeatServiceProvider = _getProvider(app, 'heartbeat');
4040

4141
const installationsImpl: FirebaseInstallationsImpl = {
4242
app,
4343
appConfig,
44-
platformLoggerProvider,
44+
heartbeatServiceProvider,
4545
_delete: () => Promise.resolve()
4646
};
4747
return installationsImpl;

packages/installations/src/functions/create-installation-request.test.ts

+21-11
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ import { FirebaseError } from '@firebase/util';
1919
import { expect } from 'chai';
2020
import { SinonStub, stub } from 'sinon';
2121
import { CreateInstallationResponse } from '../interfaces/api-response';
22-
import { AppConfig } from '../interfaces/installation-impl';
22+
import { FirebaseInstallationsImpl } from '../interfaces/installation-impl';
2323
import {
2424
InProgressInstallationEntry,
2525
RequestStatus
2626
} from '../interfaces/installation-entry';
2727
import { compareHeaders } from '../testing/compare-headers';
28-
import { getFakeAppConfig } from '../testing/fake-generators';
28+
import { getFakeInstallations } from '../testing/fake-generators';
2929
import '../testing/setup';
3030
import {
3131
INSTALLATIONS_API_URL,
@@ -38,13 +38,13 @@ import { createInstallationRequest } from './create-installation-request';
3838
const FID = 'defenders-of-the-faith';
3939

4040
describe('createInstallationRequest', () => {
41-
let appConfig: AppConfig;
41+
let fakeInstallations: FirebaseInstallationsImpl;
4242
let fetchSpy: SinonStub<[RequestInfo, RequestInit?], Promise<Response>>;
4343
let inProgressInstallationEntry: InProgressInstallationEntry;
4444
let response: CreateInstallationResponse;
4545

4646
beforeEach(() => {
47-
appConfig = getFakeAppConfig();
47+
fakeInstallations = getFakeInstallations();
4848

4949
inProgressInstallationEntry = {
5050
fid: FID,
@@ -71,7 +71,7 @@ describe('createInstallationRequest', () => {
7171

7272
it('registers a pending InstallationEntry', async () => {
7373
const registeredInstallationEntry = await createInstallationRequest(
74-
appConfig,
74+
fakeInstallations,
7575
inProgressInstallationEntry
7676
);
7777
expect(registeredInstallationEntry.registrationStatus).to.equal(
@@ -83,12 +83,13 @@ describe('createInstallationRequest', () => {
8383
const expectedHeaders = new Headers({
8484
'Content-Type': 'application/json',
8585
Accept: 'application/json',
86-
'x-goog-api-key': 'apiKey'
86+
'x-goog-api-key': 'apiKey',
87+
'x-firebase-client': 'a/1.2.3 b/2.3.4'
8788
});
8889
const expectedBody = {
8990
fid: FID,
9091
authVersion: INTERNAL_AUTH_VERSION,
91-
appId: appConfig.appId,
92+
appId: fakeInstallations.appConfig.appId,
9293
sdkVersion: PACKAGE_VERSION
9394
};
9495
const expectedRequest: RequestInit = {
@@ -98,7 +99,10 @@ describe('createInstallationRequest', () => {
9899
};
99100
const expectedEndpoint = `${INSTALLATIONS_API_URL}/projects/projectId/installations`;
100101

101-
await createInstallationRequest(appConfig, inProgressInstallationEntry);
102+
await createInstallationRequest(
103+
fakeInstallations,
104+
inProgressInstallationEntry
105+
);
102106
expect(fetchSpy).to.be.calledOnceWith(expectedEndpoint, expectedRequest);
103107
const actualHeaders = fetchSpy.lastCall.lastArg.headers;
104108
compareHeaders(expectedHeaders, actualHeaders);
@@ -117,7 +121,7 @@ describe('createInstallationRequest', () => {
117121
fetchSpy.resolves(new Response(JSON.stringify(response)));
118122

119123
const registeredInstallationEntry = await createInstallationRequest(
120-
appConfig,
124+
fakeInstallations,
121125
inProgressInstallationEntry
122126
);
123127
expect(registeredInstallationEntry.fid).to.equal(FID);
@@ -138,7 +142,10 @@ describe('createInstallationRequest', () => {
138142
);
139143

140144
await expect(
141-
createInstallationRequest(appConfig, inProgressInstallationEntry)
145+
createInstallationRequest(
146+
fakeInstallations,
147+
inProgressInstallationEntry
148+
)
142149
).to.be.rejectedWith(FirebaseError);
143150
});
144151

@@ -157,7 +164,10 @@ describe('createInstallationRequest', () => {
157164
fetchSpy.onCall(1).resolves(new Response(JSON.stringify(response)));
158165

159166
await expect(
160-
createInstallationRequest(appConfig, inProgressInstallationEntry)
167+
createInstallationRequest(
168+
fakeInstallations,
169+
inProgressInstallationEntry
170+
)
161171
).to.be.fulfilled;
162172
expect(fetchSpy).to.be.calledTwice;
163173
});

packages/installations/src/functions/create-installation-request.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,27 @@ import {
2929
getInstallationsEndpoint,
3030
retryIfServerError
3131
} from './common';
32-
import { AppConfig } from '../interfaces/installation-impl';
32+
import { FirebaseInstallationsImpl } from '../interfaces/installation-impl';
3333

3434
export async function createInstallationRequest(
35-
appConfig: AppConfig,
35+
{ appConfig, heartbeatServiceProvider }: FirebaseInstallationsImpl,
3636
{ fid }: InProgressInstallationEntry
3737
): Promise<RegisteredInstallationEntry> {
3838
const endpoint = getInstallationsEndpoint(appConfig);
3939

4040
const headers = getHeaders(appConfig);
41+
42+
// If heartbeat service exists, add the heartbeat string to the header.
43+
const heartbeatService = heartbeatServiceProvider.getImmediate({
44+
optional: true
45+
});
46+
if (heartbeatService) {
47+
const heartbeatsHeader = await heartbeatService.getHeartbeatsHeader();
48+
if (heartbeatsHeader) {
49+
headers.append('x-firebase-client', heartbeatsHeader);
50+
}
51+
}
52+
4153
const body = {
4254
fid,
4355
authVersion: INTERNAL_AUTH_VERSION,

packages/installations/src/functions/generate-auth-token-request.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ describe('generateAuthTokenRequest', () => {
9191
});
9292
const expectedBody = {
9393
installation: {
94-
sdkVersion: PACKAGE_VERSION
94+
sdkVersion: PACKAGE_VERSION,
95+
appId: installations.appConfig.appId
9596
}
9697
};
9798
const expectedRequest: RequestInit = {

packages/installations/src/functions/generate-auth-token-request.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,28 @@ import {
3434
} from '../interfaces/installation-impl';
3535

3636
export async function generateAuthTokenRequest(
37-
{ appConfig, platformLoggerProvider }: FirebaseInstallationsImpl,
37+
{ appConfig, heartbeatServiceProvider }: FirebaseInstallationsImpl,
3838
installationEntry: RegisteredInstallationEntry
3939
): Promise<CompletedAuthToken> {
4040
const endpoint = getGenerateAuthTokenEndpoint(appConfig, installationEntry);
4141

4242
const headers = getHeadersWithAuth(appConfig, installationEntry);
4343

44-
// If platform logger exists, add the platform info string to the header.
45-
const platformLogger = platformLoggerProvider.getImmediate({
44+
// If heartbeat service exists, add the heartbeat string to the header.
45+
const heartbeatService = heartbeatServiceProvider.getImmediate({
4646
optional: true
4747
});
48-
if (platformLogger) {
49-
headers.append('x-firebase-client', platformLogger.getPlatformInfoString());
48+
if (heartbeatService) {
49+
const heartbeatsHeader = await heartbeatService.getHeartbeatsHeader();
50+
if (heartbeatsHeader) {
51+
headers.append('x-firebase-client', heartbeatsHeader);
52+
}
5053
}
5154

5255
const body = {
5356
installation: {
54-
sdkVersion: PACKAGE_VERSION
57+
sdkVersion: PACKAGE_VERSION,
58+
appId: appConfig.appId
5559
}
5660
};
5761

0 commit comments

Comments
 (0)