diff --git a/README.md b/README.md index 58bdafd..888e6ca 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,10 @@ import { Expo } from 'expo-server-sdk'; // Create a new Expo SDK client // optionally providing an access token if you have enabled push security -let expo = new Expo({ accessToken: process.env.EXPO_ACCESS_TOKEN }); +let expo = new Expo({ + accessToken: process.env.EXPO_ACCESS_TOKEN, + useFcmV1: false // this can be set to true in order to use the FCM v1 API +}); // Create the messages that you want to send to clients let messages = []; diff --git a/src/ExpoClient.ts b/src/ExpoClient.ts index 3ac546e..15c5af3 100644 --- a/src/ExpoClient.ts +++ b/src/ExpoClient.ts @@ -40,6 +40,7 @@ export class Expo { private httpAgent: Agent | undefined; private limitConcurrentRequests: (thunk: () => Promise) => Promise; private accessToken: string | undefined; + private useFcmV1: boolean | undefined; constructor(options: ExpoClientOptions = {}) { this.httpAgent = options.httpAgent; @@ -49,6 +50,7 @@ export class Expo { : DEFAULT_CONCURRENT_REQUEST_LIMIT ); this.accessToken = options.accessToken; + this.useFcmV1 = options.useFcmV1; } /** @@ -75,13 +77,17 @@ export class Expo { * sized chunks. */ async sendPushNotificationsAsync(messages: ExpoPushMessage[]): Promise { + // @ts-expect-error We don't yet have type declarations for URL + const url = new URL(`${BASE_API_URL}/push/send`); + if (typeof this.useFcmV1 === 'boolean') { + url.searchParams.append('useFcmV1', this.useFcmV1); + } const actualMessagesCount = Expo._getActualMessageCount(messages); - const data = await this.limitConcurrentRequests(async () => { return await promiseRetry( async (retry): Promise => { try { - return await this.requestAsync(`${BASE_API_URL}/push/send`, { + return await this.requestAsync(url.toString(), { httpMethod: 'post', body: messages, shouldCompress(body) { @@ -353,6 +359,7 @@ export type ExpoClientOptions = { httpAgent?: Agent; maxConcurrentRequests?: number; accessToken?: string; + useFcmV1?: boolean; }; export type ExpoPushToken = string; diff --git a/src/__tests__/ExpoClient-test.ts b/src/__tests__/ExpoClient-test.ts index de4d319..7682017 100644 --- a/src/__tests__/ExpoClient-test.ts +++ b/src/__tests__/ExpoClient-test.ts @@ -49,6 +49,34 @@ describe('sending push notification messages', () => { expect(options.headers.get('Authorization')).toContain('Bearer foobar'); }); + describe('the useFcmV1 option', () => { + beforeEach(() => { + (fetch as any).any({ data: [{ status: 'ok', id: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' }] }); + }); + + test('sends requests to the Expo API server without the useFcmV1 parameter', async () => { + const client = new ExpoClient(); + await client.sendPushNotificationsAsync([{ to: 'a' }]); + expect((fetch as any).called('https://exp.host/--/api/v2/push/send')).toBe(true); + }); + + test('sends requests to the Expo API server with useFcmV1=true', async () => { + const client = new ExpoClient({ useFcmV1: true }); + await client.sendPushNotificationsAsync([{ to: 'a' }]); + expect((fetch as any).called('https://exp.host/--/api/v2/push/send?useFcmV1=true')).toBe( + true + ); + }); + + test('sends requests to the Expo API server with useFcmV1=false', async () => { + const client = new ExpoClient({ useFcmV1: false }); + await client.sendPushNotificationsAsync([{ to: 'a' }]); + expect((fetch as any).called('https://exp.host/--/api/v2/push/send?useFcmV1=false')).toBe( + true + ); + }); + }); + test('compresses request bodies over 1 KiB', async () => { const mockTickets = [{ status: 'ok', id: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' }]; (fetch as any).mock('https://exp.host/--/api/v2/push/send', { data: mockTickets });