| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- package firestore
- import (
- "context"
- "time"
- fs "cloud.google.com/go/firestore"
- "github.com/kellegous/go/internal"
- "golang.org/x/oauth2/google"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/status"
- )
- // NextID is the next numeric ID to use for auto-generated IDs
- type NextID struct {
- ID uint32 `json:"id" firestore:"id"`
- }
- // Backend provides access to Google Firestore.
- type Backend struct {
- db *fs.Client
- }
- // New instantiates a new Backend
- func New(ctx context.Context, path string) (*Backend, error) {
- if path == "" {
- path = getGoogleProject()
- }
- client, err := fs.NewClient(ctx, path)
- if err != nil {
- return nil, err
- }
- backend := Backend{
- db: client,
- }
- return &backend, nil
- }
- // Close the resources associated with this backend.
- func (backend *Backend) Close() error {
- return backend.db.Close()
- }
- // Get retreives a shortcut from the data store.
- func (backend *Backend) Get(ctx context.Context, name string) (*internal.Route, error) {
- ref := backend.db.Doc("routes/" + name)
- snap, err := ref.Get(ctx)
- if err != nil {
- if status.Code(err) == codes.NotFound {
- return nil, internal.ErrRouteNotFound
- }
- return nil, err
- }
- var rt internal.Route
- if err := snap.DataTo(&rt); err != nil {
- return nil, err
- }
- return &rt, nil
- }
- // Put stores a new shortcut in the data store.
- func (backend *Backend) Put(ctx context.Context, key string, rt *internal.Route) error {
- ref := backend.db.Doc("routes/" + key)
- _, err := ref.Set(ctx, rt)
- if err != nil {
- return err
- }
- return nil
- }
- // Del removes an existing shortcut from the data store.
- func (backend *Backend) Del(ctx context.Context, key string) error {
- ref := backend.db.Doc("routes/" + key)
- _, err := ref.Delete(ctx)
- if err != nil {
- return err
- }
- return nil
- }
- // List all routes in an iterator, starting with the key prefix of start (which can also be nil).
- func (backend *Backend) List(ctx context.Context, start string) (internal.RouteIterator, error) {
- col := backend.db.Collection("routes").OrderBy(fs.DocumentID, fs.Asc)
- if start != "" {
- // we have a starting ID.
- col = col.StartAt(start)
- }
- return &RouteIterator{
- ctx: ctx,
- db: backend.db,
- it: col.Documents(ctx),
- }, nil
- }
- // GetAll gets everything in the db to dump it out for backup purposes
- func (backend *Backend) GetAll(ctx context.Context) (map[string]internal.Route, error) {
- golinks := map[string]internal.Route{}
- col := backend.db.Collection("routes").OrderBy(fs.DocumentID, fs.Asc)
- routes, err := col.Documents(ctx).GetAll()
- if err != nil {
- return nil, err
- }
- for _, doc := range routes {
- var rt internal.Route
- if err := doc.DataTo(&rt); err != nil {
- return nil, err
- }
- golinks[doc.Ref.ID] = rt
- }
- return golinks, nil
- }
- // NextID generates the next numeric ID to be used for an auto-named shortcut.
- func (backend *Backend) NextID(ctx context.Context) (uint64, error) {
- ref := backend.db.Doc("IDs/nextID")
- var nid uint32
- err := backend.db.RunTransaction(ctx, func(ctx context.Context, tx *fs.Transaction) error {
- var nextID *NextID
- doc, err := tx.Get(ref)
- if err != nil {
- if status.Code(err) == codes.NotFound {
- // this is the very first auto-generated ID, we can make it
- // as :1 and return it
- nextID = new(NextID)
- nextID.ID = 1
- nid = 1
- err := tx.Create(ref, nextID)
- if err != nil {
- return err
- }
- return nil
- }
- return err
- }
- if err := doc.DataTo(&nextID); err != nil {
- return err
- }
- nextID.ID += 1
- nid = nextID.ID
- return tx.Set(ref, &nextID)
- })
- if err != nil {
- return 0, err
- }
- return uint64(nid), nil
- }
- func getGoogleProject() string {
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
- creds, err := google.FindDefaultCredentials(ctx)
- if err != nil {
- return ""
- }
- return creds.ProjectID
- }
|