Skip to content

Instantly share code, notes, and snippets.

@realgenekim
Last active August 28, 2020 16:43
Show Gist options
  • Save realgenekim/2d9b14da54e71bb14ffe69afc7947bc8 to your computer and use it in GitHub Desktop.
Save realgenekim/2d9b14da54e71bb14ffe69afc7947bc8 to your computer and use it in GitHub Desktop.
attempt to get Google Cloud Firestore working in Clojure
(ns firebase1.core
(:require [clojure.java.io :as io]
[clojure.core.async :as async]
[clojure.reflect :as r])
(:import (com.google.auth.oauth2 GoogleCredentials)
(com.google.firebase FirebaseOptions$Builder)
(com.google.firebase FirebaseApp)
(com.google.cloud.firestore.v1beta1 FirestoreClient))
(:gen-class))
; tutorial from Google:
; https://firebase.google.com/docs/firestore/quickstart
;
; FileInputStream serviceAccount =
; new FileInputStream("path/to/serviceAccountKey.json");
;
; FirebaseOptions options = new FirebaseOptions.Builder()
; .setCredentials(GoogleCredentials.fromStream(serviceAccount))
; .setDatabaseUrl("https://test1-e1b2f.firebaseio.com")
; .build();
(def creds (GoogleCredentials/fromStream
(io/input-stream "test1-xxxx-firebase-adminsdk-xxxx.json")))
(def o (-> (new FirebaseOptions$Builder)
(.setCredentials creds)
(.setDatabaseUrl "https://test1-xxxx.firebaseio.com")
(.build)))
; tutorial resumes
;
; FirebaseApp.initializeApp(options);
(def app (FirebaseApp/initializeApp o))
; Firestore db = FirestoreClient.getFirestore();
(FirestoreClient/getFirestore app)
; ^^^ agh, cannot find the .getFirestore() method in the FirestoreClient class!
;
; I've browsed the JavaDocs, and cannot for the life of me access the Java object FirebaseApp.initalizeApp.
;
; I've used Java reflection, looked at all the methods;
; I've browsed through the class in IntellJ, and still cannot find.
;
; here's the entire reflection of FirestoreClient below: note that there is no method named
; .getFirestore() at bottom of this file...
;
; I would love any help!
;
; Thank you!
; Gene
;
; This seems to get me a db object — 
; Copied from: https://github.com/GoogleCloudPlatform/java-docs-samples/blob/master/firestore/src/main/java/com/example/firestore/Quickstart.java
;
; Getting warmer?
(def db (-> (new FirebaseOptions$Builder)
(.setCredentials creds)
(.setDatabaseUrl "https://test1-xxx.firebaseio.com")
(.build)
(.getService)))
; this works!
(def result
(-> db
(.collection "quotes")
(.document "quotes")
(.get)
(.get)))
; my question: why couldn't I geta a FirestoreClient as originally documented?
;
; thank you!
; Gene
;
; output from (r/reflect app):
{:bases #{java.lang.Object com.google.api.gax.core.BackgroundResource},
:flags #{:public},
:members #{#clojure.reflect.Method{:name parseProjectFromDocumentPathName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name beginTransaction,
:return-type com.google.firestore.v1beta1.BeginTransactionResponse,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name runQueryCallable,
:return-type com.google.api.gax.rpc.ServerStreamingCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name rollbackCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name parseDocumentFromAnyPathName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name parseDocumentPathFromDocumentPathName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name deleteDocument,
:return-type void,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.firestore.v1beta1.DeleteDocumentRequest],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name isTerminated,
:return-type boolean,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public}}
#clojure.reflect.Method{:name updateDocument,
:return-type com.google.firestore.v1beta1.Document,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.firestore.v1beta1.UpdateDocumentRequest],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name shutdown,
:return-type void,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public}}
#clojure.reflect.Method{:name rollback,
:return-type void,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.firestore.v1beta1.RollbackRequest],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name getDocument,
:return-type com.google.firestore.v1beta1.Document,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.firestore.v1beta1.GetDocumentRequest],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name close,
:return-type void,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name formatDocumentPathName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String java.lang.String java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name create,
:return-type com.google.cloud.firestore.v1beta1.FirestoreClient,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.cloud.firestore.v1beta1.FirestoreSettings],
:exception-types [java.io.IOException],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name batchGetDocumentsCallable,
:return-type com.google.api.gax.rpc.ServerStreamingCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name deleteDocument,
:return-type void,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name parseProjectFromDocumentRootName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name listDocuments,
:return-type com.google.cloud.firestore.v1beta1.FirestoreClient$ListDocumentsPagedResponse,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.firestore.v1beta1.ListDocumentsRequest],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name commitCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Field{:name DOCUMENT_ROOT_PATH_TEMPLATE,
:type com.google.api.pathtemplate.PathTemplate,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:flags #{:private :static :final}}
#clojure.reflect.Method{:name parseProjectFromAnyPathName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name commit,
:return-type com.google.firestore.v1beta1.CommitResponse,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.firestore.v1beta1.CommitRequest],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name updateDocumentCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name parseAnyPathFromAnyPathName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Field{:name settings,
:type com.google.cloud.firestore.v1beta1.FirestoreSettings,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:flags #{:private :final}}
#clojure.reflect.Method{:name create,
:return-type com.google.cloud.firestore.v1beta1.FirestoreClient,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.cloud.firestore.v1beta1.stub.FirestoreStub],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name commit,
:return-type com.google.firestore.v1beta1.CommitResponse,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String java.util.List],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name awaitTermination,
:return-type boolean,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [long java.util.concurrent.TimeUnit],
:exception-types [java.lang.InterruptedException],
:flags #{:public}}
#clojure.reflect.Method{:name parseProjectFromDatabaseRootName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name getStub,
:return-type com.google.cloud.firestore.v1beta1.stub.FirestoreStub,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public}}
#clojure.reflect.Field{:name stub,
:type com.google.cloud.firestore.v1beta1.stub.FirestoreStub,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:flags #{:private :final}}
#clojure.reflect.Method{:name createDocumentCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name listDocumentsCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name beginTransaction,
:return-type com.google.firestore.v1beta1.BeginTransactionResponse,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.firestore.v1beta1.BeginTransactionRequest],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name listenCallable,
:return-type com.google.api.gax.rpc.BidiStreamingCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Field{:name DATABASE_ROOT_PATH_TEMPLATE,
:type com.google.api.pathtemplate.PathTemplate,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:flags #{:private :static :final}}
#clojure.reflect.Method{:name parseDatabaseFromDocumentRootName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Constructor{:name com.google.cloud.firestore.v1beta1.FirestoreClient,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.cloud.firestore.v1beta1.FirestoreSettings],
:exception-types [java.io.IOException],
:flags #{:protected}}
#clojure.reflect.Method{:name shutdownNow,
:return-type void,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public}}
#clojure.reflect.Method{:name writeCallable,
:return-type com.google.api.gax.rpc.BidiStreamingCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name beginTransactionCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name listCollectionIds,
:return-type com.google.cloud.firestore.v1beta1.FirestoreClient$ListCollectionIdsPagedResponse,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.firestore.v1beta1.ListCollectionIdsRequest],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name formatDocumentRootName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name listCollectionIdsPagedCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name deleteDocumentCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name listCollectionIdsCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Field{:name DOCUMENT_PATH_PATH_TEMPLATE,
:type com.google.api.pathtemplate.PathTemplate,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:flags #{:private :static :final}}
#clojure.reflect.Method{:name formatAnyPathName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String
java.lang.String
java.lang.String
java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name createDocument,
:return-type com.google.firestore.v1beta1.Document,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.firestore.v1beta1.CreateDocumentRequest],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name getDocumentCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Constructor{:name com.google.cloud.firestore.v1beta1.FirestoreClient,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.cloud.firestore.v1beta1.stub.FirestoreStub],
:exception-types [],
:flags #{:protected}}
#clojure.reflect.Method{:name rollback,
:return-type void,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String com.google.protobuf.ByteString],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name formatDatabaseRootName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name parseDatabaseFromDocumentPathName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name updateDocument,
:return-type com.google.firestore.v1beta1.Document,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [com.google.firestore.v1beta1.Document
com.google.firestore.v1beta1.DocumentMask],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name listCollectionIds,
:return-type com.google.cloud.firestore.v1beta1.FirestoreClient$ListCollectionIdsPagedResponse,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name isShutdown,
:return-type boolean,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public}}
#clojure.reflect.Field{:name ANY_PATH_PATH_TEMPLATE,
:type com.google.api.pathtemplate.PathTemplate,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:flags #{:private :static :final}}
#clojure.reflect.Method{:name create,
:return-type com.google.cloud.firestore.v1beta1.FirestoreClient,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [java.io.IOException],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name getSettings,
:return-type com.google.cloud.firestore.v1beta1.FirestoreSettings,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name parseDatabaseFromAnyPathName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}
#clojure.reflect.Method{:name listDocumentsPagedCallable,
:return-type com.google.api.gax.rpc.UnaryCallable,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [],
:exception-types [],
:flags #{:public :final}}
#clojure.reflect.Method{:name parseDatabaseFromDatabaseRootName,
:return-type java.lang.String,
:declaring-class com.google.cloud.firestore.v1beta1.FirestoreClient,
:parameter-types [java.lang.String],
:exception-types [],
:flags #{:public :static :final}}}}
@realgenekim
Copy link
Author

