diff --git a/.gitignore b/.gitignore index 127eafecb4..9535b26697 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ **/.firebase **/.firebaserc **/.runtimeconfig.json +**/functions.yaml +**/firestore-debug.log */npm-debug.log lerna-debug.log *~ diff --git a/Dart/quickstarts/https-increment-number/.gitignore b/Dart/quickstarts/https-increment-number/.gitignore new file mode 100644 index 0000000000..8ae1a6ef6d --- /dev/null +++ b/Dart/quickstarts/https-increment-number/.gitignore @@ -0,0 +1,5 @@ +.dart_tool/ +.packages +build/ +*.dart_tool +pubspec.lock diff --git a/Dart/quickstarts/https-increment-number/README.md b/Dart/quickstarts/https-increment-number/README.md new file mode 100644 index 0000000000..a84020417d --- /dev/null +++ b/Dart/quickstarts/https-increment-number/README.md @@ -0,0 +1,48 @@ +# HTTPS Increment Number Quickstart + +This quickstart demonstrates how to handle HTTP requests for Cloud Functions using Dart. +It features two endpoints: +- `incrementLocal`: Simple POST request that takes a local count and returns it incremented by 1. +- `incrementSynced`: GET and POST endpoints that sync a counter variable to Firestore, returning the newly updated count. + + + +## Local Testing + +First, fetch dependencies: +```bash +dart pub get +``` + +Then, you can use the Firebase CLI to test the function locally. To ensure both endpoints work properly, make sure to start both the functions and firestore emulators: +```bash +firebase emulators:start --project="demo-example" --only firestore,functions +``` + +### Testing `incrementLocal` + +In a separate terminal, use cURL to POST to the function (replace the URL with the one provided by the emulator): + +```bash +curl -X POST http://127.0.0.1:5001/demo-example/us-central1/increment-local \ + -H "Content-Type: application/json" \ + -d '{"count": 5}' +``` + +You should see: +```json +{"message":"Local increment complete!","newCount":6} +``` + +### Testing `incrementSynced` + +To test using Firestore, fetch the initial count: +```bash +curl -X GET http://127.0.0.1:5001/demo-example/us-central1/increment-synced +``` + +Then increment it using POST: +```bash +curl -X POST http://127.0.0.1:5001/demo-example/us-central1/increment-synced \ + -H "Content-Type: application/json" +``` diff --git a/Dart/quickstarts/https-increment-number/bin/server.dart b/Dart/quickstarts/https-increment-number/bin/server.dart new file mode 100644 index 0000000000..12f11c1dc5 --- /dev/null +++ b/Dart/quickstarts/https-increment-number/bin/server.dart @@ -0,0 +1,65 @@ +import 'dart:convert'; +import 'package:firebase_functions/firebase_functions.dart'; +import 'package:google_cloud_firestore/google_cloud_firestore.dart' + show FieldValue; +import 'package:shared/shared.dart'; + +void main(List args) async { + await fireUp(args, (firebase) { + // Listen for calls to the http request and name defined in the shared package. + firebase.https.onRequest(name: incrementCallable, (request) async { + // In a production app, verify the user with request.auth?.uid here. + print('Incrementing counter on the server...'); + + // Get firestore database instance + final firestore = firebase.adminApp.firestore(); + + // Get a reference to the counter document + final counterDoc = firestore.collection('counters').doc('global'); + + // Get the current snapshot for the count data + final snapshot = await counterDoc.get(); + + // Increment response we will send back + IncrementResponse incrementResponse; + + // Check for the current count and if the snapshot exists + if (snapshot.data() case {'count': int value} when snapshot.exists) { + if (request.method == 'GET') { + // Get the current result + incrementResponse = IncrementResponse( + success: true, + message: 'Read-only sync complete', + newCount: value, + ); + } else if (request.method == 'POST') { + // Increment count by one + final step = request.url.queryParameters['step'] as int? ?? 1; + await counterDoc.update({'count': FieldValue.increment(step)}); + incrementResponse = IncrementResponse( + success: true, + message: 'Atomic increment complete', + newCount: value + 1, + ); + } else { + return Response(405, body: 'Method Not Allowed'); + } + } else { + // Create a new document with a count of 1 + await counterDoc.set({'count': 1}); + incrementResponse = const IncrementResponse( + success: true, + message: 'Cloud-sync complete', + newCount: 1, + ); + } + + // Return the response as JSON + return Response( + 200, + body: jsonEncode(incrementResponse.toJson()), + headers: {'Content-Type': 'application/json'}, + ); + }); + }); +} diff --git a/Dart/quickstarts/https-increment-number/firebase.json b/Dart/quickstarts/https-increment-number/firebase.json new file mode 100644 index 0000000000..e155cc8e57 --- /dev/null +++ b/Dart/quickstarts/https-increment-number/firebase.json @@ -0,0 +1,9 @@ +{ + "functions": { + "source": ".", + "codebase": "dart-quickstarts-https-increment-number" + }, + "firestore": { + "rules": "firestore.rules" + } +} diff --git a/Dart/quickstarts/https-increment-number/firestore.rules b/Dart/quickstarts/https-increment-number/firestore.rules new file mode 100644 index 0000000000..0e33c9c2d4 --- /dev/null +++ b/Dart/quickstarts/https-increment-number/firestore.rules @@ -0,0 +1,8 @@ +rules_version = '2'; +service cloud.firestore { + match /databases/{database}/documents { + match /{document=**} { + allow read: if true; + } + } +} diff --git a/Dart/quickstarts/https-increment-number/pubspec.yaml b/Dart/quickstarts/https-increment-number/pubspec.yaml new file mode 100644 index 0000000000..b18643ae2d --- /dev/null +++ b/Dart/quickstarts/https-increment-number/pubspec.yaml @@ -0,0 +1,40 @@ +name: https_increment_number +description: HTTPS trigger examples showcasing state increment operations. +publish_to: none + +environment: + sdk: ^3.11.0 + +dependencies: + shared: + path: ../shared + firebase_functions: + git: + url: https://github.com/firebase/firebase-functions-dart + ref: main + dart_firebase_admin: + git: + url: https://github.com/firebase/firebase-admin-dart + path: packages/dart_firebase_admin + ref: main + google_cloud_firestore: + git: + url: https://github.com/firebase/firebase-admin-dart + path: packages/google_cloud_firestore + ref: main + +dev_dependencies: + build_runner: ^2.10.5 + lints: ^6.0.0 + +dependency_overrides: + dart_firebase_admin: + git: + url: https://github.com/firebase/firebase-admin-dart + path: packages/dart_firebase_admin + ref: main + google_cloud_firestore: + git: + url: https://github.com/firebase/firebase-admin-dart + path: packages/google_cloud_firestore + ref: main diff --git a/Dart/quickstarts/shared/.gitignore b/Dart/quickstarts/shared/.gitignore new file mode 100644 index 0000000000..8ae1a6ef6d --- /dev/null +++ b/Dart/quickstarts/shared/.gitignore @@ -0,0 +1,5 @@ +.dart_tool/ +.packages +build/ +*.dart_tool +pubspec.lock diff --git a/Dart/quickstarts/shared/lib/shared.dart b/Dart/quickstarts/shared/lib/shared.dart new file mode 100644 index 0000000000..bfbc6405b4 --- /dev/null +++ b/Dart/quickstarts/shared/lib/shared.dart @@ -0,0 +1,19 @@ +class IncrementResponse { + final bool success; + final String message; + final int newCount; + + const IncrementResponse({ + required this.success, + required this.message, + required this.newCount, + }); + + Map toJson() => { + 'success': success, + 'message': message, + 'newCount': newCount, + }; +} + +const String incrementCallable = 'incrementSynced'; diff --git a/Dart/quickstarts/shared/pubspec.yaml b/Dart/quickstarts/shared/pubspec.yaml new file mode 100644 index 0000000000..be30da55fa --- /dev/null +++ b/Dart/quickstarts/shared/pubspec.yaml @@ -0,0 +1,6 @@ +name: shared +description: Shared classes. +publish_to: none + +environment: + sdk: ^3.11.0