firestruct

package module
v1.0.3 Latest Latest
Warning

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

Go to latest
Published: Jul 1, 2024 License: Apache-2.0 Imports: 9 Imported by: 0

README

firestruct

GoDoc Codecov Go Report Card FOSSA Status

This package deserializes protojson encoded Firestore documents as found in Firestore Cloud Events into a native Go map[string]interface{} or struct (without any protojson tags).

Why Should You Use This Package?

This package is useful when you need to read incoming Firestore documents within a Google Cloud Function written in Go. As of writing, the official Firestore Go SDK is not compatible with Cloud Functions because it does not support Firestore triggers or Cloud Events. This unofficial Go SDK for Firestore Cloud Events implements the missing functionality.

Google Cloud Functions can be triggered by events in Firestore (onCreate, onUpdate, onDelete, onWrite). The resulting http call to your cloud function includes the Firestore document in the http payload, but all the document's fields will be wrapped in protojson type descriptor tags. These tags makes parsing the document much more difficult than it needs to be.

This package makes it trivial to unwrap the encoded Firestore data and unmarshal your Cloud Event into native Go data structures. You can simply re-use the same structs used to create Firestore documents to handle incoming cloud events. This in turn neatly simplifies your data processing and validation within your codebase.

Installation

go get github.com/bennovw/firestruct

Usage

See the examples folder for more examples.

import (
    "github.com/bennovw/firestruct"
)

func MyCloudFunction(ctx context.Context, e event.Event) error {
    cloudEvent := firestruct.FirestoreCloudEvent{}
    err := json.Unmarshal(e.DataEncoded, &cloudEvent)
    if err != nil {
        fmt.Printf("Error unmarshalling firestore cloud event: %s", err)
        return err
    }

    // Extract and unwrap a protojson encoded Firestore document from a Cloud Event
    // Outputs a flattened map[string]interface{} without Firestore protojson tags
    m, err := cloudEvent.ToMap()
    if err != nil {
        fmt.Printf("Error converting firestore document to map: %s", err)
    }

    // Unwrap and unmarshal a protojson encoded Firestore document into a struct
    x := MyStruct{}
    err = cloudEvent.DataTo(&x)
    if err != nil {
        fmt.Printf("Error converting firestore document to MyStruct: %s", err)
        return err
    }

    return nil
}

// Supports all Firestore data types, including nested maps and arrays,
// Firestore struct tags are optional
type MyStruct struct {
    SomeTime   time.Time      `firestore:"timeData"`
    Title      string         `firestore:"stringData"`
    ID         uuid.UUID      `firestore:"uuidData"`
    IsWild     bool           `firestore:"boolData"`
    Age        int64          `firestore:"intData"`
    Weight     float64        `firestore:"doubleData"`
    Bytes      []byte         `firestore:"bytesData"`
    WildNull   any            `firestore:"nilData"`
    Place      latlng.LatLng  `firestore:"geoPointData"`
    NestedData map[string]any `firestore:"nestedMapData"`
}

Advanced Example

The package also provides two stand-alone functions to flatten a subset of Firestore data into a map[string]interface{} or unmarshal data directly into a struct without having to rely on type assertions or json.Marshal followed by json.Unmarshal

import (
    "github.com/bennovw/firestruct"
)

func MyCloudFunction(ctx context.Context, e event.Event) error {
    cloudEvent := firestruct.FirestoreCloudEvent{}
    err := json.Unmarshal(e.DataEncoded, &cloudEvent)
    if err != nil {
        fmt.Printf("Error unmarshalling firestore cloud event: %s", err)
        return err
    }

    // Unwraps a protojson encoded Firestore document, outputs a flattened map[string]interface{}
    uf, err := firestruct.UnwrapFirestoreFields(cloudEvent.Value.Fields)
    if err != nil {
        fmt.Printf("Error unwrapping firestore data: %s", err)
    }

    // Unmarshals a map[string]interface{} directly into a struct
    st := MyStruct{}
    err = firestruct.DataTo(&st, uf)
    if err != nil {
        fmt.Printf("Error populating MyStruct: %s", err)
    }

    return nil
}

License

FOSSA Status

Documentation

Index

Constants

This section is empty.

Variables

View Source
var FirestoreFlatDataTypes = []string{
	"stringValue",
	"booleanValue",
	"integerValue",
	"doubleValue",
	"timestampValue",
	"nullValue",
	"bytesValue",
	"referenceValue",
	"geoPointValue",
}

List of Firestore protojson tags without any nested data structures For full listing of Firestore protojson tags, see https://firebase.google.com/docs/firestore/reference/rest/v1/Value

Functions

func DataTo

func DataTo(pointer interface{}, data any) error

DataTo uses the input data to populate p, which can be a pointer to a struct or a pointer to a map[string]interface{}. You may add tags to your struct fields formatted as `firestore:"changeme"` to specify the Firestore field name to use. If you do not specify a tag, the field name will be used. If the input data contains a field that is not present in the struct, it will be ignored. If the struct contains a field that is not present in the input data, it will be set to its zero value.

