Skip to content

Commit e5593ce

Browse files
FCM sample updated for CF3v2 (#1112)
* FCM sample updated for CF3v2 * pnpm lock file
1 parent 9928db1 commit e5593ce

File tree

14 files changed

+882
-839
lines changed

14 files changed

+882
-839
lines changed

Node/fcm-notifications/.gitignore

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
firebase-debug.log*
8+
firebase-debug.*.log*
9+
10+
# Firebase cache
11+
.firebase/
12+
13+
# Firebase config
14+
15+
# Uncomment this if you'd like others to create their own Firebase project.
16+
# For a team working on the same Firebase project(s), it is recommended to leave
17+
# it commented so all members can deploy to the same project(s) in .firebaserc.
18+
# .firebaserc
19+
20+
# Runtime data
21+
pids
22+
*.pid
23+
*.seed
24+
*.pid.lock
25+
26+
# Directory for instrumented libs generated by jscoverage/JSCover
27+
lib-cov
28+
29+
# Coverage directory used by tools like istanbul
30+
coverage
31+
32+
# nyc test coverage
33+
.nyc_output
34+
35+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36+
.grunt
37+
38+
# Bower dependency directory (https://bower.io/)
39+
bower_components
40+
41+
# node-waf configuration
42+
.lock-wscript
43+
44+
# Compiled binary addons (http://nodejs.org/api/addons.html)
45+
build/Release
46+
47+
# Dependency directories
48+
node_modules/
49+
50+
# Optional npm cache directory
51+
.npm
52+
53+
# Optional eslint cache
54+
.eslintcache
55+
56+
# Optional REPL history
57+
.node_repl_history
58+
59+
# Output of 'npm pack'
60+
*.tgz
61+
62+
# Yarn Integrity file
63+
.yarn-integrity
64+
65+
# dotenv environment variables file
66+
.env

Node/fcm-notifications/README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Send Firebase Cloud Messaging notifications for new followers.
2+
3+
This sample demonstrates how to send a Firebase Cloud Messaging (FCM) notification from a Realtime Database triggered Function. The sample also features a Web UI to experience the FCM notification.
4+
5+
6+
## Functions Code
7+
8+
See file [functions/index.js](functions/index.js) for the code.
9+
10+
Sending the notification is done using the [Firebase Admin SDK](https://www.npmjs.com/package/firebase-admin). The Web client writes the individual device tokens to the realtime database which the Function uses to send the notification.
11+
12+
The dependencies are listed in [functions/package.json](functions/package.json).
13+
14+
15+
## Sample Database Structure
16+
17+
Users sign into the app and are requested to enable notifications on their browsers. If they successfully enable notifications the device token is saved into the datastore under `/users/$uid/notificationTokens`.:
18+
19+
```
20+
/functions-project-12345
21+
/users
22+
/Uid-12345
23+
displayName: "Bob Dole"
24+
/notificationTokens
25+
1234567890: true
26+
photoURL: "https://lh3.googleusercontent.com/..."
27+
28+
```
29+
30+
If a user starts following another user we'll write to `/followers/$followedUid/$followerUid`:
31+
32+
```
33+
/functions-project-12345
34+
/followers
35+
/followedUid-12345
36+
followerUid-67890: true
37+
/users
38+
/Uid-12345
39+
displayName: "Bob Dole"
40+
/notificationTokens
41+
1234567890: true
42+
photoURL: "https://lh3.googleusercontent.com/..."
43+
44+
```
45+
46+
47+
## Trigger rules
48+
49+
The function triggers every time the value of a follow flag changes at `/followers/$followedUid/$followerUid`.
50+
51+
52+
## Deploy and test
53+
54+
This sample comes with a web-based UI for testing the function. To test it out:
55+
56+
1. Set up your Firebase project:
57+
1. [Create a Firebase project](https://firebase.google.com/docs/web/setup/#create-firebase-project)
58+
1. [Register your web app with Firebase](https://firebase.google.com/docs/web/setup/#register-app)
59+
1. Enable **Google Provider** in the [Auth section](https://console.firebase.google.com/project/_/authentication/providers)
60+
1. Clone or download this repo and open the `fcm-notification` directory.
61+
1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.
62+
1. Configure the CLI locally by using `firebase use --add` and select your project in the list.
63+
1. Install dependencies locally by running: `cd functions; npm install; cd -`
64+
1. Deploy your project using `firebase deploy`
65+
1. Open the app using `firebase open hosting:site`, this will open a browser.
66+
1. Start following a user, this will send a notification to them.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"rules": {
3+
"users": {
4+
".read": true,
5+
"$uid": {
6+
".write": "auth.uid === $uid"
7+
}
8+
},
9+
"followers": {
10+
"$followedUid": {
11+
"$followerUid": {
12+
".read": "auth.uid === $followerUid",
13+
".write": "auth.uid === $followerUid"
14+
}
15+
}
16+
}
17+
}
18+
}

Node/fcm-notifications/firebase.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"database": {
3+
"rules": "database.rules.json"
4+
},
5+
"hosting": {
6+
"public": "public"
7+
},
8+
"functions": [
9+
{
10+
"source": "functions",
11+
"codebase": "fcm-notifications",
12+
"ignore": [
13+
"node_modules",
14+
".git",
15+
"firebase-debug.log",
16+
"firebase-debug.*.log"
17+
],
18+
"predeploy": [
19+
"npm --prefix \"$RESOURCE_DIR\" run lint"
20+
]
21+
}
22+
]
23+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module.exports = {
2+
env: {
3+
es2022: true,
4+
node: true,
5+
},
6+
parserOptions: {
7+
"ecmaVersion": 2022,
8+
"sourceType": "module",
9+
},
10+
extends: [
11+
"eslint:recommended",
12+
"google",
13+
],
14+
rules: {
15+
"no-restricted-globals": ["error", "name", "length"],
16+
"prefer-arrow-callback": "error",
17+
"quotes": ["error", "double", {"allowTemplateLiterals": true}],
18+
},
19+
overrides: [
20+
{
21+
files: ["**/*.spec.*"],
22+
env: {
23+
mocha: true,
24+
},
25+
rules: {},
26+
},
27+
],
28+
globals: {},
29+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules/
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* Copyright 2023 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {initializeApp} from "firebase-admin/app";
18+
import {getAuth} from "firebase-admin/auth";
19+
import {getDatabase} from "firebase-admin/database";
20+
import {getMessaging} from "firebase-admin/messaging";
21+
import {log, warn} from "firebase-functions/logger";
22+
import {onValueWritten} from "firebase-functions/v2/database";
23+
24+
initializeApp();
25+
const auth = getAuth();
26+
const db = getDatabase();
27+
const messaging = getMessaging();
28+
29+
/**
30+
* Triggers when a user gets a new follower and sends a notification. Followers
31+
* add a flag to `/followers/{followedUid}/{followerUid}`. Users save their
32+
* device notification tokens to
33+
* `/users/{followedUid}/notificationTokens/{notificationToken}`.
34+
*/
35+
export const sendFollowerNotification = onValueWritten(
36+
"/followers/{followedUid}/{followerUid}",
37+
async (event) => {
38+
// If un-follow we exit the function.
39+
if (!event.data.after.val()) {
40+
log(`User ${event.params.followerUid} unfollowed` +
41+
` user ${event.params.followedUid} :(`);
42+
return;
43+
}
44+
45+
log(`User ${event.params.followerUid} is now following` +
46+
` user ${event.params.followedUid}`);
47+
const tokensRef =
48+
db.ref(`/users/${event.params.followedUid}/notificationTokens`);
49+
const notificationTokens = await tokensRef.get();
50+
if (!notificationTokens.hasChildren()) {
51+
log("There are no tokens to send notifications to.");
52+
return;
53+
}
54+
55+
log(`There are ${notificationTokens.numChildren()} tokens` +
56+
" to send notifications to.");
57+
const followerProfile = await auth.getUser(event.params.followerUid);
58+
59+
// Notification details.
60+
const notification = {
61+
title: "You have a new follower!",
62+
body: (followerProfile.displayName ?? "Someone") +
63+
" is now following you.",
64+
image: followerProfile.photoURL ?? "",
65+
};
66+
67+
// Send notifications to all tokens.
68+
const messages = [];
69+
notificationTokens.forEach((child) => {
70+
messages.push({
71+
token: child.key,
72+
notification: notification,
73+
});
74+
});
75+
const batchResponse = await messaging.sendEach(messages);
76+
77+
if (batchResponse.failureCount < 1) {
78+
// Messages sent sucessfully. We're done!
79+
log("Messages sent.");
80+
return;
81+
}
82+
warn(`${batchResponse.failureCount} messages weren't sent.`,
83+
batchResponse);
84+
85+
// Clean up the tokens that are not registered any more.
86+
for (const response of batchResponse.responses) {
87+
if (response.error?.code ===
88+
"messaging/invalid-registration-token" ||
89+
response.error?.code ===
90+
"messaging/registration-token-not-registered") {
91+
await tokensRef.child(response.messageId).remove();
92+
}
93+
}
94+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "functions",
3+
"description": "Cloud Functions for Firebase",
4+
"scripts": {
5+
"lint": "eslint .",
6+
"serve": "firebase emulators:start --only functions",
7+
"shell": "firebase functions:shell",
8+
"start": "npm run shell",
9+
"deploy": "firebase deploy --only functions",
10+
"logs": "firebase functions:log"
11+
},
12+
"engines": {
13+
"node": "18"
14+
},
15+
"main": "index.js",
16+
"type": "module",
17+
"dependencies": {
18+
"firebase-admin": "^11.8.0",
19+
"firebase-functions": "^4.3.1"
20+
},
21+
"devDependencies": {
22+
"eslint": "^8.15.0",
23+
"eslint-config-google": "^0.14.0",
24+
"firebase-functions-test": "^3.1.0"
25+
},
26+
"private": true
27+
}
3.46 KB
Loading
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// Import and configure the Firebase SDK
18+
// These scripts are made available when the app is served or deployed on Firebase Hosting
19+
// If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup
20+
importScripts('/__/firebase/10.0.0/firebase-app-compat.js');
21+
importScripts('/__/firebase/10.0.0/firebase-messaging-compat.js');
22+
importScripts('/__/firebase/init.js');
23+
24+
firebase.messaging();

0 commit comments

Comments
 (0)