realgenekim commented Sep 7, 2018

This seems to work? Copied from: https://github.com/GoogleCloudPlatform/java-docs-samples/blob/master/firestore/src/main/java/com/example/firestore/Quickstart.java

Getting warmer?

(def db (-> (new FirebaseOptions$Builder)
            (.setCredentials creds)
            (.setDatabaseUrl "https://test1-xxx.firebaseio.com")
            (.build)))

@lurodrigo
Copy link

lurodrigo commented Jan 29, 2020

Not sure if you still need it, but since it's one of the first google results, my company just open source our clojure wrapper for Firestore. Getting Firestore to work properly sure is tricky.

@realgenekim
Copy link
Author

@lurodrigo Thanks so much for the note! Somehow, I wasn't getting my GitHub notifications — what a wonderful example of how to use the GCP libraries. (Not having much Java experience, it's been super difficult for me to replicate their tutorials in Clojure.)

Just out of curiosity, do you use any of the non-Firestore services in GCP? If so, what libraries do you use to access them? Do you use the Java libraries, or use the HTTP interface? (I'm still trying to form an opinion on which is "better.").

@lurodrigo
Copy link

@realgenekim The only other product I use from GCP ~programatically is the artifact registry, for which I also wrote a small lein plugin https://github.com/lurodrigo/lein-artifact-registry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment