Skip to content

12/retrieve collection documents #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: collections
Choose a base branch
from
8 changes: 8 additions & 0 deletions src/Web/Firestore.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,11 @@ exports.addImpl = function (collectionRef, doc) {
return collectionRef.add(doc)
}
}

exports.getCollectionImpl = function (collectionRef, options) {
options = options === null ? undefined : options

return function () {
return collectionRef.get(options)
}
}
31 changes: 28 additions & 3 deletions src/Web/Firestore.purs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ module Web.Firestore
, DocumentSnapshot
, Firestore
, add
, clearCollection
, collection
, delete
, deleteApp
, doc
, docCollection
, firestore
, get
, getCollection
, initializeApp
, onSnapshot
, set
Expand All @@ -31,26 +33,29 @@ module Web.Firestore
) where

import Prelude
import Control.Promise (Promise)
import Control.Promise (Promise, toAff)
import Data.Argonaut (Json, encodeJson)
import Data.Either (Either(..))
import Data.Function.Uncurried (Fn1, Fn2, Fn3, Fn4, runFn1, runFn2, runFn3, runFn4)
import Data.Maybe (Maybe)
import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable, toNullable)
import Data.Profunctor.Choice ((+++))
import Effect (Effect)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)

import Web.Firestore.CollectionPath (CollectionPath)
import Web.Firestore.CollectionReference (CollectionReference)
import Web.Firestore.DocumentData (DocumentData)
import Web.Firestore.DocumentPath (DocumentPath)
import Web.Firestore.DocumentReference (DocumentReference)
import Web.Firestore.Error.FirestoreError (FirestoreError)
import Web.Firestore.Errors.FirebaseError (FirebaseError, fromFirebaseError, fromString)
import Web.Firestore.Errors.InitializeError (InitializeError)
import Web.Firestore.GetOptions (GetOptions)
import Web.Firestore.Options (Options)
import Web.Firestore.PartialObserver (PartialObserver)
import Web.Firestore.DocumentPath (DocumentPath)
import Web.Firestore.QuerySnapshot (QuerySnapshot, forEach, queryDocumentReference)
import Web.Firestore.SetOptions (SetOptions)
import Web.Firestore.SnapshotListenOptions (SnapshotListenOptions)
import Web.Firestore.SnapshotOptions (SnapshotOptions)
Expand Down Expand Up @@ -162,3 +167,23 @@ foreign import addImpl :: forall a. Fn2 (CollectionReference a) a (Effect (Promi

add :: forall a. CollectionReference a -> a -> Effect (Promise (DocumentReference a))
add = runFn2 addImpl

foreign import getCollectionImpl :: forall a. Fn2
(CollectionReference a)
(Nullable Json)
(Effect (Promise (QuerySnapshot a)))

getCollection :: forall a. CollectionReference a -> Maybe GetOptions -> Effect (Promise (QuerySnapshot a))
getCollection collectionRef options = runFn2 getCollectionImpl collectionRef (toNullable $ encodeJson <$> options)

-- | deleting data from a web client is not recommended. You should avoid to use this function
-- |
-- | see https://firebase.google.com/docs/firestore/manage-data/delete-data for more details
clearCollection :: forall a. CollectionReference a -> Aff Unit
clearCollection collectionRef = do
getPromise <- liftEffect $ getCollection collectionRef Nothing
querySnapshot <- toAff getPromise
forEach querySnapshot (\queryDocumentSnapshot -> do
let queryDocRef = queryDocumentReference queryDocumentSnapshot
deletePromise <- liftEffect $ delete queryDocRef
toAff deletePromise)
19 changes: 19 additions & 0 deletions src/Web/Firestore/QuerySnapshot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use strict";

exports.forEachImpl = function (snapshot, callback) {
return function () {
return snapshot.forEach(function (documentSnapshot) {
return callback(documentSnapshot)()
})
}
}

exports.queryDocumentDataImpl = function (documentSnapshot, options) {
options = options === null ? undefined : options

return documentSnapshot.data()
}

exports.queryDocumentReferenceImpl = function (documentSnapshot) {
return documentSnapshot.ref
}
31 changes: 31 additions & 0 deletions src/Web/Firestore/QuerySnapshot.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module Web.Firestore.QuerySnapshot where

import Prelude

import Data.Function.Uncurried (Fn1, Fn2, runFn1, runFn2)
import Data.Maybe (Maybe)
import Data.Nullable (Nullable, toNullable)
import Effect (Effect)
import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect)
import Web.Firestore.DocumentReference (DocumentReference)
import Web.Firestore.SnapshotOptions (SnapshotOptions)

foreign import data QuerySnapshot :: Type -> Type

foreign import data QueryDocumentSnapshot :: Type -> Type

foreign import forEachImpl :: forall a. Fn2 (QuerySnapshot a) (QueryDocumentSnapshot a -> Effect Unit) (Effect Unit)

forEach :: forall a. QuerySnapshot a -> (QueryDocumentSnapshot a -> Aff Unit) -> Aff Unit
forEach querySnapshot callback = liftEffect $ runFn2 forEachImpl querySnapshot (launchAff_ <<< callback)

foreign import queryDocumentDataImpl :: forall a. Fn2 (QueryDocumentSnapshot a) (Nullable SnapshotOptions) a

queryDocumentData :: forall a. QueryDocumentSnapshot a -> Maybe SnapshotOptions -> a
queryDocumentData documentSnapshot options = runFn2 queryDocumentDataImpl documentSnapshot (toNullable options)

foreign import queryDocumentReferenceImpl :: forall a. Fn1 (QueryDocumentSnapshot a) (DocumentReference a)

queryDocumentReference :: forall a. QueryDocumentSnapshot a -> DocumentReference a
queryDocumentReference = runFn1 queryDocumentReferenceImpl
11 changes: 6 additions & 5 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,25 @@ this program. If not, see <https://firstdonoharm.dev/>.
module Test.Main where

import Prelude
import Data.Maybe (Maybe(..))
import Effect (Effect)
import Effect.Aff (Fiber, launchAff)
import Test.Spec.Reporter.Console (consoleReporter)
import Test.Spec.Runner (runSpec)

import Test.Web.FirestoreCollectionSpec as FirestoreCollection
import Test.Web.FirestoreDocumentSpec as FirestoreDocument
import Test.Spec.Runner (defaultConfig, runSpec')
import Test.Web.Firestore.BlobSpec as Blob
import Test.Web.Firestore.CollectionPathSpec as CollectionPath
import Test.Web.Firestore.DocumentDataSpec as DocumentData
import Test.Web.Firestore.DocumentPathSpec as DocumentPath
import Test.Web.Firestore.DocumentValueSpec as DocumentValue
import Test.Web.Firestore.OptionsSpec as Options
import Test.Web.Firestore.DocumentPathSpec as DocumentPath
import Test.Web.Firestore.PrimitiveValueSpec as PrimitiveValue
import Test.Web.Firestore.TimestampSpec as Timestamp
import Test.Web.FirestoreCollectionSpec as FirestoreCollection
import Test.Web.FirestoreDocumentSpec as FirestoreDocument

main :: Effect (Fiber Unit)
main = launchAff $ runSpec [consoleReporter] do
main = launchAff $ runSpec' (defaultConfig {timeout = Nothing}) [consoleReporter] do
Blob.suite
CollectionPath.suite
DocumentData.suite
Expand Down
153 changes: 150 additions & 3 deletions test/Web/FirestoreCollectionSpec.purs
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,24 @@ import Test.Spec (Spec, describe, it)
import Test.Spec.Assertions (fail, shouldSatisfy)

import Test.Web.Firestore.OptionsUtils (buildTestOptions)
import Web.Firestore (add, collection, firestore, initializeApp)
import Web.Firestore (add, clearCollection, collection, deleteApp, firestore, getCollection, initializeApp)
import Web.Firestore.Blob (blob)
import Web.Firestore.CollectionPath (pathFromString)
import Web.Firestore.DocumentData (DocumentData(..))
import Web.Firestore.DocumentValue (arrayDocument, mapArrayValue, mapDocument, primitiveArrayValue, primitiveDocument)
import Web.Firestore.GeographicalPoint (point)
import Web.Firestore.GetOptions (GetOptions(..), SourceOption(..))
import Web.Firestore.LatLon (lat, lon)
import Web.Firestore.PrimitiveValue (pvBytes, pvBoolean, pvDateTime, pvGeographicalPoint, pvNull, pvNumber, pvText)
import Web.Firestore.QuerySnapshot (forEach)
import Web.Firestore.Timestamp (microseconds, seconds, timestamp)

suite :: Spec Unit
suite = do
describe "Firestore collection" do
it "does create a collection reference" do
testOptions <- buildTestOptions
eitherErrorApp <- liftEffect $ initializeApp testOptions (Just "firestore-collection-test")
eitherErrorApp <- liftEffect $ initializeApp testOptions (Just "firestore-test")
case eitherErrorApp of
Left error -> fail $ show error
Right app -> do
Expand All @@ -38,6 +40,8 @@ suite = do
Right firestoreInstance -> do
maybeCollectionRef <- liftEffect $ sequence $ collection firestoreInstance <$> (pathFromString "collection")
maybeCollectionRef `shouldSatisfy` isJust
deletePromise <- liftEffect $ deleteApp app
toAff deletePromise

let mapDoc = mapDocument (fromFoldable [ "mapText" /\ (primitiveDocument (pvText "some other text"))
, "mapInteger" /\ (primitiveDocument (pvNumber 42.0 ))
Expand All @@ -64,7 +68,7 @@ suite = do

it "adds documents to a collection" do
testOptions <- buildTestOptions
eitherErrorApp <- liftEffect $ initializeApp testOptions (Just "firestore-collection-test-1")
eitherErrorApp <- liftEffect $ initializeApp testOptions (Just "firestore-test")
case eitherErrorApp of
Left error -> fail $ show error
Right app -> do
Expand All @@ -81,3 +85,146 @@ suite = do
addPromise2 <- liftEffect $ add collectionRef document2
_ <- toAff addPromise2
pure unit
deletePromise <- liftEffect $ deleteApp app
toAff deletePromise

it "gets data from a collection" do
testOptions <- buildTestOptions
eitherErrorApp <- liftEffect $ initializeApp testOptions (Just "firestore-test")
case eitherErrorApp of
Left error -> fail $ show error
Right app -> do
eitherFirestoreInstance <- liftEffect $ firestore app
case eitherFirestoreInstance of
Left error -> fail $ show error
Right firestoreInstance -> do
maybeCollectionRef <- liftEffect $ sequence $ collection firestoreInstance <$> (pathFromString "collection")
case maybeCollectionRef of
Nothing -> fail "invalid path"
Just collectionRef -> do
addPromise1 <- liftEffect $ add collectionRef document1
_ <- toAff addPromise1
addPromise2 <- liftEffect $ add collectionRef document2
_ <- toAff addPromise2
getPromise <- liftEffect $ getCollection collectionRef Nothing
querySnapshot <- toAff getPromise
pure unit
deletePromise <- liftEffect $ deleteApp app
toAff deletePromise

it "gets data from a collection with cache options" do
testOptions <- buildTestOptions
eitherErrorApp <- liftEffect $ initializeApp testOptions (Just "firestore-test")
case eitherErrorApp of
Left error -> fail $ show error
Right app -> do
eitherFirestoreInstance <- liftEffect $ firestore app
case eitherFirestoreInstance of
Left error -> fail $ show error
Right firestoreInstance -> do
maybeCollectionRef <- liftEffect $ sequence $ collection firestoreInstance <$> (pathFromString "collection")
case maybeCollectionRef of
Nothing -> fail "invalid path"
Just collectionRef -> do
addPromise1 <- liftEffect $ add collectionRef document1
_ <- toAff addPromise1
addPromise2 <- liftEffect $ add collectionRef document2
_ <- toAff addPromise2
getPromise <- liftEffect $ getCollection collectionRef (Just $ GetOptions Cache)
querySnapshot <- toAff getPromise
pure unit
deletePromise <- liftEffect $ deleteApp app
toAff deletePromise

it "gets data from a collection with server options" do
testOptions <- buildTestOptions
eitherErrorApp <- liftEffect $ initializeApp testOptions (Just "firestore-test")
case eitherErrorApp of
Left error -> fail $ show error
Right app -> do
eitherFirestoreInstance <- liftEffect $ firestore app
case eitherFirestoreInstance of
Left error -> fail $ show error
Right firestoreInstance -> do
maybeCollectionRef <- liftEffect $ sequence $ collection firestoreInstance <$> (pathFromString "collection")
case maybeCollectionRef of
Nothing -> fail "invalid path"
Just collectionRef -> do
addPromise1 <- liftEffect $ add collectionRef document1
_ <- toAff addPromise1
addPromise2 <- liftEffect $ add collectionRef document2
_ <- toAff addPromise2
getPromise <- liftEffect $ getCollection collectionRef (Just $ GetOptions Server)
querySnapshot <- toAff getPromise
pure unit
deletePromise <- liftEffect $ deleteApp app
toAff deletePromise

it "gets data from an empty collection" do
testOptions <- buildTestOptions
eitherErrorApp <- liftEffect $ initializeApp testOptions (Just "firestore-test")
case eitherErrorApp of
Left error -> fail $ show error
Right app -> do
eitherFirestoreInstance <- liftEffect $ firestore app
case eitherFirestoreInstance of
Left error -> fail $ show error
Right firestoreInstance -> do
maybeCollectionRef <- liftEffect $ sequence $ collection firestoreInstance <$> (pathFromString "collection")
case maybeCollectionRef of
Nothing -> fail "invalid path"
Just collectionRef -> do
getPromise <- liftEffect $ getCollection collectionRef Nothing
querySnapshot <- toAff getPromise
pure unit
deletePromise <- liftEffect $ deleteApp app
toAff deletePromise

it "cycles through the documents of a collection" do
testOptions <- buildTestOptions
eitherErrorApp <- liftEffect $ initializeApp testOptions (Just "firestore-test")
case eitherErrorApp of
Left error -> fail $ show error
Right app -> do
eitherFirestoreInstance <- liftEffect $ firestore app
case eitherFirestoreInstance of
Left error -> fail $ show error
Right firestoreInstance -> do
maybeCollectionRef <- liftEffect $ sequence $ collection firestoreInstance <$> (pathFromString "collection")
case maybeCollectionRef of
Nothing -> fail "invalid path"
Just collectionRef -> do
addPromise1 <- liftEffect $ add collectionRef document1
_ <- toAff addPromise1
addPromise2 <- liftEffect $ add collectionRef document2
_ <- toAff addPromise2
getPromise <- liftEffect $ getCollection collectionRef Nothing
querySnapshot <- toAff getPromise
forEach querySnapshot (const $ pure unit)
deletePromise <- liftEffect $ deleteApp app
toAff deletePromise

it "clears a collection" do
testOptions <- buildTestOptions
eitherErrorApp <- liftEffect $ initializeApp testOptions (Just "firestore-test")
case eitherErrorApp of
Left error -> fail $ show error
Right app -> do
eitherFirestoreInstance <- liftEffect $ firestore app
case eitherFirestoreInstance of
Left error -> fail $ show error
Right firestoreInstance -> do
maybeCollectionRef <- liftEffect $ sequence $ collection firestoreInstance <$> (pathFromString "collection")
case maybeCollectionRef of
Nothing -> fail "invalid path"
Just collectionRef -> do
addPromise1 <- liftEffect $ add collectionRef document1
_ <- toAff addPromise1
addPromise2 <- liftEffect $ add collectionRef document2
_ <- toAff addPromise2
clearCollection collectionRef
getPromise <- liftEffect $ getCollection collectionRef Nothing
querySnapshot <- toAff getPromise
forEach querySnapshot (const $ fail "no document should be present now!")
deletePromise <- liftEffect $ deleteApp app
toAff deletePromise
Loading