firebase

package module
v0.0.0-...-2bebe19 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2020 License: MIT Imports: 14 Imported by: 0

README

Go Firebase

NOTE: This repo is in maintenance mode. If you're developing a new application, you should take a look at Google's official Firebase bindings. As of June 2020, this code is still stable and being used in production for real products. Maintenance / bug fixing will still be done if any is needed.

Helper library for invoking the Firebase REST API from your Go program. Supports the following operations:

  • Read and write values using Firebase's REST API operations
  • Stream updates to a Firebase path via the SSE / Event Source protocol
  • Use native Go types/structs in all Firebase operations
  • Server-side timestamps that are automatically converted into native Go times
  • Read and modify security rules

My starting point was the great work of cosn and JustinTulloss. Most of the code has since been refactored, and comprehensive unit tests that do not call out to the network have been added. Also, support for streaming via SSE / Event Source from Firebase has been added.

Please star on Github if you find this library useful! Thanks!

Build Status

Circle CI

Reference Documentation

GoDoc

Installation
  • Setup your GOPATH and workspace. If you are new to Go and you're not sure how to do this, read How to Write Go Code.
  • Dowload the package:
go get -u -t github.com/ereyes01/firebase
Run the Tests
  • To run the tests:
go test -race github.com/ereyes01/firebase
Usage

The usage examples below will use the sample Dinosaur Facts Firebase used in the REST tutorial

client := firebase.NewClient("https://dinosaur-facts.firebaseio.com", "", nil)

Suppose we have a struct defined that matches each entry in the dinosaurs/ path of this firebase. Our struct might be declared as follows:

type Dinosaur struct {
	Appeared int
	Height   float64
	Length   float64
	Order    string
	Vanished int
	Weight   float64
}

We could retrieve the lambeosarus record as follows:

var dino Dinosaur

err := client.Child("dinosaurs/lambeosaurus").Value(&dino)

We could query dinosaurs whose scores are greater than 50 as follows:

dinoScores := make(map[string]int)

err := client.Child("scores").OrderBy("$value").StartAt(50).Value(&dinoScores)

If I wanted to create a new dinosaur score (NOTE: the permissions of this firebase do not allow this), we could try:

value, err := client.Child("scores").Set("velociraptor", 500, nil)

We of course, don't have permissions to write to this Firebase. The error you'd get back should be:

Permission denied

Create your own free test Firebase and feel free to experiment with writing values!

Now suppose we wanted to watch changes to the Triceratops dinosaur in real-time. This, of course, will be a boring example because Triceratops will probably never change. However, this sample demonstrates how you would stream changes to a Firebase path in real-time (and stops streaming after 10 seconds):

    stop := make(chan bool)
	go func() {
		<-time.After(10 * time.Second)
		close(stop)
	}()

    // this helps convert incoming events into Dinosaur objects
    dinoParser := func(path string, data []byte) (interface{}, error) {
		var dino *Dinosaur
		err := json.Unmarshal(data, &dino)
		return dino, err
	}

	events, err := client.Child("dinosaurs/triceratops").Watch(dinoParser, stop)
	if err != nil {
		log.Fatal(err)
	}

	for event := range events {
		if event.Error != nil {
			log.Println("Stream error:", event.Error)
            continue
		}

		if event.UnmarshallerError != nil {
			log.Println("Malformed event:" event.UnmarshallerError)
            continue
		}

		newTriceratops := event.Resource.(*Dinosaur)
	}

The code above will yield a stream of Go Dinosaur objects (or rather, pointers to them). The magic is in the dinoParser callback. This function (passed to Watch) tells the watcher how to parse the json payload of the incoming events- in this case as Dinosaur pointers. When the streaming connection is closed, the events channel also closes.

When you watch a Firebase location, you'll get back an initial event showing the state of the location as it was when you started watching it. Thereafter, you will receive an event when a change happens that matches your criteria, or when some place in the location stopped matching your criteria. This can be a little confusing at first, especially when you combine queries with watching resources. It's just the way Firebase watching of resources works.

You can read more about this behavior in my Stack Overflow question and the subsequent discussion with one of the Firebase dudes.

Please see the Godoc reference for a guide to the code and a more detailed list of operations. Also, please familiarize yourself with Firebase's REST API capabilities before trying anything with this library.