func UnwrapFirestoreFields

func UnwrapFirestoreFields(input map[string]any) (map[string]any, error)

UnwrapFirestoreFields unwraps a map[string]any containing one or more nested Firestore protojson encoded fields and returns a Go map[string]any without Firestore protojson tags.

Types

type FirestoreCloudEvent

type FirestoreCloudEvent struct {
	OldValue   FirestoreDocument `firestore:"oldValue,omitempty" json:"oldValue,omitempty"`
	Value      FirestoreDocument `firestore:"value" json:"value"`
	UpdateMask struct {
		FieldPaths []string `firestore:"fieldPaths,omitempty" json:"fieldPaths,omitempty"`
	} `firestore:"updateMask,omitempty" json:"updateMask,omitempty"`
}

A Google Cloud Event fired when a Firestore document is created, updated or deleted. FirestoreCloudEvent is the payload of a Firestore event, it contains the current and old version of the Firestore document triggering the event.

func (*FirestoreCloudEvent) DataTo

func (e *FirestoreCloudEvent) DataTo(p interface{}) error

DataTo uses the current version of the Firestore document to populate p, which should be a pointer to a struct or a pointer to a map[string]interface{}. You may add tags to your struct fields formatted as `firestore:"changeme"` to specify the Firestore field name to use. If you do not specify a tag, the field name will be used. If the Firestore document contains a field that is not present in the struct, it will be ignored. If the struct contains a field that is not present in the Firestore document, it will be set to its zero value.

func (*FirestoreCloudEvent) Document

func (e *FirestoreCloudEvent) Document() *FirestoreDocument

Document is an alias for Value, it returns the current version of the Firestore document triggering the event.

func (*FirestoreCloudEvent) ToMap

func (e *FirestoreCloudEvent) ToMap() (map[string]any, error)

ToMap returns the current version of the Firestore document as an unwrapped map[string]interface{} without any nested protojson type descriptor tags.

type FirestoreDocument

type FirestoreDocument struct {
	Name       string         `firestore:"name,omitempty" json:"name,omitempty"`
	Fields     map[string]any `firestore:"fields,omitempty" json:"fields,omitempty"`
	CreateTime time.Time      `firestore:"createTime,serverTimestamp,omitempty" json:"createTime,omitempty"`
	UpdateTime time.Time      `firestore:"updateTime,serverTimestamp,omitempty" json:"updateTime,omitempty"`
}

A Firestore document. Fields contains Firestore JSON encoded data types, see https://Firestore.google.com/docs/firestore/reference/rest/v1/Value

func (*FirestoreDocument) DataTo

func (d *FirestoreDocument) DataTo(p interface{}) error

DataTo uses the document's fields to populate p, which can be a pointer to a map[string]interface{} or a pointer to a struct. You may add tags to your struct fields formatted as `firestore:"changeme"` to specify the Firestore field name to use. If you do not specify a tag, the field name will be used. If the Firestore document contains a field that is not present in the struct, it will be ignored. If the struct contains a field that is not present in the Firestore document, it will be set to its zero value.

Firestore field values are converted to Go values as follows:

  • Null converts to nil.
  • Bool converts to bool.
  • String converts to string.
  • Integer converts int64. When setting a struct field, any signed or unsigned integer type is permitted except uint, uint64 or uintptr. Overflow is detected and results in an error.
  • Double converts to float64. When setting a struct field, float32 is permitted. Overflow is detected and results in an error.
  • Bytes is converted to []byte.
  • Timestamp converts to time.Time.
  • GeoPoint converts to *latlng.LatLng, where latlng is the package "google.golang.org/genproto/googleapis/type/latlng".
  • Arrays convert to []interface{}. When setting a struct field, the field may be a slice or array of any type and is populated recursively. Slices are resized to the incoming value's size, while arrays that are too long have excess elements filled with zero values. If the array is too short, excess incoming values will be dropped.
  • Maps convert to map[string]interface{}. When setting a struct field, maps of key type string and any value type are permitted, and are populated recursively.
  • WARNING: Firestore document references are NOT handled.

Field names given by struct field tags are observed, as described in DocumentRef.Create.

Only the fields actually present in the document are used to populate p. Other fields of p are left unchanged.

func (*FirestoreDocument) ToMap

func (e *FirestoreDocument) ToMap() (map[string]any, error)

ToMap converts a Firestore document to a native Go map[string]interface{} without protojson tags

Directories

Path Synopsis
internal
fields
Package fields provides a view of the fields of a struct that follows the Go rules, amended to consider tags and case insensitivity.
Package fields provides a view of the fields of a struct that follows the Go rules, amended to consider tags and case insensitivity.

Jump to

Keyboard shortcuts

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