summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/goamz/goamz/exp/sdb
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/goamz/goamz/exp/sdb')
-rw-r--r--vendor/github.com/goamz/goamz/exp/sdb/export_test.go9
-rw-r--r--vendor/github.com/goamz/goamz/exp/sdb/responses_test.go120
-rw-r--r--vendor/github.com/goamz/goamz/exp/sdb/sdb.go413
-rw-r--r--vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go219
-rw-r--r--vendor/github.com/goamz/goamz/exp/sdb/sign.go54
-rw-r--r--vendor/github.com/goamz/goamz/exp/sdb/sign_test.go29
6 files changed, 844 insertions, 0 deletions
diff --git a/vendor/github.com/goamz/goamz/exp/sdb/export_test.go b/vendor/github.com/goamz/goamz/exp/sdb/export_test.go
new file mode 100644
index 000000000..7807a914a
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/exp/sdb/export_test.go
@@ -0,0 +1,9 @@
+package sdb
+
+import (
+ "github.com/goamz/goamz/aws"
+)
+
+func Sign(auth aws.Auth, method, path string, params map[string][]string, headers map[string][]string) {
+ sign(auth, method, path, params, headers)
+}
diff --git a/vendor/github.com/goamz/goamz/exp/sdb/responses_test.go b/vendor/github.com/goamz/goamz/exp/sdb/responses_test.go
new file mode 100644
index 000000000..034c2b31c
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/exp/sdb/responses_test.go
@@ -0,0 +1,120 @@
+package sdb_test
+
+var TestCreateDomainXmlOK = `
+<?xml version="1.0"?>
+<CreateDomainResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
+ <ResponseMetadata>
+ <RequestId>63264005-7a5f-e01a-a224-395c63b89f6d</RequestId>
+ <BoxUsage>0.0055590279</BoxUsage>
+ </ResponseMetadata>
+</CreateDomainResponse>
+`
+
+var TestListDomainsXmlOK = `
+<?xml version="1.0"?>
+<ListDomainsResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
+ <ListDomainsResult>
+ <DomainName>Account</DomainName>
+ <DomainName>Domain</DomainName>
+ <DomainName>Record</DomainName>
+ </ListDomainsResult>
+ <ResponseMetadata>
+ <RequestId>15fcaf55-9914-63c2-21f3-951e31193790</RequestId>
+ <BoxUsage>0.0000071759</BoxUsage>
+ </ResponseMetadata>
+</ListDomainsResponse>
+`
+
+var TestListDomainsWithNextTokenXmlOK = `
+<?xml version="1.0"?>
+<ListDomainsResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
+ <ListDomainsResult>
+ <DomainName>Domain1-200706011651</DomainName>
+ <DomainName>Domain2-200706011652</DomainName>
+ <NextToken>TWV0ZXJpbmdUZXN0RG9tYWluMS0yMDA3MDYwMTE2NTY=</NextToken>
+ </ListDomainsResult>
+ <ResponseMetadata>
+ <RequestId>eb13162f-1b95-4511-8b12-489b86acfd28</RequestId>
+ <BoxUsage>0.0000219907</BoxUsage>
+ </ResponseMetadata>
+</ListDomainsResponse>
+`
+
+var TestDeleteDomainXmlOK = `
+<?xml version="1.0"?>
+<DeleteDomainResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
+ <ResponseMetadata>
+ <RequestId>039e1e25-9a64-2a74-93da-2fda36122a97</RequestId>
+ <BoxUsage>0.0055590278</BoxUsage>
+ </ResponseMetadata>
+</DeleteDomainResponse>
+`
+
+var TestDomainMetadataXmlNoSuchDomain = `
+<?xml version="1.0"?>
+<Response xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
+ <Errors>
+ <Error>
+ <Code>NoSuchDomain</Code>
+ <Message>The specified domain does not exist.</Message>
+ <BoxUsage>0.0000071759</BoxUsage>
+ </Error>
+ </Errors>
+ <RequestID>e050cea2-a772-f90e-2cb0-98ebd42c2898</RequestID>
+</Response>
+`
+
+var TestPutAttrsXmlOK = `
+<?xml version="1.0"?>
+<PutAttributesResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
+ <ResponseMetadata>
+ <RequestId>490206ce-8292-456c-a00f-61b335eb202b</RequestId>
+ <BoxUsage>0.0000219907</BoxUsage>
+ </ResponseMetadata>
+</PutAttributesResponse>
+`
+
+var TestAttrsXmlOK = `
+<?xml version="1.0"?>
+<GetAttributesResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
+ <GetAttributesResult>
+ <Attribute><Name>Color</Name><Value>Blue</Value></Attribute>
+ <Attribute><Name>Size</Name><Value>Med</Value></Attribute>
+ </GetAttributesResult>
+ <ResponseMetadata>
+ <RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId>
+ <BoxUsage>0.0000219942</BoxUsage>
+ </ResponseMetadata>
+</GetAttributesResponse>
+`
+
+var TestSelectXmlOK = `
+<?xml version="1.0"?>
+<SelectResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
+ <SelectResult>
+ <Item>
+ <Name>Item_03</Name>
+ <Attribute><Name>Category</Name><Value>Clothes</Value></Attribute>
+ <Attribute><Name>Subcategory</Name><Value>Pants</Value></Attribute>
+ <Attribute><Name>Name</Name><Value>Sweatpants</Value></Attribute>
+ <Attribute><Name>Color</Name><Value>Blue</Value></Attribute>
+ <Attribute><Name>Color</Name><Value>Yellow</Value></Attribute>
+ <Attribute><Name>Color</Name><Value>Pink</Value></Attribute>
+ <Attribute><Name>Size</Name><Value>Large</Value></Attribute>
+ </Item>
+ <Item>
+ <Name>Item_06</Name>
+ <Attribute><Name>Category</Name><Value>Motorcycle Parts</Value></Attribute>
+ <Attribute><Name>Subcategory</Name><Value>Bodywork</Value></Attribute>
+ <Attribute><Name>Name</Name><Value>Fender Eliminator</Value></Attribute>
+ <Attribute><Name>Color</Name><Value>Blue</Value></Attribute>
+ <Attribute><Name>Make</Name><Value>Yamaha</Value></Attribute>
+ <Attribute><Name>Model</Name><Value>R1</Value></Attribute>
+ </Item>
+ </SelectResult>
+ <ResponseMetadata>
+ <RequestId>b1e8f1f7-42e9-494c-ad09-2674e557526d</RequestId>
+ <BoxUsage>0.0000219907</BoxUsage>
+ </ResponseMetadata>
+</SelectResponse>
+`
diff --git a/vendor/github.com/goamz/goamz/exp/sdb/sdb.go b/vendor/github.com/goamz/goamz/exp/sdb/sdb.go
new file mode 100644
index 000000000..0d13f4a9e
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/exp/sdb/sdb.go
@@ -0,0 +1,413 @@
+//
+// goamz - Go packages to interact with the Amazon Web Services.
+//
+// https://wiki.ubuntu.com/goamz
+//
+// Copyright (c) 2011 AppsAttic Ltd.
+//
+// sdb package written by:
+//
+// Andrew Chilton <chilts@appsattic.com>
+// Brad Rydzewski <brad.rydzewski@gmail.com>
+
+// This package is in an experimental state, and does not currently
+// follow conventions and style of the rest of goamz or common
+// Go conventions. It must be polished before it's considered a
+// first-class package in goamz.
+package sdb
+
+// BUG: SelectResp isn't properly organized. It must change.
+
+//
+
+import (
+ "encoding/xml"
+ "github.com/goamz/goamz/aws"
+ "log"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "strconv"
+ "time"
+)
+
+const debug = false
+
+// The SDB type encapsulates operations with a specific SimpleDB region.
+type SDB struct {
+ aws.Auth
+ aws.Region
+ private byte // Reserve the right of using private data.
+}
+
+// New creates a new SDB.
+func New(auth aws.Auth, region aws.Region) *SDB {
+ return &SDB{auth, region, 0}
+}
+
+// The Domain type represents a collection of items that are described
+// by name-value attributes.
+type Domain struct {
+ *SDB
+ Name string
+}
+
+// Domain returns a Domain with the given name.
+func (sdb *SDB) Domain(name string) *Domain {
+ return &Domain{sdb, name}
+}
+
+// The Item type represent individual objects that contain one or more
+// name-value attributes stored within a SDB Domain as rows.
+type Item struct {
+ *SDB
+ *Domain
+ Name string
+}
+
+// Item returns an Item with the given name.
+func (domain *Domain) Item(name string) *Item {
+ return &Item{domain.SDB, domain, name}
+}
+
+// The Attr type represent categories of data that can be assigned to items.
+type Attr struct {
+ Name string
+ Value string
+}
+
+// ----------------------------------------------------------------------------
+// Service-level operations.
+
+// --- ListDomains
+
+// Response to a ListDomains request.
+//
+// See http://goo.gl/3u0Cf for more details.
+type ListDomainsResp struct {
+ Domains []string `xml:"ListDomainsResult>DomainName"`
+ NextToken string `xml:"ListDomainsResult>NextToken"`
+ ResponseMetadata ResponseMetadata
+}
+
+// ListDomains lists all domains in sdb.
+//
+// See http://goo.gl/Dsw15 for more details.
+func (sdb *SDB) ListDomains() (resp *ListDomainsResp, err error) {
+ return sdb.ListDomainsN(0, "")
+}
+
+// ListDomainsN lists domains in sdb up to maxDomains.
+// If nextToken is not empty, domains listed will start at the given token.
+//
+// See http://goo.gl/Dsw15 for more details.
+func (sdb *SDB) ListDomainsN(maxDomains int, nextToken string) (resp *ListDomainsResp, err error) {
+ params := makeParams("ListDomains")
+ if maxDomains != 0 {
+ params["MaxNumberOfDomains"] = []string{strconv.Itoa(maxDomains)}
+ }
+ if nextToken != "" {
+ params["NextToken"] = []string{nextToken}
+ }
+ resp = &ListDomainsResp{}
+ err = sdb.query(nil, nil, params, nil, resp)
+ return
+}
+
+// --- SelectExpression
+
+// Response to a Select request.
+//
+// See http://goo.gl/GTsSZ for more details.
+type SelectResp struct {
+ Items []struct {
+ Name string
+ Attrs []Attr `xml:"Attribute"`
+ } `xml:"SelectResult>Item"`
+ ResponseMetadata ResponseMetadata
+}
+
+// Select returns a set of items and attributes that match expr.
+// Select is similar to the standard SQL SELECT statement.
+//
+// See http://goo.gl/GTsSZ for more details.
+func (sdb *SDB) Select(expr string, consistent bool) (resp *SelectResp, err error) {
+ resp = &SelectResp{}
+ params := makeParams("Select")
+ params["SelectExpression"] = []string{expr}
+ if consistent {
+ params["ConsistentRead"] = []string{"true"}
+ }
+ err = sdb.query(nil, nil, params, nil, resp)
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Domain-level operations.
+
+// --- CreateDomain
+
+// CreateDomain creates a new domain.
+//
+// See http://goo.gl/jDjGH for more details.
+func (domain *Domain) CreateDomain() (resp *SimpleResp, err error) {
+ params := makeParams("CreateDomain")
+ resp = &SimpleResp{}
+ err = domain.SDB.query(domain, nil, params, nil, resp)
+ return
+}
+
+// DeleteDomain deletes an existing domain.
+//
+// See http://goo.gl/S0dCL for more details.
+func (domain *Domain) DeleteDomain() (resp *SimpleResp, err error) {
+ params := makeParams("DeleteDomain")
+ resp = &SimpleResp{}
+ err = domain.SDB.query(domain, nil, params, nil, resp)
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Item-level operations.
+
+type PutAttrs struct {
+ attrs []Attr
+ expected []Attr
+ replace map[string]bool
+ missing map[string]bool
+}
+
+func (pa *PutAttrs) Add(name, value string) {
+ pa.attrs = append(pa.attrs, Attr{name, value})
+}
+
+func (pa *PutAttrs) Replace(name, value string) {
+ pa.Add(name, value)
+ if pa.replace == nil {
+ pa.replace = make(map[string]bool)
+ }
+ pa.replace[name] = true
+}
+
+// The PutAttrs request will only succeed if the existing
+// item in SimpleDB contains a matching name / value pair.
+func (pa *PutAttrs) IfValue(name, value string) {
+ pa.expected = append(pa.expected, Attr{name, value})
+}
+
+// Flag to test the existence of an attribute while performing
+// conditional updates. X can be any positive integer or 0.
+//
+// This should set Expected.N.Name=name and Expected.N.Exists=false
+func (pa *PutAttrs) IfMissing(name string) {
+ if pa.missing == nil {
+ pa.missing = make(map[string]bool)
+ }
+ pa.missing[name] = true
+}
+
+// PutAttrs adds attrs to item.
+//
+// See http://goo.gl/yTAV4 for more details.
+func (item *Item) PutAttrs(attrs *PutAttrs) (resp *SimpleResp, err error) {
+ params := makeParams("PutAttributes")
+ resp = &SimpleResp{}
+
+ // copy these attrs over to the parameters
+ itemNum := 1
+ for _, attr := range attrs.attrs {
+ itemNumStr := strconv.Itoa(itemNum)
+
+ // do the name, value and replace
+ params["Attribute."+itemNumStr+".Name"] = []string{attr.Name}
+ params["Attribute."+itemNumStr+".Value"] = []string{attr.Value}
+
+ if _, ok := attrs.replace[attr.Name]; ok {
+ params["Attribute."+itemNumStr+".Replace"] = []string{"true"}
+ }
+
+ itemNum++
+ }
+
+ //append expected values to params
+ expectedNum := 1
+ for _, attr := range attrs.expected {
+ expectedNumStr := strconv.Itoa(expectedNum)
+ params["Expected."+expectedNumStr+".Name"] = []string{attr.Name}
+ params["Expected."+expectedNumStr+".Value"] = []string{attr.Value}
+
+ if attrs.missing[attr.Name] {
+ params["Expected."+expectedNumStr+".Exists"] = []string{"false"}
+ }
+ expectedNum++
+ }
+
+ err = item.query(params, nil, resp)
+ if err != nil {
+ return nil, err
+ }
+ return
+}
+
+// Response to an Attrs request.
+//
+// See http://goo.gl/45X1M for more details.
+type AttrsResp struct {
+ Attrs []Attr `xml:"GetAttributesResult>Attribute"`
+ ResponseMetadata ResponseMetadata
+}
+
+// Attrs returns one or more of the named attributes, or
+// all of item's attributes if names is nil.
+// If consistent is true, previous writes will necessarily
+// be observed.
+//
+// See http://goo.gl/45X1M for more details.
+func (item *Item) Attrs(names []string, consistent bool) (resp *AttrsResp, err error) {
+ params := makeParams("GetAttributes")
+ params["ItemName"] = []string{item.Name}
+ if consistent {
+ params["ConsistentRead"] = []string{"true"}
+ }
+
+ // Copy these attributes over to the parameters
+ for i, name := range names {
+ params["AttributeName."+strconv.Itoa(i+1)] = []string{name}
+ }
+
+ resp = &AttrsResp{}
+ err = item.query(params, nil, resp)
+ if err != nil {
+ return nil, err
+ }
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Generic data structures for all requests/responses.
+
+// Error encapsulates an error returned by SDB.
+type Error struct {
+ StatusCode int // HTTP status code (200, 403, ...)
+ StatusMsg string // HTTP status message ("Service Unavailable", "Bad Request", ...)
+ Code string // SimpleDB error code ("InvalidParameterValue", ...)
+ Message string // The human-oriented error message
+ RequestId string // A unique ID for this request
+ BoxUsage float64 // The measure of machine utilization for this request.
+}
+
+func (err *Error) Error() string {
+ return err.Message
+}
+
+// SimpleResp represents a response to an SDB request which on success
+// will return no other information besides ResponseMetadata.
+type SimpleResp struct {
+ ResponseMetadata ResponseMetadata
+}
+
+// ResponseMetadata
+type ResponseMetadata struct {
+ RequestId string // A unique ID for tracking the request
+ BoxUsage float64 // The measure of machine utilization for this request.
+}
+
+func buildError(r *http.Response) error {
+ err := Error{}
+ err.StatusCode = r.StatusCode
+ err.StatusMsg = r.Status
+ xml.NewDecoder(r.Body).Decode(&err)
+ return &err
+}
+
+// ----------------------------------------------------------------------------
+// Request dispatching logic.
+
+func (item *Item) query(params url.Values, headers http.Header, resp interface{}) error {
+ return item.Domain.SDB.query(item.Domain, item, params, headers, resp)
+}
+
+func (domain *Domain) query(item *Item, params url.Values, headers http.Header, resp interface{}) error {
+ return domain.SDB.query(domain, item, params, headers, resp)
+}
+
+func (sdb *SDB) query(domain *Domain, item *Item, params url.Values, headers http.Header, resp interface{}) error {
+ // all SimpleDB operations have path="/"
+ method := "GET"
+ path := "/"
+
+ // if we have been given no headers or params, create them
+ if headers == nil {
+ headers = map[string][]string{}
+ }
+ if params == nil {
+ params = map[string][]string{}
+ }
+
+ // setup some default parameters
+ params["Version"] = []string{"2009-04-15"}
+ params["Timestamp"] = []string{time.Now().UTC().Format(time.RFC3339)}
+
+ // set the DomainName param (every request must have one)
+ if domain != nil {
+ params["DomainName"] = []string{domain.Name}
+ }
+
+ // set the ItemName if we have one
+ if item != nil {
+ params["ItemName"] = []string{item.Name}
+ }
+
+ // check the endpoint URL
+ u, err := url.Parse(sdb.Region.SDBEndpoint)
+ if err != nil {
+ return err
+ }
+ headers["Host"] = []string{u.Host}
+ sign(sdb.Auth, method, path, params, headers)
+
+ u.Path = path
+ if len(params) > 0 {
+ u.RawQuery = params.Encode()
+ }
+ req := http.Request{
+ URL: u,
+ Method: method,
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Close: true,
+ Header: headers,
+ }
+
+ if v, ok := headers["Content-Length"]; ok {
+ req.ContentLength, _ = strconv.ParseInt(v[0], 10, 64)
+ delete(headers, "Content-Length")
+ }
+
+ r, err := http.DefaultClient.Do(&req)
+ if err != nil {
+ return err
+ }
+ defer r.Body.Close()
+
+ if debug {
+ dump, _ := httputil.DumpResponse(r, true)
+ log.Printf("response:\n")
+ log.Printf("%v\n}\n", string(dump))
+ }
+
+ // status code is always 200 when successful (since we're always doing a GET)
+ if r.StatusCode != 200 {
+ return buildError(r)
+ }
+
+ // everything was fine, so unmarshal the XML and return what it's err is (if any)
+ err = xml.NewDecoder(r.Body).Decode(resp)
+ return err
+}
+
+func makeParams(action string) map[string][]string {
+ params := make(map[string][]string)
+ params["Action"] = []string{action}
+ return params
+}
diff --git a/vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go b/vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go
new file mode 100644
index 000000000..83e7f9f86
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/exp/sdb/sdb_test.go
@@ -0,0 +1,219 @@
+package sdb_test
+
+import (
+ "testing"
+
+ "github.com/goamz/goamz/aws"
+ "github.com/goamz/goamz/exp/sdb"
+ "github.com/goamz/goamz/testutil"
+ . "gopkg.in/check.v1"
+)
+
+func Test(t *testing.T) {
+ TestingT(t)
+}
+
+var _ = Suite(&S{})
+
+type S struct {
+ sdb *sdb.SDB
+}
+
+var testServer = testutil.NewHTTPServer()
+
+func (s *S) SetUpSuite(c *C) {
+ testServer.Start()
+ auth := aws.Auth{AccessKey: "abc", SecretKey: "123"}
+ s.sdb = sdb.New(auth, aws.Region{SDBEndpoint: testServer.URL})
+}
+
+func (s *S) TearDownTest(c *C) {
+ testServer.Flush()
+}
+
+func (s *S) TestCreateDomainOK(c *C) {
+ testServer.Response(200, nil, TestCreateDomainXmlOK)
+
+ domain := s.sdb.Domain("domain")
+ resp, err := domain.CreateDomain()
+ req := testServer.WaitRequest()
+
+ c.Assert(req.Method, Equals, "GET")
+ c.Assert(req.URL.Path, Equals, "/")
+ c.Assert(req.Header["Date"], Not(Equals), "")
+
+ c.Assert(resp.ResponseMetadata.RequestId, Equals, "63264005-7a5f-e01a-a224-395c63b89f6d")
+ c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0055590279)
+
+ c.Assert(err, IsNil)
+}
+
+func (s *S) TestListDomainsOK(c *C) {
+ testServer.Response(200, nil, TestListDomainsXmlOK)
+
+ resp, err := s.sdb.ListDomains()
+ req := testServer.WaitRequest()
+
+ c.Assert(req.Method, Equals, "GET")
+ c.Assert(req.URL.Path, Equals, "/")
+ c.Assert(req.Header["Date"], Not(Equals), "")
+
+ c.Assert(resp.ResponseMetadata.RequestId, Equals, "15fcaf55-9914-63c2-21f3-951e31193790")
+ c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000071759)
+ c.Assert(resp.Domains, DeepEquals, []string{"Account", "Domain", "Record"})
+
+ c.Assert(err, IsNil)
+}
+
+func (s *S) TestListDomainsWithNextTokenXmlOK(c *C) {
+ testServer.Response(200, nil, TestListDomainsWithNextTokenXmlOK)
+
+ resp, err := s.sdb.ListDomains()
+ req := testServer.WaitRequest()
+
+ c.Assert(req.Method, Equals, "GET")
+ c.Assert(req.URL.Path, Equals, "/")
+ c.Assert(req.Header["Date"], Not(Equals), "")
+
+ c.Assert(resp.ResponseMetadata.RequestId, Equals, "eb13162f-1b95-4511-8b12-489b86acfd28")
+ c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907)
+ c.Assert(resp.Domains, DeepEquals, []string{"Domain1-200706011651", "Domain2-200706011652"})
+ c.Assert(resp.NextToken, Equals, "TWV0ZXJpbmdUZXN0RG9tYWluMS0yMDA3MDYwMTE2NTY=")
+
+ c.Assert(err, IsNil)
+}
+
+func (s *S) TestDeleteDomainOK(c *C) {
+ testServer.Response(200, nil, TestDeleteDomainXmlOK)
+
+ domain := s.sdb.Domain("domain")
+ resp, err := domain.DeleteDomain()
+ req := testServer.WaitRequest()
+
+ c.Assert(req.Method, Equals, "GET")
+ c.Assert(req.URL.Path, Equals, "/")
+ c.Assert(req.Header["Date"], Not(Equals), "")
+
+ c.Assert(resp.ResponseMetadata.RequestId, Equals, "039e1e25-9a64-2a74-93da-2fda36122a97")
+ c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0055590278)
+
+ c.Assert(err, IsNil)
+}
+
+func (s *S) TestPutAttrsOK(c *C) {
+ testServer.Response(200, nil, TestPutAttrsXmlOK)
+
+ domain := s.sdb.Domain("MyDomain")
+ item := domain.Item("Item123")
+
+ putAttrs := new(sdb.PutAttrs)
+ putAttrs.Add("FirstName", "john")
+ putAttrs.Add("LastName", "smith")
+ putAttrs.Replace("MiddleName", "jacob")
+
+ putAttrs.IfValue("FirstName", "john")
+ putAttrs.IfMissing("FirstName")
+
+ resp, err := item.PutAttrs(putAttrs)
+ req := testServer.WaitRequest()
+ c.Assert(req.Method, Equals, "GET")
+ c.Assert(req.URL.Path, Equals, "/")
+ c.Assert(req.Form["Action"], DeepEquals, []string{"PutAttributes"})
+ c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"})
+ c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"})
+ c.Assert(req.Form["Attribute.1.Name"], DeepEquals, []string{"FirstName"})
+ c.Assert(req.Form["Attribute.1.Value"], DeepEquals, []string{"john"})
+ c.Assert(req.Form["Attribute.2.Name"], DeepEquals, []string{"LastName"})
+ c.Assert(req.Form["Attribute.2.Value"], DeepEquals, []string{"smith"})
+ c.Assert(req.Form["Attribute.3.Name"], DeepEquals, []string{"MiddleName"})
+ c.Assert(req.Form["Attribute.3.Value"], DeepEquals, []string{"jacob"})
+ c.Assert(req.Form["Attribute.3.Replace"], DeepEquals, []string{"true"})
+
+ c.Assert(req.Form["Expected.1.Name"], DeepEquals, []string{"FirstName"})
+ c.Assert(req.Form["Expected.1.Value"], DeepEquals, []string{"john"})
+ c.Assert(req.Form["Expected.1.Exists"], DeepEquals, []string{"false"})
+
+ c.Assert(err, IsNil)
+ c.Assert(resp.ResponseMetadata.RequestId, Equals, "490206ce-8292-456c-a00f-61b335eb202b")
+ c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907)
+
+}
+
+func (s *S) TestAttrsOK(c *C) {
+ testServer.Response(200, nil, TestAttrsXmlOK)
+
+ domain := s.sdb.Domain("MyDomain")
+ item := domain.Item("Item123")
+
+ resp, err := item.Attrs(nil, true)
+ req := testServer.WaitRequest()
+
+ c.Assert(req.Method, Equals, "GET")
+ c.Assert(req.URL.Path, Equals, "/")
+ c.Assert(req.Header["Date"], Not(Equals), "")
+ c.Assert(req.Form["Action"], DeepEquals, []string{"GetAttributes"})
+ c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"})
+ c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"})
+ c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"})
+
+ c.Assert(resp.Attrs[0].Name, Equals, "Color")
+ c.Assert(resp.Attrs[0].Value, Equals, "Blue")
+ c.Assert(resp.Attrs[1].Name, Equals, "Size")
+ c.Assert(resp.Attrs[1].Value, Equals, "Med")
+ c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d")
+ c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219942)
+
+ c.Assert(err, IsNil)
+}
+
+func (s *S) TestAttrsSelectOK(c *C) {
+ testServer.Response(200, nil, TestAttrsXmlOK)
+
+ domain := s.sdb.Domain("MyDomain")
+ item := domain.Item("Item123")
+
+ resp, err := item.Attrs([]string{"Color", "Size"}, true)
+ req := testServer.WaitRequest()
+
+ c.Assert(req.Method, Equals, "GET")
+ c.Assert(req.URL.Path, Equals, "/")
+ c.Assert(req.Header["Date"], Not(Equals), "")
+ c.Assert(req.Form["Action"], DeepEquals, []string{"GetAttributes"})
+ c.Assert(req.Form["ItemName"], DeepEquals, []string{"Item123"})
+ c.Assert(req.Form["DomainName"], DeepEquals, []string{"MyDomain"})
+ c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"})
+ c.Assert(req.Form["AttributeName.1"], DeepEquals, []string{"Color"})
+ c.Assert(req.Form["AttributeName.2"], DeepEquals, []string{"Size"})
+
+ c.Assert(resp.Attrs[0].Name, Equals, "Color")
+ c.Assert(resp.Attrs[0].Value, Equals, "Blue")
+ c.Assert(resp.Attrs[1].Name, Equals, "Size")
+ c.Assert(resp.Attrs[1].Value, Equals, "Med")
+ c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d")
+ c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219942)
+
+ c.Assert(err, IsNil)
+}
+
+func (s *S) TestSelectOK(c *C) {
+ testServer.Response(200, nil, TestSelectXmlOK)
+
+ resp, err := s.sdb.Select("select Color from MyDomain where Color like 'Blue%'", true)
+ req := testServer.WaitRequest()
+
+ c.Assert(req.Method, Equals, "GET")
+ c.Assert(req.URL.Path, Equals, "/")
+ c.Assert(req.Header["Date"], Not(Equals), "")
+ c.Assert(req.Form["Action"], DeepEquals, []string{"Select"})
+ c.Assert(req.Form["ConsistentRead"], DeepEquals, []string{"true"})
+
+ c.Assert(resp.ResponseMetadata.RequestId, Equals, "b1e8f1f7-42e9-494c-ad09-2674e557526d")
+ c.Assert(resp.ResponseMetadata.BoxUsage, Equals, 0.0000219907)
+ c.Assert(len(resp.Items), Equals, 2)
+ c.Assert(resp.Items[0].Name, Equals, "Item_03")
+ c.Assert(resp.Items[1].Name, Equals, "Item_06")
+ c.Assert(resp.Items[0].Attrs[0].Name, Equals, "Category")
+ c.Assert(resp.Items[0].Attrs[0].Value, Equals, "Clothes")
+
+ c.Assert(err, IsNil)
+}
diff --git a/vendor/github.com/goamz/goamz/exp/sdb/sign.go b/vendor/github.com/goamz/goamz/exp/sdb/sign.go
new file mode 100644
index 000000000..040ed5385
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/exp/sdb/sign.go
@@ -0,0 +1,54 @@
+package sdb
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
+ "github.com/goamz/goamz/aws"
+ "net/http"
+ "net/url"
+ "sort"
+ "strings"
+)
+
+var b64 = base64.StdEncoding
+
+// ----------------------------------------------------------------------------
+// SimpleDB signing (http://goo.gl/CaY81)
+
+func sign(auth aws.Auth, method, path string, params url.Values, headers http.Header) {
+ var host string
+ for k, v := range headers {
+ k = strings.ToLower(k)
+ switch k {
+ case "host":
+ host = v[0]
+ }
+ }
+
+ // set up some defaults used for signing the request
+ params["AWSAccessKeyId"] = []string{auth.AccessKey}
+ params["SignatureVersion"] = []string{"2"}
+ params["SignatureMethod"] = []string{"HmacSHA256"}
+ if auth.Token() != "" {
+ params["SecurityToken"] = []string{auth.Token()}
+ }
+
+ // join up all the incoming params
+ var sarray []string
+ for k, v := range params {
+ sarray = append(sarray, aws.Encode(k)+"="+aws.Encode(v[0]))
+ }
+ sort.StringSlice(sarray).Sort()
+ joined := strings.Join(sarray, "&")
+
+ // create the payload, sign it and create the signature
+ payload := strings.Join([]string{method, host, "/", joined}, "\n")
+ hash := hmac.New(sha256.New, []byte(auth.SecretKey))
+ hash.Write([]byte(payload))
+ signature := make([]byte, b64.EncodedLen(hash.Size()))
+ b64.Encode(signature, hash.Sum(nil))
+
+ // add the signature to the outgoing params
+ params["Signature"] = []string{string(signature)}
+}
diff --git a/vendor/github.com/goamz/goamz/exp/sdb/sign_test.go b/vendor/github.com/goamz/goamz/exp/sdb/sign_test.go
new file mode 100644
index 000000000..f45ad15e1
--- /dev/null
+++ b/vendor/github.com/goamz/goamz/exp/sdb/sign_test.go
@@ -0,0 +1,29 @@
+package sdb_test
+
+import (
+ "github.com/goamz/goamz/aws"
+ "github.com/goamz/goamz/exp/sdb"
+ . "gopkg.in/check.v1"
+)
+
+// SimpleDB ReST authentication docs: http://goo.gl/CaY81
+
+var testAuth = aws.Auth{AccessKey: "access-key-id-s8eBOWuU", SecretKey: "secret-access-key-UkQjTLd9"}
+
+func (s *S) TestSignExampleDomainCreate(c *C) {
+ method := "GET"
+ params := map[string][]string{
+ "Action": {"CreateDomain"},
+ "DomainName": {"MyDomain"},
+ "Timestamp": {"2011-08-20T07:23:57+12:00"},
+ "Version": {"2009-04-15"},
+ }
+ headers := map[string][]string{
+ "Host": {"sdb.amazonaws.com"},
+ }
+ sdb.Sign(testAuth, method, "", params, headers)
+ expected := "ot2JaeeqMRJqgAqW67hkzUlffgxdOz4RykbrECB+tDU="
+ c.Assert(params["Signature"], DeepEquals, []string{expected})
+}
+
+// Do a few test methods which takes combinations of params