Skip to content

Instantly share code, notes, and snippets.

@mattbajorek
Last active August 23, 2023 06:03
Show Gist options
  • Save mattbajorek/99291d08ae077a896f45ea2a9a110cc7 to your computer and use it in GitHub Desktop.
Save mattbajorek/99291d08ae077a896f45ea2a9a110cc7 to your computer and use it in GitHub Desktop.
Full notification service
import { Injectable } from '@nestjs/common';
import { mapLimit } from 'async';
import * as firebase from 'firebase-admin';
import { BatchResponse } from 'firebase-admin/lib/messaging/messaging-api';
import { chunk } from 'lodash';
import * as shell from 'shelljs';
export interface ISendFirebaseMessages {
token: string;
title?: string;
message: string;
}
@Injectable()
export class NotificationsService {
constructor() {
// For simplicity these credentials are just stored in the environment
// However these should be stored in a key management system
const firebaseCredentials = JSON.parse(process.env.FIREBASE_CREDENTIAL_JSON);
firebase.initializeApp({
credential: firebase.credential.cert(firebaseCredentials),
databaseURL: process.env.FIREBASE_DATABASE_URL,
});
}
public async sendFirebaseMessages(firebaseMessages: ISendFirebaseMessages[], dryRun?: boolean): Promise<BatchResponse> {
const batchedFirebaseMessages = chunk(firebaseMessages, 500);
const batchResponses = await mapLimit<ISendFirebaseMessages[], BatchResponse>(
batchedFirebaseMessages,
process.env.FIREBASE_PARALLEL_LIMIT, // 3 is a good place to start
async (groupedFirebaseMessages: ISendFirebaseMessages[]): Promise<BatchResponse> => {
try {
const tokenMessages: firebase.messaging.TokenMessage[] = groupedFirebaseMessages.map(({ message, title, token }) => ({
notification: { body: message, title },
token,
apns: {
payload: {
aps: {
'content-available': 1,
},
},
},
}));
return await this.sendAll(tokenMessages, dryRun);
} catch (error) {
return {
responses: groupedFirebaseMessages.map(() => ({
success: false,
error,
})),
successCount: 0,
failureCount: groupedFirebaseMessages.length,
};
}
},
);
return batchResponses.reduce(
({ responses, successCount, failureCount }, currentResponse) => {
return {
responses: responses.concat(currentResponse.responses),
successCount: successCount + currentResponse.successCount,
failureCount: failureCount + currentResponse.failureCount,
};
},
({
responses: [],
successCount: 0,
failureCount: 0,
} as unknown) as BatchResponse,
);
}
public async sendAll(messages: firebase.messaging.TokenMessage[], dryRun?: boolean): Promise<BatchResponse> {
if (process.env.NODE_ENV === 'local') {
for (const { notification, token } of messages) {
shell.exec(
`echo '{ "aps": { "alert": ${JSON.stringify(notification)}, "token": "${token}" } }' | xcrun simctl push booted com.company.appname -`,
);
}
}
return firebase.messaging().sendAll(messages, dryRun);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment