Create new client for every operation to firestore? - firebase

I see examples like these in firestore Go Docs. Should we have to create a client like below for every operation to firestore or can we create a client during application startup and use the same client to perform an operation? Please let me know.
ctx := context.Background()
client, err := firestore.NewClient(ctx, "project-id")
if err != nil {
// TODO: Handle error.
}
defer client.Close()
type State struct {
Capital string `firestore:"capital"`
Population float64 `firestore:"pop"` // in millions
}
wr, err := client.Doc("States/Colorado").Create(ctx, State{
Capital: "Denver",
Population: 5.5,
})
if err != nil {
// TODO: Handle error.
}
fmt.Println(wr.UpdateTime)

No.Create the client once and re-use the same client. More details on this post - Should a Firestore client be created per a request with Google App Engine?

Related

How to get any data from my real-time database in firebase using Go

I have simple test database with one row (key - value) and I can't get any data from database, although I use docs for Go (admin sdk - https://firebase.google.com/docs/database/admin/start?authuser=0#go). I tried search but info how to use by Go is very small
Here my code.
Here my json-file.
In playground don't work, need use json-file and execute from code editor/IDE.
All I found was a couple of video tutorials from the firebase guys themselves where they show how to connect, but the same thing does not work for me. All other information is about how to use firebase via android, iphone and web (js).
package main
import (
"context"
"fmt"
"log"
firebase "firebase.google.com/go"
"google.golang.org/api/option"
)
type Data struct {
TypeClient string `json:"typeClient,omitempty"`
}
var responseData structs.Data
func main() {
ctx := context.Background()
// Initialize the app with a custom auth variable, limiting the server's
access
ao := map[string]interface{}{"uid": "my-service-worker"}
conf := &firebase.Config{
DatabaseURL: "https://test-v06f06-default-rtdb.firebaseio.com",
AuthOverride: &ao,
}
// Fetch the service account key JSON file contents
opt := option.WithCredentialsFile("./test-v06f06-firebase-adminsdk-1ze0m-
bbf3b57ef3.json")
app, err := firebase.NewApp(ctx, conf, opt)
if err != nil {
log.Fatalln("Error initializing app:", err)
}
// fmt.Printf("%T\n", app)
client, err := app.Database(ctx)
if err != nil {
log.Fatalln("Error initializing database client:", err)
}
// fmt.Printf("%T\n", client)
// The app only has access as defined in the Security Rules
ref := client.NewRef("/admin_permission")
// fmt.Printf("%T\n", ref)
// Get data
if err := ref.Get(ctx, &responseData); err != nil {
log.Fatalln("Error reading from database:", err)
}
fmt.Println("Client Type -", responseData)}
In last row where print I get empty variable. The data is simply not written to the variable.
Help if you can. Thanks in advance.
screenshot tiny db

How to verify if Google Firebase admin SDK credentials are correct

Is there any way to verify if Firebase Admin SDK Credentials is correct when initializing app with below code?
ctx := context.Background()
opt := option.WithCredentialsFile("path/to/firebase-admin-sdk-cred.json")
app, err := firebase.NewApp(ctx, nil, opt)
if err != nil {
return nil, err
}
Because I seem to not get any error when I tried to intentionally put the wrong credentials. I check the implementation of the firebase.NewApp() but it seems it only throw error when there is no config. Below is the code of firebase.NewApp()
func NewApp(ctx context.Context, config *Config, opts ...option.ClientOption) (*App, error) {
o := []option.ClientOption{option.WithScopes(internal.FirebaseScopes...)}
o = append(o, opts...)
if config == nil {
var err error
if config, err = getConfigDefaults(); err != nil {
return nil, err
}
}
pid := getProjectID(ctx, config, o...)
ao := defaultAuthOverrides
if config.AuthOverride != nil {
ao = *config.AuthOverride
}
return &App{
authOverride: ao,
dbURL: config.DatabaseURL,
projectID: pid,
serviceAccountID: config.ServiceAccountID,
storageBucket: config.StorageBucket,
opts: o,
}, nil
}
so Is there any way to check if the credentials is valid during the initialization of Firebase Admin(app) instance because it seems catching error isn't the solution here?
I think the answer is in the documentation:
Some use cases require you to create multiple apps at the same time.
For example, you might want to read data from the Realtime Database of
one Firebase project and mint custom tokens for another project. Or
you might want to authenticate two apps with separate credentials. The
Firebase SDK allows you create multiple apps at the same time, each
with their own configuration information.
Source
I assume the only way to check credentials is invoke an Auth method for example:
client, err := app.Auth(context.Background())
I ended up using google.golang.org/api/transport to force the validation and fail fast
// Check if credential is correct
_, err = transport.Creds(ctx, opt)
if err != nil {
return nil, err
}

Force Gitlab to retry webhooks on failure with Go

I want to watch for every events in a Gitlab project and store them in an external service. For this, I use Gitlab Webhooks. I made a little local HTTP server in Go that listens for Gitlab's POSTs and forward them to an external service. Hooks contains every information I needed so it seems that this architecture is fine:
Gitlab > HTTPServer > External Service.
My problem is when the external service is down, I cannot manage to make Gitlab retry the failed requests. As the documentation says:
GitLab ignores the HTTP status code returned by your endpoint.
Your endpoint should ALWAYS return a valid HTTP response. If you do not do this then GitLab will think the hook failed and retry it.
It is very surprising that Gitlab does not have a proper way to ask for a webhook retry. I have to explicitly return an invalid http response. Moreover, I cannot find an API endpoint to list all failed webhooks and ask for resend.
Question: How to explicitly return an invalid HTTP response with the standard "net/http" library in order to force Gitlab to retry Webhooks?
As written in the comments, a webhook is a mere notification that an event occurred, and potentially some data is sent, typically as JSON data.
It is your responsibility to persist the event itself and the data you want/need to process that was sent with it. Below you will find a commented example. Note that this does not include incremental backoffs, but that should be easy to add:
package main
import (
"encoding/json"
"flag"
"io"
"log"
"net/http"
"os"
"path/filepath"
"github.com/joncrlsn/dque"
)
var (
bind string
queueDir string
segmentSize int
)
// You might want to add request headers and stuff
type webhookContent struct {
Foo string
Bar int
}
func init() {
flag.StringVar(&bind, "bind", ":8080", "The address to bind to")
flag.StringVar(&queueDir, "path", "./queue", "path to store the queue in")
flag.IntVar(&segmentSize, "size", 50, "number of entries for the queue")
}
// The "webserver" component
func runserver(q *dque.DQue) {
http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
// A new decoder for each call, as we want to have a new LimitReader
// for each call. This is a simple, albeit a bit crude method to prevent
// accidental or malicious overload of your server.
dec := json.NewDecoder(io.LimitReader(r.Body, 4096))
defer r.Body.Close()
c := &webhookContent{}
if err := dec.Decode(c); err != nil {
log.Printf("reading body: %s", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
// When the content is successfully decoded, we can persist it into
// our queue.
if err := q.Enqueue(c); err != nil {
log.Printf("enqueueing webhook data: %s", err)
// PROPER ERROR HANDLING IS MISSING HERE
}
})
http.ListenAndServe(bind, nil)
}
func main() {
flag.Parse()
var (
q *dque.DQue
err error
)
if !dirExists(queueDir) {
if err = os.MkdirAll(queueDir, 0750); err != nil {
log.Fatalf("creating queue dir: %s", err)
}
}
if !dirExists(filepath.Join(queueDir, "webhooks")) {
q, err = dque.New("webhooks", queueDir, segmentSize, func() interface{} { return &webhookContent{} })
} else {
q, err = dque.Open("webhooks", queueDir, segmentSize, func() interface{} { return &webhookContent{} })
}
if err != nil {
log.Fatalf("setting up queue: %s", err)
}
defer q.Close()
go runserver(q)
var (
// Placeholder during event loop
i interface{}
// Payload
w *webhookContent
// Did the type assertion succeed
ok bool
)
for {
// We peek only. The semantic of this is that
// you can already access the next item in the queue
// without removing it from the queue and "mark" it as read.
// We use PeekBlock since we want to wait for an item in the
// queue to be available.
if i, err = q.PeekBlock(); err != nil {
// If we can not peek, something is SERIOUSLY wrong.
log.Fatalf("reading from queue: %s", err)
}
if w, ok = i.(*webhookContent); !ok {
// If the type assertion fails, something is seriously wrong, too.
log.Fatalf("reading from queue: %s", err)
}
if err = doSomethingUseful(w); err != nil {
log.Printf("Something went wrong: %s", err)
log.Println("I strongly suggest entering an incremental backoff!")
continue
}
// We did something useful, so we can dequeue the item we just processed from the queue.
q.Dequeue()
}
}
func doSomethingUseful(w *webhookContent) error {
log.Printf("Instead of this log message, you can do something useful with: %#v", w)
return nil
}
func dirExists(path string) bool {
fileInfo, err := os.Stat(path)
if err == nil {
return fileInfo.IsDir()
}
return false
}
Now when you do something like:
$ curl -X POST --data '{"Foo":"Baz","Bar":42}' http://localhost:8080/webhook
you should get a log entry like
2020/04/18 11:34:23 Instead of this log message, you can do something useful with: &main.webhookContent{Foo:"Baz", Bar:42}
Note that See GitLab 15.7 (December 2022) implements an opposite approach:
Automatic disabling of failing webhooks
To protect GitLab and users across the system from any potential abuse or misuse, we’ve implemented a feature to disable webhooks that fail consistently.
Webhooks that return response codes in the 5xx range are understood to be failing intermittently and are temporarily disabled. These webhooks are initially disabled for 1 minute, which is extended on each retry up to a maximum of 24 hours.
Webhooks that fail with 4xx errors are permanently disabled.
All project owners and maintainers are alerted in the app to investigate and re-enable any failed webhooks.
This feature is now available on GitLab.com and self-managed instances along with feature enhancements including handling cold starts.
See Epic and Documentation.
So not only sending back "an invalid HTTP response" would not work, it would result in a disabled webhook, starting with GitLab 15.7+.

How to authenticate to Google Cloud Printing

I'm making an app that needs to send printing jobs through Google Cloud Printing to two printers owned by me (i.e., the printers are always the same one and does not belong to the user). I've set up the printers with Google Cloud Printing and it is now accessible from my Google Account.
Now, how can I access this account's printers through the API? I have found some documentation here that says that I need to authenticate myself when making requests. It seems to me that the authentication should be done with OAuth2. But the instructions on how to do that are lacking for a beginner. I've gotten so far as to getting my OAuth client ID and secret (step 1 in the OAuth link). But for step 2, I have no idea what to do.
It says:
Before your application can access private data using a Google
API, it must obtain an access token that grants access to that API. A
single access token can grant varying degrees of access to multiple
APIs.
But doesn't explain how to obtain this access token. I looked at this SO question where OP seems to have been able to get this access token, but I can't understand how he did it.
Could someone please explain how to get an access token to use with Google Cloud Printing? Or a good resource which explains how?
PS. The printing functionality is triggered by a firebase function. Would this help us get the access token, considering firebase is also made by Google?
I ran into the same issue and came up with this two-step solution:
Create an OAuth2 client in your Google Cloud Console as described here
and download its client credentials from the console and copy & past its json content to credJSON in the code snippet below.
Run the code below.
Follow the auth link and authorize your OAuth2 client to access Googel Cloud Printers with your Google account.
Copy & paste the auth code to the script
Once you obtained a refresh token make sure to store it in the variable refreshToken
Don't forget to update the proxy name.
package main
import (
"context"
"fmt"
"log"
"github.com/google/cloud-print-connector/gcp"
"github.com/google/cloud-print-connector/lib"
"github.com/google/uuid"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
var (
credJSON = ``
refreshToken = ""
// Find the proxy in the Advanced Details of your printer at https://www.google.com/cloudprint#printers
proxy = "HP"
)
func main() {
// Obtain the OAuth config
config, err := google.ConfigFromJSON([]byte(credJSON), gcp.ScopeCloudPrint)
if err != nil {
log.Fatalf("Failed to obtain OAuth config: %v", err)
}
// If no request token is present, obtain a new one
if refreshToken == "" {
// Get the auth link
authLink := config.AuthCodeURL(uuid.New().String(), oauth2.AccessTypeOffline)
log.Printf("Follow the link to obtain an auth code: %s", authLink)
fmt.Printf("Paste your auth code here: ")
var code string
fmt.Scanln(&code)
// Get a token form the auth code
token, err := config.Exchange(context.Background(), code, oauth2.AccessTypeOffline)
if err != nil {
log.Fatalf("Failed to obtain OAuth token: %v", err)
}
if token.RefreshToken != "" {
refreshToken = token.RefreshToken
} else {
refreshToken = token.AccessToken
}
log.Printf("Refresh token: %s", refreshToken)
}
// Connect to Google Cloud Print
jobCh := make(chan *lib.Job)
client, err := gcp.NewGoogleCloudPrint(lib.DefaultConfig.GCPBaseURL, refreshToken, refreshToken, proxy, config.ClientID, config.ClientSecret, config.Endpoint.AuthURL, config.Endpoint.TokenURL, lib.DefaultConfig.NativeJobQueueSize, jobCh, true)
if err != nil {
log.Fatalf("Failed to connect to GCP: %v", err)
}
// List all printers
printers, _, err := client.ListPrinters()
if err != nil {
log.Fatalf("Failed to list printers: %v", err)
}
for _, p := range printers {
log.Printf("Name: %s UUID: %s", p.Name, p.UUID)
}
}
Please refer to following documentation:
https://developers.google.com/identity/protocols/OAuth2ServiceAccount?authuser=1
I followed the same steps specified in the doc and was able to obtain the access token. First make Google Service Account, select furnish new private key. You ll have service account email addresss and private key. Using these credentials, you can obtain your access token. Below is the source code in Golang , this ll surely help you.
package main
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"net/http"
"encoding/json"
"bytes"
)
type MyCustomClaims struct {
Scope string `json:"scope,omitempty"`
jwt.StandardClaims
}
type Toke struct {
Access string `json:"access_token,omitempty"`
Type string `json:"token_type,omitempty"`
Expire string `json:"expires_in,omitempty"`
}
func main() {
key := []byte("<your private key>")
key1, _ := jwt.ParseRSAPrivateKeyFromPEM(key)
claims := MyCustomClaims{
"https://www.googleapis.com/auth/cloudprint",
jwt.StandardClaims{
IssuedAt: <currrent-epoch-time>, // eg 1234566000
ExpiresAt: <currrent-epoch-time + 3600>, // 3600 secs = 1hour, so expires in 1 hour, eg 1234569600
Issuer: "<your service account email>",
Audience: "https://www.googleapis.com/oauth2/v4/token",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
ss, err := token.SignedString(key1)
if err != nil {
fmt.Println(err)
}
fmt.Println(ss)
url := "https://www.googleapis.com/oauth2/v4/token"
any := "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + ss
a := []byte(any)
b := bytes.NewBuffer(a)
var tok Toke
req, err := http.NewRequest("POST", url, b)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
} else {
json.NewDecoder(resp.Body).Decode(&tok)
}
fmt.Println("----------- Access Token -----------------")
fmt.Println("Access: ", tok.Access)
}

FCM HTTP v1: how to get access token using Go?

In order to send Firebase Cloud Messaging with Go, we need to place the access token in the HTTP request header.
On Firebase documentation,
there are examples on how to retrieve the access token using Node.JS, Python and Java:
https://firebase.google.com/docs/cloud-messaging/auth-server
can anyone show the get the access token using Go?
There seems to be many Go packages about Firebase/Google authentication. It's very confusing to understand which ones should be used:
firebase.google.com/go
firebase.google.com/go/auth
github.com/firebase/firebase-admin-go
google.golang.org/api/option
golang.org/x/oauth2
golang.org/x/oauth2/google
github.com/google/google-api-go-client
I'm working on the go firebase SDK to add FCM HTTP v1.
For now it's almost finished, i have to write tests and integration tests, you can check the code here : https://github.com/chemidy/firebase-admin-go/tree/fcm/messaging
I will finish tests and send a PR maybe next week, (it's a little bit tricky to test on ios + android + web)
FCM httpv1 use JSON file for it credentials.
Download it first, then move it to your project.
download JSON credentials in your firebase
second, do :
go get "golang.org/x/oauth2/google"
then use this method to get token
const firebaseScope = "https://www.googleapis.com/auth/firebase.messaging"
type tokenProvider struct {
tokenSource oauth2.TokenSource
}
// newTokenProvider function to get token for fcm-send
func newTokenProvider(credentialsLocation string) (*tokenProvider, error) {
jsonKey, err := ioutil.ReadFile(credentialsLocation)
if err != nil {
return nil, errors.New("fcm: failed to read credentials file at: " + credentialsLocation)
}
cfg, err := google.JWTConfigFromJSON(jsonKey, firebaseScope)
if err != nil {
return nil, errors.New("fcm: failed to get JWT config for the firebase.messaging scope")
}
ts := cfg.TokenSource(context.Background())
return &tokenProvider{
tokenSource: ts,
}, nil
}
func (src *tokenProvider) token() (string, error) {
token, err := src.tokenSource.Token()
if err != nil {
return "", errors.New("fcm: failed to generate Bearer token")
}
return token.AccessToken, nil
}
Then call it in your FCM-Send method :
tp, err := newTokenProvider("yourJSONFileLocation")
if err != nil {
return nil, err
}
token, err := tp.token()
if err != nil {
return nil, err
}
last, add it to header :
r.Header.Add("Authorization", "Bearer "+token)
Done.
i am put header Authorization by used volly
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headerMap = new HashMap<String, String>();
headerMap.put("Content-Type", "application/json");
headerMap.put("Authorization", "Bearer " + key);
Log.v(TAG,"getHeaders "+headerMap);
return headerMap;
}
but send Authorization=Bearer Add..
how change to current
Authorization:Bearer

Resources