firestruct

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Apr 20, 2023 License: Apache-2.0 Imports: 9 Imported by: 0

README

firestruct

GoDoc Codecov Go Report Card FOSSA Status

This package flattens and unmarshals protojson encoded Firestore documents contained in Cloud Events into a native Go map[string]interface{} or struct (without Firestore protojson tags)

The package is ideal for cloud functions that are triggered by Cloud Events when you need simplified and easy to use Go data structures.

Why Should You Use It

Firestore Cloud Events wrap document fields in protojson type descriptor tags that are difficult to parse in Go. This package simplifies Firestore data by unwrapping documents into a type safe map or struct.

It allows you to re-use the native Go models used to create Firestore documents to also handle incoming cloud events, which in turn simplifies data processing or validation by other Go functions.

Example Usage

See the examples folder for all 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