Error initialising firebase in golang app - firebase

I am using firebase go sdk (https://github.com/acoshift/go-firebase-admin) and have followed the docs to set up my app.
But when I try to initialize the app with firebase.NewApp I get an error saying
google: could not find default credentials.
Can someone please help
Here is the code snippet
opt = option.WithCredentialsFile(viper.GetString("firebase"))
app, err = firebase.NewApp(context.Background(), nil, opt)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}

The problem in opt = option.WithCredentialsFile(viper.GetString("firebase"))
It couldn't find the path to your config file. Try to use path to file and then find how to add it via viper

Related

How to handle errors with firebase admin sdk for Go?

New to Go and trying to understand how to access the error details. I've already created a user, and now I'm expecting to get a "email-already-exists" error:
fbUser, err := s.auth.CreateUser(ctx, fbUserParams)
if err != nil {
return nil, errors.New("[email] already exists") // <- it could be any other error, and I want to be able to handle it
}
Here's what I see in my debugger:
How can I handle the error so that I can get the Code from it?
I think that the best option you have is using the Errors.As function. You can learn more about it at here: https://pkg.go.dev/errors#As
The error returned by Google Firebase is of type FirebaseError that involves two properties: Code and String. You can try with the following code snippet:
fbUser, err := s.auth.CreateUser(ctx, fbUserParams)
if err != nil {
var firebaseErr *FirebaseError
if errors.As(err, &firebaseErr) {
// here you can access "Code" and "String"
} else {
return nil, errors.New("[email] already exists")
}
}
Thanks to this code you should be able to manage what you need. Pay attention to import correctly the package that provides the type FirebaseError. Maybe read something on the Firebase documentation first.
Hope this helps!

Firebase Admin SDK Event Listener not triggered

I am using the Go Firebase Admin SDK and listening to changes in the Realtime Database.
The problem is that the listener is ONLY triggered if I manually update the data from the Firebase Console, if I change data from another app (in this case Flutter), the listener is NOT triggered even though the changes can be seen in the Firebase Console (so the data definitely changed).
I even tried performing an update via Firebase Database REST API https://firebase.google.com/docs/reference/rest/database#section-streaming
Which had the same result: Data changes are viewable in the Console, but still don't trigger the listener.
Here the way I'm listening to changes in Go:
func listenToFirebase(ref *db.Ref, ctx context.Context) {
iter, err := ref.Listen(ctx)
if err != nil {
fmt.Printf(" Error: failed to create Listener %v\n", err)
return
}
defer iter.Stop()
for {
if iter.Done() {
break
}
event, err := iter.Next()
if err != nil {
// Handle error here based on specific usecase
// We can continue Listening
log.Printf("%v\n", err)
continue
}
fmt.Printf("Listener | Ref Path: %s | event.Path %s | event.Snapshot() = %v\n", ref.Path, event.Path, event.Snapshot())
fmt.Printf("\n")
}
}
The fact that the listener is triggered by updating data from the Console, indicates that the listener is working properly.
P.S.:
The Listen-Method has not yet been integrated to the Go Firebase Admin SDK and is from https://github.com/firebase/firebase-admin-go/issues/229
Turned out there was no problem regarding the Go code.
The problem was the way I updated the Realtime Database from Flutter.
This is what I used to perform updates:
await ref.update({"value": value});
What fixed the problem was to use "set" instead of "update" the following way:
await ref.child("value").set(value);
This way my Go-Listener method then was finally triggered and everything was working as expected.

How to programmatically create a firestore database

I am trying to create a firebase project with a firestore instance programatically. I've been using the firebase-tools cli and have managed to create a new project, a web app and get the app config, but I still need to manually enter the console and click the "Create database" button. Is it possible to automate this process?
Given that Firestore depends on an implementation of Google App Engine, I was able to programatically create a Firestore database using the Google App Engine api:
package main
import (
"context"
"google.golang.org/api/appengine/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"log"
)
// CreateFirestoreDatabase uses the Google App Engine API
// to create a Firestore database
func CreateFirestoreDatabase(ctx context.Context) error {
// instantiate service
service, err := appengine.NewService(context.Background())
if err != nil {
log.Fatalf("appengine.NewService: %v", err)
}
// create application
op, err := service.Apps.
Create(&appengine.Application{
DatabaseType: "CLOUD_FIRESTORE",
Id: "your-google-project-id",
LocationId: "europe-west",
}).
Context(ctx).Do()
if err != nil {
return status.Errorf(
codes.Internal,
"service.Apps.Create: %s", err,
)
}
// check the status of the longrunning operations
// TODO: loop until op.Done == true
_ = op
return nil
}
The same process is used by the gcloud SDK:
gcloud firestore databases create [--region=REGION]
It is useful to have a look a the underlying python code backing the gcloud bin folder. Here is a screenshot of the gcloud firestore create command confirming this:
The process can be automated using terraform and the work around to configure an app engine application resource.
resource "google_app_engine_application" "app" {
project = "your-project-id"
location_id = "us-central"
database_type = "CLOUD_FIRESTORE"
}
As stated in the answers before firestore seems to rely on app engine even in the gcloud SDK.
Ugly but works.

How to get path from config file

I would like to get path to my sqlite DB from a config file. How could I do that in Go?
This is a code, which I wrote before:
database, _ := sql.Open("sqlite3", "C:\\Users\\username\\project\\source.db")
In this case my path is "hard coded" directly in code. I would like to set a variable, which takes a path from a config data.
My first suggestion is that you use a JSON file rather than a YAML file for configuration, since Go supports it natively; you don't need to use any external packages.
type DBConfig struct {
Path string `json:"path"`
}
func loadConfig(path string) (*DBConfig, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var conf DBConfig
err = json.Unmarshal(data, &conf)
if err != nil {
return nil, err
}
return &conf, nil
}
My second suggestion is that you pass in the path to this config file in as a flag. You can supply a flag when you run your application like this:
$ go build . -o MyApp
$ ./MyApp --config=path/to/config/file
Flags are very powerful and allow you to easily configure your applications without changing much code. Using flags in Golang is simple.
var configPath = flag.String("config", "", "Path to file containing app config")
Just make sure that you add flag.Parse to the top of your main function in order to access them.
Here's a full example.
Good luck!

Authenticate Service Account for Remote Config REST API using Go

Over here the Firebase docs explain how you can retrieve a token required to make requests to the Remote Config Rest API.
It provides example code for Python, Java and Node.js. Because there is no code for Go, it sends me to the Google Client Library (for Go). You might be able to understand why I am getting lost there...
The examples use GoogleCredential in Java, ServiceAccountCredentials in Python and google.auth.JWT in Node.js. I was not able to find any of those here. I do not know why there are no clear naming conventions.
I have found
firebaseremoteconfig-gen.go: The code looks like it already implements what the Firebase documentation page tries to achieve "manually". Comparison: doc, package.
Help
Because the "Usage example" of the package ends strangely abrupt and is the opposite of extensive, I do not understand how to make use of it.
I would be helped if someone could tell me how I can use this:
firebaseremoteconfigService, err := firebaseremoteconfig.New(oauthHttpClient)
I could not figure out where I would get oauthHttpClient from. There is an oauth2 package in the repository, but there I face the same problem:
oauth2Service, err := oauth2.New(oauthHttpClient)
I need oauthHttpClient again, so this cannot be a solution.
http.Client could be anything, but I need to authenticate with a service-account.json file, like shown in the three example snippets here.
Tags explanation
I hope that someone has either had experience with integrating Firebase Remote Config with Go, someone knows how Google Client API authentication works or someone is good enough with Go to get how the usage works.
There are a couple of main ways of authenticating with the google APIs, they are documented here:
Link to docs
The ways documented are "3-legged OAuth", "Using API Keys" and finally "Service Accounts".
From the links that you've included in the question; you are looking at the Python / Java / Node examples of "Service Accounts".
Using Service Accounts in go
The oauthHttpClient that you are referring to, is an http client that will attach the authentication information to the requests automatically.
You can create one using this package:
https://godoc.org/golang.org/x/oauth2/google
The examples linked in other languages use a "service account json key file".
Using the method linked below, you can read that keyfile and create a jwt.Config struct that will give you access to the client that you need.
https://godoc.org/golang.org/x/oauth2/google#JWTConfigFromJSON
The go equivalent of the other language examples linked is;
data, err := ioutil.ReadFile("/path/to/your-project-key.json")
if err != nil {
log.Fatal(err)
}
conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/firebase.remoteconfig")
if err != nil {
log.Fatal(err)
}
// Initiate an http.Client. The following GET request will be
// authorized and authenticated on the behalf of
// your service account.
client := conf.Client(oauth2.NoContext)
client.Get("...")
I just started using the same library (from an AppEngine Standard project). This is how I am creating the service client:
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"golang.org/x/oauth2/google"
fb "google.golang.org/api/firebaseremoteconfig/v1"
"google.golang.org/appengine"
"google.golang.org/appengine/log"
)
const (
// Name of our service account file
saFileName = "my-firebase-sa.json"
// OAuth scopes used for remote config API
scopeRemoteConfig = "https://www.googleapis.com/auth/firebase.remoteconfig"
)
func createFirebaseService(ctx context.Context) (*fb.Service, error) {
data, err := ioutil.ReadFile(saFileName)
if err != nil {
return nil, err
}
conf, err := google.JWTConfigFromJSON(data, scopeRemoteConfig)
if err != nil {
return nil, err
}
return fb.New(conf.Client(ctx))
}
And I call it as such:
func fetchConfig(ctx context.Context) (*fb.RemoteConfig, error) {
s, err := createFirebaseService(ctx)
if err != nil {
log.Errorf(ctx, "Failed to create firebase service: %v", err)
return nil, fmt.Errorf("Failed to initialize Firebase service")
}
projectID := "projects/" + appengine.AppID(ctx)
cfg, err := s.Projects.GetRemoteConfig(projectID).Do()
if err != nil {
log.Errorf(ctx, "Failed to call Firebase remote config API: %v", err)
return nil, err
}
return cfg, nil
}
The code is using the Project ID to form its path; after reading through the lib code I noticed it was missing /projects/ from that path; so I just prepended that to my project ID and it works ;-) At least until they fix that and my code stops working..
Hopefully this helps someone.

Resources