github.com/SaurabhDubey-Groww/go-cloud@v0.0.0-20221124105541-b26c29285fd8/docstore/gcpfirestore/urls.go (about) 1 // Copyright 2019 The Go Cloud Development Kit Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gcpfirestore 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "net/url" 22 "os" 23 "path" 24 "sync" 25 26 vkit "cloud.google.com/go/firestore/apiv1" 27 "gocloud.dev/docstore" 28 "gocloud.dev/gcp" 29 ) 30 31 func init() { 32 docstore.DefaultURLMux().RegisterCollection(Scheme, &lazyCredsOpener{}) 33 } 34 35 type lazyCredsOpener struct { 36 init sync.Once 37 opener *URLOpener 38 err error 39 } 40 41 func (o *lazyCredsOpener) OpenCollectionURL(ctx context.Context, u *url.URL) (*docstore.Collection, error) { 42 o.init.Do(func() { 43 var tokenSource gcp.TokenSource 44 if e := os.Getenv("FIRESTORE_EMULATOR_HOST"); e == "" { 45 // Connect to the GCP default endpoint if the 'FIRESTORE_EMULATOR_HOST' environment variable is not set. 46 creds, err := gcp.DefaultCredentials(ctx) 47 if err != nil { 48 o.err = err 49 return 50 } 51 tokenSource = creds.TokenSource 52 } 53 client, _, err := Dial(ctx, tokenSource) 54 if err != nil { 55 o.err = err 56 return 57 } 58 o.opener = &URLOpener{Client: client} 59 }) 60 if o.err != nil { 61 return nil, fmt.Errorf("open collection %s: %v", u, o.err) 62 } 63 return o.opener.OpenCollectionURL(ctx, u) 64 } 65 66 // Scheme is the URL scheme firestore registers its URLOpener under on 67 // docstore.DefaultMux. 68 const Scheme = "firestore" 69 70 // URLOpener opens firestore URLs like 71 // "firestore://projects/myproject/databases/(default)/documents/mycollection?name_field=myID". 72 // 73 // See https://firebase.google.com/docs/firestore/data-model for more details. 74 // 75 // The following query parameters are supported: 76 // 77 // - name_field (required): gcpfirestore requires that a single string field, 78 // 79 // name_field, be designated the primary key. Its values must be unique over all 80 // documents in the collection, and the primary key must be provided to retrieve 81 // a document. 82 type URLOpener struct { 83 // Client must be set to a non-nil client authenticated with Cloud Firestore 84 // scope or equivalent. 85 Client *vkit.Client 86 } 87 88 // OpenCollectionURL opens a docstore.Collection based on u. 89 func (o *URLOpener) OpenCollectionURL(ctx context.Context, u *url.URL) (*docstore.Collection, error) { 90 q := u.Query() 91 options := &Options{ 92 RevisionField: q.Get("revision_field"), 93 } 94 95 nameField := q.Get("name_field") 96 if nameField == "" { 97 return nil, errors.New("open collection %s: name_field is required to open a collection") 98 } 99 q.Del("name_field") 100 q.Del("revision_field") 101 for param := range q { 102 return nil, fmt.Errorf("open collection %s: invalid query parameter %q", u, param) 103 } 104 collResourceID := path.Join(u.Host, u.Path) 105 return OpenCollection(o.Client, collResourceID, nameField, options) 106 }