From ff3acfb6c1bf7fd39f33a11435c07eca22d28ceb Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 16 Jan 2025 16:40:26 -0800 Subject: [PATCH] Update functions sample to latest sdk version and add streaming samples. (#14357) --- FirebaseFunctions/Backend/index.js | 65 ++++++++++++++++++++------ FirebaseFunctions/Backend/package.json | 7 ++- FirebaseFunctions/Backend/start.sh | 2 + 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/FirebaseFunctions/Backend/index.js b/FirebaseFunctions/Backend/index.js index 00d5d725bce..6272b7f421f 100644 --- a/FirebaseFunctions/Backend/index.js +++ b/FirebaseFunctions/Backend/index.js @@ -13,9 +13,10 @@ // limitations under the License. const assert = require('assert'); -const functions = require('firebase-functions'); +const functionsV1 = require('firebase-functions/v1'); +const functionsV2 = require('firebase-functions/v2'); -exports.dataTest = functions.https.onRequest((request, response) => { +exports.dataTest = functionsV1.https.onRequest((request, response) => { assert.deepEqual(request.body, { data: { bool: true, @@ -41,39 +42,39 @@ exports.dataTest = functions.https.onRequest((request, response) => { }); }); -exports.scalarTest = functions.https.onRequest((request, response) => { +exports.scalarTest = functionsV1.https.onRequest((request, response) => { assert.deepEqual(request.body, { data: 17 }); response.send({ data: 76 }); }); -exports.tokenTest = functions.https.onRequest((request, response) => { +exports.tokenTest = functionsV1.https.onRequest((request, response) => { assert.equal('Bearer token', request.get('Authorization')); assert.deepEqual(request.body, { data: {} }); response.send({ data: {} }); }); -exports.FCMTokenTest = functions.https.onRequest((request, response) => { +exports.FCMTokenTest = functionsV1.https.onRequest((request, response) => { assert.equal(request.get('Firebase-Instance-ID-Token'), 'fakeFCMToken'); assert.deepEqual(request.body, { data: {} }); response.send({ data: {} }); }); -exports.nullTest = functions.https.onRequest((request, response) => { +exports.nullTest = functionsV1.https.onRequest((request, response) => { assert.deepEqual(request.body, { data: null }); response.send({ data: null }); }); -exports.missingResultTest = functions.https.onRequest((request, response) => { +exports.missingResultTest = functionsV1.https.onRequest((request, response) => { assert.deepEqual(request.body, { data: null }); response.send({}); }); -exports.unhandledErrorTest = functions.https.onRequest((request, response) => { +exports.unhandledErrorTest = functionsV1.https.onRequest((request, response) => { // Fail in a way that the client shouldn't see. throw 'nope'; }); -exports.unknownErrorTest = functions.https.onRequest((request, response) => { +exports.unknownErrorTest = functionsV1.https.onRequest((request, response) => { // Send an http error with a body with an explicit code. response.status(400).send({ error: { @@ -83,7 +84,7 @@ exports.unknownErrorTest = functions.https.onRequest((request, response) => { }); }); -exports.explicitErrorTest = functions.https.onRequest((request, response) => { +exports.explicitErrorTest = functionsV1.https.onRequest((request, response) => { // Send an http error with a body with an explicit code. // Note that eventually the SDK will have a helper to automatically return // the appropriate http status code for an error. @@ -103,18 +104,52 @@ exports.explicitErrorTest = functions.https.onRequest((request, response) => { }); }); -exports.httpErrorTest = functions.https.onRequest((request, response) => { +exports.httpErrorTest = functionsV1.https.onRequest((request, response) => { // Send an http error with no body. response.status(400).send(); }); // Regression test for https://github.com/firebase/firebase-ios-sdk/issues/9855 -exports.throwTest = functions.https.onCall((data) => { - throw new functions.https.HttpsError('invalid-argument', 'Invalid test requested.'); +exports.throwTest = functionsV1.https.onCall((data) => { + throw new functionsV1.https.HttpsError('invalid-argument', 'Invalid test requested.'); }); -exports.timeoutTest = functions.https.onRequest((request, response) => { +exports.timeoutTest = functionsV1.https.onRequest((request, response) => { // Wait for longer than 500ms. - setTimeout(() => response.send({data: true}), 500); + setTimeout(() => response.send({ data: true }), 500); }); +const streamData = ["hello", "world", "this", "is", "cool"] + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +}; + +async function* generateText() { + for (const chunk of streamData) { + yield chunk; + await sleep(1000); + } +}; + +exports.genStream = functionsV2.https.onCall( + async (request, response) => { + if (request.acceptsStreaming) { + for await (const chunk of generateText()) { + response.sendChunk({ chunk }); + } + } + return data.join(" "); + } +); + +exports.genStreamError = functionsV2.https.onCall( + async (request, response) => { + if (request.acceptsStreaming) { + for await (const chunk of generateText()) { + response.write({ chunk }); + } + throw Error("BOOM") + } + } +); diff --git a/FirebaseFunctions/Backend/package.json b/FirebaseFunctions/Backend/package.json index 232566f50f9..4324686f6f6 100644 --- a/FirebaseFunctions/Backend/package.json +++ b/FirebaseFunctions/Backend/package.json @@ -2,8 +2,11 @@ "name": "functions", "description": "Cloud Functions for Firebase", "dependencies": { - "firebase-admin": "^9.2.0", - "firebase-functions": "^3.0.0" + "firebase-admin": "^13.0.0", + "firebase-functions": "^6.2.0" + }, + "engines": { + "node": "22" }, "private": true, "devDependencies": { diff --git a/FirebaseFunctions/Backend/start.sh b/FirebaseFunctions/Backend/start.sh index 94946f45f8d..1ee3777cdc8 100755 --- a/FirebaseFunctions/Backend/start.sh +++ b/FirebaseFunctions/Backend/start.sh @@ -55,6 +55,8 @@ FUNCTIONS_BIN="./node_modules/.bin/functions" "${FUNCTIONS_BIN}" deploy httpErrorTest --trigger-http "${FUNCTIONS_BIN}" deploy throwTest --trigger-http "${FUNCTIONS_BIN}" deploy timeoutTest --trigger-http +"${FUNCTIONS_BIN}" deploy genStream --trigger-http +"${FUNCTIONS_BIN}" deploy genStreamError --trigger-http if [ "$1" != "synchronous" ]; then # Wait for the user to tell us to stop the server.