dynago

package module
v0.0.0-...-742da85 Latest Latest
Warning

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

Go to latest
Published: May 20, 2015 License: MIT Imports: 13 Imported by: 0

README

Dynago

Dynago is a DynamoDB client API for Go.

This attempts to be a really simple, principle of least-surprise client for the DynamoDB API.

Key design tenets of Dynago:

  • Most actions are done via chaining to build filters and conditions
  • all objects are completely safe for passing between goroutines (even queries and the like)
  • To make understanding easier via docs, we use amazon's naming wherever possible.

Installation

Install using go get:

go get github.com/underarmour/dynago

Docs are at http://godoc.org/github.com/underarmour/dynago

Example

Run a query:

client := dynago.NewClient(endpoint, accessKey, secretKey)

query := client.Query(table).
	FilterExpression("NumViews > :views").
	Param(":views", 50).
	Desc()

result, err := query.Execute()
if err != nil {
	// do something
}
for _, row := range result.Items {
	fmt.Printf("Name: %s, Views: %d", row["Name"], row["NumViews"])
}

Type Marshaling

Dynago attempts to let you use go types instead of having to understand a whole lot about dynamo's internal type system.

Example:

doc := dynago.Document{
	"name": "Bob",
	"age": 45,
	"height": 2.1,
	"address": dynago.Document{
		"city": "Boston",
	},
	"tags": dynago.StringSet{"male", "middle_aged"},
}
client.PutItem("person", doc).Execute()
  • Strings use golang string
  • Numbers can be input as int or float64 but always are returned as dynago.Number to not lose precision.
  • Maps can be either map[string]interface{} or dynago.Document
  • Opaque binary data can be put in []byte
  • String sets, number sets, binary sets are supported using dynago.StringSet dynago.NumberSet dynago.BinarySet
  • Lists are supported using dynago.List

Documentation

Overview

Dynago is a DynamoDB client API for Go.

Dynago differs from other Dynamo clients for Go in that it tries to mirror DynamoDB's core API closely: Most methods, attributes, and names are made following Dynamo's own naming conventions. This allows it to be clear which API is being accessed and allows finding complementary docs on Amazon's side easier.

Filter Chaining

A key design concept is the use of chaining to build filters and conditions, similar to some ORM frameworks. This makes using sub-features like conditional puts, expression post-filtering, and so on to be clearer, because this means a conditional put is simply a PutItem with a condition expression tacked on.

query := client.Query("Table").Limit(40).Desc()
result, err := query.Execute()

All the various item-based query actions are evaluated when you call the Execute() method on a filter chain.

Type Marshaling

Dynago tries to marshal to/from Go types where possible:

  • Strings use golang `string`
  • Numbers can be input as `int` or `float64` but always are returned as `dynago.Number` to not lose precision.
  • Maps can be either `map[string]interface{}` or `dynago.Document`
  • Opaque binary data can be put in `[]byte`
  • String sets, number sets, binary sets are supported using `dynago.StringSet` `dynago.NumberSet` `dynago.BinarySet`
  • Lists are supported using `dynago.List`

Query Parameters

Nearly all the operations on items allow using DynamoDB's expression language to do things like query filtering, attribute projection, and so on. In order to provide literal values, queries are parametric, just like many SQL engines:

SET Foo = Foo + :incr
DELETE Person.#n

DynamoDB has two fields it uses for parameters: ExpressionAttributeNames for name aliases, and ExpressionAttributeValues for parametric values. For simplicity, in the Dynago library both of those are serviced by Param. This is okay because parameters and aliases are non-ambiguous in that the former are named e.g. ":foo" and the latter "#foo".

So a conditional PutItem might look like:

client.PutItem(table, item).
	ConditionExpression("Foo.#n = :fooName").
	Param("#n", "Name").Param(":fooName", "Bob").
	Execute()

In this case, we only execute the query if the value at document path Foo.Name was the string value "Bob". Note we used the "Param" helper for setting both values.

Example (AtomicUpdateItem)
package main

import (
	"fmt"
	"github.com/underarmour/dynago"
)

var client dynago.Client

func main() {
	key := dynago.HashKey("id", 12345)
	result, err := client.UpdateItem("products", key).
		ReturnValues(dynago.ReturnUpdatedNew).
		UpdateExpression("SET SoldCount = SoldCount + :numSold").
		Param(":numSold", 5).
		Execute()
	if err != nil {
		// TODO error handling
	}
	fmt.Printf("We have now sold %d frobbers", result.Attributes["SoldCount"])
}
Output:

Example (Query)
package main

import (
	"fmt"
	"github.com/underarmour/dynago"
)

var endpoint, accessKey, secretKey, table string

func main() {
	client := dynago.NewClient(endpoint, accessKey, secretKey)

	query := client.Query(table).
		FilterExpression("NumViews > :views").
		Param(":views", 50).
		Desc()

	result, err := query.Execute()
	if err != nil {
		// do something
	}
	for _, row := range result.Items {
		fmt.Printf("Name: %s, Views: %d", row["Name"], row["NumViews"])
	}
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var AmazonErrors = []amazonErrorConfig{
	{"ConditionalCheckFailedException", 400, ErrorConditionFailed},
	{"ResourceNotFoundException", 400, ErrorNotFound},
	{"InternalFailure", 500, ErrorInternalFailure},
	{"InternalServerError", 500, ErrorInternalFailure},
	{"IncompleteSignature", 400, ErrorAuth},
	{"InvalidParameterCombination", 400, ErrorInvalidParameter},
	{"InvalidParameterValue", 400, ErrorInvalidParameter},
	{"InvalidQueryParameter", 400, ErrorInvalidParameter},
	{"ItemCollectionSizeLimitExceededException", 400, ErrorCollectionSizeExceeded},
	{"MalformedQueryString", 404, ErrorInvalidParameter},
	{"MissingAction", 400, ErrorInvalidParameter},
	{"MissingAuthenticationToken", 403, ErrorAuth},
	{"MissingParameter", 400, ErrorInvalidParameter},
	{"OptInRequired", 403, ErrorAuth},
	{"ProvisionedThroughputExceededException", 400, ErrorThroughputExceeded},
	{"RequestExpired", 400, ErrorAuth},
	{"ServiceUnavailable", 503, ErrorServiceUnavailable},
	{"ValidationError", 400, ErrorInvalidParameter},
}

This variable is mostly exposed so that we can document how errors are mapped

Functions

This section is empty.

Types

type AmazonError

type AmazonError int
const (
	ErrorUnknown                AmazonError = iota
	ErrorConditionFailed                    // When a conditional put/update fails due to condition not being met
	ErrorCollectionSizeExceeded             // Item collection (local secondary index) too large
	ErrorThroughputExceeded                 // We exceeded our provisioned throughput for this table or shard
	ErrorNotFound                           // Resource referenced by key not found
	ErrorInternalFailure                    // Internal server error
	ErrorAuth                               // Encapsulates various authorization errors
	ErrorInvalidParameter                   // Encapsulates many forms of invalid input errors
	ErrorServiceUnavailable                 // Amazon service unavailable
	ErrorThrottling
)

type BatchWrite

type BatchWrite struct {
	// contains filtered or unexported fields
}

func (BatchWrite) Delete

func (b BatchWrite) Delete(table string, keys ...Document) *BatchWrite

Add some number of deletes for a table.

func (*BatchWrite) Execute

func (b *BatchWrite) Execute() (*BatchWriteResult, error)

func (BatchWrite) Put

func (b BatchWrite) Put(table string, items ...Document) *BatchWrite

Add some number of puts for a table.

type BatchWriteResult

type BatchWriteResult struct {
	UnprocessedItems BatchWriteTableMap
}

type BatchWriteTableEntry

type BatchWriteTableEntry struct {
	DeleteRequest *batchDelete `json:",omitempty"`
	PutRequest    *batchPut    `json:",omitempty"`
}

func (*BatchWriteTableEntry) SetDelete

func (e *BatchWriteTableEntry) SetDelete(key Document)

Set this table entry as a delete request

func (*BatchWriteTableEntry) SetPut

func (e *BatchWriteTableEntry) SetPut(item Document)

type BatchWriteTableMap

type BatchWriteTableMap map[string][]*BatchWriteTableEntry

type BinarySet

type BinarySet [][]byte

type CapacityDetail

type CapacityDetail string
const (
	CapacityIndexes CapacityDetail = "INDEXES"
	CapacityTotal   CapacityDetail = "TOTAL"
	CapacityNone    CapacityDetail = "NONE"
)

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(region string, accessKey string, secretKey string) *Client

Create a new dynamo client.

region is the AWS region, e.g. us-east-1. accessKey is your amazon access key ID. secretKey is your amazon secret key ID.

func NewClientExecutor

func NewClientExecutor(executor Executor) *Client

Create a new client with a custom executor.

This is mainly used for unit test and mock scenarios.

func (*Client) BatchWrite

func (c *Client) BatchWrite() *BatchWrite

Compose a batch write.

Batch writes can compose a number of put or delete, even across multiple tables, in a single operation.

func (*Client) CreateTable

func (c *Client) CreateTable(req *schema.CreateRequest) (*schema.CreateResponse, error)

Create a table.

func (*Client) GetItem

func (c *Client) GetItem(table string, key Document) *GetItem

Compose a GetItem on a dynamo table.

key should be a Document containing enough attributes to describe the primary key.

You can use the HashKey or HashRangeKey helpers to help build a key:

client.GetItem("foo", dynago.HashKey("Id", 45))

client.GetItem("foo", dynago.HashRangeKey("UserId", 45, "Date", "20140512"))

func (*Client) PutItem

func (c *Client) PutItem(table string, item Document) *PutItem

Compose a PutItem on a dynamo table.

item should be a document representing the record and containing the attributes for the primary key.

Like all the other requests, you must call `Execute()` to run this.

func (*Client) Query

func (c *Client) Query(table string) *Query

Compose a Query on a dynamo table.

This returns a new Query struct which you can compose via chaining to build the query you want. Then finish the chain by calling Execute() to run the query.

func (*Client) UpdateItem

func (c *Client) UpdateItem(table string, key Document) *UpdateItem

Compose an UpdateItem on a dynamo table.

type Document

type Document map[string]interface{}

Represents an entire document structure composed of keys and dynamo value

func HashKey

func HashKey(name string, value interface{}) Document

Helper to build a hash key.

func HashRangeKey

func HashRangeKey(hashName string, hashVal interface{}, rangeName string, rangeVal interface{}) Document

Helper to build a hash-range key.

func (Document) GetNumber

func (d Document) GetNumber(key string) Number

func (Document) GetString

func (d Document) GetString(key string) string

Helper to get a string from a document.

func (Document) MarshalJSON

func (d Document) MarshalJSON() ([]byte, error)

func (*Document) UnmarshalJSON

func (d *Document) UnmarshalJSON(buf []byte) error

type Error

type Error struct {
	Type          AmazonError    // Parsed and mapped down type
	AmazonRawType string         // Raw error type from amazon
	Exception     string         // Exception from amazon
	Message       string         // Raw message from amazon
	Response      *http.Response // If available, HTTP response
	ResponseBody  []byte         // If available, raw response body bytes
}

Encapsulates errors coming from amazon/dynamodb

func (*Error) Error

func (e *Error) Error() string

type Executor

type Executor interface {
	GetItem(*GetItem) (*GetItemResult, error)
	PutItem(*PutItem) (*PutItemResult, error)
	Query(*Query) (*QueryResult, error)
	UpdateItem(*UpdateItem) (*UpdateItemResult, error)
	CreateTable(*schema.CreateRequest) (*schema.CreateResponse, error)
	BatchWriteItem(*BatchWrite) (*BatchWriteResult, error)
}

This interface defines how all the various queries manage their internal execution logic.

Executor is primarily provided so that testing and mocking can be done on the API level, not just the transport level.

func NewAwsExecutor

func NewAwsExecutor(endpoint, region, accessKey, secretKey string) Executor

Create an AWS executor with a specified endpoint and AWS parameters.

type GetItem

type GetItem struct {
	// contains filtered or unexported fields
}

func (GetItem) ConsistentRead

func (p GetItem) ConsistentRead() *GetItem

Set up this get to be a strongly consistent read.

func (*GetItem) Execute

func (p *GetItem) Execute() (result *GetItemResult, err error)

Execute the get item.

func (GetItem) Param

func (p GetItem) Param(key string, value interface{}) *GetItem

Shortcut to set an ExpressionAttributeValue for used in expression query

func (GetItem) Params

func (p GetItem) Params(params ...interface{}) *GetItem

func (GetItem) ProjectionExpression

func (p GetItem) ProjectionExpression(expression string) *GetItem

Set the ProjectionExpression for this GetItem (which attributes to get)

type GetItemResult

type GetItemResult struct {
	Item             Document
	ConsumedCapacity interface{} // TODO
}

The result from executing a GetItem.

type List

type List []interface{}

type Number

type Number string

func (Number) FloatVal

func (n Number) FloatVal() (float64, error)

func (Number) IntVal

func (n Number) IntVal() (int, error)

type NumberSet

type NumberSet []string

type Param

type Param struct {
	Key   string
	Value interface{}
}

type PutItem

type PutItem struct {
	// contains filtered or unexported fields
}

func (PutItem) ConditionExpression

func (p PutItem) ConditionExpression(expression string) *PutItem

Set a ConditionExpression to do a conditional PutItem.

func (*PutItem) Execute

func (p *PutItem) Execute() (res *PutItemResult, err error)

Actually Execute this putitem.

PutItemResult will be nil unless ReturnValues or ReturnConsumedCapacity is set.

func (PutItem) Param

func (p PutItem) Param(key string, value interface{}) *PutItem

Set parameter for ConditionExpression

func (PutItem) Params

func (p PutItem) Params(params ...interface{}) *PutItem

func (PutItem) ReturnValues

func (p PutItem) ReturnValues(returnValues ReturnValues) *PutItem

Set ReturnValues.

type PutItemResult

type PutItemResult struct {
	Attributes Document
}

type Query

type Query struct {
	// contains filtered or unexported fields
}

func (Query) ConsistentRead

func (q Query) ConsistentRead(strong bool) *Query

If strong is true, do a strongly consistent read. (defaults to false)

func (Query) Desc

func (q Query) Desc() *Query

Return results descending.

func (*Query) Execute

func (q *Query) Execute() (result *QueryResult, err error)

Execute this query and return results.

func (Query) FilterExpression

func (q Query) FilterExpression(expression string, params ...interface{}) *Query

Set a post-filter expression for the results we scan.

func (Query) KeyConditionExpression

func (q Query) KeyConditionExpression(expression string) *Query

Set a condition expression on the key to narrow down what we scan

func (Query) Limit

func (q Query) Limit(limit uint) *Query

func (Query) Param

func (q Query) Param(key string, value interface{}) *Query

Shortcut to set a single parameter for ExpressionAttributeValues.

func (Query) Params

func (q Query) Params(params ...interface{}) *Query

Set a param, a document of params, or multiple params

func (Query) ProjectionExpression

func (q Query) ProjectionExpression(expression string) *Query

Set a Projection Expression for controlling which attributes are returned.

type QueryResult

type QueryResult struct {
	Items []Document
	Count int // The total number of items (for pagination)
}

The result returned from a query.

type ReturnValues

type ReturnValues string
const (
	ReturnNone       ReturnValues = "NONE"
	ReturnAllOld     ReturnValues = "ALL_OLD"
	ReturnUpdatedOld ReturnValues = "UPDATED_OLD"
	ReturnAllNew     ReturnValues = "ALL_NEW"
	ReturnUpdatedNew ReturnValues = "UPDATED_NEW"
)

type Select

type Select string
const (
	SelectAllAttributes      Select = "ALL_ATTRIBUTES"
	SelectAllProjected       Select = "ALL_PROJECTED_ATTRIBUTES"
	SelectCount              Select = "COUNT"
	SelectSpecificAttributes Select = "SPECIFIC_ATTRIBUTES"
)

type StringSet

type StringSet []string

type UpdateItem

type UpdateItem struct {
	// contains filtered or unexported fields
}

func (*UpdateItem) ConditionExpression

func (u *UpdateItem) ConditionExpression(expression string) *UpdateItem

Set a condition expression for conditional update.

func (*UpdateItem) Execute

func (u *UpdateItem) Execute() (res *UpdateItemResult, err error)

Execute this UpdateItem and return the result.

func (UpdateItem) Param

func (u UpdateItem) Param(key string, value interface{}) *UpdateItem

Quick-set a single parameter

func (UpdateItem) Params

func (u UpdateItem) Params(params ...interface{}) *UpdateItem

Set multiple parameters at once.

func (*UpdateItem) ReturnValues

func (u *UpdateItem) ReturnValues(returnValues ReturnValues) *UpdateItem

If set, then we will get return values of either updated or old fields (see ReturnValues const)

func (*UpdateItem) UpdateExpression

func (u *UpdateItem) UpdateExpression(expression string) *UpdateItem

Set an update expression to update specific fields and values.

type UpdateItemResult

type UpdateItemResult struct {
	Attributes Document
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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