Please open issues for any bugs or suggestions you may have, or send me a PR. Thanks!

Documentation

Overview

Package firebase gives a thin wrapper around the firebase REST API. It tries to mirror the Official firebase API somewhat closely. https://www.firebase.com/docs/web/api/

Index

Constants

View Source
const (
	KeyProp = "$key"
)

Variables

This section is empty.

Functions

func SetStreamTimeout

func SetStreamTimeout(streamTimeout time.Duration)

SetStreamTimeout replaces the connection pool for SSE streaming connections with a new one, using the given duration as the value of its read timeout.

The impetus behind this function is that firebase disruptions of long-lived SSE clients happen occasionally. Connections are observed to remain alive but no longer report events. This function enables consumers of this library to force-set a timeout value for all stream connections to bound the amount of time they may remain open.

WARNING: This function should only be called while there are no SSE stream connections open.

Types

type Api

type Api interface {
	// Call is responsible for performing HTTP transactions such as GET, POST,
	// PUT, PATCH, and DELETE. It is used to communicate with Firebase by all
	// of the Client methods, except for Watch.
	//
	// Arguments are as follows:
	//  - `method`: The http method for this call
	//  - `path`: The full firebase url to call
	//  - `body`: Data to be marshalled to JSON (it's the responsibility of Call to do the marshalling and unmarshalling)
	//  - `params`: Additional parameters to be passed to firebase
	//  - `dest`: The object to save the unmarshalled response body to.
	//    It's up to this method to unmarshal correctly, the default implemenation just uses `json.Unmarshal`
	Call(method, path, auth string, body interface{}, params map[string]string, dest interface{}) error

	// Stream is responsible for implementing a SSE/Event Source client that
	// communicates with Firebase to watch changes to a location in real-time.
	//
	// Arguments are as follows:
	//  - `path`: The full firebase url to call
	//  - `body`: Data to be marshalled to JSON
	//  - `params`: Additional parameters to be passed to firebase
	//  - `stop`: a channel that makes Stream stop listening for events and return when it receives anything
	//
	// Return values:
	//  - `<-RawEvent`: A channel that emits events as they arrive from the stream
	//  - `error`: Non-nil if an error is encountered setting up the listener.
	Stream(path, auth string, body interface{}, params map[string]string, stop <-chan bool) (<-chan RawEvent, error)
}

Api is the internal interface for interacting with Firebase. The internal implementation of this interface is responsible for all HTTP operations that communicate with Firebase.

Users of this library can implement their own Api-conformant types for testing purposes. To use your own test Api type, pass it in to the NewClient function.

type Client

type Client interface {
	// String returns the absolute URL path for the client
	String() string

	// Key returns the last part of the URL path for the client.
	Key() string

	// Value GETs the value referenced by the client and unmarshals it into
	// the passed in destination.
	Value(destination interface{}) error

	// Watch streams changes to the Client's path in real-time, in a separate
	// goroutine.
	//
	// Arguments
	//
	// unmarshaller: Responsible for unmarshalling each resource change event's
	// payload into the desired type. If unmarshaller is nil, each event will
	// be unmarshalled into a map[string]interface{} object.
	//
	// stop: Sending any boolean value to this channel will stop the Watching
	// the client's path.
	//
	// Return Values
	//
	// <-chan StreamEvent - A channel that sends each received event.
	//
	// error - If non-nil, a fatal error was encountered trying to start the
	// Watch method's internal goroutine.
	Watch(unmarshaller EventUnmarshaller, stop <-chan bool) (<-chan StreamEvent, error)

	// Shallow returns a list of keys at a particular location
	// Only supports objects, unlike the REST artument which supports
	// literals. If the location is a literal, use Client#Value()
	Shallow() Client

	// Child returns a reference to the child specified by `path`. This does not
	// actually make a request to firebase, but you can then manipulate the reference
	// by calling one of the other methods (such as `Value`, `Update`, or `Set`).
	Child(path string) Client

	// Query functions. They map directly to the Firebase operations.
	// https://www.firebase.com/docs/rest/guide/retrieving-data.html#section-rest-queries
	OrderBy(prop string) Client
	EqualTo(value interface{}) Client
	StartAt(value interface{}) Client
	EndAt(value interface{}) Client
	LimitToFirst(limit uint) Client
	LimitToLast(limit uint) Client

	// Creates a new value under this reference.
	// Returns a reference to the newly created value.
	// https://www.firebase.com/docs/web/api/firebase/push.html
	Push(value interface{}, params map[string]string) (Client, error)

	// Overwrites the value at the specified path and returns a reference
	// that points to the path specified by `path`
	Set(path string, value interface{}, params map[string]string) (Client, error)

	// Update performs a partial update with the given value at the specified path.
	// Returns an error if the update could not be performed.
	// https://www.firebase.com/docs/web/api/firebase/update.html
	Update(path string, value interface{}, params map[string]string) error

	// Remove deletes the data at the current reference.
	// https://www.firebase.com/docs/web/api/firebase/remove.html
	Remove(path string, params map[string]string) error

	// Rules returns the security rules for the database.
	// https://www.firebase.com/docs/rest/api/#section-security-rules
	Rules(params map[string]string) (*Rules, error)

	// SetRules overwrites the existing security rules with the new rules given.
	// https://www.firebase.com/docs/rest/api/#section-security-rules
	SetRules(rules *Rules, params map[string]string) error
}

