Showing preview only (1,277K chars total). Download the full file or copy to clipboard to get everything.
Repository: firebase/functions-samples
Branch: main
Commit: a6ae4cbd3cf2
Files: 660
Total size: 1.1 MB
Directory structure:
gitextract_tgieqsqq/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── docs.md
│ └── workflows/
│ ├── test_node.yml
│ ├── test_node_1st_gen.yml
│ └── test_python.yml
├── .gitignore
├── .opensource/
│ └── project.json
├── CONTRIBUTING.md
├── LICENSE
├── Node/
│ ├── README.md
│ ├── alerts-to-discord/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.js
│ │ ├── index.js
│ │ └── package.json
│ ├── app-distribution-feedback-to-jira/
│ │ ├── .eslintrc
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.cjs
│ │ ├── .gitignore
│ │ ├── index.js
│ │ └── package.json
│ ├── call-vertex-remote-config-server/
│ │ ├── README.md
│ │ ├── client/
│ │ │ ├── README.md
│ │ │ ├── config.ts
│ │ │ ├── index.html
│ │ │ ├── main.ts
│ │ │ ├── package.json
│ │ │ ├── vite-env.d.ts
│ │ │ └── vite.config.js
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── index.js
│ │ └── package.json
│ ├── delete-unused-accounts-cron/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.js
│ │ ├── index.js
│ │ └── package.json
│ ├── fcm-notifications/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.cjs
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── public/
│ │ ├── firebase-messaging-sw.js
│ │ ├── index.html
│ │ ├── main.css
│ │ └── main.js
│ ├── instrument-with-opentelemetry/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── timer.js
│ │ └── tracing.js
│ ├── pnpm-workspace.yaml
│ ├── quickstarts/
│ │ ├── auth-blocking-functions/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── callable-functions/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ ├── package.json
│ │ │ └── sanitizer.js
│ │ ├── callable-functions-streaming/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ ├── functions/
│ │ │ │ ├── .eslintrc.js
│ │ │ │ ├── .gitignore
│ │ │ │ ├── index.js
│ │ │ │ └── package.json
│ │ │ └── website/
│ │ │ └── index.html
│ │ ├── custom-events/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── firestore-sync-auth/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ ├── firestore.indexes.json
│ │ │ ├── firestore.rules
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── genkit-helloworld/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── https-time-server/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── monitor-cloud-logging/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── pubsub-helloworld/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── testlab-matrix-completed/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── thumbnails/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── uppercase-firestore/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ ├── firestore.indexes.json
│ │ │ ├── firestore.rules
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── uppercase-rtdb/
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.js
│ │ ├── index.js
│ │ └── package.json
│ ├── remote-config-diff/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.js
│ │ ├── index.js
│ │ └── package.json
│ ├── remote-config-server-with-vertex/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── index.js
│ │ └── package.json
│ ├── taskqueues-backup-images/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.js
│ │ ├── index.js
│ │ └── package.json
│ ├── test-functions-jest/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── index.js
│ │ ├── index.test.js
│ │ └── package.json
│ ├── test-functions-jest-ts/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── jest.config.js
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── __test__/
│ │ │ │ └── index.test.ts
│ │ │ └── index.ts
│ │ ├── tsconfig.dev.json
│ │ └── tsconfig.json
│ ├── test-functions-mocha/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── index.js
│ │ ├── index.test.js
│ │ └── package.json
│ ├── testlab-to-slack/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.js
│ │ ├── index.js
│ │ └── package.json
│ └── youtube/
│ ├── README.md
│ ├── firebase.json
│ └── functions/
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── Node-1st-gen/
│ ├── README.md
│ ├── assistant-say-number/
│ │ ├── README.md
│ │ ├── action.json
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── authenticated-json-api/
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── public/
│ │ ├── index.html
│ │ ├── main.css
│ │ └── main.js
│ ├── authorized-https-endpoint/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── public/
│ │ ├── index.html
│ │ ├── main.css
│ │ └── main.js
│ ├── bigquery-import/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── child-count/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── convert-images/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── coupon-on-purchase/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── delete-old-child-nodes/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── delete-unused-accounts-cron/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── developer-motivator/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── email-confirmation/
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── public/
│ │ ├── index.html
│ │ ├── main.css
│ │ └── main.js
│ ├── exif-images/
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── public/
│ │ ├── index.html
│ │ ├── main.css
│ │ └── main.js
│ ├── fcm-notifications/
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── public/
│ │ ├── firebase-messaging-sw.js
│ │ ├── index.html
│ │ ├── main.css
│ │ └── main.js
│ ├── ffmpeg-convert-audio/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── fulltext-search/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── fulltext-search-firestore/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── .gitignore
│ │ │ ├── elastic.js
│ │ │ ├── index.js
│ │ │ ├── package.json
│ │ │ └── typesense.js
│ │ └── public/
│ │ ├── index.html
│ │ └── index.js
│ ├── github-to-slack/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── google-sheet-sync/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── image-maker/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── clock.js
│ │ │ ├── index.js
│ │ │ ├── package.json
│ │ │ ├── ray.js
│ │ │ └── sparkline.js
│ │ └── public/
│ │ └── index.html
│ ├── instagram-auth/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── main.css
│ │ └── public/
│ │ ├── index.html
│ │ ├── main.css
│ │ ├── main.js
│ │ └── popup.html
│ ├── lastmodified-tracking/
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── limit-children/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── linkedin-auth/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── main.css
│ │ └── public/
│ │ ├── index.html
│ │ ├── main.css
│ │ ├── main.js
│ │ └── popup.html
│ ├── message-translation/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── minimal-webhook/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── moderate-images/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── okta-auth/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── public/
│ │ │ └── index.html
│ │ └── setup.js
│ ├── paypal/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── public/
│ │ ├── cancel.html
│ │ ├── error.html
│ │ ├── index.html
│ │ ├── main.css
│ │ └── success.html
│ ├── presence-firestore/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── public/
│ │ ├── index.html
│ │ └── index.js
│ ├── publish-model/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── quickstarts/
│ │ ├── auth-blocking-functions/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── big-ben/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ ├── functions/
│ │ │ │ ├── .eslintrc.json
│ │ │ │ ├── index.js
│ │ │ │ └── package.json
│ │ │ └── public/
│ │ │ ├── script.js
│ │ │ └── style.css
│ │ ├── email-users/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ ├── functions/
│ │ │ │ ├── .eslintrc.json
│ │ │ │ ├── index.js
│ │ │ │ └── package.json
│ │ │ └── public/
│ │ │ ├── index.html
│ │ │ ├── main.css
│ │ │ └── main.js
│ │ ├── https-time-server/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── multicodebase-hellos/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ ├── js/
│ │ │ │ ├── .eslintrc.js
│ │ │ │ ├── .gitignore
│ │ │ │ ├── index.js
│ │ │ │ └── package.json
│ │ │ └── ts/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── package.json
│ │ │ ├── src/
│ │ │ │ └── index.ts
│ │ │ ├── tsconfig.dev.json
│ │ │ └── tsconfig.json
│ │ ├── pubsub-helloworld/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── runtime-options/
│ │ │ ├── .gitignore
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── taskqueues-backup-images/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── testlab-matrix-completed/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── thumbnails/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── uppercase-firestore/
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ ├── firestore.indexes.json
│ │ │ ├── firestore.rules
│ │ │ └── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── .gitignore
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── uppercase-rtdb/
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ ├── package.json
│ │ └── test/
│ │ ├── test.offline.js
│ │ └── test.online.js
│ ├── remote-config-diff/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── spotify-auth/
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── main.css
│ │ └── public/
│ │ ├── index.html
│ │ ├── main.css
│ │ ├── main.js
│ │ └── popup.html
│ ├── stripe/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ ├── firestore.rules
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── public/
│ │ ├── index.html
│ │ └── javascript/
│ │ └── app.js
│ ├── survey-app-update/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── template-handlebars/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── firebaseUser.js
│ │ │ ├── index.js
│ │ │ ├── package.json
│ │ │ └── views/
│ │ │ ├── layouts/
│ │ │ │ └── main.handlebars
│ │ │ └── user.handlebars
│ │ └── public/
│ │ └── main.css
│ ├── testlab-to-slack/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .gitignore
│ │ ├── index.js
│ │ └── package.json
│ ├── text-moderation/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ ├── public/
│ │ │ ├── index.html
│ │ │ ├── main.css
│ │ │ └── main.js
│ │ └── security.rules.json
│ ├── url-shortener/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .eslintrc.json
│ │ ├── index.js
│ │ └── package.json
│ ├── user-data-cleanup/
│ │ ├── README.md
│ │ └── doc/
│ │ ├── auto_rules_extraction.md
│ │ └── design.md
│ ├── username-password-auth/
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .eslintrc.json
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── public/
│ │ ├── index.html
│ │ ├── main.css
│ │ └── main.js
│ ├── vision-annotate-images/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── .gitignore
│ │ ├── eslint.config.js
│ │ ├── package.json
│ │ ├── src/
│ │ │ └── index.ts
│ │ └── tsconfig.json
│ └── youtube/
│ ├── README.md
│ ├── firebase.json
│ └── functions/
│ ├── .eslintrc.json
│ ├── index.js
│ └── package.json
├── Python/
│ ├── .gitignore
│ ├── README.md
│ ├── alerts-to-discord/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── delete-unused-accounts-cron/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── fcm-notifications/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── database.rules.json
│ │ ├── firebase.json
│ │ ├── functions/
│ │ │ ├── .gitignore
│ │ │ ├── main.py
│ │ │ └── requirements.txt
│ │ └── public/
│ │ ├── firebase-messaging-sw.js
│ │ ├── index.html
│ │ ├── main.css
│ │ └── main.js
│ ├── http-flask/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── post-signup-event/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── pyfmt.py
│ ├── pyproject.toml
│ ├── quickstarts/
│ │ ├── auth-blocking-functions/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── main.py
│ │ │ └── requirements.txt
│ │ ├── callable-functions/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── main.py
│ │ │ └── requirements.txt
│ │ ├── custom-events/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── main.py
│ │ │ └── requirements.txt
│ │ ├── firestore-sync-auth/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── main.py
│ │ │ └── requirements.txt
│ │ ├── https-time-server/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── main.py
│ │ │ └── requirements.txt
│ │ ├── monitor-cloud-logging/
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── main.py
│ │ │ └── requirements.txt
│ │ ├── pubsub-helloworld/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── main.py
│ │ │ └── requirements.txt
│ │ ├── uppercase-firestore/
│ │ │ ├── README.md
│ │ │ ├── firebase.json
│ │ │ └── functions/
│ │ │ ├── main.py
│ │ │ └── requirements.txt
│ │ └── uppercase-rtdb/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── remote-config-diff/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── requirements.txt
│ ├── taskqueues-backup-images/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── testlab-to-slack/
│ │ ├── README.md
│ │ ├── firebase.json
│ │ └── functions/
│ │ ├── main.py
│ │ └── requirements.txt
│ └── thumbnails/
│ ├── README.md
│ ├── firebase.json
│ └── functions/
│ ├── main.py
│ └── requirements.txt
├── README.md
├── community.md
├── package-lock.json
├── package.json
└── tsconfig.template.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Report an issue with a specific sample
title: '[BUG] in sample: '
labels: 'type: bug'
assignees: ''
---
### Which sample has a bug?
**Sample name or URL where you found the bug**
### How to reproduce the issue
**Failing Function code used (if you modified the sample)**
**Steps to set up and reproduce**
<!-- Help us diagnose the issue. Please provide detailed instructions to run your minimal repro or to recreate the environment -->
### Debug output
<!-- Provide any error messages or screenshots of unexpected behavior -->
**Errors in the
[console logs](https://console.firebase.google.com/project/_/functions/logs?search=&severity=DEBUG)**
**Screenshots**
### Expected behavior
<!-- What is the expected behavior? -->
### Actual behavior
<!-- What is the actual behavior? -->
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# https://docs.github.com/en/free-pro-team@latest/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
blank_issues_enabled: false
contact_links:
- name: Propose a new sample, or get help setting up a sample
url: https://github.com/firebase/functions-samples/discussions
about: Informal help and discussion about the samples in this repository.
- name: Issues with the Firebase platform
url: https://firebase.google.com/support
about: Report issues with Firebase services to Firebase support.
================================================
FILE: .github/ISSUE_TEMPLATE/docs.md
================================================
---
name: Documentation issue
about: Are a sample's docs incorrect or unclear?
title: '[DOCS] for sample: '
labels: ''
assignees: ''
---
<!-- This is only for issues with the documentation in this repository. -->
### Which sample?
**Sample name or URL** **Link to closest heading**
### What is the issue with this sample's docs?
<!-- Broken link, unclear step, outdated step, etc... -->
================================================
FILE: .github/workflows/test_node.yml
================================================
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: 2nd Gen CI Tests
on:
pull_request:
paths:
- 'Node/**'
- 'package.json'
- 'package-lock.json'
- 'pnpm-workspace.yaml'
- 'pnpm-lock.yaml'
- 'tsconfig.template.json'
push:
branches:
- main
paths:
- 'Node/**'
- 'package.json'
- 'package-lock.json'
- 'pnpm-workspace.yaml'
- 'pnpm-lock.yaml'
- 'tsconfig.template.json'
env:
CI: true
jobs:
unit:
runs-on: ubuntu-latest
strategy:
matrix:
node-version:
# The latest Node version for 2nd gen
# https://cloud.google.com/functions/docs/runtime-support#node.js
- 22.x
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Cache npm
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
- run: npm install
- run: npm run bootstrap-2nd-gen
- run: npm run lint-2nd-gen
- run: npm run compile-2nd-gen
- run: npm run test-2nd-gen
================================================
FILE: .github/workflows/test_node_1st_gen.yml
================================================
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: 1st Gen CI Tests
on:
pull_request:
paths:
- 'Node-1st-gen/**'
- 'package.json'
- 'package-lock.json'
- 'pnpm-workspace.yaml'
- 'pnpm-lock.yaml'
- 'tsconfig.template.json'
push:
branches:
- main
paths:
- 'Node-1st-gen/**'
- 'package.json'
- 'package-lock.json'
- 'pnpm-workspace.yaml'
- 'pnpm-lock.yaml'
- 'tsconfig.template.json'
env:
CI: true
jobs:
unit:
runs-on: ubuntu-latest
strategy:
matrix:
node-version:
# The latest Node version for 1st gen
# https://cloud.google.com/functions/docs/runtime-support#node.js
- 20.x
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Cache npm
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
- run: npm install
- run: npm run bootstrap-1st-gen
- run: npm run lint-1st-gen
- run: npm run compile-1st-gen
- run: npm run test-1st-gen
================================================
FILE: .github/workflows/test_python.yml
================================================
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: CI Tests
on:
pull_request:
paths:
- 'Python/**'
push:
branches:
- main
paths:
- 'Python/**'
env:
CI: true
jobs:
unit:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.10"
- "3.11"
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
working-directory: ./Python
run: pip install -r requirements.txt
- name: Lint
working-directory: ./Python
run: python pyfmt.py --check_only --exclude "**/venv/**/*.py" **/*.py
================================================
FILE: .gitignore
================================================
.idea
**/node_modules/*
**/.firebase
**/.firebaserc
**/.runtimeconfig.json
*/npm-debug.log
lerna-debug.log
*~
service-account-credentials.json
**/package-lock.json
# keep the root package-lock so that tools are consistent
!/package-lock.json
**/ui-debug.log
**/database-debug.log
**/firebase-debug.log
**/pubsub-debug.log
**/tsconfig-compile.json
yarn.lock
**/.DS_Store
================================================
FILE: .opensource/project.json
================================================
{
"name": "Cloud Functions Samples",
"type": "sample",
"platforms": [
"Admin",
"Node"
],
"content": "README.md",
"pages": {
"quickstarts/big-ben/README.md": "Quickstart: HTTP Trigger + Hosting",
"quickstarts/email-users/README.md": "Quickstart: Auth Trigger",
"quickstarts/pubsub-helloworld/README.md": "Quickstart: PubSub Trigger",
"quickstarts/thumbnails/README.md": "Quickstart: Cloud Storage Trigger",
"quickstarts/time-server/README.md": "Quickstart: HTTP Trigger",
"quickstarts/uppercase/README.md": "Quickstart: RTDB Trigger",
"quickstarts/uppercase-firestore/README.md": "Quickstart: Firestore Trigger"
},
"related": [
"firebase/firebase-functions"
]
}
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to the Firebase Cloud Functions Templates Library
We'd love for you to contribute to our source code and to make the Firebase
Cloud Functions Templates Library even better than it is today! Here are the
guidelines we'd like you to follow:
- [Code of Conduct](#coc)
- [Question or Problem?](#question)
- [Issues and Bugs](#issue)
- [Feature Requests](#feature)
- [Submission Guidelines](#submit)
- [Signing the CLA](#cla)
## <a name="coc"></a> Code of Conduct
As contributors and maintainers of the Firebase functions-samples project, we
pledge to respect everyone who contributes by posting issues, updating
documentation, submitting pull requests, providing feedback in comments, and any
other activities.
Communication through any of Firebase's channels (GitHub, StackOverflow,
Twitter, etc.) must be constructive and never resort to personal attacks,
trolling, public or private harassment, insults, or other unprofessional
conduct.
We promise to extend courtesy and respect to everyone involved in this project
regardless of gender, gender identity, sexual orientation, disability, age,
race, ethnicity, religion, or level of experience. We expect anyone contributing
to the project to do the same.
If any member of the community violates this code of conduct, the maintainers of
this project may take action, removing issues, comments, and PRs or blocking
accounts as deemed appropriate.
If you are subject to or witness unacceptable behavior, or have any other
concerns, please drop us a line at
[Firebase support](https://firebase.google.com/support).
## <a name="question"></a> Got a Question or Problem?
- To report specific problems with the samples in this repository, file an
[issue](#issue).
- If you have questions about a sample in this repository, try opening a
[discussion](https://github.com/firebase/functions-samples/discussions).
- For general Cloud Functions for Firebase questions, please ask on
[StackOverflow][stackoverflow] and use the `firebase` tag.
- If you have a problem specific to your Firebase project, or would like to
report an issue with Firebase itself, contact
[Firebase support](https://firebase.google.com/support).
## <a name="issue"></a> Found an Issue?
If you find a bug in the source code or a mistake in the documentation, you can
help us by submitting an issue to our [GitHub Repository][github]. Even better,
you can submit a Pull Request with a fix.
See [below](#submit) for some guidelines.
## <a name="submit"></a> Submission Guidelines
### Submitting an Issue
Before you submit your issue search the archive in case your question was
already answered.
Help us to maximize the effort we can spend fixing issues and adding new
features by not reporting duplicate issues.
The more information you can provide in an issue, the faster we can resolve it.
Issues that don't make an attempt to follow the issue template will likely be
closed.
### New samples
Given the maintenance cost of the large number of samples that are already in
this repository, we are unlikely to add new samples. However, if you have an
idea for a sample and would like to discuss it with others, take a look at the
["ideas" section of this repository's discussions](https://github.com/firebase/functions-samples/discussions?discussions_q=category%3AIdeas).
Pull Requests that contain new samples will only be considered if they meet the
following criteria:
- Solve a common use-case for Cloud Functions for Firebase with minimal
non-Firebase setup required
- Thoroughly documented
- Thoroughly tested
- Simple enough to be maintained alongside all other samples in the repository
If your Pull Request isn't accepted, consider hosting it in your own GitHub
repository, and adding it to our
[list of community-built extensions](./community.md)!
## <a name="cla"></a> Signing the CLA
Contributions to this project must be accompanied by a Contributor License
Agreement (CLA). You (or your employer) retain the copyright to your
contribution; this simply gives us permission to use and redistribute your
contributions as part of the project. Head over to
<https://cla.developers.google.com/> to see your current agreements on file or
to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
Please sign our [Contributor License Agreement][google-cla] (CLA) before sending
pull requests. For any code changes to be accepted, the CLA must be signed. It's
a quick process, we promise!
[github]: https://github.com/firebase/functions-samples
[google-cla]: https://cla.developers.google.com
[js-style-guide]: http://google.github.io/styleguide/javascriptguide.xml
[py-style-guide]: http://google.github.io/styleguide/pyguide.html
[jsbin]: http://jsbin.com/
[stackoverflow]: http://stackoverflow.com/questions/tagged/firebase
[global-gitignore]:
https://help.github.com/articles/ignoring-files/#create-a-global-gitignore
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Node/README.md
================================================
# Node.js (2nd gen) samples
This folder contains examples for 2nd gen Node.js functions. See a description of all samples in this folder in the [root README](../README.md).
================================================
FILE: Node/alerts-to-discord/.gitignore
================================================
# Don't accidentally push Discord Webhook URL to prod
functions/.env
================================================
FILE: Node/alerts-to-discord/README.md
================================================
# Quickstart: Send Firebase Alerts to Discord
This quickstart demonstrates how to trigger a function based on a Firebase Alert, and send information about the alert to a channel in a Discord server.
<img width="639" alt="Screen Shot 2022-04-29 at 11 12 12 AM" src="https://user-images.githubusercontent.com/3759507/165973290-2f6e6937-7c07-4006-b52d-813aa195e7cf.png">
## Set up and Deploy
### Discord Webhook URL
The sample uses [Discord Webhooks](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to send alerts to a Discord channel. You'll need to create a Webhook and hook it up the function by [creating an environment variable](https://firebase.google.com/docs/functions/config-env#env-variables):
1. Follow the "MAKING A WEBHOOK" instructions in the [Discord docs](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks).
1. Copy the Webhook URL
1. Create a `.env` file in the `functions` directory
1. Add the `DISCORD_WEBHOOK_URL` variable and set it to your Webhook URL:
```bash
DISCORD_WEBHOOK_URL="<your webhook url>"
```
### Deploy
Deploy functions using the Firebase CLI:
```bash
$ firebase deploy
```
## License
© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.
================================================
FILE: Node/alerts-to-discord/firebase.json
================================================
{
"functions": {
"codebase": "alerts-to-discord",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
}
================================================
FILE: Node/alerts-to-discord/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2017: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/alerts-to-discord/functions/index.js
================================================
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// [START v2import]
const {
onNewFatalIssuePublished,
} = require("firebase-functions/alerts/crashlytics");
const {
onNewTesterIosDevicePublished,
} = require("firebase-functions/alerts/appDistribution");
const {
onThresholdAlertPublished,
} = require("firebase-functions/alerts/performance");
const logger = require("firebase-functions/logger");
// [END v2import]
/**
* Posts a message to Discord with Discord's Webhook API
*
* @param {string} botName - The bot username to display
* @param {string} messageBody - The message to post (Discord MarkDown)
*/
async function postMessageToDiscord(botName, messageBody) {
const webhookUrl = process.env.DISCORD_WEBHOOK_URL;
if (!webhookUrl) {
throw new Error(
"No webhook URL found. Set the Discord Webhook URL before deploying. Learn more about Discord webhooks here: https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks",
);
}
return fetch(webhookUrl, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify(
// Here's what the Discord API supports in the payload:
// https://discord.com/developers/docs/resources/webhook#execute-webhook-jsonform-params
{
content: messageBody,
username: botName,
},
),
});
}
// [START v2Alerts]
/**
* function triggered by Crashlytics that publishes a message
* to Discord whenever a new fatal issue occurs.
*/
// [START v2CrashlyticsAlertTrigger]
exports.postfatalissuetodiscord = onNewFatalIssuePublished(async (event) => {
// [END v2CrashlyticsAlertTrigger]
// [START v2CrashlyticsEventPayload]
// construct a helpful message to send to Discord
const appId = event.appId;
const {id, title, subtitle, appVersion} = event.data.payload.issue;
const message = `
🚨 New fatal issue for ${appId} in version ${appVersion} 🚨
**${title}**
${subtitle}
id: \`${id}\`
`;
// [END v2CrashlyticsEventPayload]
try {
// [START v2SendToDiscord]
const response = await postMessageToDiscord("Crashlytics Bot", message);
if (response.ok) {
logger.info(
`Posted fatal Crashlytics alert ${id} for ${appId} to Discord`,
event.data.payload,
);
} else {
throw new Error(`Discord returned status code ${response.status}`);
}
// [END v2SendToDiscord]
} catch (error) {
logger.error(
`Unable to post fatal Crashlytics alert ${id} for ${appId} to Discord`,
error,
);
}
});
/**
* function triggered by App Distribution that publishes a message
* to Discord whenever a new iOS tester device is registered.
*/
// [START v2AppDistributionAlertTrigger]
exports.postnewduuidtodiscord = onNewTesterIosDevicePublished(async (event) => {
// [END v2AppDistributionAlertTrigger]
// [START v2AppDistributionEventPayload]
// construct a helpful message to send to Discord
const appId = event.appId;
const {
testerDeviceIdentifier,
testerDeviceModelName,
testerEmail,
testerName,
} = event.data.payload;
const message = `
📱 New iOS device registered by ${testerName} <${testerEmail}> for ${appId}
UDID **${testerDeviceIdentifier}** for ${testerDeviceModelName}
`;
// [END v2AppDistributionEventPayload]
try {
// [START v2SendNewTesterIosDeviceToDiscord]
const response = await postMessageToDiscord("AppDistribution Bot", message);
if (response.ok) {
logger.info(
`Posted iOS device registration alert for ${testerEmail} to Discord`,
);
} else {
throw new Error(`Discord returned status code ${response.status}`);
}
// [END v2SendNewTesterIosDeviceToDiscord]
} catch (error) {
logger.error(
`Unable to post iOS device registration for ${testerEmail} to Discord`,
error,
);
}
});
// [END v2Alerts]
/**
* Function triggered by Firebase Performance Monitoring that publishes
* a message to Discord whenever a performance threshold alert is fired.
*/
// [START v2PerformanceAlertTrigger]
exports.postperformancealerttodiscord = onThresholdAlertPublished(
async (event) => {
// [END v2PerformanceAlertTrigger]
// [START v2PerformanceEventPayload]
// construct a helpful message to send to Discord
const appId = event.appId;
const {
eventName,
metricType,
eventType,
numSamples,
thresholdValue,
thresholdUnit,
conditionPercentile,
appVersion,
violationValue,
violationUnit,
investigateUri,
} = event.data.payload;
const message = `
⚠️ Performance Alert for ${metricType} of ${eventType}: **${eventName}** ⚠️
App id: ${appId}
Alert condition: ${thresholdValue} ${thresholdUnit}
Percentile (if applicable): ${conditionPercentile}
App version (if applicable): ${appVersion}
Violation: ${violationValue} ${violationUnit}
Number of samples checked: ${numSamples}
**Investigate more:** ${investigateUri}
`;
// [END v2PerformanceEventPayload]
try {
// [START v2SendPerformanceAlertToDiscord]
const response = await postMessageToDiscord(
"Firebase Performance Bot", message);
if (response.ok) {
logger.info(
`Posted Firebase Performance alert ${eventName} to Discord`,
event.data.payload,
);
} else {
throw new Error(`Discord returned status code ${response.status}`);
}
// [END v2SendPerformanceAlertToDiscord]
} catch (error) {
logger.error(
`Unable to post Firebase Performance alert ${eventName} to Discord`,
error,
);
}
});
// [END v2Alerts]
================================================
FILE: Node/alerts-to-discord/functions/package.json
================================================
{
"name": "alerts-to-discord",
"description": "Send a message to Discord when an alert is received from Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"compile": "cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json"
},
"engines": {
"node": "22"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"@types/node": "^17.0.45",
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.4.0"
},
"private": true
}
================================================
FILE: Node/app-distribution-feedback-to-jira/.eslintrc
================================================
module.exports = {
root: true,
env: {
es2017: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/app-distribution-feedback-to-jira/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
firebase-debug.*.log*
# Firebase cache
.firebase/
# Firebase config
# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
================================================
FILE: Node/app-distribution-feedback-to-jira/README.md
================================================
# Send in-app feedback to Jira
This [code](functions/index.js) demonstrates how to use a Firebase Cloud Function triggered by an
[in-app feedback Firebase Alert from App Distribution](https://firebase.google.com/docs/functions/beta/reference/firebase-functions.alerts.appdistribution.inappfeedbackpayload),
and stores the feedback details (including screenshot) in Jira.
You can customize this code to work with your own Jira configuration (eg on-premise support, custom issue types, etc).
## Quickstart
This sample code uses Jira's built-in APIs to create issues for in-app tester feedback. For simplicity it uses [basic authorization](https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/).
1. [Generate an API token](https://id.atlassian.com/manage-profile/security/api-tokens) via your Jira profile.
Note: If the tester who files feedback does not have a Jira account, the user who generates this token will be marked as the issue's reporter.
2. This [code](functions/index.js) uses [parameterized configuration](https://firebase.google.com/docs/functions/config-env#params) to prompt for the required configuratio. To start the process, run:
```bash
$ firebase deploy
```
This will store the `API_TOKEN` using [Google Cloud Secret Manager](https://cloud.google.com/secret-manager) and the remaining settings in an `.env` file which will contain the following variables, customized to your Jira project:
```bash
JIRA_URI="<your JIRA instance's URI, e.g. 'https://mysite.atlassian.net'>"
PROJECT_KEY="<your project's key, e.g. 'DEV'>"
ISSUE_TYPE_ID="<issue type ID; defaults to '10001' (Improvement)>"
ISSUE_LABEL="<label applied to the Jira issues created; defaults to 'in-app'>"
API_TOKEN_OWNER="<creator of the token; default reporter of issues>"
```
## License
© Google, 2022. Licensed under an [Apache-2 license](../../LICENSE).
================================================
FILE: Node/app-distribution-feedback-to-jira/firebase.json
================================================
{
"functions": [
{
"source": "functions",
"codebase": "app-distribution-feedback-to-jira",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log"
],
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
]
}
================================================
FILE: Node/app-distribution-feedback-to-jira/functions/.eslintrc.cjs
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es6: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2022
}
};
================================================
FILE: Node/app-distribution-feedback-to-jira/functions/.gitignore
================================================
node_modules/
================================================
FILE: Node/app-distribution-feedback-to-jira/functions/index.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
onInAppFeedbackPublished} from "firebase-functions/alerts/appDistribution";
import {defineInt, defineSecret, defineString} from "firebase-functions/params";
import logger from "firebase-functions/logger";
import {FormData} from "formdata-polyfill/esm.min.js";
// The keys are either defined in .env or they are created
// via prompt in the CLI before deploying
const jiraUriConfig = defineString("JIRA_URI", {
description: "URI of your Jira instance (e.g. 'https://mysite.atlassian.net')",
input: {
text: {
validationRegex: /^https:\/\/.*/,
validationErrorMessage: "Please enter an 'https://' URI",
},
},
});
const projectKeyConfig = defineString("PROJECT_KEY", {
description: "Project key of your Jira instance (e.g. 'XY')",
});
const issueTypeIdConfig = defineInt("ISSUE_TYPE_ID", {
description: "Issue type ID for the Jira issues being created",
default: 10001,
});
const issueLabelConfig = defineString("ISSUE_LABEL", {
description: "Label for the Jira issues being created",
default: "in-app",
});
const apiTokenOwnerConfig = defineString("API_TOKEN_OWNER", {
description: "Owner of the Jira API token",
input: {
text: {
validationRegex:
/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
validationErrorMessage: "Please enter a valid email address",
},
},
});
const apiTokenConfig = defineSecret("API_TOKEN", {
description: "Jira API token. Created using " +
"https://id.atlassian.com/manage-profile/security/api-tokens",
});
export const handleInAppFeedback = async (event) => {
const issueUri = await createIssue(event);
if (event.data.payload.screenshotUri) {
await uploadScreenshot(issueUri, event.data.payload.screenshotUri);
}
return undefined; // returns 204 (no content) which is ignored
};
export const feedbacktojira =
onInAppFeedbackPublished({secrets: [apiTokenConfig]}, handleInAppFeedback);
/**
* Creates "Authorization" header value.
* @return {string} Basic, base64-encoded "Authorization" header value
*/
function authHeader() {
return "Basic " + Buffer
.from(apiTokenOwnerConfig.value() + ":" + apiTokenConfig.value())
.toString("base64");
}
/**
* Creates new issue in Jira.
* @param {AlertEvent<InAppFeedbackPayload>} event
*/
async function createIssue(event) {
const requestJson = await buildCreateIssueRequest(event);
const requestBody = JSON.stringify(requestJson);
const response =
await fetch(`${jiraUriConfig.value()}/rest/api/3/issue`, {
method: "POST",
headers: {
"Authorization": authHeader(),
"Accept": "application/json",
"Content-Type": "application/json",
},
body: requestBody,
});
if (!response.ok) {
throw new Error("Issue creation failed: " +
`${response.status} ${response.statusText} for ` +
requestBody);
}
const json = await response.json();
return json.self; // issueUri
}
/**
* Uploads screenshot to Jira (after downloading it from Firebase).
* @param {string} issueUri URI of the Jira issue
* @param {string} screenshotUri URI of the screenshot hosted by Firebase
*/
async function uploadScreenshot(issueUri, screenshotUri) {
const dlResonse = await fetch(screenshotUri);
if (!dlResonse.ok) {
throw new Error("Screenshot download failed: " +
`${dlResonse.status} ${dlResonse.statusText}`);
}
const blob = await dlResonse.blob();
const form = new FormData();
form.append("file", blob, "screenshot.png");
const ulResponse = await fetch(issueUri + "/attachments", {
method: "POST",
body: form,
headers: {
"Authorization": authHeader(),
"Accept": "application/json",
"X-Atlassian-Token": "no-check",
},
});
if (!ulResponse.ok) {
throw new Error("Screenshot upload failed: " +
`${ulResponse.status} ${ulResponse.statusText}`);
}
}
/**
* Looks up Jira user ID.
* @param {string} testerEmail Email address of tester who filed feedback
*/
async function lookupReporter(testerEmail) {
const response =
await fetch(
`${jiraUriConfig.value()}/rest/api/3/user/search` +
`?query=${testerEmail}`, {
method: "GET",
headers: {
"Authorization": authHeader(),
"Accept": "application/json",
}});
if (!response.ok) {
logger.info(`Failed to find Jira user for '${testerEmail}':` +
`${response.status} ${response.statusText}`);
}
const json = await response.json();
return json.length > 0 ? json[0].accountId : undefined;
}
/**
* Builds payload for creating a Jira issue.
* @param {AlertEvent<InAppFeedbackPayload>} event
*/
async function buildCreateIssueRequest(event) {
let summary = "In-app feedback: " + event.data.payload.text;
summary = summary.replace(/[\n\r].*/g, "");
if (summary.length > 40) {
summary = summary.substring(0, 39) + "…";
}
const json = {
"update": {},
"fields": {
"summary": summary,
"issuetype": {
"id": issueTypeIdConfig.value(),
},
"project": {
"key": projectKeyConfig.value(),
},
"description": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{
"text": "Firebase App ID: ",
"type": "text",
"marks": [
{
"type": "strong",
},
],
},
{
"text": event.appId,
"type": "text",
},
],
},
{
"type": "paragraph",
"content": [
{
"text": "App Version: ",
"type": "text",
"marks": [
{
"type": "strong",
},
],
},
{
"text": event.data.payload.appVersion,
"type": "text",
},
],
},
{
"type": "paragraph",
"content": [
{
"text": "Tester Email: ",
"type": "text",
"marks": [
{
"type": "strong",
},
],
},
{
"text": event.data.payload.testerEmail,
"type": "text",
},
],
},
{
"type": "paragraph",
"content": [
{
"text": "Tester Name: ",
"type": "text",
"marks": [
{
"type": "strong",
},
],
},
{
"text": event.data.payload.testerName || "None",
"type": "text",
},
],
},
{
"type": "paragraph",
"content": [
{
"text": "Feedback text: ",
"type": "text",
"marks": [
{
"type": "strong",
},
],
},
{
"text": event.data.payload.text,
"type": "text",
},
],
},
{
"type": "paragraph",
"content": [
{
"text": "Console link",
"type": "text",
"marks": [
{
"type": "link",
"attrs": {
"href": event.data.payload.feedbackConsoleUri,
"title": "Firebase console",
},
},
],
},
],
},
],
},
"labels": [issueLabelConfig.value()],
},
};
const reporter = await lookupReporter(event.data.payload.testerEmail);
if (reporter) {
json.fields.reporter = {"id": reporter};
}
return json;
}
================================================
FILE: Node/app-distribution-feedback-to-jira/functions/package.json
================================================
{
"name": "functions",
"description": "File new issue in Jira when receiving in-app feedback facilitated by Firebase App Distribution",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "22"
},
"main": "index.js",
"type": "module",
"dependencies": {
"firebase-functions": "7.0.0",
"formdata-polyfill": "^4.0.10"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0"
},
"private": true
}
================================================
FILE: Node/call-vertex-remote-config-server/README.md
================================================
Call the Vertex AI Gemini API with Remote Config and App Check
==============================================================
Introduction
------------
This is a sample callable function that authenticates clients with App
Check and then sends queries to Gemini using the Vertex AI Gemini API. Vertex
AI model parameters (including the model itself) are controlled by
Remote Config server features included in the Firebase Admin SDK for
Node.js.
Use the web client provided in `client/` to test the function.
- [Read more about Remote Config for servers](https://firebase.google.com/docs/remote-config/server).
- [Read more about App Check](https://firebase.google.com/docs/app-check).
- [Read more about the Vertex AI Node.js Client library](https://cloud.google.com/nodejs/docs/reference/aiplatform/latest).
Important: Vertex AI and Cloud Functions require a billing account. Review
[Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and
[Firebase pricing](https://firebase.google.com/pricing) before running
this function. If you're new to Firebase and Google Cloud, check to see if
you're eligible for a
[$300 credit](https://firebase.google.com/support/faq#pricing-free-trial) and
a Free Trial Cloud Billing account.
Get Started
---------------
1. Follow the instructions in client/README.md to create a Firebase project,
enable ReCAPTCHA Enterprise, enable and enforce Firebase App Check, and add
your Firebase config and ReCAPTCHA Enterprise key to the client config.
2. Enable [recommended Vertex AI APIs](https://console.cloud.google.com/vertex-ai).
3. Configure a Remote Config server template on the Firebase console. Use the template
described in
[Use server side Remote Config with Cloud Functions and Vertex
AI](https://firebase.google.com/docs/remote-config/solution-server#implementation-create-template),
which contains all of the parameters used in this function sample.
4. Install dependencies: `cd functions && npm install`
5. If you haven't already done so, install firebase-tools:
`npm i firebase-tools@latest`
6. Log into Firebase:
`firebase login`
7. Deploy the function. We recommend testing in the
[Firebase emulator](https://firebase.google.com/docs/remote-config/solution-server#implementation-deploy-and-test-in-emulator):
`firebase emulators:start`
8. If testing in the emulator, verify that `testMode` is set to `true` in
`client/main.ts`, then start the client:
`cd client && npm run dev`
TIP: If you're using the emulator, you can deploy both the function and hosting
to the emulator. From the `client` directory, run `npm run build`.
Then, from the parent directory, run `firebase server --only functions,hosting`.
Open http://localhost:5000 to access and test the web client's connection
to the `callVertexWithRC` function.
0. Open the [client app in a browser](http://localhost:5173) and enter a
prompt. To access the Vertex AI Gemini API, make sure that you have
set the `is_vertex_enabled` boolean parameter in your Remote Config
server template to `true`.
Support
-------
- [Firebase Support](https://firebase.google.com/support/)
License
-------
© Google, 2024. Licensed under an [Apache-2](../../LICENSE) license.
================================================
FILE: Node/call-vertex-remote-config-server/client/README.md
================================================
Test client for call-vertex-remote-config-server
================================================
Introduction
------------
This is a basic web app that calls the `callVertexWithRC` function. The
function uses values stored in Remote Config server templates with
the Firebase Admin SDK to dynamically update Vertex AI Gemini API
parameters. Access is controlled using Firebase App Check.
- [Read more about Remote Config for servers](https://firebase.google.com/docs/remote-config/server).
- [Read more about App Check](https://firebase.google.com/docs/app-check).
- [Read more about the Vertex AI Node.js Client library](https://cloud.google.com/nodejs/docs/reference/aiplatform/latest).
Important: Vertex AI and Cloud Functions require a billing account. Review
[Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and
[Firebase pricing](https://firebase.google.com/pricing) before running
this function. If you're new to Firebase and Google Cloud, check to see if
you're eligible for a
[$300 credit](https://firebase.google.com/support/faq#pricing-free-trial) and
a Free Trial Cloud Billing account.
Get started
---------------
1. Create a [Firebase project and register a web app](https://firebase.google.com/docs/web/setup#create-firebase-project-and-app).
2. [Create a ReCAPTCHA Enterprise key](https://firebase.google.com/docs/app-check/web/recaptcha-enterprise-provider#project-setup)
in the same project.
3. [Enable App Check](https://firebase.google.com/docs/app-check/web/recaptcha-enterprise-provider)
in the Firebase console with the ReCAPTCHA Enterprise site key you created.
4. Copy your Firebase project config and your ReCAPTCHA Enterprise site key
into the appropriate places in `config.ts` in this directory.
5. In this directory, run `npm install`.
6. Set up and deploy the function as described in [../README.md](../README.md).
7. In this directory, run `npm run dev` to run the client.
To run this app against the `callVertexWithRC` function running in the Firebase
emulator, ensure that `testMode`in `main.ts` is set to `true`. Before testing
with a deployed (i.e., not emulated) function, set `testMode` to `false`.
TIP: You can build the client and deploy to Firebase Hosting by running
`npm run build` from the `client` directory. Hosting deliverables are
generated and saved in `client/dist` and you can then deploy to
the emulator or Firebase Hosting from the parent directory. We recommend
deploying to the emulator first--you can use the following command to
deploy the function and web client simultaneously:
firebase serve --only functions,hosting
Support
-------
- [Firebase Support](https://firebase.google.com/support/)
License
-------
© Google, 2024. Licensed under an [Apache-2](../../../LICENSE) license.
================================================
FILE: Node/call-vertex-remote-config-server/client/config.ts
================================================
export const firebaseConfig = {
YOUR_FIREBASE_CONFIG
};
// Your ReCAPTCHA Enterprise site key (must be from the same project
// as the Firebase config above).
export const RECAPTCHA_ENTERPRISE_SITE_KEY =
"YOUR_RECAPTCHA_KEY";
================================================
FILE: Node/call-vertex-remote-config-server/client/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Client app for Remote Config server function with Vertex AI and App Check</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f2f2f2;
justify-content: center;
align-items: center;
height: 100vh;
margin: 70px;
}
h1 {
margin-bottom: 20px;
}
.container {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
max-width: 400px;
width: 100%;
text-align: center;
}
input[type="text"],
input[type="submit"] {
width: 35%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
input[type="submit"] {
background-color: #FFA500;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
font-weight: bold;
}
input[type="submit"]:hover {
background-color: #45a049;
}
#waitingMessage,
#generatedText,
#errorMessage {
margin-top: 20px;
text-align: left;
}
</style>
</head>
<body>
<div>
<h1>Client app for Remote Config server function with Vertex AI and App Check</h1>
<br />
</div>
<script type="module" src="main.ts"></script>
</body>
</html>
================================================
FILE: Node/call-vertex-remote-config-server/client/main.ts
================================================
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { initializeApp } from "firebase/app";
import { firebaseConfig, RECAPTCHA_ENTERPRISE_SITE_KEY } from "./config";
import {
initializeAppCheck,
ReCaptchaEnterpriseProvider,
} from "firebase/app-check";
import {
getFunctions,
httpsCallable,
connectFunctionsEmulator,
} from "firebase/functions";
// Set to true to test in emulator.
const testMode = true;
// Use showdown to convert Gemini-provided Markdown to HTML
import { Converter } from "showdown";
const converter = new Converter();
// Set up output elements.
const outputDiv = document.createElement("div");
document.body.appendChild(outputDiv);
// Initialize Firebase app.
const app = initializeApp(firebaseConfig);
// Initialize App Check.
initializeAppCheck(app, {
provider: new ReCaptchaEnterpriseProvider(RECAPTCHA_ENTERPRISE_SITE_KEY),
});
// Define callVertexWithRC as a call to the callVertexWithRC function.
const callVertexWithRC = httpsCallable(getFunctions(), "callVertexWithRC", {
limitedUseAppCheckTokens: true,
});
// Enable emulator so that it can be used in test mode.
const functions = getFunctions(app, "us-central1"); // Replace with your region
if (testMode) {
connectFunctionsEmulator(functions, "localhost", 5001);
}
// Generate body for index.html.
document.body.innerHTML += `
<div id="waitingMessage"></div>
<div id="generatedText"></div>
<div id="errorMessage"></div>
<br/>
<form id="promptForm">
<label for="promptInput">Ask Gemini a question!</label><br>
<input type="text" id="promptInput" name="prompt"><br><br>
<input type="submit" value="Submit">
</form>
`;
const promptForm = document.getElementById("promptForm") as HTMLFormElement;
promptForm.addEventListener("submit", async (event) => {
event.preventDefault();
const promptInput = document.getElementById(
"promptInput"
) as HTMLInputElement;
const prompt = promptInput.value;
const waitingMessageElement = document.getElementById("waitingMessage");
// Define a variable to keep track of the number of dots
let dotCount = 0;
// Set interval to add dots every second
const intervalId = setInterval(() => {
// Increment dotCount
dotCount = (dotCount + 1) % 7;
const dots = ".".repeat(dotCount);
waitingMessageElement.textContent = "Waiting for response" + dots;
}, 1000);
const errorMessageElement = document.getElementById("errorMessage");
errorMessageElement.textContent = "";
try {
const { data } = await callVertexWithRC({ prompt });
const generatedTextElement = document.getElementById("generatedText"); // Access the element
const htmlContent = converter.makeHtml(data);
if (!generatedTextElement) {
throw new Error("Missing generated text.");
}
generatedTextElement.innerHTML = htmlContent; // Set the element's content
waitingMessageElement.textContent = "";
errorMessageElement.textContent = "";
} catch (error) {
errorMessageElement.textContent = "Error calling function: " + error.message;
waitingMessageElement.textContent = "";
}
// Clear welcome dots.
clearInterval(intervalId);
});
================================================
FILE: Node/call-vertex-remote-config-server/client/package.json
================================================
{
"name": "call-vertex-remote-config-server-client",
"version": "1.0.0",
"description": "JavaScript quickstart for Vertex AI, Firebase Remote Config server, and App Check.",
"repository": {
"type": "git",
"url": "git+https://github.com/firebase/functions-samples.git"
},
"author": "",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/firebase/functions-samples/issues"
},
"engines": {
"npm": ">=9.0.0 <10.0.0",
"node": ">=18.0.0 <=20.0.0"
},
"homepage": "https://github.com/firebase/functions-samples#readme",
"devDependencies": {
"eslint-config-google": "^0.14.0",
"typescript": "^5.1.6",
"vite": "^4.4.9"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"format": "prettier --write ."
},
"dependencies": {
"@firebase/functions": "^0.11.5",
"eslint": "8",
"firebase": "^10.12.1",
"showdown": "^2.1.0"
}
}
================================================
FILE: Node/call-vertex-remote-config-server/client/vite-env.d.ts
================================================
/// <reference types="vite/client" />
================================================
FILE: Node/call-vertex-remote-config-server/client/vite.config.js
================================================
import { defineConfig } from 'vite';
export default defineConfig({
base: '',
build: {
rollupOptions: {
input: ['index.html','main.ts'],
},
},
logLevel: 'info',
});
================================================
FILE: Node/call-vertex-remote-config-server/firebase.json
================================================
{
"functions": [
{
"source": "functions",
"codebase": "call-vertex-remote-config-server",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log",
"*.local",
"*.bak"
]
}
],
"emulators": {
"functions": {
"port": 5001
},
"ui": {
"enabled": true
},
"singleProjectMode": true
},
"hosting": {
"public": "client/dist",
"ignore": [
"firebase.json",
"**/node_modules/**"
]
}
}
================================================
FILE: Node/call-vertex-remote-config-server/functions/index.js
================================================
/**
* @license
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// [START remote_config_server_vertex_init]
const { onCall, HttpsError } = require("firebase-functions/https");
const logger = require("firebase-functions/logger");
const { initializeApp } = require("firebase-admin/app");
const { VertexAI } = require("@google-cloud/vertexai");
const { getRemoteConfig } = require("firebase-admin/remote-config");
// Allow all origins. Set origin to restrict domain access.
const cors = require("cors")({ origin: true });
// Set and check environment variables.
const project = process.env.GCLOUD_PROJECT;
// Enable App Check
const appCheckRequired = true;
// Initialize Firebase.
const app = initializeApp();
// [END remote_config_server_vertex_init]
// [START remote_config_server_vertex_default_values]
// Define default (fallback) parameter values for Remote Config.
const defaultConfig = {
// Default values for Vertex AI.
model_name: "gemini-1.5-flash-preview-0514",
generation_config: [
{
stopSequences: [],
temperature: 0.7,
maxOutputTokens: 64,
topP: 0.1,
topK: 20,
},
],
prompt:
"I'm a developer who wants to learn about Firebase and you are a \
helpful assistant who knows everything there is to know about Firebase!",
safety_settings: [
{
category: "HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT",
threshold: "HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE",
},
],
// Vertex AI location.
location: "us-central1",
// Disable Vertex AI Gemini API access for testing.
vertex_enabled: false,
};
// [END remote_config_server_vertex_default_values]
// [START remote_config_server_vertex_create_function]
// Export the function.
exports.callVertexWithRC = onCall(
{
enforceAppCheck: appCheckRequired, // Enable App Check enforcement
consumeAppCheckToken: false, // Don't consume the token (optional)
},
async (data, context) => {
try {
// Set up Remote Config.
const rc = getRemoteConfig(app);
// Get the Remote Config template and assign default values.
const template = await rc.getServerTemplate({
defaultConfig: defaultConfig,
});
// Add the template evaluation to a constant.
const config = template.evaluate();
// Obtain values from Remote Config.
const textModel =
config.getString("model_name") || defaultConfig.model_name;
const textPrompt = config.getString("prompt") || defaultConfig.prompt;
const generationConfig =
config.getString("generation_config") ||
defaultConfig.generation_config;
const safetySettings =
config.getString("safety_settings") || defaultConfig.safety_settings;
const location = config.getString("location") || defaultConfig.location;
const vertexEnabled =
config.getBoolean("is_vertex_enabled") || defaultConfig.vertex_enabled;
// [END remote_config_server_vertex_create_function]
// [START remote_config_server_vertex_function_logic]
// Allow user input.
const userInput = data.data.prompt || "";
// Instantiate Vertex AI.
const vertex_ai = new VertexAI({ project: project, location: location });
const generativeModel = vertex_ai.getGenerativeModel({
model: textModel,
safety_settings: safetySettings,
generation_config: generationConfig,
});
// Combine prompt from Remote Config with optional user input.
const chatInput = textPrompt + " " + userInput;
if (!chatInput) {
throw new HttpsError("invalid-argument", "Missing text prompt");
}
// Check if Vertex AI is enabled
if (vertexEnabled !== true) {
logger.log("Vertex AI is not enabled");
return;
}
logger.log(
"\nRunning with model ",
textModel,
", prompt: ",
textPrompt,
", generationConfig: ",
generationConfig,
", safetySettings: ",
safetySettings,
" in ",
location,
"\n"
);
const result = await generativeModel.generateContentStream(chatInput);
const chunks = [];
for await (const item of result.stream) {
const chunk = item.candidates[0].content.parts[0].text;
logger.log("Received chunk:", chunk);
chunks.push(chunk);
}
return chunks.join(""); // Return the concatenated chunks
} catch (error) {
logger.error(error);
throw new HttpsError("internal", "Internal server error");
}
}
);
// [END remote_config_server_vertex_function_logic]
================================================
FILE: Node/call-vertex-remote-config-server/functions/package.json
================================================
{
"name": "call-vertex-remote-config-server",
"description": "An example of a callable function that uses the Admin SDK with server-side Remote Config, App Check, and Vertex AI",
"scripts": {
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "22"
},
"main": "index.js",
"dependencies": {
"@google-cloud/vertexai": "^1.2.0",
"cors": "^2.8.5",
"eslint": "8",
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.4.0"
},
"private": true
}
================================================
FILE: Node/delete-unused-accounts-cron/README.md
================================================
# Periodically delete unused accounts
This sample demonstrates how to delete the accounts of users who have not signed-in in the last month.
## Functions Code
See the file [functions/index.js](functions/index.js) for the code.
**Note:** This function uses Cloud Scheduler, which can have associated costs. Your project must be on the Blaze payment plan as these features require billing information. See the [Cloud Scheduler pricing page](https://cloud.google.com/scheduler/pricing) for more information.
The dependencies are listed in [functions/package.json](functions/package.json).
## Deploy and test
To set up the sample:
- Create a Firebase Project using the [Firebase Developer Console](https://console.firebase.google.com)
- Download this sample e.g. `git clone https://github.com/firebase/functions-samples`
- Enter the sample directory `cd functions-samples/2nd-gen/delete-unused-accounts-cron`
- Setup the sample with your project `firebase use --add` and follow the instructions.
- Install node dependencies of your Functions `cd functions; npm install; cd -`
- Deploy your project using `firebase deploy`.
- The Cloud Scheduler job should then run once a day and delete any inactive users. You can manually run the task by [navigating to Cloud Scheduler in the Google Cloud Platform Console](https://console.cloud.google.com/cloudscheduler).
================================================
FILE: Node/delete-unused-accounts-cron/firebase.json
================================================
{
"functions": {
"codebase": "delete-unused-accounts-cron"
}
}
================================================
FILE: Node/delete-unused-accounts-cron/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/delete-unused-accounts-cron/functions/index.js
================================================
/**
* Copyright 2022 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
// [START all]
// [START import]
// The Cloud Functions for Firebase SDK to set up triggers and logging.
const {onSchedule} = require("firebase-functions/scheduler");
const {logger} = require("firebase-functions");
// The Firebase Admin SDK to delete inactive users.
const admin = require("firebase-admin");
admin.initializeApp();
// The es6-promise-pool to limit the concurrency of promises.
const PromisePool = require("es6-promise-pool").default;
// Maximum concurrent account deletions.
const MAX_CONCURRENT = 3;
// [END import]
// [START accountcleanup]
// Run once a day at midnight, to clean up the users
// Manually run the task here https://console.cloud.google.com/cloudscheduler
exports.accountcleanup = onSchedule("every day 00:00", async (event) => {
// Fetch all user details.
const inactiveUsers = await getInactiveUsers();
// Use a pool so that we delete maximum `MAX_CONCURRENT` users in parallel.
const promisePool = new PromisePool(
() => deleteInactiveUser(inactiveUsers),
MAX_CONCURRENT,
);
await promisePool.start();
logger.log("User cleanup finished");
});
// [END accountcleanup]
// [START deleteInactiveUser]
/**
* Deletes one inactive user from the list.
* @param {admin.auth.UserRecord[]} inactiveUsers
* @return {null | Promise<void>}
*/
function deleteInactiveUser(inactiveUsers) {
if (inactiveUsers.length > 0) {
const userToDelete = inactiveUsers.pop();
// Delete the inactive user.
return admin.auth().deleteUser(userToDelete.uid).then(() => {
return logger.log(
"Deleted user account",
userToDelete.uid,
"because of inactivity",
);
}).catch((error) => {
return logger.error(
"Deletion of inactive user account",
userToDelete.uid,
"failed:",
error,
);
});
} else {
return null;
}
}
// [END deleteInactiveUser]
// [START getInactiveUsers]
// Returns the list of all inactive users.
/**
*
* @param {admin.auth.UserRecord[]} [users] the current list of inactive users
* @param {string} [nextPageToken]
* @return {Promise<admin.auth.UserRecord[]>}
*/
async function getInactiveUsers(users = [], nextPageToken) {
const result = await admin.auth().listUsers(1000, nextPageToken);
// Find users that have not signed in in the last 30 days.
const inactiveUsers = result.users.filter(
(user) =>
Date.parse(
user.metadata.lastRefreshTime || user.metadata.lastSignInTime,
) <
Date.now() - 30 * 24 * 60 * 60 * 1000,
);
// Add to the list of previously found inactive users.
users = users.concat(inactiveUsers);
// If there are more users to fetch we fetch them.
if (result.pageToken) {
return getInactiveUsers(users, result.pageToken);
}
return users;
}
// [END getInactiveUsers]
// [END all]
================================================
FILE: Node/delete-unused-accounts-cron/functions/package.json
================================================
{
"name": "delete-unused-accounts-cron-functions",
"description": "Periodically delete unused Firebase accounts",
"dependencies": {
"es6-promise-pool": "^2.5.0",
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0"
},
"scripts": {
"lint": "./node_modules/.bin/eslint --max-warnings=0 .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"compile": "cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json"
},
"engines": {
"node": "22"
},
"private": true
}
================================================
FILE: Node/fcm-notifications/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
firebase-debug.*.log*
# Firebase cache
.firebase/
# Firebase config
# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
================================================
FILE: Node/fcm-notifications/README.md
================================================
# Send Firebase Cloud Messaging notifications for new followers.
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.
## Functions Code
See file [functions/index.js](functions/index.js) for the code.
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.
The dependencies are listed in [functions/package.json](functions/package.json).
## Sample Database Structure
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`.:
```
/functions-project-12345
/users
/Uid-12345
displayName: "Bob Dole"
/notificationTokens
1234567890: true
photoURL: "https://lh3.googleusercontent.com/..."
```
If a user starts following another user we'll write to `/followers/$followedUid/$followerUid`:
```
/functions-project-12345
/followers
/followedUid-12345
followerUid-67890: true
/users
/Uid-12345
displayName: "Bob Dole"
/notificationTokens
1234567890: true
photoURL: "https://lh3.googleusercontent.com/..."
```
## Trigger rules
The function triggers every time the value of a follow flag changes at `/followers/$followedUid/$followerUid`.
## Deploy and test
This sample comes with a web-based UI for testing the function. To test it out:
1. Set up your Firebase project:
1. [Create a Firebase project](https://firebase.google.com/docs/web/setup/#create-firebase-project)
1. [Register your web app with Firebase](https://firebase.google.com/docs/web/setup/#register-app)
1. Enable **Google Provider** in the [Auth section](https://console.firebase.google.com/project/_/authentication/providers)
1. Clone or download this repo and open the `fcm-notification` directory.
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`.
1. Configure the CLI locally by using `firebase use --add` and select your project in the list.
1. Install dependencies locally by running: `cd functions; npm install; cd -`
1. Deploy your project using `firebase deploy`
1. Open the app using `firebase open hosting:site`, this will open a browser.
1. Start following a user, this will send a notification to them.
================================================
FILE: Node/fcm-notifications/database.rules.json
================================================
{
"rules": {
"users": {
".read": true,
"$uid": {
".write": "auth.uid === $uid"
}
},
"followers": {
"$followedUid": {
"$followerUid": {
".read": "auth.uid === $followerUid",
".write": "auth.uid === $followerUid"
}
}
}
}
}
================================================
FILE: Node/fcm-notifications/firebase.json
================================================
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public"
},
"functions": [
{
"source": "functions",
"codebase": "fcm-notifications",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log"
],
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
]
}
================================================
FILE: Node/fcm-notifications/functions/.eslintrc.cjs
================================================
module.exports = {
env: {
es2022: true,
node: true,
},
parserOptions: {
"ecmaVersion": 2022,
"sourceType": "module",
},
extends: [
"eslint:recommended",
"google",
],
rules: {
"no-restricted-globals": ["error", "name", "length"],
"prefer-arrow-callback": "error",
"quotes": ["error", "double", {"allowTemplateLiterals": true}],
},
overrides: [
{
files: ["**/*.spec.*"],
env: {
mocha: true,
},
rules: {},
},
],
globals: {},
};
================================================
FILE: Node/fcm-notifications/functions/.gitignore
================================================
node_modules/
================================================
FILE: Node/fcm-notifications/functions/index.js
================================================
/**
* Copyright 2023 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {initializeApp} from "firebase-admin/app";
import {getAuth} from "firebase-admin/auth";
import {getDatabase} from "firebase-admin/database";
import {getMessaging} from "firebase-admin/messaging";
import {log, warn} from "firebase-functions/logger";
import {onValueWritten} from "firebase-functions/database";
initializeApp();
const auth = getAuth();
const db = getDatabase();
const messaging = getMessaging();
/**
* Triggers when a user gets a new follower and sends a notification. Followers
* add a flag to `/followers/{followedUid}/{followerUid}`. Users save their
* device notification tokens to
* `/users/{followedUid}/notificationTokens/{notificationToken}`.
*/
export const sendFollowerNotification = onValueWritten(
"/followers/{followedUid}/{followerUid}",
async (event) => {
// If un-follow we exit the function.
if (!event.data.after.val()) {
log(`User ${event.params.followerUid} unfollowed` +
` user ${event.params.followedUid} :(`);
return;
}
log(`User ${event.params.followerUid} is now following` +
` user ${event.params.followedUid}`);
const tokensRef =
db.ref(`/users/${event.params.followedUid}/notificationTokens`);
const notificationTokens = await tokensRef.get();
if (!notificationTokens.hasChildren()) {
log("There are no tokens to send notifications to.");
return;
}
log(`There are ${notificationTokens.numChildren()} tokens` +
" to send notifications to.");
const followerProfile = await auth.getUser(event.params.followerUid);
// Notification details.
const notification = {
title: "You have a new follower!",
body: (followerProfile.displayName ?? "Someone") +
" is now following you.",
image: followerProfile.photoURL ?? "",
};
// Send notifications to all tokens.
const messages = [];
notificationTokens.forEach((child) => {
messages.push({
token: child.key,
notification: notification,
});
});
const batchResponse = await messaging.sendEach(messages);
if (batchResponse.failureCount < 1) {
// Messages sent sucessfully. We're done!
log("Messages sent.");
return;
}
warn(`${batchResponse.failureCount} messages weren't sent.`,
batchResponse);
// Clean up the tokens that are not registered any more.
for (let i = 0; i < batchResponse.responses.length; i++) {
const errorCode = batchResponse.responses[i].error?.code;
const errorMessage = batchResponse.responses[i].error?.message;
if ((errorCode === "messaging/invalid-registration-token") ||
(errorCode === "messaging/registration-token-not-registered") ||
(errorCode === "messaging/invalid-argument" &&
errorMessage ===
"The registration token is not a valid FCM registration token")) {
log(`Removing invalid token: ${messages[i].token}`);
await tokensRef.child(messages[i].token).remove();
}
}
});
================================================
FILE: Node/fcm-notifications/functions/package.json
================================================
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "22"
},
"main": "index.js",
"type": "module",
"dependencies": {
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.4.0"
},
"private": true
}
================================================
FILE: Node/fcm-notifications/public/firebase-messaging-sw.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Import and configure the Firebase SDK
// These scripts are made available when the app is served or deployed on Firebase Hosting
// If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup
importScripts('/__/firebase/10.0.0/firebase-app-compat.js');
importScripts('/__/firebase/10.0.0/firebase-messaging-compat.js');
importScripts('/__/firebase/init.js');
firebase.messaging();
================================================
FILE: Node/fcm-notifications/public/index.html
================================================
<!doctype html>
<!--
Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="Demonstrates of to authorize Firebase with Instagram Auth using Firebase Functions">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Firebase Functions demo send FCM notifications</title>
<!-- Material Design Lite -->
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css">
<script defer src="https://code.getmdl.io/1.1.3/material.min.js"></script>
<link rel="stylesheet" href="main.css">
</head>
<body>
<div class="demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header">
<!-- Header section containing title -->
<header class="mdl-layout__header mdl-color-text--white mdl-color--light-blue-700">
<div class="mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid">
<div class="mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop">
<h3>Send FCM notifications demo</h3>
</div>
</div>
</header>
<main class="mdl-layout__content mdl-color--grey-100">
<div class="mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid">
<!-- Card containing the sign-in UI -->
<div id="demo-signed-out-card" class="mdl-card mdl-shadow--2dp mdl-cell">
<div class="mdl-card__supporting-text mdl-color-text--grey-600">
<p>
This web application demonstrates how you can send FCM notifications using Functions and a web client.
<strong>Now sign in and activate notifications for your user!</strong>
</p>
<button id="demo-sign-in-button" class="mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect"><i class="material-icons">account_circle</i> Sign in with Google</button>
</div>
</div>
<!-- Card containing the signed-in UI -->
<div id="demo-signed-in-card" class="mdl-card mdl-shadow--2dp mdl-cell">
<div class="mdl-card__supporting-text mdl-color-text--grey-600">
<p>
Welcome <span id="demo-name-container"></span>
</p>
<div id="demo-fcm-error-container"></div>
<button id="demo-sign-out-button" class="mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect">Sign out</button>
<button id="demo-delete-button" class="mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect">Delete account</button>
</div>
</div>
<!-- Card containing the users to follow -->
<div id="demo-all-users-card" class="mdl-card mdl-shadow--2dp mdl-cell mdl-cell--9-col">
<div class="mdl-card__supporting-text mdl-color-text--grey-600">
<p>
Here are all the users. If they have enabled notifications they will get a notification when you start following them.
</p>
</div>
<div id="demo-all-users-list"></div>
</div>
</div>
<!-- Snackbar -->
<div id="demo-snackbar" aria-live="assertive" aria-atomic="true" aria-relevant="text" class="mdl-snackbar mdl-js-snackbar">
<div class="mdl-snackbar__text"></div>
<button type="button" class="mdl-snackbar__action"></button>
</div>
</main>
</div>
<!-- Import and configure the Firebase SDK -->
<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->
<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->
<script src="/__/firebase/10.0.0/firebase-app-compat.js"></script>
<script src="/__/firebase/10.0.0/firebase-auth-compat.js"></script>
<script src="/__/firebase/10.0.0/firebase-messaging-compat.js"></script>
<script src="/__/firebase/10.0.0/firebase-database-compat.js"></script>
<script src="/__/firebase/init.js"></script>
<script src="main.js"></script>
</body>
</html>
================================================
FILE: Node/fcm-notifications/public/main.css
================================================
/**
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
html, body {
font-family: 'Roboto', 'Helvetica', sans-serif;
}
.mdl-grid {
max-width: 1024px;
margin: auto;
}
.mdl-card {
min-height: 0;
padding-bottom: 5px;
}
.mdl-layout__header-row {
padding: 0;
}
h3 {
background: url('firebase-logo.png') no-repeat;
background-size: 40px;
padding-left: 50px;
}
#demo-signed-out-card,
#demo-signed-in-card {
display: none;
}
#demo-profile-pic {
height: 60px;
width: 60px;
border-radius: 30px;
margin-left: calc(50% - 30px);
margin-top: 10px;
}
#demo-name-container {
font-weight: bold;
}
#demo-fcm-error-container {
margin-bottom: 20px;
}
#demo-delete-button {
margin-left: 20px;
}
#demo-all-users-list {
margin-bottom: -5px;
}
.demo-user-container {
position: relative;
}
.demo-user-container:HOVER {
background-color: #eee;
}
.demo-profile-pic {
height: 50px;
width: 50px;
}
.demo-name {
margin-left: 15px;
}
.mdl-switch {
display: inline-block;
right: 0;
position: absolute;
width: auto;
padding-right: 40px;
top: 13px;
}
.demo-notifications-enabled {
color: #aaa;
font-size: 80%;
display: none;
}
================================================
FILE: Node/fcm-notifications/public/main.js
================================================
/**
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
// Initializes the Demo.
function Demo() {
document.addEventListener('DOMContentLoaded', function() {
// Shortcuts to DOM Elements.
this.signInButton = document.getElementById('demo-sign-in-button');
this.signOutButton = document.getElementById('demo-sign-out-button');
this.nameContainer = document.getElementById('demo-name-container');
this.fcmErrorContainer = document.getElementById('demo-fcm-error-container');
this.deleteButton = document.getElementById('demo-delete-button');
this.signedOutCard = document.getElementById('demo-signed-out-card');
this.signedInCard = document.getElementById('demo-signed-in-card');
this.usersContainer = document.getElementById('demo-all-users-list');
this.usersCard = document.getElementById('demo-all-users-card');
this.snackbar = document.getElementById('demo-snackbar');
// Bind events.
this.signInButton.addEventListener('click', this.signIn.bind(this));
this.signOutButton.addEventListener('click', this.signOut.bind(this));
this.deleteButton.addEventListener('click', this.deleteAccount.bind(this));
firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));
firebase.messaging().onMessage(this.onMessage.bind(this));
}.bind(this));
}
// Triggered on Firebase auth state change.
Demo.prototype.onAuthStateChanged = function(user) {
// If this is just an ID token refresh we exit.
if (user && this.currentUid === user.uid) {
return;
}
// Remove all Firebase realtime database listeners.
if (this.listeners) {
this.listeners.forEach(function(ref) {
ref.off();
});
}
this.listeners = [];
// Adjust UI depending on user state.
if (user) {
this.nameContainer.innerText = user.displayName;
this.signedOutCard.style.display = 'none';
this.signedInCard.style.display = 'block';
this.usersCard.style.display = 'block';
firebase.database().ref(`users/${user.uid}`).update({
displayName: user.displayName,
photoURL: user.photoURL
});
this.saveToken();
this.displayAllUsers();
this.currentUid = user.uid;
} else {
this.signedOutCard.style.display = 'block';
this.signedInCard.style.display = 'none';
this.usersCard.style.display = 'none';
this.usersContainer.innerHTML = '';
this.currentUid = null;
}
};
// Display all users so that they can be followed.
Demo.prototype.displayAllUsers = function() {
var usersRef = firebase.database().ref('users');
usersRef.on('child_added', function(snapshot) {
// Create the HTML for a user.
var photoURL = snapshot.val().photoURL;
var displayName = snapshot.val().displayName;
var uid = snapshot.key;
var userTemplate =
'<div class="demo-user-container">' +
' <img class="demo-profile-pic" src="' + photoURL + '">' +
' <span class="demo-name">' + displayName + '</span>' +
' <span class="demo-notifications-enabled">(notifications enabled)</span>' +
' <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="demo-follow-switch-' + uid + '">' +
' <input type="checkbox" id="demo-follow-switch-' + uid + '" class="mdl-switch__input">' +
' <span class="mdl-switch__label">Follow</span>' +
' </label>' +
'</div>';
// Create the DOM element from the HTML.
var div = document.createElement('div');
div.innerHTML = userTemplate;
var userElement = div.firstChild;
this.usersContainer.appendChild(userElement);
// Activate the Material Design Lite Switch element.
var materialSwitchContainer = userElement.getElementsByClassName('mdl-switch')[0];
if (componentHandler) {
componentHandler.upgradeElement(materialSwitchContainer);
}
// Check if the user has notifications enabled and show a flag if he has.
var notificationEnabledElement = userElement.getElementsByClassName('demo-notifications-enabled')[0];
var notificationsEnabledRef = snapshot.ref.child('notificationTokens');
notificationsEnabledRef.on('value', function(notificationsEnabledSnapshot) {
notificationEnabledElement.style.display = notificationsEnabledSnapshot.hasChildren() ? 'inline' : 'none';
});
this.listeners.push(notificationsEnabledRef);
// Listen for the Switch state from the Realtime database.
var switchElement = document.getElementById('demo-follow-switch-' + uid);
var followUserRef = firebase.database().ref('followers/' + uid + '/' + this.currentUid);
this.listeners.push(followUserRef);
followUserRef.on('value', function(followSnapshot) {
switchElement.checked = !!followSnapshot.val();
if (materialSwitchContainer.MaterialSwitch) {
materialSwitchContainer.MaterialSwitch.checkDisabled();
materialSwitchContainer.MaterialSwitch.checkToggleState();
}
});
// Listen for switch state changes from the user.
switchElement.addEventListener('change', function() {
followUserRef.set(!!switchElement.checked);
});
}.bind(this));
this.listeners.push(usersRef);
};
// Initiates the sign-in flow using LinkedIn sign in in a popup.
Demo.prototype.signIn = function() {
var google = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(google);
};
// Signs-out of Firebase.
Demo.prototype.signOut = function() {
firebase.auth().signOut();
};
// Deletes the user's account.
Demo.prototype.deleteAccount = function() {
return firebase.database().ref('users/' + this.currentUid).remove().then(function() {
return firebase.auth().currentUser.delete().then(function() {
window.alert('Account deleted');
}).catch(function(error) {
if (error.code === 'auth/requires-recent-login') {
window.alert('You need to have recently signed-in to delete your account. Please sign-in and try again.');
firebase.auth().signOut();
}
});
});
};
// This is called when a notification is received while the app is in focus.
// When the app is not in focus or if the tab is closed, this function is not called and the FCM notifications is
// handled by the Service worker which displays a browser popup notification if the browser supports it.
Demo.prototype.onMessage = function(payload) {
console.log('Notifications received.', payload);
// Normally our Function sends a notification payload, we check just in case.
if (payload.notification) {
// If notifications are supported on this browser we display one.
// Note: This is for demo purposes only. For a good user experience it is not recommended to display browser
// notifications while the app is in focus. In a production app you probably want to only display some form of
// in-app notifications like the snackbar (see below).
if (window.Notification instanceof Function) {
// This displays a notification if notifications have been granted.
new Notification(payload.notification.title, payload.notification);
}
// Display the notification content in the Snackbar too.
this.snackbar.MaterialSnackbar.showSnackbar({message: payload.notification.body});
}
};
// Saves the token to the database if available. If not request permissions.
Demo.prototype.saveToken = function() {
firebase.messaging().getToken().then(function(currentToken) {
if (currentToken) {
firebase.database().ref('users/' + this.currentUid + '/notificationTokens/' + currentToken).set(true);
} else {
this.requestPermission();
}
}.bind(this)).catch(function(err) {
console.error('Unable to get messaging token.', err);
if (err.code === 'messaging/permission-default') {
this.fcmErrorContainer.innerText = 'You have not enabled notifications on this browser. To enable notifications reload the page and allow notifications using the permission dialog.';
} else if (err.code === 'messaging/notifications-blocked') {
this.fcmErrorContainer.innerHTML = 'You have blocked notifications on this browser. To enable notifications follow these instructions: <a href="https://support.google.com/chrome/answer/114662?visit_id=1-636150657126357237-2267048771&rd=1&co=GENIE.Platform%3DAndroid&oco=1">Android Chrome Instructions</a><a href="https://support.google.com/chrome/answer/6148059">Desktop Chrome Instructions</a>';
}
}.bind(this));
};
// Requests permission to send notifications on this browser.
Demo.prototype.requestPermission = function() {
console.log('Requesting permission...');
firebase.messaging().requestPermission().then(function() {
console.log('Notification permission granted.');
this.saveToken();
}.bind(this)).catch(function(err) {
console.error('Unable to get permission to notify.', err);
});
};
// Load the demo.
window.demo = new Demo();
================================================
FILE: Node/instrument-with-opentelemetry/README.md
================================================
# Instrumenting Cloud Functions for Firebase with Open Telemetry
This sample demonstrates instrumenting your Cloud Functions for Firebase using [OpenTelemetry](https://opentelemetry.io).
See Firebase Summit 2022 Talk "Observability in Cloud Functions for Firebase" for motivations and context.
Open Telemetry SDK provides both automatic and manual instrumentation, both of which are demonstrated here. See [OpenTelemetry JS documentations](https://opentelemetry.io/docs/instrumentation/js/) for more information about how to use and configure OpenTelemetry for your javascript project.
## Notable Files
* `./tracing.js`: Initializes OpenTelemetry SDK to automatically instrument HTTP/GRPC/Express modules and export the generated traces to Google Cloud Trace.
* `./.env`: Configures `NODE_OPTIONS` to preload the `tracing.js` module. This is important because OpenTelemtry SDK works by monkey-patching instrumented modules and must run first before other module is loaded.
* `./index.js`: Includes sample code for generating custom spans using the OpenTelemetry API. e.g.:
```js
const opentelemetry = require('@opentelemetry/api');
const tracer = opentelemetry.trace.getTracer();
await tracer.startActiveSpan("calculatePrice", async (span) => {
totalUsd = await calculatePrice(productIds);
span.end();
});
```
## Deploy and test
1. Deploy your function using firebase deploy --only functions
2. Seed Firestore with mock data.
3. Send callable request to the deployed function, e.g.:
```
$ curl -X POST -H "content-type: application/json" https:// -d '{ "data": ... }'
```
================================================
FILE: Node/instrument-with-opentelemetry/firebase.json
================================================
{
"functions": {
"source": "functions",
"codebase": "instrument-with-opentelemetry"
},
"emulators": {
"functions": {
"port": 5001
},
"firestore": {
"port": 8080
},
"ui": {
"enabled": true
},
"singleProjectMode": true
}
}
================================================
FILE: Node/instrument-with-opentelemetry/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/instrument-with-opentelemetry/functions/index.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {onCall} = require("firebase-functions/https");
const logger = require("firebase-functions/logger");
const {initializeApp} = require("firebase-admin/app");
const {getFirestore} = require("firebase-admin/firestore");
const opentelemetry = require("@opentelemetry/api");
const {Timer} = require("./timer");
initializeApp();
const db = getFirestore();
/**
* Divide an array into chunks of `chunkSize`
* @param {any[]} arr
* @param {Number} chunkSize
* @return {Array<Array<any>>}
*/
function sliceIntoChunks(arr, chunkSize) {
const res = [];
for (let i = 0; i < arr.length; i += chunkSize) {
const chunk = arr.slice(i, i + chunkSize);
res.push(chunk);
}
return res;
}
/**
* Get prices for all products in `productIds` array
* @param {string[]} productIds
* @return {number}
*/
async function calculatePrice(productIds) {
const timer = new Timer();
let totalUsd = 0;
const products = await db.getAll(
...productIds.map((id) => db.doc(`products/${id}`)),
);
for (const product of products) {
totalUsd += product.data()?.usd || 0;
}
logger.info("calculatePrice", {calcPriceMs: timer.measureMs()});
return totalUsd;
}
/**
* Sum discounts of all `productIds`
* @param {string[]} productIds
* @return {number}
*/
async function calculateDiscount(productIds) {
const timer = new Timer();
let discountUsd = 0;
const processConcurrently = sliceIntoChunks(productIds, 10)
.map(async (productIds) => {
const discounts = await db.collection("discounts")
.where("products", "array-contains-any", productIds)
.get();
for (const discount of discounts.docs) {
discountUsd += discount.data().usd || 0;
}
});
await Promise.all(processConcurrently);
logger.info("calculateDiscount", {calcDiscountMs: timer.measureMs()});
return discountUsd;
}
exports.calculatetotal = onCall(async (req) => {
const {productIds} = req.data;
let totalUsd = 0;
const tracer = opentelemetry.trace.getTracer();
await tracer.startActiveSpan("calculatePrice", async (span) => {
totalUsd = await calculatePrice(productIds);
span.end();
});
await tracer.startActiveSpan("calculateDiscount", async (span) => {
totalUsd -= await calculateDiscount(productIds);
span.end();
});
return {totalUsd};
});
================================================
FILE: Node/instrument-with-opentelemetry/functions/package.json
================================================
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "22"
},
"dependencies": {
"@google-cloud/opentelemetry-cloud-trace-exporter": "^1.1.0",
"@google-cloud/opentelemetry-cloud-trace-propagator": "^0.14.0",
"@opentelemetry/api": "^1.2.0",
"@opentelemetry/instrumentation": "^0.33.0",
"@opentelemetry/instrumentation-grpc": "^0.33.0",
"@opentelemetry/instrumentation-http": "^0.33.0",
"@opentelemetry/resource-detector-gcp": "^0.27.3",
"@opentelemetry/sdk-node": "^0.33.0",
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0",
"opentelemetry-instrumentation-express": "^0.29.0"
},
"private": true,
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0"
}
}
================================================
FILE: Node/instrument-with-opentelemetry/functions/timer.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
exports.Timer = class {
/**
*
*/
constructor() {
this.start = process.hrtime.bigint();
}
/**
* Get the time since this timer was constructed
* @return {string}
*/
measureMs() {
const duration = process.hrtime.bigint() - this.start;
return (duration / 1000000).toString();
}
};
================================================
FILE: Node/instrument-with-opentelemetry/functions/tracing.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const opentelemetry = require("@opentelemetry/sdk-node");
const {
TraceExporter,
} = require("@google-cloud/opentelemetry-cloud-trace-exporter");
const {HttpInstrumentation} = require("@opentelemetry/instrumentation-http");
const {GrpcInstrumentation} = require("@opentelemetry/instrumentation-grpc");
const {
ExpressInstrumentation,
} = require("opentelemetry-instrumentation-express");
const {gcpDetector} = require("@opentelemetry/resource-detector-gcp");
const {
CloudPropagator,
} = require("@google-cloud/opentelemetry-cloud-trace-propagator");
// Only enable OpenTelemetry if the function is actually deployed.
// Emulators don't reflect real-world latency"
if (!process.env.FUNCTIONS_EMULATOR) {
const sdk = new opentelemetry.NodeSDK({
// Setup automatic instrumentation for
// http, grpc, and express modules.
instrumentations: [
new HttpInstrumentation(),
new GrpcInstrumentation(),
new ExpressInstrumentation(),
],
// Make sure opentelemetry know about Cloud Trace http headers
// i.e. 'X-Cloud-Trace-Context'
textMapPropagator: new CloudPropagator(),
// Automatically detect and include span metadata when running
// in GCP, e.g. region of the function.
resourceDetectors: [gcpDetector],
// Export generated traces to Cloud Trace.
traceExporter: new TraceExporter(),
});
sdk.start();
// Ensure that generated traces are exported when the container is
// shutdown.
process.on("SIGTERM", async () => {
await sdk.shutdown();
});
}
================================================
FILE: Node/pnpm-workspace.yaml
================================================
packages:
- "./**"
================================================
FILE: Node/quickstarts/auth-blocking-functions/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
firebase-debug.*.log*
# Firebase cache
.firebase/
# Firebase config
# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
================================================
FILE: Node/quickstarts/auth-blocking-functions/README.md
================================================
# Firebase SDK for Cloud Functions 2nd Gen Quickstart - Auth Blocking Functions
================================================
The Auth Blocking functions Quickstart demonstrates how to block account sign in and creation when using Firebase Auth or Google Cloud Identity Platform in a Firebase App.
- [Read more about auth blocking functions](https://firebase.google.com/docs/auth/extend-with-blocking-functions)
- [Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)
Getting Started
---------------
To try this sample, you need a test app with Firebase Auth and Cloud Firestore enabled. Don't use a live app with real users!
1. Install dependencies with `npm install`
2. Deploy the functions with `firebase deploy --only functions`
3. Try to create an account using an email address with a domain _other than_ `@acme.com`. It should fail.
4. Add an existing user's email address to the `banned` collection in Cloud Firestore. Then, try to sign in as that user. It should fail.
License
-------
© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.
================================================
FILE: Node/quickstarts/auth-blocking-functions/firebase.json
================================================
{
"functions": {
"codebase": "auth-blocking-functions",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
}
================================================
FILE: Node/quickstarts/auth-blocking-functions/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/quickstarts/auth-blocking-functions/functions/.gitignore
================================================
node_modules/
================================================
FILE: Node/quickstarts/auth-blocking-functions/functions/index.js
================================================
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {
beforeUserCreated,
beforeUserSignedIn,
HttpsError,
} = require("firebase-functions/identity");
const {admin} = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
// [START v2ValidateNewUser]
// [START v2beforeCreateFunctionTrigger]
// Block account creation with any non-acme email address.
exports.validatenewuser = beforeUserCreated((event) => {
// [END v2beforeCreateFunctionTrigger]
// [START v2readUserData]
// User data passed in from the CloudEvent.
const user = event.data;
// [END v2readUserData]
// [START v2domainHttpsError]
// Only users of a specific domain can sign up.
if (!user?.email?.includes("@acme.com")) {
// Throw an HttpsError so that Firebase Auth rejects the account creation.
throw new HttpsError("invalid-argument", "Unauthorized email");
}
// [END v2domainHttpsError]
});
// [END v2ValidateNewUser]
// [START v2CheckForBan]
// [START v2beforeSignInFunctionTrigger]
// Block account sign in with any banned account.
exports.checkforban = beforeUserSignedIn(async (event) => {
// [END v2beforeSignInFunctionTrigger]
// [START v2readEmailData]
// Email passed from the CloudEvent.
const email = event.data.email || "";
// [END v2readEmailData]
// [START v2documentGet]
// Obtain a document in Firestore of the banned email address.
const doc = await db.collection("banned").doc(email).get();
// [END v2documentGet]
// [START v2bannedHttpsError]
// Checking that the document exists for the email address.
if (doc.exists) {
// Throw an HttpsError so that Firebase Auth rejects the account sign in.
throw new HttpsError("invalid-argument", "Unauthorized email");
}
// [END v2bannedHttpsError]
});
// [START v2CheckForBan]
================================================
FILE: Node/quickstarts/auth-blocking-functions/functions/package.json
================================================
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "22"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.4.0"
},
"private": true
}
================================================
FILE: Node/quickstarts/callable-functions/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
firebase-debug.*.log*
# Firebase cache
.firebase/
# Firebase config
# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
================================================
FILE: Node/quickstarts/callable-functions/README.md
================================================
Firebase HTTPS Callable functions Quickstart
================================================
The HTTPS Callable functions Quickstart demonstrates how to send requests to a server-side function and get a response back using one of the Client SDKs. It interoperates with the Web, iOS and Android quickstarts.
[Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)
Getting Started
---------------
1. Install dependencies with `npm install` and deploy with `firebase deploy --only functions`
1. Set up a client (import the functions client SDK, initializeApp, init the functions sdk)
2. Call the function
License
-------
© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.
================================================
FILE: Node/quickstarts/callable-functions/firebase.json
================================================
{
"functions": {
"codebase": "callable-functions",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
}
================================================
FILE: Node/quickstarts/callable-functions/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/quickstarts/callable-functions/functions/.gitignore
================================================
node_modules/
================================================
FILE: Node/quickstarts/callable-functions/functions/index.js
================================================
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// [START imports]
// Dependencies for callable functions.
const {onCall, HttpsError} = require("firebase-functions/https");
const {logger} = require("firebase-functions");
// Dependencies for the addMessage function.
const {getDatabase} = require("firebase-admin/database");
const sanitizer = require("./sanitizer");
// [END imports]
// [START v2allAdd]
// [START v2addFunctionTrigger]
// Adds two numbers to each other.
exports.addnumbers = onCall((request) => {
// [END v2addFunctionTrigger]
// [START v2readAddData]
// Numbers passed from the client.
const firstNumber = request.data.firstNumber;
const secondNumber = request.data.secondNumber;
// [END v2readAddData]
// [START v2addHttpsError]
// Checking that attributes are present and are numbers.
if (!Number.isFinite(firstNumber) || !Number.isFinite(secondNumber)) {
// Throwing an HttpsError so that the client gets the error details.
throw new HttpsError("invalid-argument", "The function must be called " +
"with two arguments \"firstNumber\" and \"secondNumber\" which " +
"must both be numbers.");
}
// [END v2addHttpsError]
// [START v2returnAddData]
// returning result.
return {
firstNumber: firstNumber,
secondNumber: secondNumber,
operator: "+",
operationResult: firstNumber + secondNumber,
};
// [END v2returnAddData]
});
// [END v2allAdd]
// [START v2messageFunctionTrigger]
// Saves a message to the Firebase Realtime Database but sanitizes the
// text by removing swearwords.
exports.addmessage = onCall((request) => {
// [START_EXCLUDE]
// [START v2readMessageData]
// Message text passed from the client.
const text = request.data.text;
// [END v2readMessageData]
// [START v2messageHttpsErrors]
// Checking attribute.
if (!(typeof text === "string") || text.length === 0) {
// Throwing an HttpsError so that the client gets the error details.
throw new HttpsError("invalid-argument", "The function must be called " +
"with one arguments \"text\" containing the message text to add.");
}
// Checking that the user is authenticated.
if (!request.auth) {
// Throwing an HttpsError so that the client gets the error details.
throw new HttpsError("failed-precondition", "The function must be " +
"called while authenticated.");
}
// [END v2messageHttpsErrors]
// [START v2authIntegration]
// Authentication / user information is automatically added to the request.
const uid = request.auth.uid;
const name = request.auth.token.name || null;
const picture = request.auth.token.picture || null;
const email = request.auth.token.email || null;
// [END v2authIntegration]
// [START v2returnMessageAsync]
// Saving the new message to the Realtime Database.
const sanitizedMessage = sanitizer.sanitizeText(text); // Sanitize message.
return getDatabase().ref("/messages").push({
text: sanitizedMessage,
author: {uid, name, picture, email},
}).then(() => {
logger.info("New Message written");
// Returning the sanitized message to the client.
return {text: sanitizedMessage};
})
// [END v2returnMessageAsync]
.catch((error) => {
// Re-throwing the error as an HttpsError so that the client gets
// the error details.
throw new HttpsError("unknown", error.message, error);
});
// [END_EXCLUDE]
});
// [END v2messageFunctionTrigger]
================================================
FILE: Node/quickstarts/callable-functions/functions/package.json
================================================
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"lintfix": "eslint . --fix",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"compile": "cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json"
},
"engines": {
"node": "22"
},
"main": "index.js",
"dependencies": {
"bad-words": "^3.0.4",
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.4.0"
},
"private": true
}
================================================
FILE: Node/quickstarts/callable-functions/functions/sanitizer.js
================================================
/**
* Copyright 2022 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
const Filter = require("bad-words");
const badWordsFilter = new Filter();
// Sanitizes the given text if needed by replacing bad words with '*'.
exports.sanitizeText = (text) => {
// Re-capitalize if the user is Shouting.
if (isShouting(text)) {
console.log("User is shouting. Fixing sentence case...");
text = stopShouting(text);
}
// Moderate if the user uses SwearWords.
if (containsSwearwords(text)) {
console.log("User is swearing. moderating...");
text = replaceSwearwords(text);
}
return text;
};
/**
* Returns true if the string contains swearwords.
* @param {string} message
* @return {boolean}
*/
function containsSwearwords(message) {
return message !== badWordsFilter.clean(message);
}
/**
* Hide all swearwords. e.g: Crap => ****.
* @param {string} message
* @return {string}
*/
function replaceSwearwords(message) {
return badWordsFilter.clean(message);
}
/**
* Detect if the current message is shouting. i.e. there are too many Uppercase
* characters or exclamation points.
* @param {string} message message to be analyzed
* @return {boolean}
*/
function isShouting(message) {
return message.replace(/[^A-Z]/g, "").length > message.length / 2 ||
message.replace(/[^!]/g, "").length >= 3;
}
/**
* Correctly capitalize the string as a sentence (e.g. uppercase after dots)
* and remove exclamation ppints.
* @param {string} message message to capitalize
* @return {string} capitalized string
*/
function stopShouting(message) {
const sentenceCaseRegex = /(:?\.\s?|^)([A-Za-z\u00C0-\u1FFF\u2800-\uFFFD])/gi;
const noExclamationsRegex = /!+/g;
return message
.toLowerCase()
.replace(sentenceCaseRegex, (match) => match.toUpperCase())
.replace(noExclamationsRegex, ".");
}
================================================
FILE: Node/quickstarts/callable-functions-streaming/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
firebase-debug.*.log*
# Firebase cache
.firebase/
# Firebase config
# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
================================================
FILE: Node/quickstarts/callable-functions-streaming/README.md
================================================
Firebase HTTPS Callable functions streaming quickstart
================================================
This quickstart demonstrates how to send requests to a server-side function and _stream_ a response to a client SDK.
[Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)
Getting Started
---------------
1. Install dependencies with `npm install`
1. Start the hosting and functions emulators with `firebase emulators:start --only functions,hosting`
1. Visit the url of the emulated Hosting site, and click "Get forecasts"
License
-------
© Google, 2025. Licensed under an [Apache-2](../../../LICENSE) license.
================================================
FILE: Node/quickstarts/callable-functions-streaming/firebase.json
================================================
{
"functions": {
"codebase": "callable-functions",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
},
"hosting": {
"public": "website"
}
}
================================================
FILE: Node/quickstarts/callable-functions-streaming/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/quickstarts/callable-functions-streaming/functions/.gitignore
================================================
node_modules/
================================================
FILE: Node/quickstarts/callable-functions-streaming/functions/index.js
================================================
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// [START full-sample]
// Dependencies for callable functions.
const {onCall, HttpsError} = require("firebase-functions/https");
/**
* Gets the weather from the national weather service
* https://www.weather.gov/documentation/services-web-api
*
* @param {number} lat
* @param {number} lng
*/
async function weatherForecastApi(lat, lng) {
const resp = await fetch(`https://api.weather.gov/points/${lat},${lng}`);
if (!resp.ok) {
return `error: ${resp.status}`;
}
const forecastUrl = (await resp.json()).properties.forecast;
const forecastResp = await fetch(forecastUrl);
if (!forecastResp.ok) {
return `error: ${forecastResp.status}`;
}
// add an artificial wait to emphasize stream-iness
await new Promise((resolve) => setTimeout(resolve, Math.random() * 1500));
return forecastResp.json();
}
// [START streaming-callable]
exports.getForecast = onCall(async (request, response) => {
if (request.data?.locations?.length < 1) {
throw new HttpsError("invalid-argument", "Missing locations to forecast");
}
// fetch forecast data for all requested locations
const allRequests = request.data.locations.map(
async ({latitude, longitude}) => {
const forecast = await weatherForecastApi(latitude, longitude);
const result = {latitude, longitude, forecast};
// clients that support streaming will have each
// forecast streamed to them as they complete
if (request.acceptsStreaming) {
response.sendChunk(result);
}
return result;
},
);
// Return the full set of data to all clients
return Promise.all(allRequests);
});
// [END streaming-callable]
// [END full-sample]
================================================
FILE: Node/quickstarts/callable-functions-streaming/functions/package.json
================================================
{
"name": "callable-functions-streaming",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"lintfix": "eslint . --fix",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"compile": "cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json"
},
"engines": {
"node": "22"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.4.0"
},
"private": true
}
================================================
FILE: Node/quickstarts/callable-functions-streaming/website/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>title</title>
</head>
<body>
<h1>Many forecasts</h1>
<p>Click the button to get the forecast for all your favorite locations.</p>
<button id="btn-call-func">Get forecasts</button>
<div id="forecasts"></div>
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.2.0/firebase-app.js";
import {
getFunctions,
httpsCallable,
connectFunctionsEmulator,
} from "https://www.gstatic.com/firebasejs/11.2.0/firebase-functions.js";
const callableButton = document.getElementById("btn-call-func");
const resultElement = document.getElementById("forecasts");
callableButton.onclick = handleClick;
// https://firebase.google.com/docs/hosting/reserved-urls#sdk_auto-configuration
const firebaseConfig = await fetch("/__/firebase/init.json").then(
(response) => {
return response.json();
}
);
const app = initializeApp(firebaseConfig);
const functions = getFunctions(app);
connectFunctionsEmulator(functions, "127.0.0.1", 5001);
const favoriteLocations = [
{
name: "The Googleplex",
latitude: 37.4220199895279,
longitude: -122.08531347325561,
},
{
name: "Yosemite Valley",
latitude: 37.745192257741984,
longitude: -119.5945133017153,
},
{
name: "Old Faithful",
latitude: 44.46037818049411,
longitude: -110.82802255265777,
},
];
async function handleClick() {
// reset result
initializeUi();
// [START stream_data_client]
// Get the callable by passing an initialized functions SDK.
const getForecast = httpsCallable(functions, "getForecast");
// Call the function with the `.stream()` method to start streaming.
const { stream, data } = await getForecast.stream({
locations: favoriteLocations,
});
// The `stream` async iterable returned by `.stream()`
// will yield a new value every time the callable
// function calls `sendChunk()`.
for await (const forecastDataChunk of stream) {
// update the UI every time a new chunk is received
// from the callable function
updateUi(forecastDataChunk);
}
// The `data` promise resolves when the callable
// function completes.
const allWeatherForecasts = await data;
finalizeUi(allWeatherForecasts);
// [END stream_data_client]
}
function initializeUi() {
resultElement.innerHTML = "";
callableButton.disabled = true;
callableButton.innerText = "Streaming forecasts...";
}
function finalizeUi() {
callableButton.disabled = false;
callableButton.innerText = "Get forecasts";
}
function updateUi(forecastData) {
const newWeatherCard = document.createElement("div");
const locationName = document.createElement("h2");
locationName.innerHTML = favoriteLocations.find(
(v) => v.latitude === forecastData.latitude
).name;
const forecast = document.createElement("p");
console.log(forecastData);
forecast.innerHTML =
forecastData.forecast.properties.periods[0].detailedForecast;
newWeatherCard.append(locationName, forecast);
resultElement.appendChild(newWeatherCard);
}
</script>
</body>
</html>
================================================
FILE: Node/quickstarts/custom-events/ README.md
================================================
Firebase Custom Events sample
================================================
A custom event trigger function that handles `firebase.extensions.storage-resize-images.v1.complete` and adds custom workflow.
Getting Started
---------------
1. Install the [Resize Images
](https://firebase.google.com/products/extensions/firebase-storage-resize-images)
1. Install dependencies with `npm install` and deploy with `firebase deploy --only functions`
1. Upload a test image to the storage bucket configured for the extension.
License
-------
© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.
================================================
FILE: Node/quickstarts/custom-events/firebase.json
================================================
{
"functions": {
"codebase": "custom-events",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
}
================================================
FILE: Node/quickstarts/custom-events/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/quickstarts/custom-events/functions/index.js
================================================
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// [START import]
const {onCustomEventPublished} = require("firebase-functions/eventarc");
const logger = require("firebase-functions/logger");
const {initializeApp} = require("firebase-admin/app");
const {getFirestore} = require("firebase-admin/firestore");
// [END import]
initializeApp();
// [START imageresizedEvent]
exports.onimageresized = onCustomEventPublished(
"firebase.extensions.storage-resize-images.v1.complete",
(event) => {
logger.info("Received image resize completed event", event);
// For example, write resized image details into Firestore.
return getFirestore()
.collection("images")
.doc(event.subject.replace("/", "_")) // original file path
.set(event.data); // resized images paths and sizes
});
// [END imageresizedEvent]
================================================
FILE: Node/quickstarts/custom-events/functions/package.json
================================================
{
"name": "functions-custom-events",
"description": "Custom event handler for Image Resizer extension.",
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"compile": "cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json"
},
"engines": {
"node": "22"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0"
},
"private": true
}
================================================
FILE: Node/quickstarts/firestore-sync-auth/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
# Firebase cache
.firebase/
# Firebase config
# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
================================================
FILE: Node/quickstarts/firestore-sync-auth/README.md
================================================
# Firebase SDK for Cloud Functions Quickstart - Firestore with auth context
This quickstart demonstrates using the **Firebase SDK for Cloud Functions** with
**Firestore** with authentication context.
## Introduction
This sample app adds auth metadata to all documents written to a collection.
## Set up the sample
Before you can test the functions locally or deploy to a Firebase project,
you'll need to run `npm install` in the `functions` directory.
## Run locally with the Firebase Emulator suite
The
[Firebase Local Emulator Suite](https://firebase.google.com/docs/emulator-suite)
allows you to build and test apps on your local machine instead of deploying to
a Firebase project.
1. Create a Firebase project in the
[Firebase Console](https://console.firebase.google.com)
> _Wondering why this step is needed?_ Even though the emulator will run this
> sample on your local machine, it needs to interact with a Firebase project
> to retrieve some configuration values.
1. [Set up or update the Firebase CLI](https://firebase.google.com/docs/cli#setup_update_cli)
1. Run `firebase emulators:start`
1. Open the Emulator Suite UI
1. Look in the output of the `firebase emulators:start` command for the URL
of the Emulator Suite UI. It defaults to
[localhost:4000](http://localhost:4000), but may be hosted on a different
port on your machine.
1. Enter that URL in your browser to open the UI.
1. Trigger the functions
1. Look in the output of the `firebase emulators:start` command for the URL
of the http function "verifyComment". It will look similar to:
`http://localhost:5001/MY_PROJECT/us-central1/verifyComment`
1. `MY_PROJECT` will be replaced with your project ID
1. The port may be different on your local machine
1. Create a new document in the `comments` collection in Firestore in the emulator UI.
1. View the effects of the functions in the Emulator Suite UI
1. In the "Logs" tab, you should see new logs indicating that the functions
"verifyComment" and "makeuppercase" ran:
> `functions: Beginning execution of "verifyComment"`
1. In the "Firestore" tab, you should see the document containing your original
message updated to include auth context.
## Deploy and test on a live Firebase project
To deploy and test the sample:
1. Create a Firebase project on the
[Firebase Console](https://console.firebase.google.com)
1. Deploy your project's code using `firebase deploy`
1. Create a new document in the `comments` collection in Firestore in the Firebase console.
You should see the document containing your original message updated to include auth context.
## Contributing
We'd love that you contribute to the project. Before doing so please read our
[Contributor guide](../../CONTRIBUTING.md).
## License
© Google, 2023. Licensed under an [Apache-2](../../LICENSE) license.
================================================
FILE: Node/quickstarts/firestore-sync-auth/firebase.json
================================================
{
"functions": {
"codebase": "firestore-sync-auth"
},
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"emulators": {
"functions": {
"port": 5001
},
"firestore": {
"port": 8080
},
"ui": {
"enabled": true
}
}
}
================================================
FILE: Node/quickstarts/firestore-sync-auth/firestore.indexes.json
================================================
{
"indexes": [],
"fieldOverrides": []
}
================================================
FILE: Node/quickstarts/firestore-sync-auth/firestore.rules
================================================
service cloud.firestore {
match /databases/{database}/documents {
match /comments/{comment} {
// Allow authenticated users to read/write the comments collection
allow read, write: if request.auth != null;
}
}
}
================================================
FILE: Node/quickstarts/firestore-sync-auth/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/quickstarts/firestore-sync-auth/functions/.gitignore
================================================
node_modules/
================================================
FILE: Node/quickstarts/firestore-sync-auth/functions/index.js
================================================
/**
* Copyright 2023 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
// [START all]
// [START import]
// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
const {
onDocumentWrittenWithAuthContext,
} = require("firebase-functions/firestore");
const {logger} = require("firebase-functions");
exports.verifyComment = onDocumentWrittenWithAuthContext(
"comments/{commentId}",
(event) => {
const snapshot = event.data.after;
if (!snapshot) {
logger.log("No data associated with the event");
return;
}
// retrieve auth context from event
const {authType, authId} = event;
let verified = false;
if (authType === "system") {
// system-generated users are automatically verified
verified = true;
} else if (authType === "unknown" || authType === "unauthenticated") {
// admin users from a specific domain are verified
if (authId.endsWith("@example.com")) {
verified = true;
}
}
// add auth medadata to the document
return snapshot.ref.set(
{
created_by: authId ?? "undefined",
verified,
},
{merge: true},
);
},
);
// [END all]
================================================
FILE: Node/quickstarts/firestore-sync-auth/functions/package.json
================================================
{
"name": "firestore-sync-auth",
"description": "Cloud Functions for Firebase",
"dependencies": {
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"chai": "^4.3.6",
"chai-as-promised": "^7.1.1",
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0",
"mocha": "^7.2.0",
"sinon": "^9.2.4"
},
"scripts": {
"lint": "./node_modules/.bin/eslint --max-warnings=0 .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"compile": "cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json"
},
"engines": {
"node": "22"
},
"private": true
}
================================================
FILE: Node/quickstarts/genkit-helloworld/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
firebase-debug.*.log*
# Firebase cache
.firebase/
# Firebase config
# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
================================================
FILE: Node/quickstarts/genkit-helloworld/README.md
================================================
Genkit quickstart
================================================
This quickstart demonstrates how to initialize a [Genkit flow](https://firebase.google.com/docs/genkit/flows) and serve it with Cloud Functions for Firebase.
[Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)
Getting Started
---------------
1. Install dependencies with `npm install`
1. Start the functions emulator with `firebase emulators:start --only functions`
1. The emulator will output the function URL. It is usually of the form:
```
https://127.0.0.1:5001/{$PROJECT}/us-central1/tellJoke
```
1. Call the function from a terminal, replacing the `url` argument with your function's URL:
```bash
$ curl -X POST \
--url https://127.0.0.1:5001/{$PROJECT}/us-central1/tellJoke \
--header "Content-Type: application/json" \
--header "Accept: text/event-stream" \
--data '{"data": "Observational comedy"}'
```
License
-------
© Google, 2025. Licensed under an [Apache-2](../../../LICENSE) license.
================================================
FILE: Node/quickstarts/genkit-helloworld/firebase.json
================================================
{
"functions": {
"codebase": "oncallgenkit-helloworld",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
}
================================================
FILE: Node/quickstarts/genkit-helloworld/functions/.eslintrc.js
================================================
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/quickstarts/genkit-helloworld/functions/.gitignore
================================================
node_modules/
================================================
FILE: Node/quickstarts/genkit-helloworld/functions/index.js
================================================
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// [START complete-example]
// [START imports]
// [START import-trigger]
const {onCallGenkit} = require("firebase-functions/https");
// [END import-trigger]
// [START import-params]
const {defineSecret} = require("firebase-functions/params");
// [END import-params]
// [START import-genkit]
// Dependencies for Genkit.
const {gemini15Flash, googleAI} = require("@genkit-ai/googleai");
const {genkit, z} = require("genkit");
// [END import-genkit]
// [END imports]
// [START define-secret]
// Store the Gemini API key in Cloud Secret Manager.
const apiKey = defineSecret("GOOGLE_GENAI_API_KEY");
// [END define-secret]
// [START flow]
const ai = genkit({
plugins: [googleAI()],
model: gemini15Flash,
});
const jokeTeller = ai.defineFlow({
name: "jokeTeller",
inputSchema: z.string().nullable(),
outputSchema: z.string(),
streamSchema: z.string(),
}, async (jokeType = "knock-knock", {sendChunk}) => {
const prompt = `Tell me a ${jokeType} joke.`;
// Call the `generateStream()` method to
// receive the `stream` async iterable.
const {stream, response: aiResponse} = ai.generateStream(prompt);
// Send new words of the generative AI response
// to the client as they are generated.
for await (const chunk of stream) {
sendChunk(chunk.text);
}
// Return the full generative AI response
// to clients that may not support streaming.
return (await aiResponse).text;
},
);
// [END flow]
// [START trigger]
exports.tellJoke = onCallGenkit({
// [START bind-secrets]
// Bind the Gemini API key secret parameter to the function.
secrets: [apiKey],
// [END bind-secrets]
},
// Pass in the genkit flow.
jokeTeller,
);
// [END trigger]
// [END complete-example]
================================================
FILE: Node/quickstarts/genkit-helloworld/functions/package.json
================================================
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"lintfix": "eslint . --fix",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"compile": "cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json"
},
"engines": {
"node": "22"
},
"main": "index.js",
"dependencies": {
"@genkit-ai/googleai": "1.0.0-rc.12",
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0",
"genkit": "1.0.0-rc.12"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.4.0"
},
"private": true
}
================================================
FILE: Node/quickstarts/https-time-server/README.md
================================================
# Firebase SDK for Cloud Functions Quickstart - HTTPS trigger
This quickstart demonstrates using the **Firebase SDK for Cloud Functions** with an HTTPS trigger through building an endpoint returning the current time.
## Introduction
The function `date` returns the current server date. You can pass it a `format` URL Query parameter to format the date.
Further reading:
- [Read more about the Firebase SDK for Cloud Functions](https://firebase.google.com/docs/functions)
## Initial setup, build tools and dependencies
### 1. Clone this repo
Clone or download this repo and open the `quickstarts/time-server` directory.
### 2. Create a Firebase project and configure the quickstart
Create a Firebase Project on the [Firebase Console](https://console.firebase.google.com).
Set up your Firebase project by running `firebase use --add`, select your Project ID and follow the instructions.
### 3. Install the Firebase CLI and enable Functions on your Firebase CLI
You need to have installed the Firebase CLI. If you haven't run:
```bash
npm install -g firebase-tools
```
> Doesn't work? You may need to [change npm permissions](https://docs.npmjs.com/getting-started/fixing-npm-permissions).
## Deploy the app to prod
First you need to install the `npm` dependencies of the functions:
```bash
cd functions && npm install; cd ..
```
This installs locally:
- The Firebase SDK and the Firebase Functions SDK.
- The [moment](https://www.npmjs.com/package/moment) npm package to format time.
Deploy to Firebase using the following command:
```bash
firebase deploy
```
This deploys and activates the date Function.
> The first time you call `firebase deploy` on a new project with Functions will take longer than usual.
Alteratively, you can call `firebase emulators:start` to test the functions on the local emulator suite.
## Try the sample
After deploying the function, check the CLI's output to see the URL for your function.
It will look something like: `https://<function-name>-<random-hash>-<region>.a.run.app`
You can also send the format in the request body. For instance using cURL in the command line:
```bash
curl -H 'Content-Type: application/json' /
-d '{"format": "MMMM Do YYYY, h:mm:ss a"}' /
<function url>/date
```
Formatted dates should be displayed.
We are responding with a 403 error in case of PUT requests:
```bash
curl -X PUT -d '{"format": "MMMM Do YYYY, h:mm:ss a"}' <function-url>/date
```
## Contributing
We'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).
## License
© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.
================================================
FILE: Node/quickstarts/https-time-server/firebase.json
================================================
{
"functions": {
"codebase": "https-time-server",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
}
================================================
FILE: Node/quickstarts/https-time-server/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/quickstarts/https-time-server/functions/index.js
================================================
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
// [START v2httpImport]
const {onRequest} = require("firebase-functions/https");
// [END v2httpImport]
// [START v2httpAdditionalImports]
const logger = require("firebase-functions/logger");
// Moments library to format dates.
const moment = require("moment");
// [END v2httpAdditionalImports]
// [START v2httpAll]
/**
* Returns the server's date.
* Options `timeoutSeconds` and `region` are optional.
*
* You must provide a `format` URL query parameter or `format` value in
* the request body with which we'll try to format the date.
*
* Format must follow the Node moment library. See: http://momentjs.com/
*
* Example format: "MMMM Do YYYY, h:mm:ss a".
* Example request using URL query parameters:
* https://us-central1-<project-id>.cloudfunctions.net/date?format=MMMM%20Do%20YYYY%2C%20h%3Amm%3Ass%20a
* Example request using request body with cURL:
* curl -H 'Content-Type: application/json' /
* -d '{"format": "MMMM Do YYYY, h:mm:ss a"}' /
* https://us-central1-<project-id>.cloudfunctions.net/date
*/
// [START v2httpTrigger]
exports.date = onRequest(
{timeoutSeconds: 1200, region: ["us-west1", "us-east1"]},
(req, res) => {
// [END v2httpTrigger]
// [START v2httpSendError]
// Forbidding PUT requests.
if (req.method === "PUT") {
res.status(403).send("Forbidden!");
return;
}
// [END v2httpSendError]
// Reading date format from URL query parameter.
// [START v2httpReadQueryParam]
let format = req.query.format;
// [END v2httpReadQueryParam]
// Reading date format from request body query parameter
if (!format) {
// [START v2httpReadBodyParam]
format = req.body.format;
// [END v2httpReadBodyParam]
}
// [START v2httpSendResponse]
const formattedDate = moment().format(`${format}`);
logger.log("Sending formatted date:", formattedDate);
res.status(200).send(formattedDate);
// [END v2httpSendResponse]
},
);
// [END v2httpAll]
================================================
FILE: Node/quickstarts/https-time-server/functions/package.json
================================================
{
"name": "time-server-functions",
"description": "A simple time server using HTTPS Cloud Function",
"dependencies": {
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0",
"moment": "^2.29.4"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0"
},
"scripts": {
"lint": "./node_modules/.bin/eslint --max-warnings=0 .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"compile": "cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json"
},
"engines": {
"node": "22"
},
"private": true
}
================================================
FILE: Node/quickstarts/monitor-cloud-logging/README.md
================================================
# Firebase SDK for Cloud Functions Quickstart - Structured Logs
This quickstart demonstrates using the **`logger`** subpackage to write structured logs to Cloud Logging.
Learn more about logging in Cloud Functions for Firebase [in the docs](https://firebase.google.com/docs/functions/writing-and-viewing-logs).
## License
© Google, 2023. Licensed under an [Apache-2](../../../LICENSE) license.
================================================
FILE: Node/quickstarts/monitor-cloud-logging/firebase.json
================================================
{
"functions": [
{
"source": "functions",
"codebase": "monitor-cloud-logging",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log"
],
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
]
}
================================================
FILE: Node/quickstarts/monitor-cloud-logging/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
env: {
es6: true,
node: true,
},
parserOptions: {
"ecmaVersion": 2018,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
"no-restricted-globals": ["error", "name", "length"],
"prefer-arrow-callback": "error",
"quotes": ["error", "double", {"allowTemplateLiterals": true}],
},
overrides: [
{
files: ["**/*.spec.*"],
env: {
mocha: true,
},
rules: {},
},
],
globals: {},
};
================================================
FILE: Node/quickstarts/monitor-cloud-logging/functions/index.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {onRequest} = require("firebase-functions/https");
const {
onRegressionAlertPublished,
} = require("firebase-functions/alerts/crashlytics");
// [START loggerImport]
// All available logging functions
const {
log,
info,
debug,
warn,
error,
write,
} = require("firebase-functions/logger");
// [END loggerImport]
const {initializeApp} = require("firebase-admin/app");
const {getFirestore} = require("firebase-admin/firestore");
// Initialize admin sdk for Firestore read in getInspirationalQuote
initializeApp();
// [START helloLogs]
exports.helloWorld = onRequest((request, response) => {
// sends a log to Cloud Logging
log("Hello logs!");
response.send("Hello from Firebase!");
});
// [END helloLogs]
// [START logsKitchenSink]
exports.getInspirationalQuote = onRequest(async (request, response) => {
const db = getFirestore();
const today = new Date();
const quoteOfTheMonthRef = db
.collection("quotes")
.doc(`${today.getFullYear()}`)
.collection("months")
.doc(`${today.getMonth()}`);
const DEFAULT_QUOTE =
"You miss 100% of the shots you don't take. -Wayne Gretzky";
let quote;
try {
const quoteOfTheMonthDocSnap = await quoteOfTheMonthRef.get();
// Attach relevant debugging information with debug()
debug("Monthly quote fetch result", {
docRef: quoteOfTheMonthRef.path,
exists: quoteOfTheMonthDocSnap.exists,
createTime: quoteOfTheMonthDocSnap.createTime,
});
if (quoteOfTheMonthDocSnap.exists) {
quote = quoteOfTheMonthDocSnap.data().text;
} else {
// Use warn() for lower-severity issues than error()
warn("Quote not found for month, sending default instead", {
docRef: quoteOfTheMonthRef.path,
dateRequested: today.toLocaleDateString("en-US"),
});
quote = DEFAULT_QUOTE;
}
// [START logError]
} catch (err) {
// Attach an error object as the second argument
error("Unable to read quote from Firestore, sending default instead",
err);
// [END logError]
quote = DEFAULT_QUOTE;
}
// Attach relevant structured data to any log
info("Sending a quote!", {quote: quote});
response.json({inspirationalQuote: quote});
});
// [END logsKitchenSink]
// [START customLogWrite]
exports.appHasARegression = onRegressionAlertPublished((event) => {
write({
// write() lets you set additional severity levels
// beyond the built-in logger functions
severity: "EMERGENCY",
message: "Regression in production app",
issue: event.data.payload.issue,
lastOccurred: event.data.payload.resolveTime,
});
});
// [END customLogWrite]
================================================
FILE: Node/quickstarts/monitor-cloud-logging/functions/package.json
================================================
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"compile": "cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json"
},
"engines": {
"node": "22"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.4.0"
},
"private": true
}
================================================
FILE: Node/quickstarts/pubsub-helloworld/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
firebase-debug.log*
firebase-debug.*.log*
# Firebase cache
.firebase/
# Firebase config
# Uncomment this if you'd like others to create their own Firebase project.
# For a team working on the same Firebase project(s), it is recommended to leave
# it commented so all members can deploy to the same project(s) in .firebaserc.
# .firebaserc
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
================================================
FILE: Node/quickstarts/pubsub-helloworld/README.md
================================================
# Firebase SDK for Cloud Functions Quickstart - PubSub trigger
This quickstart demonstrates how to setup a PubSub triggered Cloud Function using the **Firebase SDK for Cloud Functions**.
## Introduction
We'll deploy a PubSub triggered Functions that prints out a Hello World message to the Cloud Logs.
Further reading:
- [Read more about the Firebase SDK for Cloud Functions](https://firebase.google.com/docs/functions/)
## Initial setup, build tools and dependencies
### 1. Clone this repo
Clone or download this repo and open the `2nd-gen/pubsub-helloworld` directory.
### 2. Create a Firebase project and configure the quickstart
Create a Firebase Project on the [Firebase Console](https://console.firebase.google.com).
Set up your Firebase project by running `firebase use --add`, select your Project ID and follow the instructions.
### 3. Install the Firebase CLI and enable Functions on your Firebase CLI
You need to have installed the Firebase CLI. If you haven't run:
```bash
npm install -g firebase-tools
```
> Doesn't work? You may need to [change npm permissions](https://docs.npmjs.com/getting-started/fixing-npm-permissions).
## Deploy the app to prod
First you need to install the `npm` dependencies of the functions:
```bash
cd functions && npm install; cd ..
```
This installs locally the Firebase Admin SDK and the Firebase SDK for Cloud Functions.
Deploy to Firebase using the following command:
```bash
firebase deploy
```
This deploys and activate the PubSub hello World Functions.
> The first time you call `firebase deploy` on a new project with Functions will take longer than usual.
## Try the sample
Once deployed, to try the sample use the `gcloud` CLI to publish a message to the `topic-name` topic:
```
gcloud pubsub topics publish topic-name --message='YourName'
```
Open the Functions logs in the Firebase Console, you should see a messages that reads "Hello YourName".
Then you can also publish a message to the `another-topic-name` topic using JSON data:
```
gcloud pubsub topics publish another-topic-name --message='{"name":"YourName"}'
```
Open the Functions logs in the Firebase Console, you should see a messages that reads "Hello YourName".
Last you can also publish a message to the `yet-another-topic-name` topic using JSON data:
```
gcloud pubsub topics publish yet-another-topic-name --attribute name=YourName
```
Open the Functions logs in the Firebase Console, you should see a messages that reads "Hello YourName".
## Contributing
We'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../../CONTRIBUTING.md).
## License
© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.
================================================
FILE: Node/quickstarts/pubsub-helloworld/firebase.json
================================================
{
"functions": {
"codebase": "pubsub-helloworld",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
}
================================================
FILE: Node/quickstarts/pubsub-helloworld/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/quickstarts/pubsub-helloworld/functions/.gitignore
================================================
node_modules/
================================================
FILE: Node/quickstarts/pubsub-helloworld/functions/index.js
================================================
/**
* Copyright 2022 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
// [START v2import]
const {onMessagePublished} = require("firebase-functions/pubsub");
const logger = require("firebase-functions/logger");
// [END v2import]
// [START v2helloWorld]
/**
* Cloud Function to be triggered by Pub/Sub that logs a message using the
* data published to the topic.
*/
// [START v2trigger]
exports.hellopubsub = onMessagePublished("topic-name", (event) => {
// [END v2trigger]
// [START v2readBase64]
// Decode the PubSub Message body.
const message = event.data.message;
const messageBody = message.data ?
Buffer.from(message.data, "base64").toString() :
null;
// [END v2readBase64]
// Print the message in the logs.
logger.log(`Hello ${messageBody || "World"}!`);
return null;
});
// [END v2helloWorld]
/**
* Cloud Function to be triggered by Pub/Sub that logs a message using the
* data published to the topic as JSON.
*/
exports.hellopubsubjson = onMessagePublished("another-topic-name", (event) => {
// [START v2readJson]
// Get the `name` attribute of the PubSub message JSON body.
let name = null;
try {
name = event.data.message.json.name;
} catch (e) {
logger.error("PubSub message was not JSON", e);
}
// [END v2readJson]
// Print the message in the logs.
logger.log(`Hello ${name || "World"}!`);
return null;
});
/**
* Cloud Function to be triggered by Pub/Sub that logs a message using the
* data attributes published to the topic.
*/
exports.hellopubsubattributes = onMessagePublished("yet-another-topic-name",
(event) => {
// [START v2readAttributes]
// Get the `name` attribute of the message.
const name = event.data.message.attributes.name;
// [END v2readAttributes]
// Print the message in the logs.
logger.log(`Hello ${name || "World"}!`);
return null;
});
================================================
FILE: Node/quickstarts/pubsub-helloworld/functions/package.json
================================================
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "22"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.4.0"
},
"private": true
}
================================================
FILE: Node/quickstarts/testlab-matrix-completed/README.md
================================================
# Log test complete
This sample demonstrates how to trigger a function in response to the
completion of a Test Matrix in Firebase Test Lab.
## Setting up the sample
1. Clone or download this repo and open the `2nd-gen/test-complete`
directory.
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`.
1. Configure the CLI locally by using `firebase use --add` and select your
project in the list.
1. Install Cloud Functions dependencies locally by running:
`cd functions; npm install; cd -`
## Deploy and test
1. Deploy your function using `firebase deploy --only functions`
1. Navigate to the
[Test Lab](https://console.firebase.google.com/u/0/project/_/testlab/histories)
section of the Firebase Console and start a test.
1. Once the test finishes running,
[view the functions logs](https://console.firebase.google.com/u/0/project/_/functions/logs?severity=DEBUG)
for your project, and check that the test run status was logged.
## Next Steps
To see how to post to Slack instead of just `console.log`-ing, check out
[this sample](https://github.com/firebase/functions-samples/tree/main/2nd-gen/testlab-to-slack).
================================================
FILE: Node/quickstarts/testlab-matrix-completed/firebase.json
================================================
{
"functions": {
"codebase": "testlab-matrix-completed"
}
}
================================================
FILE: Node/quickstarts/testlab-matrix-completed/functions/.eslintrc.js
================================================
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = {
root: true,
env: {
es2020: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
rules: {
quotes: ["error", "double"],
},
};
================================================
FILE: Node/quickstarts/testlab-matrix-completed/functions/index.js
================================================
/**
* Copyright 2022 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
// [START all]
// [START import]
// The Cloud Functions for Firebase SDK to set up triggers and logging.
const {onTestMatrixCompleted} = require("firebase-functions/testLab");
const {logger} = require("firebase-functions");
// [END import]
// [START logtestcomplete]
exports.logtestcomplete = onTestMatrixCompleted((event) => {
// Obtain Test Matrix properties from the CloudEvent
const {testMatrixId, createTime, state, outcomeSummary} = event.data;
// Log the properties of the completed Test Matrix
logger.log(
`TEST ${testMatrixId} (created at ${createTime}): ${state}. ${
outcomeSummary || ""
}`,
);
});
// [END logtestcomplete]
// [END all]
================================================
FILE: Node/quickstarts/testlab-matrix-completed/functions/package.json
================================================
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"compile": "cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json"
},
"engines": {
"node": "22"
},
"dependencies": {
"firebase-admin": "^13.0.2",
"firebase-functions": "7.0.0"
},
"devDependencies": {
"eslint": "^8.57.1",
"eslint-config-google": "^0.14.0",
"firebase-functions-test": "^3.4.0"
},
"private": true
}
================================================
FILE: Node/quickstarts/thumbnails/README.md
================================================
# Firebase SDK for Cloud Functions Quickstart - Cloud Storage trigger
This quickstart demonstrates using **Firebase SDK for Cloud Functions** setup with a Cloud Storage trigger.
## Introduction
This sample automatically generates thumbnails for images that are uploaded to Cloud Storage.
## Functions Code
See file [functions/index.js](functions/index.js) for the thumbnail generation code.
The thumbnail generation is performed using [sharp](https://www.npmjs.com/package/sharp).
The dependencies are listed in [functions/package.json](functions/package.json).
## Trigger rules
The function triggers on upload of any file to your Firebase project's default Cloud Storage bucket.
## Deploy and test
To deploy and test the sample:
1. Create a Firebase project on the [Firebase Console](https://console.firebas
gitextract_tgieqsqq/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── config.yml │ │ └── docs.md │ └── workflows/ │ ├── test_node.yml │ ├── test_node_1st_gen.yml │ └── test_python.yml ├── .gitignore ├── .opensource/ │ └── project.json ├── CONTRIBUTING.md ├── LICENSE ├── Node/ │ ├── README.md │ ├── alerts-to-discord/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.js │ │ ├── index.js │ │ └── package.json │ ├── app-distribution-feedback-to-jira/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.cjs │ │ ├── .gitignore │ │ ├── index.js │ │ └── package.json │ ├── call-vertex-remote-config-server/ │ │ ├── README.md │ │ ├── client/ │ │ │ ├── README.md │ │ │ ├── config.ts │ │ │ ├── index.html │ │ │ ├── main.ts │ │ │ ├── package.json │ │ │ ├── vite-env.d.ts │ │ │ └── vite.config.js │ │ ├── firebase.json │ │ └── functions/ │ │ ├── index.js │ │ └── package.json │ ├── delete-unused-accounts-cron/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.js │ │ ├── index.js │ │ └── package.json │ ├── fcm-notifications/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.cjs │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ └── public/ │ │ ├── firebase-messaging-sw.js │ │ ├── index.html │ │ ├── main.css │ │ └── main.js │ ├── instrument-with-opentelemetry/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.js │ │ ├── index.js │ │ ├── package.json │ │ ├── timer.js │ │ └── tracing.js │ ├── pnpm-workspace.yaml │ ├── quickstarts/ │ │ ├── auth-blocking-functions/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── callable-functions/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ └── sanitizer.js │ │ ├── callable-functions-streaming/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ ├── functions/ │ │ │ │ ├── .eslintrc.js │ │ │ │ ├── .gitignore │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ └── website/ │ │ │ └── index.html │ │ ├── custom-events/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── firestore-sync-auth/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ ├── firestore.indexes.json │ │ │ ├── firestore.rules │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── genkit-helloworld/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── https-time-server/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── monitor-cloud-logging/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── pubsub-helloworld/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── testlab-matrix-completed/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── thumbnails/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── uppercase-firestore/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ ├── firestore.indexes.json │ │ │ ├── firestore.rules │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ └── uppercase-rtdb/ │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.js │ │ ├── index.js │ │ └── package.json │ ├── remote-config-diff/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.js │ │ ├── index.js │ │ └── package.json │ ├── remote-config-server-with-vertex/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── index.js │ │ └── package.json │ ├── taskqueues-backup-images/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.js │ │ ├── index.js │ │ └── package.json │ ├── test-functions-jest/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── index.js │ │ ├── index.test.js │ │ └── package.json │ ├── test-functions-jest-ts/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── jest.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __test__/ │ │ │ │ └── index.test.ts │ │ │ └── index.ts │ │ ├── tsconfig.dev.json │ │ └── tsconfig.json │ ├── test-functions-mocha/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── index.js │ │ ├── index.test.js │ │ └── package.json │ ├── testlab-to-slack/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.js │ │ ├── index.js │ │ └── package.json │ └── youtube/ │ ├── README.md │ ├── firebase.json │ └── functions/ │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── Node-1st-gen/ │ ├── README.md │ ├── assistant-say-number/ │ │ ├── README.md │ │ ├── action.json │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── authenticated-json-api/ │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ └── public/ │ │ ├── index.html │ │ ├── main.css │ │ └── main.js │ ├── authorized-https-endpoint/ │ │ ├── README.md │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ └── public/ │ │ ├── index.html │ │ ├── main.css │ │ └── main.js │ ├── bigquery-import/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── child-count/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── convert-images/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── coupon-on-purchase/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── delete-old-child-nodes/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── delete-unused-accounts-cron/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── developer-motivator/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── email-confirmation/ │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ └── public/ │ │ ├── index.html │ │ ├── main.css │ │ └── main.js │ ├── exif-images/ │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ └── public/ │ │ ├── index.html │ │ ├── main.css │ │ └── main.js │ ├── fcm-notifications/ │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ └── public/ │ │ ├── firebase-messaging-sw.js │ │ ├── index.html │ │ ├── main.css │ │ └── main.js │ ├── ffmpeg-convert-audio/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── fulltext-search/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── fulltext-search-firestore/ │ │ ├── README.md │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── .gitignore │ │ │ ├── elastic.js │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ └── typesense.js │ │ └── public/ │ │ ├── index.html │ │ └── index.js │ ├── github-to-slack/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── google-sheet-sync/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── image-maker/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── clock.js │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ ├── ray.js │ │ │ └── sparkline.js │ │ └── public/ │ │ └── index.html │ ├── instagram-auth/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── main.css │ │ └── public/ │ │ ├── index.html │ │ ├── main.css │ │ ├── main.js │ │ └── popup.html │ ├── lastmodified-tracking/ │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── limit-children/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── linkedin-auth/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── main.css │ │ └── public/ │ │ ├── index.html │ │ ├── main.css │ │ ├── main.js │ │ └── popup.html │ ├── message-translation/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── minimal-webhook/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── moderate-images/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── okta-auth/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── public/ │ │ │ └── index.html │ │ └── setup.js │ ├── paypal/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ └── public/ │ │ ├── cancel.html │ │ ├── error.html │ │ ├── index.html │ │ ├── main.css │ │ └── success.html │ ├── presence-firestore/ │ │ ├── README.md │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ └── public/ │ │ ├── index.html │ │ └── index.js │ ├── publish-model/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── quickstarts/ │ │ ├── auth-blocking-functions/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── big-ben/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ ├── functions/ │ │ │ │ ├── .eslintrc.json │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ └── public/ │ │ │ ├── script.js │ │ │ └── style.css │ │ ├── email-users/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ ├── functions/ │ │ │ │ ├── .eslintrc.json │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ └── public/ │ │ │ ├── index.html │ │ │ ├── main.css │ │ │ └── main.js │ │ ├── https-time-server/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── multicodebase-hellos/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ ├── js/ │ │ │ │ ├── .eslintrc.js │ │ │ │ ├── .gitignore │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ └── ts/ │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ └── index.ts │ │ │ ├── tsconfig.dev.json │ │ │ └── tsconfig.json │ │ ├── pubsub-helloworld/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── runtime-options/ │ │ │ ├── .gitignore │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.js │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── taskqueues-backup-images/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── testlab-matrix-completed/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── thumbnails/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── uppercase-firestore/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ ├── firestore.indexes.json │ │ │ ├── firestore.rules │ │ │ └── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── .gitignore │ │ │ ├── index.js │ │ │ └── package.json │ │ └── uppercase-rtdb/ │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ ├── package.json │ │ └── test/ │ │ ├── test.offline.js │ │ └── test.online.js │ ├── remote-config-diff/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── spotify-auth/ │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── main.css │ │ └── public/ │ │ ├── index.html │ │ ├── main.css │ │ ├── main.js │ │ └── popup.html │ ├── stripe/ │ │ ├── README.md │ │ ├── firebase.json │ │ ├── firestore.rules │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ └── public/ │ │ ├── index.html │ │ └── javascript/ │ │ └── app.js │ ├── survey-app-update/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── template-handlebars/ │ │ ├── README.md │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── firebaseUser.js │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ └── views/ │ │ │ ├── layouts/ │ │ │ │ └── main.handlebars │ │ │ └── user.handlebars │ │ └── public/ │ │ └── main.css │ ├── testlab-to-slack/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .gitignore │ │ ├── index.js │ │ └── package.json │ ├── text-moderation/ │ │ ├── README.md │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── public/ │ │ │ ├── index.html │ │ │ ├── main.css │ │ │ └── main.js │ │ └── security.rules.json │ ├── url-shortener/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .eslintrc.json │ │ ├── index.js │ │ └── package.json │ ├── user-data-cleanup/ │ │ ├── README.md │ │ └── doc/ │ │ ├── auto_rules_extraction.md │ │ └── design.md │ ├── username-password-auth/ │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .eslintrc.json │ │ │ ├── index.js │ │ │ └── package.json │ │ └── public/ │ │ ├── index.html │ │ ├── main.css │ │ └── main.js │ ├── vision-annotate-images/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── .gitignore │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ └── tsconfig.json │ └── youtube/ │ ├── README.md │ ├── firebase.json │ └── functions/ │ ├── .eslintrc.json │ ├── index.js │ └── package.json ├── Python/ │ ├── .gitignore │ ├── README.md │ ├── alerts-to-discord/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── main.py │ │ └── requirements.txt │ ├── delete-unused-accounts-cron/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── main.py │ │ └── requirements.txt │ ├── fcm-notifications/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── database.rules.json │ │ ├── firebase.json │ │ ├── functions/ │ │ │ ├── .gitignore │ │ │ ├── main.py │ │ │ └── requirements.txt │ │ └── public/ │ │ ├── firebase-messaging-sw.js │ │ ├── index.html │ │ ├── main.css │ │ └── main.js │ ├── http-flask/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── main.py │ │ └── requirements.txt │ ├── post-signup-event/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── main.py │ │ └── requirements.txt │ ├── pyfmt.py │ ├── pyproject.toml │ ├── quickstarts/ │ │ ├── auth-blocking-functions/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── main.py │ │ │ └── requirements.txt │ │ ├── callable-functions/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── main.py │ │ │ └── requirements.txt │ │ ├── custom-events/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── main.py │ │ │ └── requirements.txt │ │ ├── firestore-sync-auth/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── main.py │ │ │ └── requirements.txt │ │ ├── https-time-server/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── main.py │ │ │ └── requirements.txt │ │ ├── monitor-cloud-logging/ │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── main.py │ │ │ └── requirements.txt │ │ ├── pubsub-helloworld/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── main.py │ │ │ └── requirements.txt │ │ ├── uppercase-firestore/ │ │ │ ├── README.md │ │ │ ├── firebase.json │ │ │ └── functions/ │ │ │ ├── main.py │ │ │ └── requirements.txt │ │ └── uppercase-rtdb/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── main.py │ │ └── requirements.txt │ ├── remote-config-diff/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── main.py │ │ └── requirements.txt │ ├── requirements.txt │ ├── taskqueues-backup-images/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── main.py │ │ └── requirements.txt │ ├── testlab-to-slack/ │ │ ├── README.md │ │ ├── firebase.json │ │ └── functions/ │ │ ├── main.py │ │ └── requirements.txt │ └── thumbnails/ │ ├── README.md │ ├── firebase.json │ └── functions/ │ ├── main.py │ └── requirements.txt ├── README.md ├── community.md ├── package-lock.json ├── package.json └── tsconfig.template.json
SYMBOL INDEX (191 symbols across 76 files)
FILE: Node-1st-gen/authenticated-json-api/public/main.js
function Demo (line 19) | function Demo() {
FILE: Node-1st-gen/authorized-https-endpoint/public/main.js
function Demo (line 19) | function Demo() {
FILE: Node-1st-gen/convert-images/functions/index.js
constant JPEG_EXTENSION (line 29) | const JPEG_EXTENSION = '.jpg';
FILE: Node-1st-gen/coupon-on-purchase/functions/index.js
function sendCouponViaFCM (line 50) | async function sendCouponViaFCM(uid, userLanguage) {
function sendHighValueCouponViaFCM (line 87) | async function sendHighValueCouponViaFCM(uid, userLanguage) {
function getDeviceTokens (line 123) | async function getDeviceTokens(uid) {
FILE: Node-1st-gen/delete-old-child-nodes/functions/index.js
constant CUT_OFF_TIME (line 23) | const CUT_OFF_TIME = 2 * 60 * 60 * 1000;
FILE: Node-1st-gen/delete-unused-accounts-cron/functions/index.js
constant MAX_CONCURRENT (line 23) | const MAX_CONCURRENT = 3;
function deleteInactiveUser (line 43) | function deleteInactiveUser(inactiveUsers) {
function getInactiveUsers (line 70) | async function getInactiveUsers(users = [], nextPageToken) {
FILE: Node-1st-gen/email-confirmation/public/main.js
function Demo (line 19) | function Demo() {
FILE: Node-1st-gen/exif-images/functions/index.js
function imageMagickOutputToObject (line 68) | function imageMagickOutputToObject(output) {
function makeKeyFirebaseCompatible (line 99) | function makeKeyFirebaseCompatible(key) {
FILE: Node-1st-gen/exif-images/public/main.js
function Demo (line 19) | function Demo() {
FILE: Node-1st-gen/fcm-notifications/public/main.js
function Demo (line 19) | function Demo() {
FILE: Node-1st-gen/ffmpeg-convert-audio/functions/index.js
function promisifyCommand (line 29) | function promisifyCommand(command) {
FILE: Node-1st-gen/fulltext-search-firestore/functions/index.js
constant ALGOLIA_INDEX_NAME (line 30) | const ALGOLIA_INDEX_NAME = 'notes';
function getFirebaseUser (line 57) | async function getFirebaseUser(req, res, next) {
FILE: Node-1st-gen/fulltext-search-firestore/public/index.js
constant PROJECT_ID (line 17) | const PROJECT_ID = '...' // Required - your Firebase project ID
constant ALGOLIA_APP_ID (line 19) | const ALGOLIA_APP_ID = '...';
constant ALGOLIA_SEARCH_KEY (line 20) | const ALGOLIA_SEARCH_KEY = '...';
constant TYPESENSE_SEARCH_API_KEY (line 25) | const TYPESENSE_SEARCH_API_KEY = '...';
function searchAlgoliaUnauthenticated (line 27) | function searchAlgoliaUnauthenticated(query) {
function searchAlgoliaAuthenticated (line 47) | function searchAlgoliaAuthenticated(query) {
function search (line 78) | function search(query) {
function searchElastic (line 99) | function searchElastic(query) {
function searchTypesenseUnauthenticated (line 110) | async function searchTypesenseUnauthenticated(query) {
function searchTypesenseAuthenticated (line 135) | async function searchTypesenseAuthenticated(query) {
FILE: Node-1st-gen/fulltext-search/functions/index.js
constant ALGOLIA_POSTS_INDEX_NAME (line 36) | const ALGOLIA_POSTS_INDEX_NAME = 'blogposts';
FILE: Node-1st-gen/github-to-slack/functions/index.js
function postToSlack (line 64) | async function postToSlack(url, commits, repo) {
FILE: Node-1st-gen/google-sheet-sync/functions/index.js
constant FUNCTIONS_REDIRECT (line 38) | const FUNCTIONS_REDIRECT = `https://${process.env.GCLOUD_PROJECT}.fireba...
constant SCOPES (line 41) | const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];
constant DB_TOKEN_PATH (line 63) | const DB_TOKEN_PATH = '/api_tokens';
function appendPromise (line 100) | function appendPromise(requestWithoutAuth) {
function getAuthorizedClient (line 118) | async function getAuthorizedClient() {
FILE: Node-1st-gen/instagram-auth/functions/index.js
constant OAUTH_REDIRECT_URI (line 33) | const OAUTH_REDIRECT_URI = `https://${process.env.GCLOUD_PROJECT}.fireba...
constant OAUTH_SCOPES (line 34) | const OAUTH_SCOPES = 'basic';
function createFirebaseAccount (line 127) | async function createFirebaseAccount(instagramID, displayName, photoURL,...
FILE: Node-1st-gen/instagram-auth/public/main.js
function Demo (line 19) | function Demo() {
FILE: Node-1st-gen/limit-children/functions/index.js
constant MAX_LOG_COUNT (line 21) | const MAX_LOG_COUNT = 5;
FILE: Node-1st-gen/linkedin-auth/functions/index.js
constant OAUTH_SCOPES (line 33) | const OAUTH_SCOPES = ['r_basicprofile', 'r_emailaddress'];
function createFirebaseAccount (line 122) | async function createFirebaseAccount(linkedinID, displayName, photoURL, ...
FILE: Node-1st-gen/linkedin-auth/public/main.js
function Demo (line 19) | function Demo() {
FILE: Node-1st-gen/message-translation/functions/index.js
constant LANGUAGES (line 26) | const LANGUAGES = ['en', 'es', 'de', 'fr', 'sv', 'ga', 'it', 'jp'];
FILE: Node-1st-gen/minimal-webhook/functions/index.js
constant WEBHOOK_URL (line 23) | const WEBHOOK_URL = 'http://requestb.in/1mqw97l1';
FILE: Node-1st-gen/moderate-images/functions/index.js
constant BLURRED_FOLDER (line 35) | const BLURRED_FOLDER = 'blurred';
function blurImage (line 76) | async function blurImage(filePath, bucketName, metadata) {
FILE: Node-1st-gen/okta-auth/setup.js
constant DEPLOYING (line 28) | const DEPLOYING = process.argv.length >= 3 && process.argv[2] === '-d';
constant GCLOUD_PROJECT (line 29) | const GCLOUD_PROJECT = process.env.GCLOUD_PROJECT || '<YOUR_PROJECT_ID>';
constant PROJECT_DIR (line 30) | const PROJECT_DIR = process.env.PROJECT_DIR || __dirname;
constant OKTA_URL_PROMPT (line 32) | const OKTA_URL_PROMPT = `\
constant OKTA_CLIENT_ID_PROMPT (line 39) | const OKTA_CLIENT_ID_PROMPT = `\
constant CORS_ORIGIN (line 45) | const CORS_ORIGIN = `\
constant SERVICE_ACCOUNT_PATH_PROMPT (line 51) | const SERVICE_ACCOUNT_PATH_PROMPT = `\
constant EXAMPLE_ENDPOINT (line 60) | const EXAMPLE_ENDPOINT = (
constant TOKEN_ENDPOINT_PROMPT (line 69) | const TOKEN_ENDPOINT_PROMPT = `\
FILE: Node-1st-gen/presence-firestore/public/index.js
function rtdb_presence (line 17) | function rtdb_presence() {
function rtdb_and_local_fs_presence (line 66) | function rtdb_and_local_fs_presence() {
function fs_listen (line 116) | function fs_listen() {
function fs_listen_online (line 125) | function fs_listen_online() {
FILE: Node-1st-gen/quickstarts/big-ben/functions/index.js
function secondsLeftBeforeEndOfHour (line 68) | function secondsLeftBeforeEndOfHour(date) {
FILE: Node-1st-gen/quickstarts/big-ben/public/script.js
function refresh (line 17) | function refresh(_this) {
FILE: Node-1st-gen/quickstarts/email-users/functions/index.js
constant APP_NAME (line 44) | const APP_NAME = 'Cloud Storage for Firebase quickstart';
function sendWelcomeEmail (line 77) | async function sendWelcomeEmail(email, displayName) {
function sendGoodbyeEmail (line 92) | async function sendGoodbyeEmail(email, displayName) {
FILE: Node-1st-gen/quickstarts/email-users/public/main.js
function Demo (line 19) | function Demo() {
FILE: Node-1st-gen/quickstarts/taskqueues-backup-images/functions/index.js
constant BACKUP_START_DATE (line 26) | const BACKUP_START_DATE = new Date("1995-06-17");
constant BACKUP_COUNT (line 27) | const BACKUP_COUNT = parseInt(process.env.BACKUP_COUNT) || 100;
constant HOURLY_BATCH_SIZE (line 28) | const HOURLY_BATCH_SIZE = parseInt(process.env.HOURLY_BATCH_SIZE) || 500;
constant BACKUP_BUCKET (line 29) | const BACKUP_BUCKET = process.env.BACKUP_BUCKET;
FILE: Node-1st-gen/remote-config-diff/functions/index.js
function getTemplate (line 53) | async function getTemplate(version, accessToken) {
FILE: Node-1st-gen/spotify-auth/functions/index.js
constant OAUTH_SCOPES (line 50) | const OAUTH_SCOPES = ['user-read-email'];
function createFirebaseAccount (line 128) | async function createFirebaseAccount(spotifyID, displayName, photoURL, e...
FILE: Node-1st-gen/spotify-auth/public/main.js
function Demo (line 19) | function Demo() {
FILE: Node-1st-gen/stripe/functions/index.js
function reportError (line 180) | function reportError(err, context = {}) {
function userFacingMessage (line 221) | function userFacingMessage(error) {
FILE: Node-1st-gen/stripe/public/javascript/app.js
constant STRIPE_PUBLISHABLE_KEY (line 16) | const STRIPE_PUBLISHABLE_KEY = '<YOUR STRIPE PUBLISHABLE KEY>';
function startDataListeners (line 92) | function startDataListeners() {
function formatAmount (line 272) | function formatAmount(amount, currency) {
function formatAmountForStripe (line 283) | function formatAmountForStripe(amount, currency) {
function zeroDecimalCurrency (line 291) | function zeroDecimalCurrency(amount, currency) {
function handleCardAction (line 308) | async function handleCardAction(payment, docId) {
FILE: Node-1st-gen/survey-app-update/functions/index.js
constant LINK_TO_SURVEY (line 37) | const LINK_TO_SURVEY = 'https://goo.gl/forms/IdurnOZ66h3FtlO33';
constant LATEST_VERSION (line 38) | const LATEST_VERSION = '2.0';
function sendSurveyEmail (line 63) | async function sendSurveyEmail(email, name) {
FILE: Node-1st-gen/template-handlebars/functions/firebaseUser.js
function validateFirebaseIdToken (line 27) | async function validateFirebaseIdToken (req, res, next) {
function getIdTokenFromRequest (line 40) | function getIdTokenFromRequest(req, res) {
function addDecodedIdTokenToRequest (line 62) | async function addDecodedIdTokenToRequest(idToken, req) {
FILE: Node-1st-gen/testlab-to-slack/functions/index.js
function postToSlack (line 28) | async function postToSlack(title, details) {
function getSlackmoji (line 57) | function getSlackmoji(term) {
FILE: Node-1st-gen/text-moderation/functions/index.js
function moderateMessage (line 48) | function moderateMessage(message) {
function containsSwearwords (line 65) | function containsSwearwords(message) {
function moderateSwearwords (line 70) | function moderateSwearwords(message) {
function isShouting (line 76) | function isShouting(message) {
function stopShouting (line 82) | function stopShouting(message) {
FILE: Node-1st-gen/text-moderation/public/main.js
function Guestbook (line 19) | function Guestbook() {
FILE: Node-1st-gen/username-password-auth/functions/index.js
function authenticate (line 99) | async function authenticate(username, password) {
FILE: Node-1st-gen/username-password-auth/public/main.js
function getFirebaseProjectId (line 21) | function getFirebaseProjectId() {
function Demo (line 26) | function Demo() {
FILE: Node-1st-gen/youtube/functions/index.js
constant FIREBASE_YOUTUBE_CHANNEL_ID (line 24) | const FIREBASE_YOUTUBE_CHANNEL_ID = 'UCP4bf6IHJJQehibu6ai__cg';
FILE: Node/alerts-to-discord/functions/index.js
function postMessageToDiscord (line 36) | async function postMessageToDiscord(botName, messageBody) {
FILE: Node/app-distribution-feedback-to-jira/functions/index.js
function authHeader (line 75) | function authHeader() {
function createIssue (line 85) | async function createIssue(event) {
function uploadScreenshot (line 112) | async function uploadScreenshot(issueUri, screenshotUri) {
function lookupReporter (line 141) | async function lookupReporter(testerEmail) {
function buildCreateIssueRequest (line 163) | async function buildCreateIssueRequest(event) {
FILE: Node/call-vertex-remote-config-server/client/config.ts
constant RECAPTCHA_ENTERPRISE_SITE_KEY (line 7) | const RECAPTCHA_ENTERPRISE_SITE_KEY =
FILE: Node/delete-unused-accounts-cron/functions/index.js
constant MAX_CONCURRENT (line 31) | const MAX_CONCURRENT = 3;
function deleteInactiveUser (line 58) | function deleteInactiveUser(inactiveUsers) {
function getInactiveUsers (line 91) | async function getInactiveUsers(users = [], nextPageToken) {
FILE: Node/fcm-notifications/public/main.js
function Demo (line 19) | function Demo() {
FILE: Node/instrument-with-opentelemetry/functions/index.js
function sliceIntoChunks (line 33) | function sliceIntoChunks(arr, chunkSize) {
function calculatePrice (line 47) | async function calculatePrice(productIds) {
function calculateDiscount (line 65) | async function calculateDiscount(productIds) {
FILE: Node/instrument-with-opentelemetry/functions/timer.js
method constructor (line 21) | constructor() {
method measureMs (line 29) | measureMs() {
FILE: Node/quickstarts/callable-functions-streaming/functions/index.js
function weatherForecastApi (line 28) | async function weatherForecastApi(lat, lng) {
FILE: Node/quickstarts/callable-functions/functions/sanitizer.js
function containsSwearwords (line 43) | function containsSwearwords(message) {
function replaceSwearwords (line 52) | function replaceSwearwords(message) {
function isShouting (line 62) | function isShouting(message) {
function stopShouting (line 73) | function stopShouting(message) {
FILE: Node/taskqueues-backup-images/functions/index.js
constant BACKUP_START_DATE (line 32) | const BACKUP_START_DATE = new Date("1995-06-17");
constant BACKUP_COUNT (line 33) | const BACKUP_COUNT = parseInt(process.env.BACKUP_COUNT) || 100;
constant HOURLY_BATCH_SIZE (line 34) | const HOURLY_BATCH_SIZE = parseInt(process.env.HOURLY_BATCH_SIZE) || 500;
constant BACKUP_BUCKET (line 35) | const BACKUP_BUCKET = process.env.BACKUP_BUCKET;
function getFunctionUrl (line 111) | async function getFunctionUrl(name, location="us-central1") {
FILE: Node/testlab-to-slack/functions/index.js
function postToSlack (line 32) | async function postToSlack(title, details) {
function getSlackmoji (line 68) | function getSlackmoji(term) {
FILE: Node/youtube/functions/index.ts
constant FIREBASE_YOUTUBE_CHANNEL_ID (line 23) | const FIREBASE_YOUTUBE_CHANNEL_ID = "UCP4bf6IHJJQehibu6ai__cg";
FILE: Python/alerts-to-discord/functions/main.py
function post_message_to_discord (line 27) | def post_message_to_discord(bot_name: str, message_body: str,
function post_fatal_issue_to_discord (line 54) | def post_fatal_issue_to_discord(event: crashlytics_fn.CrashlyticsNewFata...
function post_new_udid_to_discord (line 87) | def post_new_udid_to_discord(event: app_distribution_fn.NewTesterDeviceE...
function post_performance_alert_to_discord (line 118) | def post_performance_alert_to_discord(event: performance_fn.PerformanceT...
FILE: Python/delete-unused-accounts-cron/functions/main.py
function accountcleanup (line 34) | def accountcleanup(event: scheduler_fn.ScheduledEvent) -> None:
function is_inactive (line 46) | def is_inactive(user: auth.UserRecord, inactive_limit: timedelta) -> bool:
FILE: Python/fcm-notifications/functions/main.py
function send_follower_notification (line 9) | def send_follower_notification(event: db_fn.Event[db_fn.Change]) -> None:
FILE: Python/fcm-notifications/public/main.js
function Demo (line 19) | function Demo() {
FILE: Python/http-flask/functions/main.py
function get_widget (line 28) | def get_widget(id=None):
function add_widget (line 36) | def add_widget():
function httpsflaskexample (line 46) | def httpsflaskexample(req: https_fn.Request) -> https_fn.Response:
FILE: Python/post-signup-event/functions/main.py
function savegoogletoken (line 33) | def savegoogletoken(
function scheduleonboarding (line 75) | def scheduleonboarding(request: tasks_fn.CallableRequest) -> https_fn.Re...
function get_function_url (line 129) | def get_function_url(name: str, location: str = options.SupportedRegion....
FILE: Python/pyfmt.py
function reformat_in_place (line 29) | def reformat_in_place(files: list[str]) -> None:
function check_and_diff (line 37) | def check_and_diff(files: list[str]) -> int:
function format (line 55) | def format(src: str) -> str:
function fix_region_tags (line 61) | def fix_region_tags(src: str) -> str:
FILE: Python/quickstarts/auth-blocking-functions/functions/main.py
function created_noop (line 25) | def created_noop(event: identity_fn.AuthBlockingEvent) -> identity_fn.Be...
function signedin_noop (line 32) | def signedin_noop(event: identity_fn.AuthBlockingEvent) -> identity_fn.B...
function validatenewuser (line 41) | def validatenewuser(
function setdefaultname (line 61) | def setdefaultname(event: identity_fn.AuthBlockingEvent) -> identity_fn....
function requireverified (line 70) | def requireverified(
function send_verification_email_using_your_smtp_server (line 78) | def send_verification_email_using_your_smtp_server(email, link):
function sendverification (line 85) | def sendverification(
function requireverifiedsignin (line 95) | def requireverifiedsignin(
function markverified (line 105) | def markverified(event: identity_fn.AuthBlockingEvent) -> identity_fn.Be...
function is_suspicious (line 111) | def is_suspicious(ip_address):
function ipban (line 117) | def ipban(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSig...
function setemployeeid (line 126) | def setemployeeid(event: identity_fn.AuthBlockingEvent) -> identity_fn.B...
function copyclaimstosession (line 134) | def copyclaimstosession(
function logip (line 147) | def logip(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSig...
function analyze_photo_with_ml (line 152) | def analyze_photo_with_ml(url):
function sanitizeprofilephoto (line 162) | def sanitizeprofilephoto(
function checkforban (line 175) | def checkforban(event: identity_fn.AuthBlockingEvent) -> identity_fn.Bef...
FILE: Python/quickstarts/callable-functions/functions/main.py
function addnumbers (line 32) | def addnumbers(req: https_fn.CallableRequest) -> Any:
function addmessage (line 66) | def addmessage(req: https_fn.CallableRequest) -> Any:
function sanitize_text (line 130) | def sanitize_text(text: str) -> str:
FILE: Python/quickstarts/custom-events/functions/main.py
function onimageresized (line 27) | def onimageresized(event: eventarc_fn.CloudEvent) -> None:
function onimageresizedwest (line 47) | def onimageresizedwest(event: eventarc_fn.CloudEvent) -> None:
FILE: Python/quickstarts/firestore-sync-auth/functions/main.py
function verify_comment (line 21) | def verify_comment(event: Event[Change[DocumentSnapshot]]) -> None:
FILE: Python/quickstarts/https-time-server/functions/main.py
function date (line 43) | def date(req: https_fn.Request) -> https_fn.Response:
FILE: Python/quickstarts/monitor-cloud-logging/functions/main.py
function hello_world (line 16) | def hello_world(req: https_fn.Request) -> https_fn.Response:
function get_inspirational_quote (line 26) | def get_inspirational_quote(req: https_fn.Request) -> https_fn.Response:
function app_has_regression (line 72) | def app_has_regression(alert: crashlytics_fn.CrashlyticsRegressionAlertE...
FILE: Python/quickstarts/pubsub-helloworld/functions/main.py
function hellopubsub (line 25) | def hellopubsub(event: pubsub_fn.CloudEvent[pubsub_fn.MessagePublishedDa...
function hellopubsubjson (line 39) | def hellopubsubjson(event: pubsub_fn.CloudEvent[pubsub_fn.MessagePublish...
function hellopubsubattributes (line 61) | def hellopubsubattributes(event: pubsub_fn.CloudEvent[pubsub_fn.MessageP...
FILE: Python/quickstarts/uppercase-firestore/functions/main.py
function addmessage (line 31) | def addmessage(req: https_fn.Request) -> https_fn.Response:
function makeuppercase (line 54) | def makeuppercase(event: firestore_fn.Event[firestore_fn.DocumentSnapsho...
function makeuppercase2 (line 78) | def makeuppercase2(
FILE: Python/quickstarts/uppercase-rtdb/functions/main.py
function addmessage (line 33) | def addmessage(req: https_fn.Request) -> https_fn.Response:
function makeuppercase (line 58) | def makeuppercase(event: db_fn.Event[Any]) -> None:
function makeuppercase2 (line 82) | def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
FILE: Python/remote-config-diff/functions/main.py
function showconfigdiff (line 32) | def showconfigdiff(event: remote_config_fn.CloudEvent[remote_config_fn.C...
FILE: Python/taskqueues-backup-images/functions/main.py
function backupapod (line 45) | def backupapod(req: tasks_fn.CallableRequest) -> str:
function enqueuebackuptasks (line 97) | def enqueuebackuptasks(_: https_fn.Request) -> https_fn.Response:
function get_function_url (line 122) | def get_function_url(name: str, location: str = SupportedRegion.US_CENTR...
FILE: Python/testlab-to-slack/functions/main.py
function post_to_slack (line 28) | def post_to_slack(title: str, details: str) -> requests.Response:
function slackmoji (line 52) | def slackmoji(status: test_lab_fn.TestState | test_lab_fn.OutcomeSummary...
function posttestresultstoslack (line 71) | def posttestresultstoslack(
FILE: Python/thumbnails/functions/main.py
function generatethumbnail (line 37) | def generatethumbnail(event: storage_fn.CloudEvent[storage_fn.StorageObj...
Condensed preview — 660 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,258K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 822,
"preview": "---\nname: Bug report\nabout: Report an issue with a specific sample\ntitle: '[BUG] in sample: '\nlabels: 'type: bug'\nassign"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 1152,
"preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
},
{
"path": ".github/ISSUE_TEMPLATE/docs.md",
"chars": 392,
"preview": "---\nname: Documentation issue\nabout: Are a sample's docs incorrect or unclear?\ntitle: '[DOCS] for sample: '\nlabels: ''\na"
},
{
"path": ".github/workflows/test_node.yml",
"chars": 1754,
"preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
},
{
"path": ".github/workflows/test_node_1st_gen.yml",
"chars": 1770,
"preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
},
{
"path": ".github/workflows/test_python.yml",
"chars": 1318,
"preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
},
{
"path": ".gitignore",
"chars": 370,
"preview": ".idea\n**/node_modules/*\n**/.firebase\n**/.firebaserc\n**/.runtimeconfig.json\n*/npm-debug.log\nlerna-debug.log\n*~\nservice-ac"
},
{
"path": ".opensource/project.json",
"chars": 777,
"preview": "{\n \"name\": \"Cloud Functions Samples\",\n \"type\": \"sample\",\n \"platforms\": [\n \"Admin\",\n \"Node\"\n ],"
},
{
"path": "CONTRIBUTING.md",
"chars": 5015,
"preview": "# Contributing to the Firebase Cloud Functions Templates Library\n\nWe'd love for you to contribute to our source code and"
},
{
"path": "LICENSE",
"chars": 11341,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Node/README.md",
"chars": 173,
"preview": "# Node.js (2nd gen) samples\n\nThis folder contains examples for 2nd gen Node.js functions. See a description of all sampl"
},
{
"path": "Node/alerts-to-discord/.gitignore",
"chars": 68,
"preview": "# Don't accidentally push Discord Webhook URL to prod\nfunctions/.env"
},
{
"path": "Node/alerts-to-discord/README.md",
"chars": 1265,
"preview": "# Quickstart: Send Firebase Alerts to Discord\n\nThis quickstart demonstrates how to trigger a function based on a Firebas"
},
{
"path": "Node/alerts-to-discord/firebase.json",
"chars": 135,
"preview": "{\n \"functions\": {\n \"codebase\": \"alerts-to-discord\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" run lin"
},
{
"path": "Node/alerts-to-discord/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/alerts-to-discord/functions/index.js",
"chars": 6332,
"preview": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/alerts-to-discord/functions/package.json",
"chars": 829,
"preview": "{\n \"name\": \"alerts-to-discord\",\n \"description\": \"Send a message to Discord when an alert is received from Firebase\",\n "
},
{
"path": "Node/app-distribution-feedback-to-jira/.eslintrc",
"chars": 191,
"preview": "module.exports = {\n root: true,\n env: {\n es2017: true,\n node: true,\n },\n extends: [\n \"eslint:recommended\",\n"
},
{
"path": "Node/app-distribution-feedback-to-jira/.gitignore",
"chars": 1166,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
},
{
"path": "Node/app-distribution-feedback-to-jira/README.md",
"chars": 1914,
"preview": "# Send in-app feedback to Jira\n \nThis [code](functions/index.js) demonstrates how to use a Firebase Cloud Function trigg"
},
{
"path": "Node/app-distribution-feedback-to-jira/firebase.json",
"chars": 328,
"preview": "{\n \"functions\": [\n {\n \"source\": \"functions\",\n \"codebase\": \"app-distribution-feedback-to-jira\",\n \"igno"
},
{
"path": "Node/app-distribution-feedback-to-jira/functions/.eslintrc.cjs",
"chars": 861,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/app-distribution-feedback-to-jira/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/app-distribution-feedback-to-jira/functions/index.js",
"chars": 8915,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/app-distribution-feedback-to-jira/functions/package.json",
"chars": 685,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"File new issue in Jira when receiving in-app feedback facilitated by Firebase"
},
{
"path": "Node/call-vertex-remote-config-server/README.md",
"chars": 3318,
"preview": "Call the Vertex AI Gemini API with Remote Config and App Check\n========================================================="
},
{
"path": "Node/call-vertex-remote-config-server/client/README.md",
"chars": 2821,
"preview": "Test client for call-vertex-remote-config-server\n================================================\n\nIntroduction\n--------"
},
{
"path": "Node/call-vertex-remote-config-server/client/config.ts",
"chars": 235,
"preview": "export const firebaseConfig = {\n YOUR_FIREBASE_CONFIG\n};\n\n // Your ReCAPTCHA Enterprise site key (must be from the sam"
},
{
"path": "Node/call-vertex-remote-config-server/client/index.html",
"chars": 1477,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, i"
},
{
"path": "Node/call-vertex-remote-config-server/client/main.ts",
"chars": 3736,
"preview": "/**\n * @license\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * yo"
},
{
"path": "Node/call-vertex-remote-config-server/client/package.json",
"chars": 920,
"preview": "{\n \"name\": \"call-vertex-remote-config-server-client\",\n \"version\": \"1.0.0\",\n \"description\": \"JavaScript quickstart for"
},
{
"path": "Node/call-vertex-remote-config-server/client/vite-env.d.ts",
"chars": 38,
"preview": "/// <reference types=\"vite/client\" />\n"
},
{
"path": "Node/call-vertex-remote-config-server/client/vite.config.js",
"chars": 187,
"preview": "import { defineConfig } from 'vite';\n\nexport default defineConfig({\n base: '',\n build: {\n rollupOptions: {\n in"
},
{
"path": "Node/call-vertex-remote-config-server/firebase.json",
"chars": 542,
"preview": "{\n \"functions\": [\n {\n \"source\": \"functions\",\n \"codebase\": \"call-vertex-remote-config-server\",\n \"ignor"
},
{
"path": "Node/call-vertex-remote-config-server/functions/index.js",
"chars": 5141,
"preview": "/**\n * @license\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * yo"
},
{
"path": "Node/call-vertex-remote-config-server/functions/package.json",
"chars": 778,
"preview": "{\n \"name\": \"call-vertex-remote-config-server\",\n \"description\": \"An example of a callable function that uses the Admin "
},
{
"path": "Node/delete-unused-accounts-cron/README.md",
"chars": 1370,
"preview": "# Periodically delete unused accounts\n\nThis sample demonstrates how to delete the accounts of users who have not signed-"
},
{
"path": "Node/delete-unused-accounts-cron/firebase.json",
"chars": 79,
"preview": "{\n \"functions\": {\n \"codebase\": \"delete-unused-accounts-cron\"\n }\n}\n"
},
{
"path": "Node/delete-unused-accounts-cron/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/delete-unused-accounts-cron/functions/index.js",
"chars": 3480,
"preview": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/delete-unused-accounts-cron/functions/package.json",
"chars": 805,
"preview": "{\n \"name\": \"delete-unused-accounts-cron-functions\",\n \"description\": \"Periodically delete unused Firebase accounts\",\n "
},
{
"path": "Node/fcm-notifications/.gitignore",
"chars": 1166,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
},
{
"path": "Node/fcm-notifications/README.md",
"chars": 2751,
"preview": "# Send Firebase Cloud Messaging notifications for new followers.\n\nThis sample demonstrates how to send a Firebase Cloud "
},
{
"path": "Node/fcm-notifications/database.rules.json",
"chars": 315,
"preview": "{\n \"rules\": {\n \"users\": {\n \".read\": true,\n \"$uid\": {\n \".write\": \"auth.uid === $uid\"\n }\n },\n"
},
{
"path": "Node/fcm-notifications/firebase.json",
"chars": 411,
"preview": "{\n \"database\": {\n \"rules\": \"database.rules.json\"\n },\n \"hosting\": {\n \"public\": \"public\"\n },\n \"functions\": [\n "
},
{
"path": "Node/fcm-notifications/functions/.eslintrc.cjs",
"chars": 523,
"preview": "module.exports = {\n env: {\n es2022: true,\n node: true,\n },\n parserOptions: {\n \"ecmaVersion\": 2022,\n \"sour"
},
{
"path": "Node/fcm-notifications/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/fcm-notifications/functions/index.js",
"chars": 3759,
"preview": "/**\n * Copyright 2023 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/fcm-notifications/functions/package.json",
"chars": 657,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": \"eslint .\",\n \"se"
},
{
"path": "Node/fcm-notifications/public/firebase-messaging-sw.js",
"chars": 1029,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/fcm-notifications/public/index.html",
"chars": 4692,
"preview": "<!doctype html>\n<!--\n Copyright 2016 Google Inc. All rights reserved.\n Licensed under the Apache License, Version 2.0 "
},
{
"path": "Node/fcm-notifications/public/main.css",
"chars": 1722,
"preview": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/fcm-notifications/public/main.js",
"chars": 9410,
"preview": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/instrument-with-opentelemetry/README.md",
"chars": 1587,
"preview": "# Instrumenting Cloud Functions for Firebase with Open Telemetry\nThis sample demonstrates instrumenting your Cloud Funct"
},
{
"path": "Node/instrument-with-opentelemetry/firebase.json",
"chars": 283,
"preview": "{\n \"functions\": {\n \"source\": \"functions\",\n \"codebase\": \"instrument-with-opentelemetry\"\n },\n \"emulators\": {\n "
},
{
"path": "Node/instrument-with-opentelemetry/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/instrument-with-opentelemetry/functions/index.js",
"chars": 2928,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/instrument-with-opentelemetry/functions/package.json",
"chars": 1055,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": \"eslint .\",\n \"se"
},
{
"path": "Node/instrument-with-opentelemetry/functions/timer.js",
"chars": 923,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/instrument-with-opentelemetry/functions/tracing.js",
"chars": 2138,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/pnpm-workspace.yaml",
"chars": 20,
"preview": "packages:\n - \"./**\""
},
{
"path": "Node/quickstarts/auth-blocking-functions/.gitignore",
"chars": 1166,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
},
{
"path": "Node/quickstarts/auth-blocking-functions/README.md",
"chars": 1117,
"preview": "# Firebase SDK for Cloud Functions 2nd Gen Quickstart - Auth Blocking Functions\n========================================"
},
{
"path": "Node/quickstarts/auth-blocking-functions/firebase.json",
"chars": 141,
"preview": "{\n \"functions\": {\n \"codebase\": \"auth-blocking-functions\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" r"
},
{
"path": "Node/quickstarts/auth-blocking-functions/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/auth-blocking-functions/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/quickstarts/auth-blocking-functions/functions/index.js",
"chars": 2355,
"preview": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/auth-blocking-functions/functions/package.json",
"chars": 637,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": \"eslint .\",\n \"se"
},
{
"path": "Node/quickstarts/callable-functions/.gitignore",
"chars": 1166,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
},
{
"path": "Node/quickstarts/callable-functions/README.md",
"chars": 736,
"preview": "Firebase HTTPS Callable functions Quickstart\n================================================\n\nThe HTTPS Callable functi"
},
{
"path": "Node/quickstarts/callable-functions/firebase.json",
"chars": 136,
"preview": "{\n \"functions\": {\n \"codebase\": \"callable-functions\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" run li"
},
{
"path": "Node/quickstarts/callable-functions/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/callable-functions/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/quickstarts/callable-functions/functions/index.js",
"chars": 4033,
"preview": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/callable-functions/functions/package.json",
"chars": 816,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": \"eslint .\",\n \"li"
},
{
"path": "Node/quickstarts/callable-functions/functions/sanitizer.js",
"chars": 2404,
"preview": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/quickstarts/callable-functions-streaming/.gitignore",
"chars": 1166,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
},
{
"path": "Node/quickstarts/callable-functions-streaming/README.md",
"chars": 658,
"preview": "Firebase HTTPS Callable functions streaming quickstart\n================================================\n\nThis quickstart"
},
{
"path": "Node/quickstarts/callable-functions-streaming/firebase.json",
"chars": 180,
"preview": "{\n \"functions\": {\n \"codebase\": \"callable-functions\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" run li"
},
{
"path": "Node/quickstarts/callable-functions-streaming/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/callable-functions-streaming/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/quickstarts/callable-functions-streaming/functions/index.js",
"chars": 2297,
"preview": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/callable-functions-streaming/functions/package.json",
"chars": 808,
"preview": "{\n \"name\": \"callable-functions-streaming\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": "
},
{
"path": "Node/quickstarts/callable-functions-streaming/website/index.html",
"chars": 3617,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>title</title>\n </head>\n <body>\n <"
},
{
"path": "Node/quickstarts/custom-events/ README.md",
"chars": 612,
"preview": "Firebase Custom Events sample\n================================================\n\nA custom event trigger function that han"
},
{
"path": "Node/quickstarts/custom-events/firebase.json",
"chars": 131,
"preview": "{\n \"functions\": {\n \"codebase\": \"custom-events\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n "
},
{
"path": "Node/quickstarts/custom-events/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/custom-events/functions/index.js",
"chars": 1408,
"preview": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/custom-events/functions/package.json",
"chars": 784,
"preview": "{\n \"name\": \"functions-custom-events\",\n \"description\": \"Custom event handler for Image Resizer extension.\",\n \"scripts\""
},
{
"path": "Node/quickstarts/firestore-sync-auth/.gitignore",
"chars": 1144,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\n\n# Firebase cache\n.firebase/\n\n# Fir"
},
{
"path": "Node/quickstarts/firestore-sync-auth/README.md",
"chars": 2904,
"preview": "# Firebase SDK for Cloud Functions Quickstart - Firestore with auth context\n\nThis quickstart demonstrates using the **Fi"
},
{
"path": "Node/quickstarts/firestore-sync-auth/firebase.json",
"chars": 309,
"preview": "{\n \"functions\": {\n \"codebase\": \"firestore-sync-auth\"\n },\n \"firestore\": {\n \"rules\": \"firestore.rules\",\n \"inde"
},
{
"path": "Node/quickstarts/firestore-sync-auth/firestore.indexes.json",
"chars": 44,
"preview": "{\n \"indexes\": [],\n \"fieldOverrides\": []\n}\n"
},
{
"path": "Node/quickstarts/firestore-sync-auth/firestore.rules",
"chars": 235,
"preview": "service cloud.firestore {\n match /databases/{database}/documents {\n match /comments/{comment} {\n // Allow authe"
},
{
"path": "Node/quickstarts/firestore-sync-auth/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/firestore-sync-auth/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/quickstarts/firestore-sync-auth/functions/index.js",
"chars": 1816,
"preview": "/**\n * Copyright 2023 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'Licens"
},
{
"path": "Node/quickstarts/firestore-sync-auth/functions/package.json",
"chars": 842,
"preview": "{\n \"name\": \"firestore-sync-auth\",\n \"description\": \"Cloud Functions for Firebase\",\n \"dependencies\": {\n \"firebase-ad"
},
{
"path": "Node/quickstarts/genkit-helloworld/.gitignore",
"chars": 1166,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
},
{
"path": "Node/quickstarts/genkit-helloworld/README.md",
"chars": 1076,
"preview": "Genkit quickstart\n================================================\n\nThis quickstart demonstrates how to initialize a [Ge"
},
{
"path": "Node/quickstarts/genkit-helloworld/firebase.json",
"chars": 141,
"preview": "{\n \"functions\": {\n \"codebase\": \"oncallgenkit-helloworld\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" r"
},
{
"path": "Node/quickstarts/genkit-helloworld/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/genkit-helloworld/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/quickstarts/genkit-helloworld/functions/index.js",
"chars": 2301,
"preview": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/genkit-helloworld/functions/package.json",
"chars": 859,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": \"eslint .\",\n \"li"
},
{
"path": "Node/quickstarts/https-time-server/README.md",
"chars": 2684,
"preview": "# Firebase SDK for Cloud Functions Quickstart - HTTPS trigger\n\nThis quickstart demonstrates using the **Firebase SDK for"
},
{
"path": "Node/quickstarts/https-time-server/firebase.json",
"chars": 135,
"preview": "{\n \"functions\": {\n \"codebase\": \"https-time-server\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" run lin"
},
{
"path": "Node/quickstarts/https-time-server/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/https-time-server/functions/index.js",
"chars": 2641,
"preview": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/https-time-server/functions/package.json",
"chars": 786,
"preview": "{\n \"name\": \"time-server-functions\",\n \"description\": \"A simple time server using HTTPS Cloud Function\",\n \"dependencies"
},
{
"path": "Node/quickstarts/monitor-cloud-logging/README.md",
"chars": 397,
"preview": "# Firebase SDK for Cloud Functions Quickstart - Structured Logs\n\nThis quickstart demonstrates using the **`logger`** sub"
},
{
"path": "Node/quickstarts/monitor-cloud-logging/firebase.json",
"chars": 316,
"preview": "{\n \"functions\": [\n {\n \"source\": \"functions\",\n \"codebase\": \"monitor-cloud-logging\",\n \"ignore\": [\n "
},
{
"path": "Node/quickstarts/monitor-cloud-logging/functions/.eslintrc.js",
"chars": 1088,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/monitor-cloud-logging/functions/index.js",
"chars": 3250,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/monitor-cloud-logging/functions/package.json",
"chars": 756,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": \"eslint .\",\n \"se"
},
{
"path": "Node/quickstarts/pubsub-helloworld/.gitignore",
"chars": 1166,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
},
{
"path": "Node/quickstarts/pubsub-helloworld/README.md",
"chars": 2729,
"preview": "# Firebase SDK for Cloud Functions Quickstart - PubSub trigger\n\nThis quickstart demonstrates how to setup a PubSub trigg"
},
{
"path": "Node/quickstarts/pubsub-helloworld/firebase.json",
"chars": 135,
"preview": "{\n \"functions\": {\n \"codebase\": \"pubsub-helloworld\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" run lin"
},
{
"path": "Node/quickstarts/pubsub-helloworld/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/pubsub-helloworld/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/quickstarts/pubsub-helloworld/functions/index.js",
"chars": 2462,
"preview": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/quickstarts/pubsub-helloworld/functions/package.json",
"chars": 637,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": \"eslint .\",\n \"se"
},
{
"path": "Node/quickstarts/testlab-matrix-completed/README.md",
"chars": 1258,
"preview": "# Log test complete\n\nThis sample demonstrates how to trigger a function in response to the\ncompletion of a Test Matrix i"
},
{
"path": "Node/quickstarts/testlab-matrix-completed/firebase.json",
"chars": 68,
"preview": "{\n \"functions\": {\n \"codebase\": \"testlab-matrix-completed\"\n }\n}\n"
},
{
"path": "Node/quickstarts/testlab-matrix-completed/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/testlab-matrix-completed/functions/index.js",
"chars": 1312,
"preview": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/quickstarts/testlab-matrix-completed/functions/package.json",
"chars": 734,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": \"eslint .\",\n \"se"
},
{
"path": "Node/quickstarts/thumbnails/README.md",
"chars": 1661,
"preview": "# Firebase SDK for Cloud Functions Quickstart - Cloud Storage trigger\n\nThis quickstart demonstrates using **Firebase SDK"
},
{
"path": "Node/quickstarts/thumbnails/firebase.json",
"chars": 128,
"preview": "{\n \"functions\": {\n \"codebase\": \"thumbnails\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n "
},
{
"path": "Node/quickstarts/thumbnails/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/thumbnails/functions/index.js",
"chars": 3113,
"preview": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/quickstarts/thumbnails/functions/package.json",
"chars": 800,
"preview": "{\n \"name\": \"generate-thumbnail-functions-quickstart\",\n \"description\": \"Generate Thumbnail Firebase Functions sample\",\n"
},
{
"path": "Node/quickstarts/uppercase-firestore/.gitignore",
"chars": 1144,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\n\n# Firebase cache\n.firebase/\n\n# Fir"
},
{
"path": "Node/quickstarts/uppercase-firestore/README.md",
"chars": 3457,
"preview": "# Firebase SDK for Cloud Functions Quickstart - Firestore\n\nThis quickstart demonstrates using the **Firebase SDK for Clo"
},
{
"path": "Node/quickstarts/uppercase-firestore/firebase.json",
"chars": 309,
"preview": "{\n \"functions\": {\n \"codebase\": \"uppercase-firestore\"\n },\n \"firestore\": {\n \"rules\": \"firestore.rules\",\n \"inde"
},
{
"path": "Node/quickstarts/uppercase-firestore/firestore.indexes.json",
"chars": 44,
"preview": "{\n \"indexes\": [],\n \"fieldOverrides\": []\n}\n"
},
{
"path": "Node/quickstarts/uppercase-firestore/firestore.rules",
"chars": 235,
"preview": "service cloud.firestore {\n match /databases/{database}/documents {\n match /messages/{message} {\n // Allow authe"
},
{
"path": "Node/quickstarts/uppercase-firestore/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/uppercase-firestore/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/quickstarts/uppercase-firestore/functions/index.js",
"chars": 2800,
"preview": "/**\n * Copyright 2023 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'Licens"
},
{
"path": "Node/quickstarts/uppercase-firestore/functions/package.json",
"chars": 832,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"dependencies\": {\n \"firebase-admin\": \"^13"
},
{
"path": "Node/quickstarts/uppercase-rtdb/README.md",
"chars": 1371,
"preview": "# Firebase SDK for Cloud Functions 2nd Gen Quickstart - Realtime Database\n\nThis quickstart demonstrates using **Firebase"
},
{
"path": "Node/quickstarts/uppercase-rtdb/database.rules.json",
"chars": 59,
"preview": "{\n \"rules\": {\n \".read\": true,\n \".write\": true\n }\n}\n"
},
{
"path": "Node/quickstarts/uppercase-rtdb/firebase.json",
"chars": 247,
"preview": "{\n \"functions\": {\n \"codebase\": \"uppercase-rtdb\"\n },\n \"rulesFile\": \"database.rules.json\",\n \"emulators\": {\n \"fun"
},
{
"path": "Node/quickstarts/uppercase-rtdb/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/quickstarts/uppercase-rtdb/functions/index.js",
"chars": 2707,
"preview": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/quickstarts/uppercase-rtdb/functions/package.json",
"chars": 637,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": \"eslint .\",\n \"se"
},
{
"path": "Node/remote-config-diff/README.md",
"chars": 546,
"preview": "## Get the difference between the previous and current Remote Config templates.\n\nUse the Firebase Remote Config Cloud Fu"
},
{
"path": "Node/remote-config-diff/firebase.json",
"chars": 62,
"preview": "{\n \"functions\": {\n \"codebase\": \"remote-config-diff\"\n }\n}\n"
},
{
"path": "Node/remote-config-diff/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/remote-config-diff/functions/index.js",
"chars": 2643,
"preview": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/remote-config-diff/functions/package.json",
"chars": 772,
"preview": "{\n \"name\": \"remote-config-diff\",\n \"description\": \"Get the difference between the previous and current Remote Config te"
},
{
"path": "Node/remote-config-server-with-vertex/README.md",
"chars": 1874,
"preview": "# Use server-side Remote Config with Cloud Functions and Vertex AI\n\nThis Cloud Function (`generateWithVertex`) demonstra"
},
{
"path": "Node/remote-config-server-with-vertex/firebase.json",
"chars": 76,
"preview": "{\n \"functions\": {\n \"codebase\": \"remote-config-server-with-vertex\"\n }\n}\n"
},
{
"path": "Node/remote-config-server-with-vertex/functions/index.js",
"chars": 4036,
"preview": "// [START remote_config_server_vertex_init]\nconst { onRequest } = require(\"firebase-functions/https\");\nconst logger = re"
},
{
"path": "Node/remote-config-server-with-vertex/functions/package.json",
"chars": 670,
"preview": "{\n \"name\": \"remote-config-server-with-vertex\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"ser"
},
{
"path": "Node/taskqueues-backup-images/README.md",
"chars": 5172,
"preview": "# Back up images using a Task Queue Function (v2)\nThis quickstart demonstrates how to setup a Task Queue Function using "
},
{
"path": "Node/taskqueues-backup-images/firebase.json",
"chars": 68,
"preview": "{\n \"functions\": {\n \"codebase\": \"taskqueues-backup-images\"\n }\n}\n"
},
{
"path": "Node/taskqueues-backup-images/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/taskqueues-backup-images/functions/index.js",
"chars": 5330,
"preview": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/taskqueues-backup-images/functions/package.json",
"chars": 854,
"preview": "{\n \"name\": \"functions-taskqueues-backup-images\",\n \"description\": \"Back up images from NASA's Astronomy Picture of the "
},
{
"path": "Node/test-functions-jest/.gitignore",
"chars": 1166,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
},
{
"path": "Node/test-functions-jest/README.md",
"chars": 478,
"preview": "# Firebase Functions Test (with jest) - Quickstart\n\nThis quickstart demonstrates how to run unit tests on gen-2 function"
},
{
"path": "Node/test-functions-jest/firebase.json",
"chars": 164,
"preview": "{\n \"functions\": {\n \"codebase\": \"test-functions-jest\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" run l"
},
{
"path": "Node/test-functions-jest/functions/.eslintrc.js",
"chars": 803,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/test-functions-jest/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/test-functions-jest/functions/index.js",
"chars": 810,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/test-functions-jest/functions/index.test.js",
"chars": 1077,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/test-functions-jest/functions/package.json",
"chars": 850,
"preview": "{\n \"name\": \"test-functions\",\n \"scripts\": {\n \"lint\": \"eslint --ext .js,.ts .\",\n \"build\": \"tsc\",\n \"serve\": \"npm"
},
{
"path": "Node/test-functions-jest-ts/.gitignore",
"chars": 1166,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
},
{
"path": "Node/test-functions-jest-ts/README.md",
"chars": 487,
"preview": "# Firebase Functions Test (with jest-ts) - Quickstart\n\nThis quickstart demonstrates how to run unit tests on gen-2 funct"
},
{
"path": "Node/test-functions-jest-ts/firebase.json",
"chars": 190,
"preview": "{\n \"functions\": {\n \"codebase\": \"test-functions-jest-ts\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" ru"
},
{
"path": "Node/test-functions-jest-ts/functions/.eslintrc.js",
"chars": 1242,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/test-functions-jest-ts/functions/.gitignore",
"chars": 146,
"preview": "# Compiled JavaScript files\nlib/**/*.js\nlib/**/*.js.map\n\n# TypeScript v1 declaration files\ntypings/\n\n# Node.js dependenc"
},
{
"path": "Node/test-functions-jest-ts/functions/jest.config.js",
"chars": 730,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/test-functions-jest-ts/functions/package.json",
"chars": 1110,
"preview": "{\n \"name\": \"test-functions\",\n \"scripts\": {\n \"lint\": \"eslint --ext .js,.ts .\",\n \"build\": \"tsc\",\n \"serve\": \"npm"
},
{
"path": "Node/test-functions-jest-ts/functions/src/__test__/index.test.ts",
"chars": 1298,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/test-functions-jest-ts/functions/src/index.ts",
"chars": 805,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/test-functions-jest-ts/functions/tsconfig.dev.json",
"chars": 42,
"preview": "{\n \"include\": [\n \".eslintrc.js\"\n ]\n}\n"
},
{
"path": "Node/test-functions-jest-ts/functions/tsconfig.json",
"chars": 287,
"preview": "{\n \"compilerOptions\": {\n \"module\": \"commonjs\",\n \"esModuleInterop\": true,\n \"noImplicitReturns\": true,\n \"noUn"
},
{
"path": "Node/test-functions-mocha/.gitignore",
"chars": 1166,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
},
{
"path": "Node/test-functions-mocha/README.md",
"chars": 493,
"preview": "# Firebase Functions Test (with mocha) - Quickstart\n\nThis quickstart demonstrates how to run unit tests on Cloud Functio"
},
{
"path": "Node/test-functions-mocha/firebase.json",
"chars": 165,
"preview": "{\n \"functions\": {\n \"codebase\": \"test-functions-mocha\",\n \"predeploy\": [\n \"npm --prefix \\\"$RESOURCE_DIR\\\" run "
},
{
"path": "Node/test-functions-mocha/functions/.eslintrc.js",
"chars": 803,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/test-functions-mocha/functions/.gitignore",
"chars": 13,
"preview": "node_modules/"
},
{
"path": "Node/test-functions-mocha/functions/index.js",
"chars": 810,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/test-functions-mocha/functions/index.test.js",
"chars": 1135,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/test-functions-mocha/functions/package.json",
"chars": 939,
"preview": "{\n \"name\": \"functions\",\n \"scripts\": {\n \"lint\": \"eslint --ext .js,.ts .\",\n \"build\": \"\",\n \"serve\": \"npm run bui"
},
{
"path": "Node/testlab-to-slack/README.md",
"chars": 1641,
"preview": "# Post Test Lab Results to Slack channel\n\nThis sample demonstrates how to post to a Slack channel in response to the\ncom"
},
{
"path": "Node/testlab-to-slack/firebase.json",
"chars": 60,
"preview": "{\n \"functions\": {\n \"codebase\": \"testlab-to-slack\"\n }\n}\n"
},
{
"path": "Node/testlab-to-slack/functions/.eslintrc.js",
"chars": 787,
"preview": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/testlab-to-slack/functions/index.js",
"chars": 3171,
"preview": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"Licens"
},
{
"path": "Node/testlab-to-slack/functions/package.json",
"chars": 731,
"preview": "{\n \"name\": \"functions\",\n \"description\": \"Cloud Functions for Firebase\",\n \"scripts\": {\n \"lint\": \"eslint .\",\n \"se"
},
{
"path": "Node/youtube/README.md",
"chars": 3108,
"preview": "# YouTube: Get information about a YouTube channel (2nd Gen)\n\nThis quickstart demonstrates how to query the\n[YouTube Dat"
},
{
"path": "Node/youtube/firebase.json",
"chars": 129,
"preview": "{\n \"functions\": {\n \"source\": \"functions\",\n \"predeploy\": [\n \"pnpm --prefix \\\"$RESOURCE_DIR\\\" run compile\"\n "
},
{
"path": "Node/youtube/functions/index.ts",
"chars": 2303,
"preview": "/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "Node/youtube/functions/package.json",
"chars": 727,
"preview": "{\n \"name\": \"youtube-2nd-gen\",\n \"description\": \"Cloud Functions for Firebase 2nd Gen YouTube Sample\",\n \"main\": \"lib/in"
},
{
"path": "Node/youtube/functions/tsconfig.json",
"chars": 254,
"preview": "{\n \"compilerOptions\": {\n \"moduleResolution\": \"node\",\n \"noEmit\": true,\n \"sourceMap\": true,\n \"strict\": true,\n"
},
{
"path": "Node-1st-gen/README.md",
"chars": 271,
"preview": "# Node.js (1st gen) samples\n\nThis folder contains examples for 1st gen functions. Note that 2nd gen functions are availa"
},
{
"path": "Node-1st-gen/assistant-say-number/README.md",
"chars": 1620,
"preview": "# Google Assistant action that reads the ordinal of a number.\n\nThis sample shows how to create an action for the Google "
}
]
// ... and 460 more files (download for full content)
About this extraction
This page contains the full source code of the firebase/functions-samples GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 660 files (1.1 MB), approximately 302.8k tokens, and a symbol index with 191 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.