diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3452af7e..f2ac9c9e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,16 @@ jobs: path: ~/.npm key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }} - - run: npm install + - name: Install dependencies + run: | + npm install + npm run lerna-bootstrap + + - name: Compile snippets + run: npm run lerna-compile + + - name: Check generated snippets + run: | + npm run snippets + ./scripts/checkdirty.sh - - name: "Lint and Test" - run: ./scripts/test.sh diff --git a/firestore-next/emulator-suite.js b/firestore-next/emulator-suite.js new file mode 100644 index 00000000..6e608dc7 --- /dev/null +++ b/firestore-next/emulator-suite.js @@ -0,0 +1,17 @@ +// [SNIPPETS_SEPARATION enabled] +function onDocumentReady(firebaseApp) { + // [START fs_emulator_connect] + const { initializeFirestore } = require("firebase/firestore"); + + let settings = {}; + if (location.hostname === "localhost") { + settings = { + host: "localhost:8080", + ssl: false + }; + } + + // firebaseApps previously initialized using initializeApp() + const db = initializeFirestore(firebaseApp, settings); + // [END fs_emulator_connect] +} diff --git a/firestore-next/package.json b/firestore-next/package.json new file mode 100644 index 00000000..310f730b --- /dev/null +++ b/firestore-next/package.json @@ -0,0 +1,17 @@ +{ + "name": "firestore-next", + "version": "1.0.0", + "scripts": { + "compile": "cp ../tsconfig.json.template ./tsconfig.json && tsc" + }, + "license": "Apache-2.0", + "dependencies": { + "firebase": "^0.800.7" + }, + "devDependencies": { + "@types/chai": "^4.2.12", + "@types/mocha": "^8.0.3", + "chai": "^4.2.0", + "mocha": "^8.1.3" + } +} diff --git a/firestore-next/test.firestore.js b/firestore-next/test.firestore.js new file mode 100644 index 00000000..bcd5e7ef --- /dev/null +++ b/firestore-next/test.firestore.js @@ -0,0 +1,1191 @@ +// [SNIPPETS_SEPARATION enabled] +const { expect } = require('chai'); + +// [START city_custom_object] +class City { + constructor (name, state, country ) { + this.name = name; + this.state = state; + this.country = country; + } + toString() { + return this.name + ', ' + this.state + ', ' + this.country; + } +} + +// Firestore data converter +var cityConverter = { + toFirestore: function(city) { + return { + name: city.name, + state: city.state, + country: city.country + } + }, + fromFirestore: function(snapshot, options){ + const data = snapshot.data(options); + return new City(data.name, data.state, data.country) + } +} +// [END city_custom_object] + +describe("firestore", () => { + const { FirebaseFirestore } = require("firebase/firestore"); + + /** @type {FirebaseFirestore} */ + let db; + let app; + + before(() => { + const { initializeApp } = require("firebase/app"); + const { getFirestore } = require("firebase/firestore"); + + const config = { + apiKey: "AIzaSyCM61mMr_iZnP1DzjT1PMB5vDGxfyWNM64", + authDomain: "firestore-snippets.firebaseapp.com", + projectId: "firestore-snippets" + }; + app = initializeApp(config); + db = getFirestore(app); + }); + + it("should be able to set the cache size", () => { + // [START fs_setup_cache] + const { initializeFirestore, CACHE_SIZE_UNLIMITED } = require("firebase/firestore"); + + const firestoreDb = initializeFirestore(app, { + cacheSizeBytes: CACHE_SIZE_UNLIMITED + }); + // [END fs_setup_cache] + }); + + it("should be initializable with persistence", () => { + const { initializeApp } = require("firebase/app"); + const { getFirestore } = require("firebase/firestore"); + + const app = initializeApp({ + apiKey: '### FIREBASE API KEY ###', + authDomain: '### FIREBASE AUTH DOMAIN ###', + projectId: '### CLOUD FIRESTORE PROJECT ID ###', + } ,"persisted_app"); + + const db = getFirestore(app); + + // [START initialize_persistence] + const { enableIndexedDbPersistence } = require("firebase/firestore"); + + enableIndexedDbPersistence(db) + .catch((err) => { + if (err.code == 'failed-precondition') { + // Multiple tabs open, persistence can only be enabled + // in one tab at a a time. + // ... + } else if (err.code == 'unimplemented') { + // The current browser does not support all of the + // features required to enable persistence + // ... + } + }); + // Subsequent queries will use persistence, if it was enabled successfully + // [END initialize_persistence] + }); + + it("should be able to enable/disable network", async () => { + // [START disable_network] + const { disableNetwork } = require("firebase/firestore"); + + await disableNetwork(db); + console.log("Network disabled!"); + // Do offline actions + // [START_EXCLUDE] + console.log("Network disabled!"); + // [END_EXCLUDE] + // [END disable_network] + + // [START enable_network] + const { enableNetwork } = require("firebase/firestore"); + + await enableNetwork(db) + // Do online actions + // [START_EXCLUDE] + console.log("Network enabled!"); + // [END_EXCLUDE] + // [END enable_network] + + }); + + it("should reply with .fromCache fields", () => { + // [START use_from_cache] + const { collection, onSnapshot, where, query } = require("firebase/firestore"); + + const q = query(collection(db, "cities"), where("state", "==", "CA")); + onSnapshot(q, { includeMetadataChanges: true }, (snapshot) => { + snapshot.docChanges().forEach((change) => { + if (change.type === "added") { + console.log("New city: ", change.doc.data()); + } + + const source = snapshot.metadata.fromCache ? "local cache" : "server"; + console.log("Data came from " + source); + }); + }); + // [END use_from_cache] + }); + + describe("collection('users')", () => { + it("should add data to a collection", async () => { + // [START add_ada_lovelace] + const { collection, addDoc } = require("firebase/firestore"); + + try { + const docRef = await addDoc(collection(db, "users"), { + first: "Ada", + last: "Lovelace", + born: 1815 + }); + console.log("Document written with ID: ", docRef.id); + } catch (e) { + console.error("Error adding document: ", e); + } + // [END add_ada_lovelace] + }); + + it("should get all users", async () => { + // [START get_all_users] + const { collection, getDocs } = require("firebase/firestore"); + + const querySnapshot = await getDocs(collection(db, "users")); + querySnapshot.forEach((doc) => { + console.log(`${doc.id} => ${doc.data()}`); + }); + // [END get_all_users] + }); + + it("should add data to a collection with new fields", async () => { + // [START add_alan_turing] + // Add a second document with a generated ID. + const { addDoc, collection } = require("firebase/firestore"); + + try { + const docRef = await addDoc(collection(db, "users"), { + first: "Alan", + middle: "Mathison", + last: "Turing", + born: 1912 + }); + + console.log("Document written with ID: ", docRef.id); + } catch (e) { + console.error("Error adding document: ", e); + } + // [END add_alan_turing] + }); + + it("should loop through a watched collection", (done) => { + // [START listen_for_users] + const { collection, where, query, onSnapshot } = require("firebase/firestore"); + + const q = query(collection(db, "users"), where("born", "<", 1900)); + const unsubscribe = onSnapshot(q, (snapshot) => { + console.log("Current users born before 1900:"); + snapshot.forEach(function (userSnapshot) { + console.log(userSnapshot.data()) + }); + }); + // [END listen_for_users] + + setTimeout(() => { + unsubscribe(); + done(); + }, 1500); + }); + + it("should reference a specific document", () => { + // [START doc_reference] + const { collection, doc } = require("firebase/firestore"); + + const alovelaceDocumentRef = doc(collection(db, 'users'), 'alovelace'); + // [END doc_reference] + }); + + it("should reference a specific collection", () => { + // [START collection_reference] + const { collection } = require("firebase/firestore"); + + const usersCollectionRef = collection(db, 'users'); + // [END collection_reference] + }); + + it("should reference a specific document (alternative)", () => { + // [START doc_reference_alternative] + const { doc } = require("firebase/firestore"); + + const alovelaceDocumentRef = doc(db, 'users/alovelace'); + // [END doc_reference_alternative] + }) + + it("should reference a document in a subcollection", () => { + // [START subcollection_reference] + const { doc, collection } = require("firebase/firestore"); + + const messageRef = doc(collection(doc(collection(db, "rooms"), "roomA"), "messages"), "message1"); + // [END subcollection_reference] + }); + + it("should set a document", async () => { + // [START set_document] + const { doc, collection, setDoc } = require("firebase/firestore"); + + // Add a new document in collection "cities" + await setDoc(doc(collection(db, "cities"), "LA"), { + name: "Los Angeles", + state: "CA", + country: "USA" + }); + // [END set_document] + }); + + it("should set document with a custom object converter", async () => { + // [START set_custom_object] + const { doc, collection, setDoc } = require("firebase/firestore"); + + // Set with cityConverter + const ref = doc(collection(db, "cities"), "LA").withConverter(cityConverter); + await setDoc(ref, new City("Los Angeles", "CA", "USA")); + // [END set_custom_object] + }); + + it("should get document with a custom object converter", async () => { + // [START get_custom_object] + const { doc, collection, getDoc} = require("firebase/firestore"); + + const ref = doc(collection(db, "cities"), "LA").withConverter(cityConverter); + const docSnap = await getDoc(ref); + if (docSnap.exists()) { + // Convert to City object + const city = docSnap.data(); + // Use a City instance method + console.log(city.toString()); + } else { + console.log("No such document!") + } + // [END get_custom_object] + }); + + it("should support batch writes", async () => { + // [START write_batch] + const { writeBatch, doc, collection } = require("firebase/firestore"); + + // Get a new write batch + const batch = writeBatch(db); + + // Set the value of 'NYC' + const nycRef = doc(collection(db, "cities"), "NYC"); + batch.set(nycRef, {name: "New York City"}); + + // Update the population of 'SF' + const sfRef = doc(collection(db, "cities"), "SF"); + batch.update(sfRef, {"population": 1000000}); + + // Delete the city 'LA' + const laRef = doc(collection(db, "cities"), "LA"); + batch.delete(laRef); + + // Commit the batch + await batch.commit(); + // [END write_batch] + }); + + it("should set a document with every datatype #UNVERIFIED", async () => { + // [START data_types] + const { doc, collection, setDoc, Timestamp } = require("firebase/firestore"); + + const docData = { + stringExample: "Hello world!", + booleanExample: true, + numberExample: 3.14159265, + dateExample: Timestamp.fromDate(new Date("December 10, 1815")), + arrayExample: [5, true, "hello"], + nullExample: null, + objectExample: { + a: 5, + b: { + nested: "foo" + } + } + }; + await setDoc(doc(collection(db, "data"), "one"), docData); + // [END data_types] + }); + + it("should allow set with merge", async () => { + // [START set_with_merge] + const { doc, collection, setDoc } = require("firebase/firestore"); + + const cityRef = doc(collection(db, 'cities'), 'BJ'); + setDoc(cityRef, { capital: true }, { merge: true }); + // [END set_with_merge] + }); + + it("should update a document's nested fields #UNVERIFIED", async () => { + // [START update_document_nested] + const { doc, collection, setDoc, updateDoc } = require("firebase/firestore"); + + // Create an initial document to update. + const frankDocRef = doc(collection(db, "users"), "frank"); + await setDoc(frankDocRef, { + name: "Frank", + favorites: { food: "Pizza", color: "Blue", subject: "recess" }, + age: 12 + }); + + // To update age and favorite color: + await updateDoc(frankDocRef, { + "age": 13, + "favorites.color": "Red" + }); + // [END update_document_nested] + }); + + it("should delete a collection", () => { + // [START delete_collection] + /** + * Delete a collection, in batches of batchSize. Note that this does + * not recursively delete subcollections of documents in the collection + */ + const { collection, query, orderBy, limit, getDocs, writeBatch } = require("firebase/firestore"); + + function deleteCollection(db, collectionRef, batchSize) { + const q = query(collectionRef, orderBy('__name__'), limit(batchSize)) + + return new Promise(function(resolve) { + deleteQueryBatch(db, q, batchSize, resolve); + }); + } + + async function deleteQueryBatch(db, query, batchSize, resolve) { + const snapshot = await getDocs(query); + + // When there are no documents left, we are done + let numDeleted = 0; + if (snapshot.size > 0) { + // Delete documents in a batch + const batch = writeBatch(db); + snapshot.docs.forEach((doc) => { + batch.delete(doc.ref); + }); + + await batch.commit(); + numDeleted = snapshot.size; + } + + if (numDeleted < batchSize) { + resolve(); + return; + } + + // Recurse on the next process tick, to avoid + // exploding the stack. + setTimeout(() => { + deleteQueryBatch(db, query, batchSize, resolve); + }, 0); + } + // [END delete_collection] + + return deleteCollection(db, collection(db, "users"), 2); + }).timeout(2000); + }); + + describe("collection('cities')", () => { + it("should set documents", async () => { + // [START example_data] + const { collection, doc, setDoc } = require("firebase/firestore"); + + const citiesRef = collection(db, "cities"); + + await setDoc(doc(citiesRef, "SF"), { + name: "San Francisco", state: "CA", country: "USA", + capital: false, population: 860000, + regions: ["west_coast", "norcal"] }); + await setDoc(doc(citiesRef, "LA"), { + name: "Los Angeles", state: "CA", country: "USA", + capital: false, population: 3900000, + regions: ["west_coast", "socal"] }); + await setDoc(doc(citiesRef, "DC"), { + name: "Washington, D.C.", state: null, country: "USA", + capital: true, population: 680000, + regions: ["east_coast"] }); + await setDoc(doc(citiesRef, "TOK"), { + name: "Tokyo", state: null, country: "Japan", + capital: true, population: 9000000, + regions: ["kanto", "honshu"] }); + await setDoc(doc(citiesRef, "BJ"), { + name: "Beijing", state: null, country: "China", + capital: true, population: 21500000, + regions: ["jingjinji", "hebei"] }); + // [END example_data] + }); + it("should set a document", async () => { + const data = {}; + + // [START cities_document_set] + const { collection, doc, setDoc } = require("firebase/firestore"); + + await setDoc(doc(collection(db, "cities"), "new-city-id"), data); + // [END cities_document_set] + }); + + it("should add a document", async () => { + // [START add_document] + const { collection, addDoc } = require("firebase/firestore"); + + // Add a new document with a generated id. + const docRef = await addDoc(collection(db, "cities"), { + name: "Tokyo", + country: "Japan" + }); + console.log("Document written with ID: ", docRef.id); + // [END add_document] + }); + + it("should add an empty a document", async () => { + const data = {}; + // [START new_document] + const { collection, doc, setDoc } = require("firebase/firestore"); + + // Add a new document with a generated id + const newCityRef = doc(collection(db, "cities")); + + // later... + await setDoc(newCityRef, data); + // [END new_document] + }); + + it("should update a document", async () => { + const data = {}; + // [START update_document] + const { collection, doc, updateDoc } = require("firebase/firestore"); + + const washingtonRef = doc(collection(db, "cities"), "DC"); + + // Set the "capital" field of the city 'DC' + await updateDoc(washingtonRef, { + capital: true + }); + // [END update_document] + }); + + it("should update an array field in a document", async () => { + // [START update_document_array] + const { collection, doc, updateDoc, arrayUnion, arrayRemove } = require("firebase/firestore"); + + const washingtonRef = doc(collection(db, "cities"), "DC"); + + // Atomically add a new region to the "regions" array field. + await updateDoc(washingtonRef, { + regions: arrayUnion("greater_virginia") + }); + + // Atomically remove a region from the "regions" array field. + await updateDoc(washingtonRef, { + regions: arrayRemove("east_coast") + }); + // [END update_document_array] + }); + + it("should update a document using numeric transforms", async () => { + // [START update_document_increment] + const { collection, doc, updateDoc, increment } = require("firebase/firestore"); + + const washingtonRef = doc(collection(db, "cities"), "DC"); + + // Atomically increment the population of the city by 50. + await updateDoc(washingtonRef, { + population: increment(50) + }); + // [END update_document_increment] + }) + + it("should delete a document", async () => { + // [START delete_document] + const { collection, doc, deleteDoc } = require("firebase/firestore"); + + await deleteDoc(doc(collection(db, "cities"), "DC")); + // [END delete_document] + }); + + it("should handle transactions", async () => { + const { collection, doc, setDoc } = require("firebase/firestore"); + + const sfDocRef = doc(collection(db, "cities"), "SF"); + await setDoc(sfDocRef, { population: 0 }); + + // [START transaction] + const { runTransaction } = require("firebase/firestore"); + + try { + await runTransaction(db, async (transaction) => { + const sfDoc = await transaction.get(sfDocRef); + if (!sfDoc.exists()) { + throw "Document does not exist!"; + } + + const newPopulation = sfDoc.data().population + 1; + transaction.update(sfDocRef, { population: newPopulation }); + }); + console.log("Transaction successfully committed!"); + } catch (e) { + console.log("Transaction failed: ", e); + } + // [END transaction] + }); + + it("should handle transaction which bubble out data", async () => { + // [START transaction_promise] + const { collection, doc, runTransaction } = require("firebase/firestore"); + + // Create a reference to the SF doc. + const sfDocRef = doc(collection(db, "cities"), "SF"); + + try { + const newPopulation = await runTransaction(db, async (transaction) => { + const sfDoc = await transaction.get(sfDocRef); + if (!sfDoc.exists()) { + throw "Document does not exist!"; + } + + const newPop = sfDoc.data().population + 1; + if (newPop <= 1000000) { + transaction.update(sfDocRef, { population: newPop }); + } else { + return Promise.reject("Sorry! Population is too big"); + } + }); + + console.log("Population increased to ", newPopulation); + } catch (e) { + // This will be a "population is too big" error. + console.error(e); + } + // [END transaction_promise] + }); + + it("should get a single document", async () => { + // [START get_document] + const { collection, doc, getDoc } = require("firebase/firestore"); + + const docRef = doc(collection(db, "cities"), "SF"); + const docSnap = await getDoc(docRef); + + if (docSnap.exists()) { + console.log("Document data:", docSnap.data()); + } else { + // doc.data() will be undefined in this case + console.log("No such document!"); + } + // [END get_document] + }); + + it("should get a document with options", async () => { + // [START get_document_options] + const { collection, doc, getDocFromCache } = require("firebase/firestore"); + + const docRef = doc(collection(db, "cities"), "SF"); + + // Get a document, forcing the SDK to fetch from the offline cache. + try { + const doc = await getDocFromCache(docRef); + + // Document was found in the cache. If no cached document exists, + // an error will be returned to the 'catch' block below. + console.log("Cached document data:", doc.data()); + } catch (e) { + console.log("Error getting cached document:", e); + } + // [END get_document_options] + }); + + it("should listen on a single document", (done) => { + // [START listen_document] + const { collection, doc, onSnapshot } = require("firebase/firestore"); + + const unsub = onSnapshot(doc(collection(db, "cities"), "SF"), (doc) => { + console.log("Current data: ", doc.data()); + }); + // [END listen_document] + + setTimeout(function() { + unsub(); + done(); + }, 3000); + }).timeout(5000); + + it("should listen on a single document with metadata", (done) => { + // [START listen_document_local] + const { collection, doc, onSnapshot } = require("firebase/firestore"); + + const unsub = onSnapshot(doc(collection(db, "cities"), "SF"), (doc) => { + const source = doc.metadata.hasPendingWrites ? "Local" : "Server"; + console.log(source, " data: ", doc.data()); + }); + // [END listen_document_local] + + setTimeout(function() { + unsub(); + done(); + }, 3000); + }).timeout(5000); + + it("should listen on a single document with options #UNVERIFIED", (done) => { + // [START listen_with_metadata] + const { collection, doc, onSnapshot } = require("firebase/firestore"); + + const unsub = onSnapshot( + doc(collection(db, "cities"), "SF"), + { includeMetadataChanges: true }, + (doc) => { + // ... + }); + // [END listen_with_metadata] + + setTimeout(function() { + unsub(); + done(); + }, 3000); + }).timeout(5000); + + it("should get multiple documents from a collection", async () => { + // [START get_multiple] + const { collection, query, where, getDocs } = require("firebase/firestore"); + + const q = query(collection(db, "cities"), where("capital", "==", true)); + + const querySnapshot = await getDocs(q); + querySnapshot.forEach((doc) => { + // doc.data() is never undefined for query doc snapshots + console.log(doc.id, " => ", doc.data()); + }); + // [END get_multiple] + }).timeout(5000); + + it("should get all documents from a collection", async () => { + // [START get_multiple_all] + const { collection, getDocs } = require("firebase/firestore"); + + const querySnapshot = await getDocs(collection(db, "cities")); + querySnapshot.forEach((doc) => { + // doc.data() is never undefined for query doc snapshots + console.log(doc.id, " => ", doc.data()); + }); + // [END get_multiple_all] + }); + + it("should listen on multiple documents #UNVERIFIED", (done) => { + // [START listen_multiple] + const { collection, query, where, onSnapshot } = require("firebase/firestore"); + + const q = query(collection(db, "cities"), where("state", "==", "CA")); + const unsubscribe = onSnapshot(q, (querySnapshot) => { + const cities = []; + querySnapshot.forEach((doc) => { + cities.push(doc.data().name); + }); + console.log("Current cities in CA: ", cities.join(", ")); + }); + // [END listen_multiple] + setTimeout(function() { + unsubscribe(); + done(); + }, 2500); + }).timeout(5000); + + it("should view changes between snapshots #UNVERIFIED", (done) => { + // [START listen_diffs] + const { collection, query, where, onSnapshot } = require("firebase/firestore"); + + const q = query(collection(db, "cities"), where("state", "==", "CA")); + const unsubscribe = onSnapshot(q, (snapshot) => { + snapshot.docChanges().forEach((change) => { + if (change.type === "added") { + console.log("New city: ", change.doc.data()); + } + if (change.type === "modified") { + console.log("Modified city: ", change.doc.data()); + } + if (change.type === "removed") { + console.log("Removed city: ", change.doc.data()); + } + }); + }); + // [END listen_diffs] + setTimeout(function() { + unsubscribe(); + done(); + }, 2500); + }).timeout(5000); + + it("should unsubscribe a listener", () => { + // [START detach_listener] + const { collection, onSnapshot } = require("firebase/firestore"); + + const unsubscribe = onSnapshot(collection(db, "cities"), () => { + // Respond to data + // ... + }); + + // Later ... + + // Stop listening to changes + unsubscribe(); + // [END detach_listener] + }); + + it("should handle listener errors", () => { + // [START handle_listen_errors] + const { collection, onSnapshot } = require("firebase/firestore"); + + const unsubscribe = onSnapshot( + collection(db, "cities"), + (snapshot) => { + // ... + }, + (error) => { + // ... + }); + // [END handle_listen_errors] + unsubscribe(); + }); + + it("should update a document with server timestamp", async () => { + async function update() { + // [START update_with_server_timestamp] + const { collection, updateDoc, serverTimestamp } = require("firebase/firestore"); + + const docRef = doc(collection(db, 'objects'), 'some-id'); + + // Update the timestamp field with the value from the server + const updateTimestamp = await updateDoc(docRef, { + timestamp: serverTimestamp() + }); + // [END update_with_server_timestamp] + + return updateTimestamp; + } + + const { collection, doc, setDoc } = require("firebase/firestore"); + + await setDoc(doc(collection(db, 'objects'), 'some-id'), {}); + await update(); + console.log('Document updated with server timestamp'); + }); + + it("should use options to control server timestamp resolution", async () => { + // [START server_timestamp_resolution_options] + const { collection, doc, updateDoc, serverTimestamp, onSnapshot } = require("firebase/firestore"); + // Perform an update followed by an immediate read without + // waiting for the update to complete. Due to the snapshot + // options we will get two results: one with an estimate + // timestamp and one with the resolved server timestamp. + const docRef = doc(collection(db, 'objects'), 'some-id'); + updateDoc(docRef, { + timestamp: serverTimestamp() + }); + + onSnapshot(docRef, (snapshot) => { + const data = snapshot.data({ + // Options: 'estimate', 'previous', or 'none' + serverTimestamps: "estimate" + }); + console.log( + 'Timestamp: ' + data.timestamp + + ', pending: ' + snapshot.metadata.hasPendingWrites); + }); + // [END server_timestamp_resolution_options] + }); + + it("should delete a document field", async () => { + async function update() { + // [START update_delete_field] + const { doc, collection, updateDoc, deleteField } = require("firebase/firestore"); + + const cityRef = doc(collection(db, 'cities'), 'BJ'); + + // Remove the 'capital' field from the document + await updateDoc(cityRef, { + capital: deleteField() + }); + // [END update_delete_field] + } + + const { doc, collection, setDoc } = require("firebase/firestore"); + + await setDoc(doc(collection(db,'cities'), 'BJ'), { capital: true }); + await update(); + }); + + describe("queries", () => { + it("should handle simple where", () => { + // [START simple_queries] + // Create a reference to the cities collection + const { collection, query, where } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // Create a query against the collection. + const q = query(citiesRef, where("state", "==", "CA")); + // [END simple_queries] + }); + + it("should handle another simple where", () => { + // [START simple_queries_again] + const { collection, query, where } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + const q = query(citiesRef, where("capital", "==", true)); + // [END simple_queries_again] + }); + + it("should handle other wheres", () => { + const { collection, query, where } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START example_filters] + const q1 = query(citiesRef, where("state", "==", "CA")); + const q2 = query(citiesRef, where("population", "<", 100000)); + const q3 = query(citiesRef, where("name", ">=", "San Francisco")); + // [END example_filters] + }); + + it("should handle array-contains where", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START array_contains_filter] + const { query, where } = require("firebase/firestore"); + const q = query(citiesRef, where("regions", "array-contains", "west_coast")); + // [END array_contains_filter] + }); + + it("should handle an array contains any where", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START array_contains_any_filter] + const { query, where } = require("firebase/firestore"); + + const q = query(citiesRef, + where('regions', 'array-contains-any', ['west_coast', 'east_coast'])); + // [END array_contains_any_filter] + }); + + it("should handle an in where", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + function inFilter() { + // [START in_filter] + const { query, where } = require("firebase/firestore"); + + const q = query(citiesRef, where('country', 'in', ['USA', 'Japan'])); + // [END in_filter] + } + + function inFilterWithArray() { + // [START in_filter_with_array] + const { query, where } = require("firebase/firestore"); + + const q = query(citiesRef, where('regions', 'in', [['west_coast', 'east_coast']])); + // [END in_filter_with_array] + } + }); + + it("should handle compound queries", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START chain_filters] + const { query, where } = require("firebase/firestore"); + + const q1 = query(citiesRef, where("state", "==", "CO"), where("name", "==", "Denver")); + const q2 = query(citiesRef, where("state", "==", "CA"), where("population", "<", 1000000)); + // [END chain_filters] + }); + + it("should handle range filters on one field", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START valid_range_filters] + const { query, where } = require("firebase/firestore"); + + const q1 = query(citiesRef, where("state", ">=", "CA"), where("state", "<=", "IN")); + const q2 = query(citiesRef, where("state", "==", "CA"), where("population", ">", 1000000)); + // [END valid_range_filters] + }); + + it("should not handle range filters on multiple field", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + expect(() => { + // [START invalid_range_filters] + const { query, where } = require("firebase/firestore"); + + const q = query(citiesRef, where("state", ">=", "CA"), where("population", ">", 100000)); + // [END invalid_range_filters] + }).to.throw; + }); + + it("should order and limit", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START order_and_limit] + const { query, orderBy, limit } = require("firebase/firestore"); + + const q = query(citiesRef, orderBy("name"), limit(3)); + // [END order_and_limit] + }); + + it("should order descending", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START order_and_limit_desc] + const { query, orderBy, limit } = require("firebase/firestore"); + + const q = query(citiesRef, orderBy("name", "desc"), limit(3)); + // [END order_and_limit_desc] + }); + + it("should order descending by other field", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START order_multiple] + const { query, orderBy } = require("firebase/firestore"); + + const q = query(citiesRef, orderBy("state"), orderBy("population", "desc")); + // [END order_multiple] + }); + + it("should where and order by with limit", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START filter_and_order] + const { query, where, orderBy, limit } = require("firebase/firestore"); + + const q = query(citiesRef, where("population", ">", 100000), orderBy("population"), limit(2)); + // [END filter_and_order] + }); + + it("should where and order on same field", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START valid_filter_and_order] + const { query, where, orderBy } = require("firebase/firestore"); + + const q = query(citiesRef, where("population", ">", 100000), orderBy("population")); + // [END valid_filter_and_order] + }); + + it("should not where and order on same field", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + expect(() => { + // [START invalid_filter_and_order] + const { query, where, orderBy } = require("firebase/firestore"); + + const q = query(citiesRef, where("population", ">", 100000), orderBy("country")); + // [END invalid_filter_and_order] + }).to.throw; + }); + + it("should handle startAt", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START order_and_start] + const { query, orderBy, startAt } = require("firebase/firestore"); + + const q = query(citiesRef, orderBy("population"), startAt(1000000)); + // [END order_and_start] + }); + + it("should handle endAt", () => { + const { collection } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + // [START order_and_end] + const { query, orderBy, endAt } = require("firebase/firestore"); + + const q = query(citiesRef, orderBy("population"), endAt(1000000)); + // [END order_and_end] + }); + + it("should handle startAt(doc) ", async () => { + // [START start_doc] + const { collection, doc, getDoc, query, orderBy, startAt } = require("firebase/firestore"); + const citiesRef = collection(db, "cities"); + + const docSnap = await getDoc(doc(citiesRef, "SF")); + + // Get all cities with a population bigger than San Francisco + const biggerThanSf = query(citiesRef, orderBy("popuation"), startAt(docSnap)); + // ... + // [END start_doc] + }); + + it("should handle multiple orderBy", () => { + // [START start_multiple_orderby] + // Will return all Springfields + const { collection, query, orderBy, startAt } = require("firebase/firestore"); + const q1 = query(collection(db, "cities"), + orderBy("name"), + orderBy("state"), + startAt("Springfield")); + + // Will return "Springfield, Missouri" and "Springfield, Wisconsin" + const q2 = query(collection(db, "cities"), + orderBy("name"), + orderBy("state"), + startAt("Springfield", "Missouri")); + // [END start_multiple_orderby] + }); + + it("should paginate", async () => { + // [START paginate] + const { collection, query, orderBy, startAfter, limit, getDocs } = require("firebase/firestore"); + + // Query the first page of docs + const first = query(collection(db, "cities"), orderBy("population"), limit(25)); + const documentSnapshots = await getDocs(first); + + // Get the last visible document + const lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1]; + console.log("last", lastVisible); + + // Construct a new query starting at this document, + // get the next 25 cities. + const next = query(collection(db, "cities"), + orderBy("population"), + startAfter(lastVisible), + limit(25)); + // [END paginate] + }); + }); + + describe('collectionGroup(landmarks)', () => { + it("should setup example data", async () => { + // [START fs_collection_group_query_data_setup] + const { collection, doc, setDoc } = require("firebase/firestore"); + + const citiesRef = collection(db, 'cities'); + + await Promise.all([ + setDoc(doc(collection(doc(citiesRef, 'SF'), 'landmarks')), { + name: 'Golden Gate Bridge', + type: 'bridge' + }), + setDoc(doc(collection(doc(citiesRef, 'SF'), 'landmarks')), { + name: 'Legion of Honor', + type: 'museum' + }), + setDoc(doc(collection(doc(citiesRef, 'LA'), 'landmarks')), { + name: 'Griffith Park', + type: 'park' + }), + setDoc(doc(collection(doc(citiesRef, 'LA'), 'landmarks')), { + name: 'The Getty', + type: 'museum' + }), + setDoc(doc(collection(doc(citiesRef, 'DC'), 'landmarks')), { + name: 'Lincoln Memorial', + type: 'memorial' + }), + setDoc(doc(collection(doc(citiesRef, 'DC'), 'landmarks')), { + name: 'National Air and Space Museum', + type: 'museum' + }), + setDoc(doc(collection(doc(citiesRef, 'TOK'), 'landmarks')), { + name: 'Ueno Park', + type: 'park' + }), + setDoc(doc(collection(doc(citiesRef, 'TOK'), 'landmarks')), { + name: 'National Museum of Nature and Science', + type: 'museum' + }), + setDoc(doc(collection(doc(citiesRef, 'BJ'), 'landmarks')), { + name: 'Jingshan Park', + type: 'park' + }), + setDoc(doc(collection(doc(citiesRef, 'BJ'), 'landmarks')), { + name: 'Beijing Ancient Observatory', + type: 'museum' + }) + ]); + // [END fs_collection_group_query_data_setup] + }); + + it("should query a collection group", async () => { + // [START fs_collection_group_query] + const { collectionGroup, query, where, getDocs } = require("firebase/firestore"); + + const museums = query(collectionGroup(db, 'landmarks'), where('type', '==', 'museum')); + const querySnapshot = await getDocs(museums); + querySnapshot.forEach((doc) => { + console.log(doc.id, ' => ', doc.data()); + }); + // [END fs_collection_group_query] + }); + }); + }); + + // TODO: Break out into separate file + describe("solution-aggregation", () => { + it("should update a restaurant in a transaction #UNVERIFIED", async () => { + // [START add_rating_transaction] + const { collection, doc, runTransaction} = require("firebase/firestore"); + + async function addRating(restaurantRef, rating) { + // Create a reference for a new rating, for use inside the transaction + const ratingRef = doc(collection(restaurantRef, 'ratings')); + + // In a transaction, add the new rating and update the aggregate totals + await runTransaction(db, async (transaction) => { + const res = await transaction.get(restaurantRef); + if (!res.exists()) { + throw "Document does not exist!"; + } + + // Compute new number of ratings + const newNumRatings = res.data().numRatings + 1; + + // Compute new average rating + const oldRatingTotal = res.data().avgRating * res.data().numRatings; + const newAvgRating = (oldRatingTotal + rating) / newNumRatings; + + // Commit to Firestore + transaction.update(restaurantRef, { + numRatings: newNumRatings, + avgRating: newAvgRating + }); + transaction.set(ratingRef, { rating: rating }); + }); + } + // [END add_rating_transaction] + + // Create document and add a rating + const { setDoc } = require("firebase/firestore"); + const ref = doc(collection(db, 'restaurants'), ('arinell-pizza')); + await setDoc(ref, { + name: 'Arinell Pizza', + avgRating: 4.63, + numRatings: 683 + }); + await addRating(ref, 5.0); + }); + }); +}); diff --git a/firestore-next/test.solution-aggregation.js b/firestore-next/test.solution-aggregation.js new file mode 100644 index 00000000..a2b8c487 --- /dev/null +++ b/firestore-next/test.solution-aggregation.js @@ -0,0 +1,41 @@ +// [SNIPPETS_SEPARATION enabled] +// [START sample_doc] +const arinellDoc = { + name: 'Arinell Pizza', + avgRating: 4.65, + numRatings: 683 +} +// [END sample_doc] + +describe("firestore-solution-arrays", () => { + const { FirebaseFirestore } = require("firebase/firestore"); + + /** @type {FirebaseFirestore} */ + let db; + + before(async () => { + const { initializeApp } = require("firebase/app"); + const { getFirestore, collection, doc, setDoc } = require("firebase/firestore"); + + const config = { + apiKey: "AIzaSyArvVh6VSdXicubcvIyuB-GZs8ua0m0DTI", + authDomain: "firestorequickstarts.firebaseapp.com", + projectId: "firestorequickstarts", + }; + const app = initializeApp(config, "solution-arrays"); + db = getFirestore(app); + + await setDoc(doc(collection(db, "restaurants"), "arinell-pizza"), arinellDoc); + }); + + describe("solution-arrays", () => { + it("should get a collection of ratings", async () => { + // [START get_collection_ratings] + const { collection, doc, getDocs } = require("firebase/firestore"); + + const ratingsRef = collection(doc(collection(db, "restaurants"), "arinell-pizza"), "ratings"); + const ratingsDocs = await getDocs(ratingsRef); + // [END get_collection_ratings] + }) + }); +}); diff --git a/firestore-next/test.solution-arrays.js b/firestore-next/test.solution-arrays.js new file mode 100644 index 00000000..7c2ee04d --- /dev/null +++ b/firestore-next/test.solution-arrays.js @@ -0,0 +1,100 @@ +// [SNIPPETS_SEPARATION enabled] +const postsWithArray = [ + // [START post_with_array] + // Sample document in the 'posts' collection. + { + title: "My great post", + categories: [ + "technology", + "opinion", + "cats" + ] + } + // [END post_with_array] +]; + +const postsWithMap = [ + // [START post_with_map] + // Sample document in the 'posts' collection + { + title: "My great post", + categories: { + "technology": true, + "opinion": true, + "cats": true + } + } + // [END post_with_map] +]; + +const postsWithMapAdvanced = [ + // [START post_with_map_advanced] + // The value of each entry in 'categories' is a unix timestamp + { + title: "My great post", + categories: { + technology: 1502144665, + opinion: 1502144665, + cats: 1502144665 + } + } + // [END post_with_map_advanced] +] + +describe("firestore-solution-arrays", () => { + const { FirebaseFirestore } = require("firebase/firestore"); + + /** @type {FirebaseFirestore} */ + let db; + + before(() => { + const { initializeApp } = require("firebase/app"); + const { getFirestore } = require("firebase/firestore"); + + const config = { + apiKey: "AIzaSyArvVh6VSdXicubcvIyuB-GZs8ua0m0DTI", + authDomain: "firestorequickstarts.firebaseapp.com", + projectId: "firestorequickstarts", + }; + const app = initializeApp(config, "solution-arrays"); + db = getFirestore(app); + }); + + describe("solution-arrays", () => { + it("should query in a category", async () => { + // [START query_in_category] + const { collection, getDocs, query, where } = require("firebase/firestore"); + + // Find all documents in the 'posts' collection that are + // in the 'cats' category. + const q = query(collection(db, "posts"), where("categories.cats", "==", true)); + const docs = await getDocs(q); + // ... + // [END query_in_category] + }); + + it("should query in a category by timestamp", () => { + function queryOne() { + // [START query_in_category_timestamp_invalid] + const { collection, query, where, orderBy, FirebaseFirestore } = require("@firebase/firestore"); + + const q = query(collection(db, "posts"), + where("categories.cats", "==", true), + orderBy("timestamp")); + // [END query_in_category_timestamp_invalid] + } + + + function queryTwo() { + // [START query_in_category_timestamp] + const { collection, query, where, orderBy } = require("@firebase/firestore"); + + const q = query(collection(db, "posts"), + where("categories.cats", ">", 0), + orderBy("categories.cats")); + // [END query_in_category_timestamp] + } + + }); + }); +}); diff --git a/firestore-next/test.solution-counters.js b/firestore-next/test.solution-counters.js new file mode 100644 index 00000000..ab9eae36 --- /dev/null +++ b/firestore-next/test.solution-counters.js @@ -0,0 +1,97 @@ +// [SNIPPETS_SEPARATION enabled] +const { FirebaseFirestore } = require('firebase/firestore'); + +/** @type {FirebaseFirestore} */ +let db; + +// [START create_counter] +function createCounter(ref, num_shards) { + const { collection, doc, writeBatch } = require("firebase/firestore"); + + const batch = writeBatch(db); + + // Initialize the counter document + batch.set(ref, { num_shards: num_shards }); + + // Initialize each shard with count=0 + for (let i = 0; i < num_shards; i++) { + const shardRef = doc(collection(ref, 'shards'), i.toString()); + batch.set(shardRef, { count: 0 }); + } + + // Commit the write batch + return batch.commit(); +} +// [END create_counter] + +// [START increment_counter] +function incrementCounter(db, ref, num_shards) { + const { collection, doc, updateDoc, increment, FirebaseFirestore } = require("@firebase/firestore"); + + // Select a shard of the counter at random + const shardId = Math.floor(Math.random() * num_shards).toString(); + const shardRef = doc(collection(ref, 'shards'), shardId); + + // Update count + return updateDoc(shardRef, "count", increment(1)); +} +// [END increment_counter] + +// [START get_count] +async function getCount(ref) { + const { collection, getDocs } = require("@firebase/firestore"); + + // Sum the count of each shard in the subcollection + const snapshot = await getDocs(collection(ref, 'shards')); + + let totalCount = 0; + snapshot.forEach(doc => { + totalCount += doc.data().count; + }); + + return totalCount; +} +// [END get_count] + +describe("firestore-solution-counters", () => { + before(() => { + const { initializeApp } = require("firebase/app"); + const { getFirestore } = require("firebase/firestore"); + + const config = { + apiKey: "AIzaSyArvVh6VSdXicubcvIyuB-GZs8ua0m0DTI", + authDomain: "firestorequickstarts.firebaseapp.com", + projectId: "firestorequickstarts", + }; + const app = initializeApp(config, "solution-arrays"); + db = getFirestore(app); + }); + + describe("solution-counters", () => { + it("should create a counter", () => { + // Create a counter with 10 shards + const { collection, doc } = require("firebase/firestore"); + + return createCounter(doc(collection(db, 'counters')), 10); + }); + + it("should increment a counter", async () => { + // Create a counter, then increment it + const { collection, doc } = require("firebase/firestore"); + + const ref = doc(collection(db, 'counters')); + await createCounter(ref, 10) + await incrementCounter(db, ref, 10); + }); + + it("should get the count of a counter", async () => { + // Create a counter, increment it, then get the count + const { collection, doc } = require("firebase/firestore"); + + const ref = doc(collection(db, 'counters')); + await createCounter(ref, 10); + await incrementCounter(db, ref, 10); + await getCount(ref); + }); + }); +}); diff --git a/firestore/package.json b/firestore/package.json index e2779ec4..d9e1b2d6 100644 --- a/firestore/package.json +++ b/firestore/package.json @@ -6,7 +6,7 @@ }, "license": "Apache-2.0", "dependencies": { - "firebase": "^7.17.1" + "firebase": "^7.20.0" }, "devDependencies": { "@types/chai": "^4.2.12", diff --git a/firestore/test.firestore.js b/firestore/test.firestore.js index f5131965..b0dedce8 100644 --- a/firestore/test.firestore.js +++ b/firestore/test.firestore.js @@ -1,7 +1,8 @@ -var firebase = require('firebase/app'); -const { expect } = require('chai'); +const firebase = require('firebase/app'); require('firebase/firestore'); +const { expect } = require('chai'); + // [START city_custom_object] class City { constructor (name, state, country ) { diff --git a/functions-next/.gitignore b/functions-next/.gitignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/functions-next/.gitignore @@ -0,0 +1 @@ +dist diff --git a/functions-next/emulator-suite.js b/functions-next/emulator-suite.js new file mode 100644 index 00000000..c2e1b535 --- /dev/null +++ b/functions-next/emulator-suite.js @@ -0,0 +1,32 @@ +// [SNIPPETS_SEPARATION enabled] +import { initializeApp } from "firebase/app"; + +initializeApp({ + projectId: '### CLOUD FUNCTIONS PROJECT ID ###', + apiKey: '### FIREBASE API KEY ###', + authDomain: '### FIREBASE AUTH DOMAIN ###', +}); + +export function emulatorSettings() { + // [START functions_emulator_connect] + const { getApp } = require("firebase/app"); + const { getFunctions, useFunctionsEmulator } = require( "firebase/functions"); + + const functions = getFunctions(getApp()); + useFunctionsEmulator(functions, "http://localhost:5001"); + // [END functions_emulator_connect] +} + +export async function callFunction() { + // [START functions_callable_call] + const { getApp } = require("firebase/app"); + const { getFunctions, httpsCallable } = require( "firebase/functions"); + + const functions = getFunctions(getApp()); + const addMessage = httpsCallable(functions, 'addMessage'); + + const result = await addMessage({ text: ''}); + const sanitizedMessage = result.data.text; + // ... + // [END functions_callable_call] +} diff --git a/functions-next/package.json b/functions-next/package.json new file mode 100644 index 00000000..cb5ad467 --- /dev/null +++ b/functions-next/package.json @@ -0,0 +1,12 @@ +{ + "name": "functions-next", + "version": "1.0.0", + "scripts": { + "build": "webpack", + "compile": "cp ../tsconfig.json.template ./tsconfig.json && tsc" + }, + "license": "Apache-2.0", + "dependencies": { + "firebase": "^0.800.3" + } +} diff --git a/lerna.json b/lerna.json index 1cf4129b..bf6d962e 100644 --- a/lerna.json +++ b/lerna.json @@ -5,7 +5,9 @@ "database", "firebaseapp", "firestore", + "firestore-next", "functions", + "functions-next", "installations" ], "version": "1.0.0" diff --git a/package.json b/package.json index 9d3c88b5..641918cb 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,14 @@ "name": "snippets-web", "version": "1.0.0", "scripts": { + "snippets": "npx ts-node scripts/separate-snippets.ts", "lerna-bootstrap": "lerna bootstrap --no-ci", "lerna-compile": "lerna run compile" }, "license": "Apache-2.0", "devDependencies": { "lerna": "^3.22.1", + "ts-node": "^9.0.0", "typescript": "^3.8.3" } } diff --git a/scripts/checkdirty.sh b/scripts/checkdirty.sh new file mode 100755 index 00000000..5d3c8bea --- /dev/null +++ b/scripts/checkdirty.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +git add . + +if [[ $(git diff --stat HEAD) != '' ]]; then + git diff --stat HEAD + echo + echo 'Error: git diff is dirty ... did you forget to run "npm run snippets" after adding snippets?' + exit 1 +else + echo 'Succes: git diff is clean' +fi diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 00000000..72b9bf11 --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,9 @@ +{ + "name": "scripts", + "version": "1.0.0", + "description": "Internal repo scripts", + "scripts": { + }, + "author": "samstern@google.com", + "license": "Apache-2.0" +} diff --git a/scripts/separate-snippets.ts b/scripts/separate-snippets.ts new file mode 100644 index 00000000..b5d5d9d1 --- /dev/null +++ b/scripts/separate-snippets.ts @@ -0,0 +1,197 @@ +import * as cp from "child_process"; +import * as fs from "fs"; +import * as path from "path"; + +// Regex for comment which must be included in a file for it to be separated +const RE_SNIPPETS_SEPARATION = /\[SNIPPETS_SEPARATION\s+enabled\]/; + +// Regex for comment to control the separator prefix +const RE_SNIPPETS_PREFIX = /\[SNIPPETS_PREFIX\s+([A-Za-z0-9_]+)\]/; + +// Regex for [START] and [END] snippet tags. +const RE_START_SNIPPET = /\[START\s+([A-Za-z_]+)\s*\]/; +const RE_END_SNIPPET = /\[END\s+([A-Za-z_]+)\s*\]/; + +// Regex for const = require statements +// TODO: Handle multiline imports? +const RE_REQUIRE = /const {(.+?)} = require\((.+?)\)/; + +type SnippetsConfig = { + enabled: boolean; + prefix: string; + map: Record; +}; + +const DEFAULT_PREFIX = "modular_"; + +function isBlank(line: string) { + return line.trim().length === 0; +} + +/** + * Turns a series of source lines into a standalone snippet file by: + * - Converting require statements into top-level imports. + * - Adjusting indentation to left-align all content + * - Removing any blank lines at the starts + * - Adding a prefix to snippet names + * + * @param lines the lines containing the snippet (including START/END comments) + * @param sourceFile the source file where the original snippet lives + * @param snippetPrefix the prefix (such as modular_) + */ +function processSnippet( + lines: string[], + sourceFile: string, + snippetPrefix: string +): string { + const outputLines: string[] = []; + + for (const line of lines) { + if (line.match(RE_REQUIRE)) { + outputLines.push(line.replace(RE_REQUIRE, `import {$1} from $2`)); + } else if (line.match(RE_START_SNIPPET)) { + outputLines.push(line.replace(RE_START_SNIPPET, `[START ${snippetPrefix}$1]`)); + } else if (line.match(RE_END_SNIPPET)) { + outputLines.push( + line.replace(RE_END_SNIPPET, `[END ${snippetPrefix}$1]`) + ); + } else { + outputLines.push(line); + } + } + + // Adjust indentation of the otherLines so that they're left aligned + const nonBlankLines = outputLines.filter((l) => !isBlank(l)); + const indentSizes = nonBlankLines.map((l) => l.length - l.trimLeft().length); + const minIndent = Math.min(...indentSizes); + + const adjustedOutputLines: string[] = []; + for (const line of outputLines) { + if (isBlank(line)) { + adjustedOutputLines.push(""); + } else { + adjustedOutputLines.push(line.substr(minIndent)); + } + } + + // Special case: if the first line after the comments is blank we want to remove it + const firstNonComment = adjustedOutputLines.findIndex( + (l) => !l.startsWith("//") + ); + if (isBlank(outputLines[firstNonComment])) { + adjustedOutputLines.splice(firstNonComment, 1); + } + + const preambleLines = [ + `// This snippet file was generated by processing the source file:`, + `// ${sourceFile}`, + `//`, + `// To make edits to the snippets in this file, please edit the source`, + ``, + ]; + const content = [...preambleLines, ...adjustedOutputLines].join("\n"); + return content; +} + +/** + * Lists all the files in this repository that should be checked for snippets + */ +function listSnippetFiles(): string[] { + const output = cp + .execSync( + 'find . -type f -name "*.js" -not -path "*node_modules*" -not -path "./snippets*"' + ) + .toString(); + return output.split("\n").filter((x) => !isBlank(x)); +} + +/** + * Collect all the snippets from a file into a map of snippet name to lines. + * @param filePath the file path to read. + */ +function collectSnippets(filePath: string): SnippetsConfig { + const fileContents = fs.readFileSync(filePath).toString(); + const lines = fileContents.split("\n"); + + const config: SnippetsConfig = { + enabled: false, + prefix: DEFAULT_PREFIX, + map: {}, + }; + + config.enabled = lines.some((l) => !!l.match(RE_SNIPPETS_SEPARATION)); + if (!config.enabled) { + return config; + } + + const prefixLine = lines.find((l) => !!l.match(RE_SNIPPETS_PREFIX)); + if (prefixLine) { + const m = prefixLine.match(RE_SNIPPETS_PREFIX); + config.prefix = m[1]; + } + + let currSnippetName = ""; + let inSnippet = false; + for (const line of lines) { + const startMatch = line.match(RE_START_SNIPPET); + const endMatch = line.match(RE_END_SNIPPET); + + if (startMatch) { + inSnippet = true; + currSnippetName = startMatch[1]; + config.map[currSnippetName] = []; + } + + if (inSnippet) { + config.map[currSnippetName].push(line); + } + + if (endMatch) { + if (endMatch[1] !== currSnippetName) { + throw new Error( + `Snippet ${currSnippetName} in ${filePath} has unmatched START/END tags` + ); + } + inSnippet = false; + } + } + + return config; +} + +async function main() { + const fileNames = listSnippetFiles(); + + for (const filePath of fileNames) { + const config = collectSnippets(filePath); + if (!config.enabled) { + continue; + } + + const fileSlug = filePath + .replace(".js", "") + .replace("./", "") + .replace(/\./g, "-"); + const snippetDir = path.join("./snippets", fileSlug); + + console.log( + `Processing: ${filePath} --> ${snippetDir} (prefix=${config.prefix})` + ); + + if (!fs.existsSync(snippetDir)) { + fs.mkdirSync(snippetDir, { recursive: true }); + } + + for (const snippetName in config.map) { + const newFilePath = path.join(snippetDir, `${snippetName}.js`); + const content = processSnippet( + config.map[snippetName], + filePath, + config.prefix + ); + fs.writeFileSync(newFilePath, content); + } + } +} + +main(); diff --git a/scripts/test.sh b/scripts/test.sh deleted file mode 100755 index 7527670e..00000000 --- a/scripts/test.sh +++ /dev/null @@ -1,11 +0,0 @@ -set -e - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" - -# 0) Bootstrap -echo "Bootstrapping..." -npm run lerna-bootstrap - -# 1) "Compile" the code -echo "Compiling..." -npm run lerna-compile diff --git a/snippets/README.md b/snippets/README.md new file mode 100644 index 00000000..3819e7cf --- /dev/null +++ b/snippets/README.md @@ -0,0 +1,26 @@ +# Generated Snippets + +## Overview + +This directory contains snippets generated by the `separate-snippets` script. +Snippets in this folder should **never** be updated directly instead please +edit the source file (indicated by a comment at the top). + +## Regenerating the Snippets + +Run `npm run snippets` from the root of this repository. + +## Using the Separator + +For a file to be included in the separator script it must contain a comment like this: + +```js +// [SNIPPETS_SEPARATION enabled] +``` + +By default separated snippets will have their name prefixed with `modular_` +but you can override this with a commment: + +```js +// [SNIPPETS_PREFIX banana] +``` diff --git a/snippets/firestore-next/emulator-suite/fs_emulator_connect.js b/snippets/firestore-next/emulator-suite/fs_emulator_connect.js new file mode 100644 index 00000000..6b587d39 --- /dev/null +++ b/snippets/firestore-next/emulator-suite/fs_emulator_connect.js @@ -0,0 +1,19 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/emulator-suite.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_fs_emulator_connect] +import { initializeFirestore } from "firebase/firestore"; + +let settings = {}; +if (location.hostname === "localhost") { + settings = { + host: "localhost:8080", + ssl: false + }; +} + +// firebaseApps previously initialized using initializeApp() +const db = initializeFirestore(firebaseApp, settings); +// [END modular_fs_emulator_connect] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/add_ada_lovelace.js b/snippets/firestore-next/test-firestore/add_ada_lovelace.js new file mode 100644 index 00000000..5fa87298 --- /dev/null +++ b/snippets/firestore-next/test-firestore/add_ada_lovelace.js @@ -0,0 +1,19 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_add_ada_lovelace] +import { collection, addDoc } from "firebase/firestore"; + +try { + const docRef = await addDoc(collection(db, "users"), { + first: "Ada", + last: "Lovelace", + born: 1815 + }); + console.log("Document written with ID: ", docRef.id); +} catch (e) { + console.error("Error adding document: ", e); +} +// [END modular_add_ada_lovelace] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/add_alan_turing.js b/snippets/firestore-next/test-firestore/add_alan_turing.js new file mode 100644 index 00000000..300eaa00 --- /dev/null +++ b/snippets/firestore-next/test-firestore/add_alan_turing.js @@ -0,0 +1,22 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_add_alan_turing] +// Add a second document with a generated ID. +import { addDoc, collection } from "firebase/firestore"; + +try { + const docRef = await addDoc(collection(db, "users"), { + first: "Alan", + middle: "Mathison", + last: "Turing", + born: 1912 + }); + + console.log("Document written with ID: ", docRef.id); +} catch (e) { + console.error("Error adding document: ", e); +} +// [END modular_add_alan_turing] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/add_document.js b/snippets/firestore-next/test-firestore/add_document.js new file mode 100644 index 00000000..4f01313c --- /dev/null +++ b/snippets/firestore-next/test-firestore/add_document.js @@ -0,0 +1,15 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_add_document] +import { collection, addDoc } from "firebase/firestore"; + +// Add a new document with a generated id. +const docRef = await addDoc(collection(db, "cities"), { + name: "Tokyo", + country: "Japan" +}); +console.log("Document written with ID: ", docRef.id); +// [END modular_add_document] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/add_rating_transaction.js b/snippets/firestore-next/test-firestore/add_rating_transaction.js new file mode 100644 index 00000000..0c3a4281 --- /dev/null +++ b/snippets/firestore-next/test-firestore/add_rating_transaction.js @@ -0,0 +1,35 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_add_rating_transaction] +import { collection, doc, runTransaction} from "firebase/firestore"; + +async function addRating(restaurantRef, rating) { + // Create a reference for a new rating, for use inside the transaction + const ratingRef = doc(collection(restaurantRef, 'ratings')); + + // In a transaction, add the new rating and update the aggregate totals + await runTransaction(db, async (transaction) => { + const res = await transaction.get(restaurantRef); + if (!res.exists()) { + throw "Document does not exist!"; + } + + // Compute new number of ratings + const newNumRatings = res.data().numRatings + 1; + + // Compute new average rating + const oldRatingTotal = res.data().avgRating * res.data().numRatings; + const newAvgRating = (oldRatingTotal + rating) / newNumRatings; + + // Commit to Firestore + transaction.update(restaurantRef, { + numRatings: newNumRatings, + avgRating: newAvgRating + }); + transaction.set(ratingRef, { rating: rating }); + }); +} +// [END modular_add_rating_transaction] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/array_contains_any_filter.js b/snippets/firestore-next/test-firestore/array_contains_any_filter.js new file mode 100644 index 00000000..79046a5a --- /dev/null +++ b/snippets/firestore-next/test-firestore/array_contains_any_filter.js @@ -0,0 +1,11 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_array_contains_any_filter] +import { query, where } from "firebase/firestore"; + +const q = query(citiesRef, + where('regions', 'array-contains-any', ['west_coast', 'east_coast'])); +// [END modular_array_contains_any_filter] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/array_contains_filter.js b/snippets/firestore-next/test-firestore/array_contains_filter.js new file mode 100644 index 00000000..0771fc7a --- /dev/null +++ b/snippets/firestore-next/test-firestore/array_contains_filter.js @@ -0,0 +1,9 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_array_contains_filter] +import { query, where } from "firebase/firestore"; +const q = query(citiesRef, where("regions", "array-contains", "west_coast")); +// [END modular_array_contains_filter] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/chain_filters.js b/snippets/firestore-next/test-firestore/chain_filters.js new file mode 100644 index 00000000..993d7229 --- /dev/null +++ b/snippets/firestore-next/test-firestore/chain_filters.js @@ -0,0 +1,11 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_chain_filters] +import { query, where } from "firebase/firestore"; + +const q1 = query(citiesRef, where("state", "==", "CO"), where("name", "==", "Denver")); +const q2 = query(citiesRef, where("state", "==", "CA"), where("population", "<", 1000000)); +// [END modular_chain_filters] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/cities_document_set.js b/snippets/firestore-next/test-firestore/cities_document_set.js new file mode 100644 index 00000000..0950a138 --- /dev/null +++ b/snippets/firestore-next/test-firestore/cities_document_set.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_cities_document_set] +import { collection, doc, setDoc } from "firebase/firestore"; + +await setDoc(doc(collection(db, "cities"), "new-city-id"), data); +// [END modular_cities_document_set] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/city_custom_object.js b/snippets/firestore-next/test-firestore/city_custom_object.js new file mode 100644 index 00000000..c12b5c64 --- /dev/null +++ b/snippets/firestore-next/test-firestore/city_custom_object.js @@ -0,0 +1,32 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_city_custom_object] +class City { + constructor (name, state, country ) { + this.name = name; + this.state = state; + this.country = country; + } + toString() { + return this.name + ', ' + this.state + ', ' + this.country; + } +} + +// Firestore data converter +var cityConverter = { + toFirestore: function(city) { + return { + name: city.name, + state: city.state, + country: city.country + } + }, + fromFirestore: function(snapshot, options){ + const data = snapshot.data(options); + return new City(data.name, data.state, data.country) + } +} +// [END modular_city_custom_object] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/collection_reference.js b/snippets/firestore-next/test-firestore/collection_reference.js new file mode 100644 index 00000000..fb848405 --- /dev/null +++ b/snippets/firestore-next/test-firestore/collection_reference.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_collection_reference] +import { collection } from "firebase/firestore"; + +const usersCollectionRef = collection(db, 'users'); +// [END modular_collection_reference] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/data_types.js b/snippets/firestore-next/test-firestore/data_types.js new file mode 100644 index 00000000..e783c429 --- /dev/null +++ b/snippets/firestore-next/test-firestore/data_types.js @@ -0,0 +1,24 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_data_types] +import { doc, collection, setDoc, Timestamp } from "firebase/firestore"; + +const docData = { + stringExample: "Hello world!", + booleanExample: true, + numberExample: 3.14159265, + dateExample: Timestamp.fromDate(new Date("December 10, 1815")), + arrayExample: [5, true, "hello"], + nullExample: null, + objectExample: { + a: 5, + b: { + nested: "foo" + } + } +}; +await setDoc(doc(collection(db, "data"), "one"), docData); +// [END modular_data_types] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/delete_collection.js b/snippets/firestore-next/test-firestore/delete_collection.js new file mode 100644 index 00000000..78c73bbb --- /dev/null +++ b/snippets/firestore-next/test-firestore/delete_collection.js @@ -0,0 +1,48 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_delete_collection] +/** + * Delete a collection, in batches of batchSize. Note that this does + * not recursively delete subcollections of documents in the collection + */ +import { collection, query, orderBy, limit, getDocs, writeBatch } from "firebase/firestore"; + +function deleteCollection(db, collectionRef, batchSize) { + const q = query(collectionRef, orderBy('__name__'), limit(batchSize)) + + return new Promise(function(resolve) { + deleteQueryBatch(db, q, batchSize, resolve); + }); +} + +async function deleteQueryBatch(db, query, batchSize, resolve) { + const snapshot = await getDocs(query); + + // When there are no documents left, we are done + let numDeleted = 0; + if (snapshot.size > 0) { + // Delete documents in a batch + const batch = writeBatch(db); + snapshot.docs.forEach((doc) => { + batch.delete(doc.ref); + }); + + await batch.commit(); + numDeleted = snapshot.size; + } + + if (numDeleted < batchSize) { + resolve(); + return; + } + + // Recurse on the next process tick, to avoid + // exploding the stack. + setTimeout(() => { + deleteQueryBatch(db, query, batchSize, resolve); + }, 0); +} +// [END modular_delete_collection] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/delete_document.js b/snippets/firestore-next/test-firestore/delete_document.js new file mode 100644 index 00000000..aaa79012 --- /dev/null +++ b/snippets/firestore-next/test-firestore/delete_document.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_delete_document] +import { collection, doc, deleteDoc } from "firebase/firestore"; + +await deleteDoc(doc(collection(db, "cities"), "DC")); +// [END modular_delete_document] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/detach_listener.js b/snippets/firestore-next/test-firestore/detach_listener.js new file mode 100644 index 00000000..c22470cd --- /dev/null +++ b/snippets/firestore-next/test-firestore/detach_listener.js @@ -0,0 +1,18 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_detach_listener] +import { collection, onSnapshot } from "firebase/firestore"; + +const unsubscribe = onSnapshot(collection(db, "cities"), () => { + // Respond to data + // ... +}); + +// Later ... + +// Stop listening to changes +unsubscribe(); +// [END modular_detach_listener] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/disable_network.js b/snippets/firestore-next/test-firestore/disable_network.js new file mode 100644 index 00000000..ad9a62fb --- /dev/null +++ b/snippets/firestore-next/test-firestore/disable_network.js @@ -0,0 +1,15 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_disable_network] +import { disableNetwork } from "firebase/firestore"; + +await disableNetwork(db); +console.log("Network disabled!"); +// Do offline actions +// [START_EXCLUDE] +console.log("Network disabled!"); +// [END_EXCLUDE] +// [END modular_disable_network] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/doc_reference.js b/snippets/firestore-next/test-firestore/doc_reference.js new file mode 100644 index 00000000..ce16a0cb --- /dev/null +++ b/snippets/firestore-next/test-firestore/doc_reference.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_doc_reference] +import { collection, doc } from "firebase/firestore"; + +const alovelaceDocumentRef = doc(collection(db, 'users'), 'alovelace'); +// [END modular_doc_reference] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/doc_reference_alternative.js b/snippets/firestore-next/test-firestore/doc_reference_alternative.js new file mode 100644 index 00000000..fcef2915 --- /dev/null +++ b/snippets/firestore-next/test-firestore/doc_reference_alternative.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_doc_reference_alternative] +import { doc } from "firebase/firestore"; + +const alovelaceDocumentRef = doc(db, 'users/alovelace'); +// [END modular_doc_reference_alternative] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/enable_network.js b/snippets/firestore-next/test-firestore/enable_network.js new file mode 100644 index 00000000..031f90ab --- /dev/null +++ b/snippets/firestore-next/test-firestore/enable_network.js @@ -0,0 +1,14 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_enable_network] +import { enableNetwork } from "firebase/firestore"; + +await enableNetwork(db) +// Do online actions +// [START_EXCLUDE] +console.log("Network enabled!"); +// [END_EXCLUDE] +// [END modular_enable_network] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/example_data.js b/snippets/firestore-next/test-firestore/example_data.js new file mode 100644 index 00000000..1b1512c7 --- /dev/null +++ b/snippets/firestore-next/test-firestore/example_data.js @@ -0,0 +1,31 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_example_data] +import { collection, doc, setDoc } from "firebase/firestore"; + +const citiesRef = collection(db, "cities"); + +await setDoc(doc(citiesRef, "SF"), { + name: "San Francisco", state: "CA", country: "USA", + capital: false, population: 860000, + regions: ["west_coast", "norcal"] }); +await setDoc(doc(citiesRef, "LA"), { + name: "Los Angeles", state: "CA", country: "USA", + capital: false, population: 3900000, + regions: ["west_coast", "socal"] }); +await setDoc(doc(citiesRef, "DC"), { + name: "Washington, D.C.", state: null, country: "USA", + capital: true, population: 680000, + regions: ["east_coast"] }); +await setDoc(doc(citiesRef, "TOK"), { + name: "Tokyo", state: null, country: "Japan", + capital: true, population: 9000000, + regions: ["kanto", "honshu"] }); +await setDoc(doc(citiesRef, "BJ"), { + name: "Beijing", state: null, country: "China", + capital: true, population: 21500000, + regions: ["jingjinji", "hebei"] }); +// [END modular_example_data] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/example_filters.js b/snippets/firestore-next/test-firestore/example_filters.js new file mode 100644 index 00000000..c0a5eb05 --- /dev/null +++ b/snippets/firestore-next/test-firestore/example_filters.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_example_filters] +const q1 = query(citiesRef, where("state", "==", "CA")); +const q2 = query(citiesRef, where("population", "<", 100000)); +const q3 = query(citiesRef, where("name", ">=", "San Francisco")); +// [END modular_example_filters] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/filter_and_order.js b/snippets/firestore-next/test-firestore/filter_and_order.js new file mode 100644 index 00000000..2b4c7751 --- /dev/null +++ b/snippets/firestore-next/test-firestore/filter_and_order.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_filter_and_order] +import { query, where, orderBy, limit } from "firebase/firestore"; + +const q = query(citiesRef, where("population", ">", 100000), orderBy("population"), limit(2)); +// [END modular_filter_and_order] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/fs_collection_group_query.js b/snippets/firestore-next/test-firestore/fs_collection_group_query.js new file mode 100644 index 00000000..0fcf2d4e --- /dev/null +++ b/snippets/firestore-next/test-firestore/fs_collection_group_query.js @@ -0,0 +1,14 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_fs_collection_group_query] +import { collectionGroup, query, where, getDocs } from "firebase/firestore"; + +const museums = query(collectionGroup(db, 'landmarks'), where('type', '==', 'museum')); +const querySnapshot = await getDocs(museums); +querySnapshot.forEach((doc) => { + console.log(doc.id, ' => ', doc.data()); +}); +// [END modular_fs_collection_group_query] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/fs_collection_group_query_data_setup.js b/snippets/firestore-next/test-firestore/fs_collection_group_query_data_setup.js new file mode 100644 index 00000000..47fd6a4b --- /dev/null +++ b/snippets/firestore-next/test-firestore/fs_collection_group_query_data_setup.js @@ -0,0 +1,53 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_fs_collection_group_query_data_setup] +import { collection, doc, setDoc } from "firebase/firestore"; + +const citiesRef = collection(db, 'cities'); + +await Promise.all([ + setDoc(doc(collection(doc(citiesRef, 'SF'), 'landmarks')), { + name: 'Golden Gate Bridge', + type: 'bridge' + }), + setDoc(doc(collection(doc(citiesRef, 'SF'), 'landmarks')), { + name: 'Legion of Honor', + type: 'museum' + }), + setDoc(doc(collection(doc(citiesRef, 'LA'), 'landmarks')), { + name: 'Griffith Park', + type: 'park' + }), + setDoc(doc(collection(doc(citiesRef, 'LA'), 'landmarks')), { + name: 'The Getty', + type: 'museum' + }), + setDoc(doc(collection(doc(citiesRef, 'DC'), 'landmarks')), { + name: 'Lincoln Memorial', + type: 'memorial' + }), + setDoc(doc(collection(doc(citiesRef, 'DC'), 'landmarks')), { + name: 'National Air and Space Museum', + type: 'museum' + }), + setDoc(doc(collection(doc(citiesRef, 'TOK'), 'landmarks')), { + name: 'Ueno Park', + type: 'park' + }), + setDoc(doc(collection(doc(citiesRef, 'TOK'), 'landmarks')), { + name: 'National Museum of Nature and Science', + type: 'museum' + }), + setDoc(doc(collection(doc(citiesRef, 'BJ'), 'landmarks')), { + name: 'Jingshan Park', + type: 'park' + }), + setDoc(doc(collection(doc(citiesRef, 'BJ'), 'landmarks')), { + name: 'Beijing Ancient Observatory', + type: 'museum' + }) +]); +// [END modular_fs_collection_group_query_data_setup] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/fs_setup_cache.js b/snippets/firestore-next/test-firestore/fs_setup_cache.js new file mode 100644 index 00000000..4960b369 --- /dev/null +++ b/snippets/firestore-next/test-firestore/fs_setup_cache.js @@ -0,0 +1,12 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_fs_setup_cache] +import { initializeFirestore, CACHE_SIZE_UNLIMITED } from "firebase/firestore"; + +const firestoreDb = initializeFirestore(app, { + cacheSizeBytes: CACHE_SIZE_UNLIMITED +}); +// [END modular_fs_setup_cache] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/get_all_users.js b/snippets/firestore-next/test-firestore/get_all_users.js new file mode 100644 index 00000000..57585bda --- /dev/null +++ b/snippets/firestore-next/test-firestore/get_all_users.js @@ -0,0 +1,13 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_get_all_users] +import { collection, getDocs } from "firebase/firestore"; + +const querySnapshot = await getDocs(collection(db, "users")); +querySnapshot.forEach((doc) => { + console.log(`${doc.id} => ${doc.data()}`); +}); +// [END modular_get_all_users] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/get_custom_object.js b/snippets/firestore-next/test-firestore/get_custom_object.js new file mode 100644 index 00000000..abbf98eb --- /dev/null +++ b/snippets/firestore-next/test-firestore/get_custom_object.js @@ -0,0 +1,19 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_get_custom_object] +import { doc, collection, getDoc} from "firebase/firestore"; + +const ref = doc(collection(db, "cities"), "LA").withConverter(cityConverter); +const docSnap = await getDoc(ref); +if (docSnap.exists()) { + // Convert to City object + const city = docSnap.data(); + // Use a City instance method + console.log(city.toString()); +} else { + console.log("No such document!") +} +// [END modular_get_custom_object] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/get_document.js b/snippets/firestore-next/test-firestore/get_document.js new file mode 100644 index 00000000..9ff21ba6 --- /dev/null +++ b/snippets/firestore-next/test-firestore/get_document.js @@ -0,0 +1,18 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_get_document] +import { collection, doc, getDoc } from "firebase/firestore"; + +const docRef = doc(collection(db, "cities"), "SF"); +const docSnap = await getDoc(docRef); + +if (docSnap.exists()) { + console.log("Document data:", docSnap.data()); +} else { + // doc.data() will be undefined in this case + console.log("No such document!"); +} +// [END modular_get_document] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/get_document_options.js b/snippets/firestore-next/test-firestore/get_document_options.js new file mode 100644 index 00000000..30c0202a --- /dev/null +++ b/snippets/firestore-next/test-firestore/get_document_options.js @@ -0,0 +1,21 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_get_document_options] +import { collection, doc, getDocFromCache } from "firebase/firestore"; + +const docRef = doc(collection(db, "cities"), "SF"); + +// Get a document, forcing the SDK to fetch from the offline cache. +try { + const doc = await getDocFromCache(docRef); + + // Document was found in the cache. If no cached document exists, + // an error will be returned to the 'catch' block below. + console.log("Cached document data:", doc.data()); +} catch (e) { + console.log("Error getting cached document:", e); +} +// [END modular_get_document_options] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/get_multiple.js b/snippets/firestore-next/test-firestore/get_multiple.js new file mode 100644 index 00000000..520765cf --- /dev/null +++ b/snippets/firestore-next/test-firestore/get_multiple.js @@ -0,0 +1,16 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_get_multiple] +import { collection, query, where, getDocs } from "firebase/firestore"; + +const q = query(collection(db, "cities"), where("capital", "==", true)); + +const querySnapshot = await getDocs(q); +querySnapshot.forEach((doc) => { + // doc.data() is never undefined for query doc snapshots + console.log(doc.id, " => ", doc.data()); +}); +// [END modular_get_multiple] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/get_multiple_all.js b/snippets/firestore-next/test-firestore/get_multiple_all.js new file mode 100644 index 00000000..b306e657 --- /dev/null +++ b/snippets/firestore-next/test-firestore/get_multiple_all.js @@ -0,0 +1,14 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_get_multiple_all] +import { collection, getDocs } from "firebase/firestore"; + +const querySnapshot = await getDocs(collection(db, "cities")); +querySnapshot.forEach((doc) => { + // doc.data() is never undefined for query doc snapshots + console.log(doc.id, " => ", doc.data()); +}); +// [END modular_get_multiple_all] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/handle_listen_errors.js b/snippets/firestore-next/test-firestore/handle_listen_errors.js new file mode 100644 index 00000000..76012826 --- /dev/null +++ b/snippets/firestore-next/test-firestore/handle_listen_errors.js @@ -0,0 +1,17 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_handle_listen_errors] +import { collection, onSnapshot } from "firebase/firestore"; + +const unsubscribe = onSnapshot( + collection(db, "cities"), + (snapshot) => { + // ... + }, + (error) => { + // ... + }); +// [END modular_handle_listen_errors] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/in_filter.js b/snippets/firestore-next/test-firestore/in_filter.js new file mode 100644 index 00000000..4e752152 --- /dev/null +++ b/snippets/firestore-next/test-firestore/in_filter.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_in_filter] +import { query, where } from "firebase/firestore"; + +const q = query(citiesRef, where('country', 'in', ['USA', 'Japan'])); +// [END modular_in_filter] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/in_filter_with_array.js b/snippets/firestore-next/test-firestore/in_filter_with_array.js new file mode 100644 index 00000000..b3f1574c --- /dev/null +++ b/snippets/firestore-next/test-firestore/in_filter_with_array.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_in_filter_with_array] +import { query, where } from "firebase/firestore"; + +const q = query(citiesRef, where('regions', 'in', [['west_coast', 'east_coast']])); +// [END modular_in_filter_with_array] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/initialize_persistence.js b/snippets/firestore-next/test-firestore/initialize_persistence.js new file mode 100644 index 00000000..174539a1 --- /dev/null +++ b/snippets/firestore-next/test-firestore/initialize_persistence.js @@ -0,0 +1,22 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_initialize_persistence] +import { enableIndexedDbPersistence } from "firebase/firestore"; + +enableIndexedDbPersistence(db) + .catch((err) => { + if (err.code == 'failed-precondition') { + // Multiple tabs open, persistence can only be enabled + // in one tab at a a time. + // ... + } else if (err.code == 'unimplemented') { + // The current browser does not support all of the + // features required to enable persistence + // ... + } + }); +// Subsequent queries will use persistence, if it was enabled successfully +// [END modular_initialize_persistence] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/invalid_filter_and_order.js b/snippets/firestore-next/test-firestore/invalid_filter_and_order.js new file mode 100644 index 00000000..f24b3766 --- /dev/null +++ b/snippets/firestore-next/test-firestore/invalid_filter_and_order.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_invalid_filter_and_order] +import { query, where, orderBy } from "firebase/firestore"; + +const q = query(citiesRef, where("population", ">", 100000), orderBy("country")); +// [END modular_invalid_filter_and_order] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/invalid_range_filters.js b/snippets/firestore-next/test-firestore/invalid_range_filters.js new file mode 100644 index 00000000..d622ec8f --- /dev/null +++ b/snippets/firestore-next/test-firestore/invalid_range_filters.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_invalid_range_filters] +import { query, where } from "firebase/firestore"; + +const q = query(citiesRef, where("state", ">=", "CA"), where("population", ">", 100000)); +// [END modular_invalid_range_filters] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/listen_diffs.js b/snippets/firestore-next/test-firestore/listen_diffs.js new file mode 100644 index 00000000..96fba665 --- /dev/null +++ b/snippets/firestore-next/test-firestore/listen_diffs.js @@ -0,0 +1,23 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_listen_diffs] +import { collection, query, where, onSnapshot } from "firebase/firestore"; + +const q = query(collection(db, "cities"), where("state", "==", "CA")); +const unsubscribe = onSnapshot(q, (snapshot) => { + snapshot.docChanges().forEach((change) => { + if (change.type === "added") { + console.log("New city: ", change.doc.data()); + } + if (change.type === "modified") { + console.log("Modified city: ", change.doc.data()); + } + if (change.type === "removed") { + console.log("Removed city: ", change.doc.data()); + } + }); +}); +// [END modular_listen_diffs] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/listen_document.js b/snippets/firestore-next/test-firestore/listen_document.js new file mode 100644 index 00000000..3900cba7 --- /dev/null +++ b/snippets/firestore-next/test-firestore/listen_document.js @@ -0,0 +1,12 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_listen_document] +import { collection, doc, onSnapshot } from "firebase/firestore"; + +const unsub = onSnapshot(doc(collection(db, "cities"), "SF"), (doc) => { + console.log("Current data: ", doc.data()); +}); +// [END modular_listen_document] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/listen_document_local.js b/snippets/firestore-next/test-firestore/listen_document_local.js new file mode 100644 index 00000000..09e3b56d --- /dev/null +++ b/snippets/firestore-next/test-firestore/listen_document_local.js @@ -0,0 +1,13 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_listen_document_local] +import { collection, doc, onSnapshot } from "firebase/firestore"; + +const unsub = onSnapshot(doc(collection(db, "cities"), "SF"), (doc) => { + const source = doc.metadata.hasPendingWrites ? "Local" : "Server"; + console.log(source, " data: ", doc.data()); +}); +// [END modular_listen_document_local] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/listen_for_users.js b/snippets/firestore-next/test-firestore/listen_for_users.js new file mode 100644 index 00000000..32c9a602 --- /dev/null +++ b/snippets/firestore-next/test-firestore/listen_for_users.js @@ -0,0 +1,16 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_listen_for_users] +import { collection, where, query, onSnapshot } from "firebase/firestore"; + +const q = query(collection(db, "users"), where("born", "<", 1900)); +const unsubscribe = onSnapshot(q, (snapshot) => { + console.log("Current users born before 1900:"); + snapshot.forEach(function (userSnapshot) { + console.log(userSnapshot.data()) + }); +}); +// [END modular_listen_for_users] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/listen_multiple.js b/snippets/firestore-next/test-firestore/listen_multiple.js new file mode 100644 index 00000000..a5377c57 --- /dev/null +++ b/snippets/firestore-next/test-firestore/listen_multiple.js @@ -0,0 +1,17 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_listen_multiple] +import { collection, query, where, onSnapshot } from "firebase/firestore"; + +const q = query(collection(db, "cities"), where("state", "==", "CA")); +const unsubscribe = onSnapshot(q, (querySnapshot) => { + const cities = []; + querySnapshot.forEach((doc) => { + cities.push(doc.data().name); + }); + console.log("Current cities in CA: ", cities.join(", ")); +}); +// [END modular_listen_multiple] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/listen_with_metadata.js b/snippets/firestore-next/test-firestore/listen_with_metadata.js new file mode 100644 index 00000000..93fc59b1 --- /dev/null +++ b/snippets/firestore-next/test-firestore/listen_with_metadata.js @@ -0,0 +1,15 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_listen_with_metadata] +import { collection, doc, onSnapshot } from "firebase/firestore"; + +const unsub = onSnapshot( + doc(collection(db, "cities"), "SF"), + { includeMetadataChanges: true }, + (doc) => { + // ... + }); +// [END modular_listen_with_metadata] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/new_document.js b/snippets/firestore-next/test-firestore/new_document.js new file mode 100644 index 00000000..c854d854 --- /dev/null +++ b/snippets/firestore-next/test-firestore/new_document.js @@ -0,0 +1,14 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_new_document] +import { collection, doc, setDoc } from "firebase/firestore"; + +// Add a new document with a generated id +const newCityRef = doc(collection(db, "cities")); + +// later... +await setDoc(newCityRef, data); +// [END modular_new_document] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/order_and_end.js b/snippets/firestore-next/test-firestore/order_and_end.js new file mode 100644 index 00000000..23184342 --- /dev/null +++ b/snippets/firestore-next/test-firestore/order_and_end.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_order_and_end] +import { query, orderBy, endAt } from "firebase/firestore"; + +const q = query(citiesRef, orderBy("population"), endAt(1000000)); +// [END modular_order_and_end] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/order_and_limit.js b/snippets/firestore-next/test-firestore/order_and_limit.js new file mode 100644 index 00000000..344e231d --- /dev/null +++ b/snippets/firestore-next/test-firestore/order_and_limit.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_order_and_limit] +import { query, orderBy, limit } from "firebase/firestore"; + +const q = query(citiesRef, orderBy("name"), limit(3)); +// [END modular_order_and_limit] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/order_and_limit_desc.js b/snippets/firestore-next/test-firestore/order_and_limit_desc.js new file mode 100644 index 00000000..13dee6bc --- /dev/null +++ b/snippets/firestore-next/test-firestore/order_and_limit_desc.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_order_and_limit_desc] +import { query, orderBy, limit } from "firebase/firestore"; + +const q = query(citiesRef, orderBy("name", "desc"), limit(3)); +// [END modular_order_and_limit_desc] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/order_and_start.js b/snippets/firestore-next/test-firestore/order_and_start.js new file mode 100644 index 00000000..76c73863 --- /dev/null +++ b/snippets/firestore-next/test-firestore/order_and_start.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_order_and_start] +import { query, orderBy, startAt } from "firebase/firestore"; + +const q = query(citiesRef, orderBy("population"), startAt(1000000)); +// [END modular_order_and_start] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/order_multiple.js b/snippets/firestore-next/test-firestore/order_multiple.js new file mode 100644 index 00000000..984b4c9a --- /dev/null +++ b/snippets/firestore-next/test-firestore/order_multiple.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_order_multiple] +import { query, orderBy } from "firebase/firestore"; + +const q = query(citiesRef, orderBy("state"), orderBy("population", "desc")); +// [END modular_order_multiple] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/paginate.js b/snippets/firestore-next/test-firestore/paginate.js new file mode 100644 index 00000000..bded9e4e --- /dev/null +++ b/snippets/firestore-next/test-firestore/paginate.js @@ -0,0 +1,23 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_paginate] +import { collection, query, orderBy, startAfter, limit, getDocs } from "firebase/firestore"; + +// Query the first page of docs +const first = query(collection(db, "cities"), orderBy("population"), limit(25)); +const documentSnapshots = await getDocs(first); + +// Get the last visible document +const lastVisible = documentSnapshots.docs[documentSnapshots.docs.length-1]; +console.log("last", lastVisible); + +// Construct a new query starting at this document, +// get the next 25 cities. +const next = query(collection(db, "cities"), + orderBy("population"), + startAfter(lastVisible), + limit(25)); +// [END modular_paginate] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/server_timestamp_resolution_options.js b/snippets/firestore-next/test-firestore/server_timestamp_resolution_options.js new file mode 100644 index 00000000..fcc8fbdd --- /dev/null +++ b/snippets/firestore-next/test-firestore/server_timestamp_resolution_options.js @@ -0,0 +1,26 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_server_timestamp_resolution_options] +import { collection, doc, updateDoc, serverTimestamp, onSnapshot } from "firebase/firestore"; +// Perform an update followed by an immediate read without +// waiting for the update to complete. Due to the snapshot +// options we will get two results: one with an estimate +// timestamp and one with the resolved server timestamp. +const docRef = doc(collection(db, 'objects'), 'some-id'); +updateDoc(docRef, { + timestamp: serverTimestamp() +}); + +onSnapshot(docRef, (snapshot) => { + const data = snapshot.data({ + // Options: 'estimate', 'previous', or 'none' + serverTimestamps: "estimate" + }); + console.log( + 'Timestamp: ' + data.timestamp + + ', pending: ' + snapshot.metadata.hasPendingWrites); +}); +// [END modular_server_timestamp_resolution_options] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/set_custom_object.js b/snippets/firestore-next/test-firestore/set_custom_object.js new file mode 100644 index 00000000..e76639b7 --- /dev/null +++ b/snippets/firestore-next/test-firestore/set_custom_object.js @@ -0,0 +1,12 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_set_custom_object] +import { doc, collection, setDoc } from "firebase/firestore"; + +// Set with cityConverter +const ref = doc(collection(db, "cities"), "LA").withConverter(cityConverter); +await setDoc(ref, new City("Los Angeles", "CA", "USA")); +// [END modular_set_custom_object] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/set_document.js b/snippets/firestore-next/test-firestore/set_document.js new file mode 100644 index 00000000..e6c7040b --- /dev/null +++ b/snippets/firestore-next/test-firestore/set_document.js @@ -0,0 +1,15 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_set_document] +import { doc, collection, setDoc } from "firebase/firestore"; + +// Add a new document in collection "cities" +await setDoc(doc(collection(db, "cities"), "LA"), { + name: "Los Angeles", + state: "CA", + country: "USA" +}); +// [END modular_set_document] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/set_with_merge.js b/snippets/firestore-next/test-firestore/set_with_merge.js new file mode 100644 index 00000000..8957fe29 --- /dev/null +++ b/snippets/firestore-next/test-firestore/set_with_merge.js @@ -0,0 +1,11 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_set_with_merge] +import { doc, collection, setDoc } from "firebase/firestore"; + +const cityRef = doc(collection(db, 'cities'), 'BJ'); +setDoc(cityRef, { capital: true }, { merge: true }); +// [END modular_set_with_merge] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/simple_queries.js b/snippets/firestore-next/test-firestore/simple_queries.js new file mode 100644 index 00000000..90449071 --- /dev/null +++ b/snippets/firestore-next/test-firestore/simple_queries.js @@ -0,0 +1,13 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_simple_queries] +// Create a reference to the cities collection +import { collection, query, where } from "firebase/firestore"; +const citiesRef = collection(db, "cities"); + +// Create a query against the collection. +const q = query(citiesRef, where("state", "==", "CA")); +// [END modular_simple_queries] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/simple_queries_again.js b/snippets/firestore-next/test-firestore/simple_queries_again.js new file mode 100644 index 00000000..41616318 --- /dev/null +++ b/snippets/firestore-next/test-firestore/simple_queries_again.js @@ -0,0 +1,11 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_simple_queries_again] +import { collection, query, where } from "firebase/firestore"; +const citiesRef = collection(db, "cities"); + +const q = query(citiesRef, where("capital", "==", true)); +// [END modular_simple_queries_again] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/start_doc.js b/snippets/firestore-next/test-firestore/start_doc.js new file mode 100644 index 00000000..853a75ed --- /dev/null +++ b/snippets/firestore-next/test-firestore/start_doc.js @@ -0,0 +1,15 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_start_doc] +import { collection, doc, getDoc, query, orderBy, startAt } from "firebase/firestore"; +const citiesRef = collection(db, "cities"); + +const docSnap = await getDoc(doc(citiesRef, "SF")); + +// Get all cities with a population bigger than San Francisco +const biggerThanSf = query(citiesRef, orderBy("popuation"), startAt(docSnap)); +// ... +// [END modular_start_doc] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/start_multiple_orderby.js b/snippets/firestore-next/test-firestore/start_multiple_orderby.js new file mode 100644 index 00000000..a2782678 --- /dev/null +++ b/snippets/firestore-next/test-firestore/start_multiple_orderby.js @@ -0,0 +1,19 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_start_multiple_orderby] +// Will return all Springfields +import { collection, query, orderBy, startAt } from "firebase/firestore"; +const q1 = query(collection(db, "cities"), + orderBy("name"), + orderBy("state"), + startAt("Springfield")); + +// Will return "Springfield, Missouri" and "Springfield, Wisconsin" +const q2 = query(collection(db, "cities"), + orderBy("name"), + orderBy("state"), + startAt("Springfield", "Missouri")); +// [END modular_start_multiple_orderby] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/subcollection_reference.js b/snippets/firestore-next/test-firestore/subcollection_reference.js new file mode 100644 index 00000000..5ea77ae4 --- /dev/null +++ b/snippets/firestore-next/test-firestore/subcollection_reference.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_subcollection_reference] +import { doc, collection } from "firebase/firestore"; + +const messageRef = doc(collection(doc(collection(db, "rooms"), "roomA"), "messages"), "message1"); +// [END modular_subcollection_reference] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/transaction.js b/snippets/firestore-next/test-firestore/transaction.js new file mode 100644 index 00000000..0591ade9 --- /dev/null +++ b/snippets/firestore-next/test-firestore/transaction.js @@ -0,0 +1,23 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_transaction] +import { runTransaction } from "firebase/firestore"; + +try { + await runTransaction(db, async (transaction) => { + const sfDoc = await transaction.get(sfDocRef); + if (!sfDoc.exists()) { + throw "Document does not exist!"; + } + + const newPopulation = sfDoc.data().population + 1; + transaction.update(sfDocRef, { population: newPopulation }); + }); + console.log("Transaction successfully committed!"); +} catch (e) { + console.log("Transaction failed: ", e); +} +// [END modular_transaction] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/transaction_promise.js b/snippets/firestore-next/test-firestore/transaction_promise.js new file mode 100644 index 00000000..9d76243f --- /dev/null +++ b/snippets/firestore-next/test-firestore/transaction_promise.js @@ -0,0 +1,32 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_transaction_promise] +import { collection, doc, runTransaction } from "firebase/firestore"; + +// Create a reference to the SF doc. +const sfDocRef = doc(collection(db, "cities"), "SF"); + +try { + const newPopulation = await runTransaction(db, async (transaction) => { + const sfDoc = await transaction.get(sfDocRef); + if (!sfDoc.exists()) { + throw "Document does not exist!"; + } + + const newPop = sfDoc.data().population + 1; + if (newPop <= 1000000) { + transaction.update(sfDocRef, { population: newPop }); + } else { + return Promise.reject("Sorry! Population is too big"); + } + }); + + console.log("Population increased to ", newPopulation); +} catch (e) { + // This will be a "population is too big" error. + console.error(e); +} +// [END modular_transaction_promise] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/update_delete_field.js b/snippets/firestore-next/test-firestore/update_delete_field.js new file mode 100644 index 00000000..007db2d3 --- /dev/null +++ b/snippets/firestore-next/test-firestore/update_delete_field.js @@ -0,0 +1,15 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_update_delete_field] +import { doc, collection, updateDoc, deleteField } from "firebase/firestore"; + +const cityRef = doc(collection(db, 'cities'), 'BJ'); + +// Remove the 'capital' field from the document +await updateDoc(cityRef, { + capital: deleteField() +}); +// [END modular_update_delete_field] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/update_document.js b/snippets/firestore-next/test-firestore/update_document.js new file mode 100644 index 00000000..eb17b744 --- /dev/null +++ b/snippets/firestore-next/test-firestore/update_document.js @@ -0,0 +1,15 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_update_document] +import { collection, doc, updateDoc } from "firebase/firestore"; + +const washingtonRef = doc(collection(db, "cities"), "DC"); + +// Set the "capital" field of the city 'DC' +await updateDoc(washingtonRef, { + capital: true +}); +// [END modular_update_document] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/update_document_array.js b/snippets/firestore-next/test-firestore/update_document_array.js new file mode 100644 index 00000000..39c59bdb --- /dev/null +++ b/snippets/firestore-next/test-firestore/update_document_array.js @@ -0,0 +1,20 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_update_document_array] +import { collection, doc, updateDoc, arrayUnion, arrayRemove } from "firebase/firestore"; + +const washingtonRef = doc(collection(db, "cities"), "DC"); + +// Atomically add a new region to the "regions" array field. +await updateDoc(washingtonRef, { + regions: arrayUnion("greater_virginia") +}); + +// Atomically remove a region from the "regions" array field. +await updateDoc(washingtonRef, { + regions: arrayRemove("east_coast") +}); +// [END modular_update_document_array] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/update_document_increment.js b/snippets/firestore-next/test-firestore/update_document_increment.js new file mode 100644 index 00000000..05ff44eb --- /dev/null +++ b/snippets/firestore-next/test-firestore/update_document_increment.js @@ -0,0 +1,15 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_update_document_increment] +import { collection, doc, updateDoc, increment } from "firebase/firestore"; + +const washingtonRef = doc(collection(db, "cities"), "DC"); + +// Atomically increment the population of the city by 50. +await updateDoc(washingtonRef, { + population: increment(50) +}); +// [END modular_update_document_increment] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/update_document_nested.js b/snippets/firestore-next/test-firestore/update_document_nested.js new file mode 100644 index 00000000..0efd286c --- /dev/null +++ b/snippets/firestore-next/test-firestore/update_document_nested.js @@ -0,0 +1,22 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_update_document_nested] +import { doc, collection, setDoc, updateDoc } from "firebase/firestore"; + +// Create an initial document to update. +const frankDocRef = doc(collection(db, "users"), "frank"); +await setDoc(frankDocRef, { + name: "Frank", + favorites: { food: "Pizza", color: "Blue", subject: "recess" }, + age: 12 +}); + +// To update age and favorite color: +await updateDoc(frankDocRef, { + "age": 13, + "favorites.color": "Red" +}); +// [END modular_update_document_nested] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/update_with_server_timestamp.js b/snippets/firestore-next/test-firestore/update_with_server_timestamp.js new file mode 100644 index 00000000..3fa9febc --- /dev/null +++ b/snippets/firestore-next/test-firestore/update_with_server_timestamp.js @@ -0,0 +1,15 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_update_with_server_timestamp] +import { collection, updateDoc, serverTimestamp } from "firebase/firestore"; + +const docRef = doc(collection(db, 'objects'), 'some-id'); + +// Update the timestamp field with the value from the server +const updateTimestamp = await updateDoc(docRef, { + timestamp: serverTimestamp() +}); +// [END modular_update_with_server_timestamp] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/use_from_cache.js b/snippets/firestore-next/test-firestore/use_from_cache.js new file mode 100644 index 00000000..dc689d8a --- /dev/null +++ b/snippets/firestore-next/test-firestore/use_from_cache.js @@ -0,0 +1,20 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_use_from_cache] +import { collection, onSnapshot, where, query } from "firebase/firestore"; + +const q = query(collection(db, "cities"), where("state", "==", "CA")); +onSnapshot(q, { includeMetadataChanges: true }, (snapshot) => { + snapshot.docChanges().forEach((change) => { + if (change.type === "added") { + console.log("New city: ", change.doc.data()); + } + + const source = snapshot.metadata.fromCache ? "local cache" : "server"; + console.log("Data came from " + source); + }); +}); +// [END modular_use_from_cache] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/valid_filter_and_order.js b/snippets/firestore-next/test-firestore/valid_filter_and_order.js new file mode 100644 index 00000000..fc4d54f0 --- /dev/null +++ b/snippets/firestore-next/test-firestore/valid_filter_and_order.js @@ -0,0 +1,10 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_valid_filter_and_order] +import { query, where, orderBy } from "firebase/firestore"; + +const q = query(citiesRef, where("population", ">", 100000), orderBy("population")); +// [END modular_valid_filter_and_order] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/valid_range_filters.js b/snippets/firestore-next/test-firestore/valid_range_filters.js new file mode 100644 index 00000000..bd39bac2 --- /dev/null +++ b/snippets/firestore-next/test-firestore/valid_range_filters.js @@ -0,0 +1,11 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_valid_range_filters] +import { query, where } from "firebase/firestore"; + +const q1 = query(citiesRef, where("state", ">=", "CA"), where("state", "<=", "IN")); +const q2 = query(citiesRef, where("state", "==", "CA"), where("population", ">", 1000000)); +// [END modular_valid_range_filters] \ No newline at end of file diff --git a/snippets/firestore-next/test-firestore/write_batch.js b/snippets/firestore-next/test-firestore/write_batch.js new file mode 100644 index 00000000..605cdbb7 --- /dev/null +++ b/snippets/firestore-next/test-firestore/write_batch.js @@ -0,0 +1,26 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.firestore.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_write_batch] +import { writeBatch, doc, collection } from "firebase/firestore"; + +// Get a new write batch +const batch = writeBatch(db); + +// Set the value of 'NYC' +const nycRef = doc(collection(db, "cities"), "NYC"); +batch.set(nycRef, {name: "New York City"}); + +// Update the population of 'SF' +const sfRef = doc(collection(db, "cities"), "SF"); +batch.update(sfRef, {"population": 1000000}); + +// Delete the city 'LA' +const laRef = doc(collection(db, "cities"), "LA"); +batch.delete(laRef); + +// Commit the batch +await batch.commit(); +// [END modular_write_batch] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-aggregation/get_collection_ratings.js b/snippets/firestore-next/test-solution-aggregation/get_collection_ratings.js new file mode 100644 index 00000000..2b690134 --- /dev/null +++ b/snippets/firestore-next/test-solution-aggregation/get_collection_ratings.js @@ -0,0 +1,11 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-aggregation.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_get_collection_ratings] +import { collection, doc, getDocs } from "firebase/firestore"; + +const ratingsRef = collection(doc(collection(db, "restaurants"), "arinell-pizza"), "ratings"); +const ratingsDocs = await getDocs(ratingsRef); +// [END modular_get_collection_ratings] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-aggregation/sample_doc.js b/snippets/firestore-next/test-solution-aggregation/sample_doc.js new file mode 100644 index 00000000..2c25bc70 --- /dev/null +++ b/snippets/firestore-next/test-solution-aggregation/sample_doc.js @@ -0,0 +1,12 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-aggregation.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_sample_doc] +const arinellDoc = { + name: 'Arinell Pizza', + avgRating: 4.65, + numRatings: 683 +} +// [END modular_sample_doc] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-arrays/post_with_array.js b/snippets/firestore-next/test-solution-arrays/post_with_array.js new file mode 100644 index 00000000..2c3e5985 --- /dev/null +++ b/snippets/firestore-next/test-solution-arrays/post_with_array.js @@ -0,0 +1,16 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-arrays.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_post_with_array] +// Sample document in the 'posts' collection. +{ + title: "My great post", + categories: [ + "technology", + "opinion", + "cats" + ] +} +// [END modular_post_with_array] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-arrays/post_with_map.js b/snippets/firestore-next/test-solution-arrays/post_with_map.js new file mode 100644 index 00000000..59286b89 --- /dev/null +++ b/snippets/firestore-next/test-solution-arrays/post_with_map.js @@ -0,0 +1,16 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-arrays.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_post_with_map] +// Sample document in the 'posts' collection +{ + title: "My great post", + categories: { + "technology": true, + "opinion": true, + "cats": true + } +} +// [END modular_post_with_map] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-arrays/post_with_map_advanced.js b/snippets/firestore-next/test-solution-arrays/post_with_map_advanced.js new file mode 100644 index 00000000..3553248c --- /dev/null +++ b/snippets/firestore-next/test-solution-arrays/post_with_map_advanced.js @@ -0,0 +1,16 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-arrays.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_post_with_map_advanced] +// The value of each entry in 'categories' is a unix timestamp +{ + title: "My great post", + categories: { + technology: 1502144665, + opinion: 1502144665, + cats: 1502144665 + } +} +// [END modular_post_with_map_advanced] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-arrays/query_in_category.js b/snippets/firestore-next/test-solution-arrays/query_in_category.js new file mode 100644 index 00000000..3388cdb5 --- /dev/null +++ b/snippets/firestore-next/test-solution-arrays/query_in_category.js @@ -0,0 +1,14 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-arrays.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_query_in_category] +import { collection, getDocs, query, where } from "firebase/firestore"; + +// Find all documents in the 'posts' collection that are +// in the 'cats' category. +const q = query(collection(db, "posts"), where("categories.cats", "==", true)); +const docs = await getDocs(q); +// ... +// [END modular_query_in_category] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-arrays/query_in_category_timestamp.js b/snippets/firestore-next/test-solution-arrays/query_in_category_timestamp.js new file mode 100644 index 00000000..6c6c0726 --- /dev/null +++ b/snippets/firestore-next/test-solution-arrays/query_in_category_timestamp.js @@ -0,0 +1,12 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-arrays.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_query_in_category_timestamp] +import { collection, query, where, orderBy } from "@firebase/firestore"; + +const q = query(collection(db, "posts"), + where("categories.cats", ">", 0), + orderBy("categories.cats")); +// [END modular_query_in_category_timestamp] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-arrays/query_in_category_timestamp_invalid.js b/snippets/firestore-next/test-solution-arrays/query_in_category_timestamp_invalid.js new file mode 100644 index 00000000..511acbfd --- /dev/null +++ b/snippets/firestore-next/test-solution-arrays/query_in_category_timestamp_invalid.js @@ -0,0 +1,12 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-arrays.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_query_in_category_timestamp_invalid] +import { collection, query, where, orderBy, FirebaseFirestore } from "@firebase/firestore"; + +const q = query(collection(db, "posts"), + where("categories.cats", "==", true), + orderBy("timestamp")); +// [END modular_query_in_category_timestamp_invalid] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-counters/create_counter.js b/snippets/firestore-next/test-solution-counters/create_counter.js new file mode 100644 index 00000000..4cc05cb9 --- /dev/null +++ b/snippets/firestore-next/test-solution-counters/create_counter.js @@ -0,0 +1,24 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-counters.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_create_counter] +function createCounter(ref, num_shards) { + import { collection, doc, writeBatch } from "firebase/firestore"; + + const batch = writeBatch(db); + + // Initialize the counter document + batch.set(ref, { num_shards: num_shards }); + + // Initialize each shard with count=0 + for (let i = 0; i < num_shards; i++) { + const shardRef = doc(collection(ref, 'shards'), i.toString()); + batch.set(shardRef, { count: 0 }); + } + + // Commit the write batch + return batch.commit(); +} +// [END modular_create_counter] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-counters/get_count.js b/snippets/firestore-next/test-solution-counters/get_count.js new file mode 100644 index 00000000..dcf61f55 --- /dev/null +++ b/snippets/firestore-next/test-solution-counters/get_count.js @@ -0,0 +1,20 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-counters.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_get_count] +async function getCount(ref) { + import { collection, getDocs } from "@firebase/firestore"; + + // Sum the count of each shard in the subcollection + const snapshot = await getDocs(collection(ref, 'shards')); + + let totalCount = 0; + snapshot.forEach(doc => { + totalCount += doc.data().count; + }); + + return totalCount; +} +// [END modular_get_count] \ No newline at end of file diff --git a/snippets/firestore-next/test-solution-counters/increment_counter.js b/snippets/firestore-next/test-solution-counters/increment_counter.js new file mode 100644 index 00000000..53f5f28a --- /dev/null +++ b/snippets/firestore-next/test-solution-counters/increment_counter.js @@ -0,0 +1,17 @@ +// This snippet file was generated by processing the source file: +// ./firestore-next/test.solution-counters.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_increment_counter] +function incrementCounter(db, ref, num_shards) { + import { collection, doc, updateDoc, increment, FirebaseFirestore } from "@firebase/firestore"; + + // Select a shard of the counter at random + const shardId = Math.floor(Math.random() * num_shards).toString(); + const shardRef = doc(collection(ref, 'shards'), shardId); + + // Update count + return updateDoc(shardRef, "count", increment(1)); +} +// [END modular_increment_counter] \ No newline at end of file diff --git a/snippets/functions-next/emulator-suite/functions_callable_call.js b/snippets/functions-next/emulator-suite/functions_callable_call.js new file mode 100644 index 00000000..ad4a3bdb --- /dev/null +++ b/snippets/functions-next/emulator-suite/functions_callable_call.js @@ -0,0 +1,16 @@ +// This snippet file was generated by processing the source file: +// ./functions-next/emulator-suite.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_functions_callable_call] +import { getApp } from "firebase/app"; +import { getFunctions, httpsCallable } from "firebase/functions"; + +const functions = getFunctions(getApp()); +const addMessage = httpsCallable(functions, 'addMessage'); + +const result = await addMessage({ text: ''}); +const sanitizedMessage = result.data.text; +// ... +// [END modular_functions_callable_call] \ No newline at end of file diff --git a/snippets/functions-next/emulator-suite/functions_emulator_connect.js b/snippets/functions-next/emulator-suite/functions_emulator_connect.js new file mode 100644 index 00000000..3ac3bfc8 --- /dev/null +++ b/snippets/functions-next/emulator-suite/functions_emulator_connect.js @@ -0,0 +1,12 @@ +// This snippet file was generated by processing the source file: +// ./functions-next/emulator-suite.js +// +// To make edits to the snippets in this file, please edit the source + +// [START modular_functions_emulator_connect] +import { getApp } from "firebase/app"; +import { getFunctions, useFunctionsEmulator } from "firebase/functions"; + +const functions = getFunctions(getApp()); +useFunctionsEmulator(functions, "http://localhost:5001"); +// [END modular_functions_emulator_connect] \ No newline at end of file