func NewClient

func NewClient(root, auth string, api Api) Client

type EventUnmarshaller

type EventUnmarshaller func(path string, data []byte) (interface{}, error)

EventUnmarshaller callback accepts the path of the event, and the data payload as a raw JSON byte slice. The data payload is unmarshalled to any type of the implementor's choosing. The unmarshalled object is returned as an interface{}, or an error is returned if the unmarshal fails.

type FirebaseError

type FirebaseError struct {
	Message string `json:"error"`
}

FirebaseError is a Go representation of the error message sent back by Firebase when a request results in an error.

func (*FirebaseError) Error

func (f *FirebaseError) Error() string

type RawEvent

type RawEvent struct {
	// Event contains the string value of the message's "event:" section.
	Event string

	// Data contains the string value of the message's "data:" section.
	Data string

	// Error contains an error value when the connection was terminated
	// abnormally.
	Error error
}

RawEvent contains the raw event and data payloads of Firebase Event Source protocol messages. This is emitted by the Api's Stream method.

type Rules

type Rules map[string]interface{}

Rules is the structure for security rules.

type ServerTimestamp

type ServerTimestamp time.Time

ServerTimestamp is a Go binding for Firebase's ServerValue.TIMESTAMP fields. When marshalling a variable of ServerTimestamp type into JSON (i.e. to send to Firebase), it takes the following JSON representation, no matter what time value the variable has been assigned:

{".sv":"timestamp"}

When this JSON value is sent to Firebase, it is substituted into a number equal to milliseconds since the epoch, as measured by Firebase's servers.

When reading a value of this type from Firebase, you receive a number equal to milliseconds since the epoch. That value was computed by Firebase when the JSON detailed above was written. A JSON unmarshal of this type will convert a number of ms since the epoch into a time.Time value.

NOTE: This approach results in non-symmetric marshal/unmarshal behavior (i.e. unmarshal(marshal(ServerTimestamp{})) will return an error). It is intended to be used only when reading/writing this value from Firebase.

See the Firebase's documentation of ServerValues and timestamps for more details: https://www.firebase.com/docs/rest/api/#section-server-values

func (ServerTimestamp) MarshalJSON

func (t ServerTimestamp) MarshalJSON() ([]byte, error)

func (*ServerTimestamp) UnmarshalJSON

func (t *ServerTimestamp) UnmarshalJSON(b []byte) error

type StreamEvent

type StreamEvent struct {
	// Event is the type of event, denoted in the protocol by "event: text".
	Event string

	// Path of the changed resource.
	Path string

	// Resource is unmarshalled by unmarshaller callback supplied to Watch.
	Resource interface{}

	// The unparsed string found the event's "data:" section
	RawData string

	// UnmarshallerError contains a non-fatal error encountered when attempting
	// to parse the "data:" section to create the Resource object.
	UnmarshallerError error

	// Error contains an error value when something else goes wrong (i.e.
	// the connection is lost, permission is denied to the watched location,
	// a corrupted event is received, etc.). If the error is fatal, the
	// channel emitting these events will be subsequently closed after this
	// error is delivered.
	Error error
}

StreamEvent contains a parsed Firebase Event Source protocol message received when a watched location changes. This is emitted by the Client's Watch method.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL