Skip to content

Commit

Permalink
Table params (#32)
Browse files Browse the repository at this point in the history
* Complete parameters for table operations

* Complete parameters in entity operations

* Complete parameters in query table operation

* Better input for query tables

* Comments from review
  • Loading branch information
mcardosos authored and marstr committed Apr 28, 2017
1 parent 956544d commit a0cc775
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 164 deletions.
8 changes: 4 additions & 4 deletions storage/authorization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ func (a *AuthorizationSuite) Test_allSharedKeys(c *chk.C) {
c.Assert(tableCli.auth, chk.Equals, sharedKeyForTable)
table1 := tableCli.GetTableReference(randTable())
c.Assert(table1.tsc.auth, chk.Equals, sharedKeyForTable)
c.Assert(table1.Create(EmptyPayload, 30), chk.IsNil)
c.Assert(table1.Delete(30), chk.IsNil)
c.Assert(table1.Create(30, EmptyPayload, nil), chk.IsNil)
c.Assert(table1.Delete(30, nil), chk.IsNil)

// Change to Lite
cli.UseSharedKeyLite = true
Expand All @@ -223,6 +223,6 @@ func (a *AuthorizationSuite) Test_allSharedKeys(c *chk.C) {
c.Assert(tableCli.auth, chk.Equals, sharedKeyLiteForTable)
table2 := tableCli.GetTableReference(randTable())
c.Assert(table2.tsc.auth, chk.Equals, sharedKeyLiteForTable)
c.Assert(table2.Create(EmptyPayload, 30), chk.IsNil)
c.Assert(table2.Delete(30), chk.IsNil)
c.Assert(table2.Create(30, EmptyPayload, nil), chk.IsNil)
c.Assert(table2.Delete(30, nil), chk.IsNil)
}
71 changes: 48 additions & 23 deletions storage/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package storage
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
Expand All @@ -21,6 +23,12 @@ const (
etagErrorTemplate = "Etag didn't match: %v"
)

var (
errEmptyPayload = errors.New("Empty payload is not a valid metadata level for this operation")
errNilPreviousResult = errors.New("The previous results page is nil")
errNilNextLink = errors.New("There are no more pages in this query results")
)

// Entity represents an entity inside an Azure table.
type Entity struct {
Table *Table
Expand All @@ -45,24 +53,30 @@ func (t *Table) GetEntityReference(partitionKey, rowKey string) Entity {
}
}

// EntityOptions includes options for entity operations.
type EntityOptions struct {
Timeout uint
RequestID string `header:"x-ms-client-request-id"`
}

// Insert inserts the referenced entity in its table.
// The function fails if there is an entity with the same
// PartitionKey and RowKey in the table.
// ml determines the level of detail of metadata in the operation response,
// or no data at all.
// See: https://docs.microsoft.com/rest/api/storageservices/fileservices/insert-entity
func (e *Entity) Insert(ml MetadataLevel) error {
uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.Table.buildPath(), nil)
func (e *Entity) Insert(ml MetadataLevel, options *EntityOptions) error {
query, headers := options.getParameters()
headers = mergeHeaders(headers, e.Table.tsc.client.getStandardHeaders())

body, err := json.Marshal(e)
if err != nil {
return err
}

headers := e.Table.tsc.client.getStandardHeaders()
headers = addBodyRelatedHeaders(headers, len(body))
headers = addReturnContentHeaders(headers, ml)

uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.Table.buildPath(), query)
resp, err := e.Table.tsc.client.exec(http.MethodPost, uri, headers, bytes.NewReader(body), e.Table.tsc.auth)
if err != nil {
return err
Expand Down Expand Up @@ -94,30 +108,31 @@ func (e *Entity) Insert(ml MetadataLevel) error {
// with the same PartitionKey and RowKey in the table or if the ETag is different
// than the one in Azure.
// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/update-entity2
func (e *Entity) Update(force bool) error {
return e.updateMerge(force, http.MethodPut)
func (e *Entity) Update(force bool, options *EntityOptions) error {
return e.updateMerge(force, http.MethodPut, options)
}

// Merge merges the contents of entity specified with PartitionKey and RowKey
// with the content specified in Properties.
// The function fails if there is no entity with the same PartitionKey and
// RowKey in the table or if the ETag is different than the one in Azure.
// Read more: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/merge-entity
func (e *Entity) Merge(force bool) error {
return e.updateMerge(force, "MERGE")
func (e *Entity) Merge(force bool, options *EntityOptions) error {
return e.updateMerge(force, "MERGE", options)
}

// Delete deletes the entity.
// The function fails if there is no entity with the same PartitionKey and
// RowKey in the table or if the ETag is different than the one in Azure.
// See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/delete-entity1
func (e *Entity) Delete(force bool) error {
uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.buildPath(), nil)
func (e *Entity) Delete(force bool, options *EntityOptions) error {
query, headers := options.getParameters()
headers = mergeHeaders(headers, e.Table.tsc.client.getStandardHeaders())

headers := e.Table.tsc.client.getStandardHeaders()
headers = addIfMatchHeader(headers, force, e.OdataEtag)
headers = addReturnContentHeaders(headers, EmptyPayload)

uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.buildPath(), query)
resp, err := e.Table.tsc.client.exec(http.MethodDelete, uri, headers, nil, e.Table.tsc.auth)
if err != nil {
if resp.statusCode == http.StatusPreconditionFailed {
Expand All @@ -136,14 +151,14 @@ func (e *Entity) Delete(force bool) error {

// InsertOrReplace inserts an entity or replaces the existing one.
// Read more: https://docs.microsoft.com/rest/api/storageservices/fileservices/insert-or-replace-entity
func (e *Entity) InsertOrReplace() error {
return e.insertOr(http.MethodPut)
func (e *Entity) InsertOrReplace(options *EntityOptions) error {
return e.insertOr(http.MethodPut, options)
}

// InsertOrMerge inserts an entity or merges the existing one.
// Read more: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/insert-or-merge-entity
func (e *Entity) InsertOrMerge() error {
return e.insertOr("MERGE")
func (e *Entity) InsertOrMerge(options *EntityOptions) error {
return e.insertOr("MERGE", options)
}

func (e *Entity) buildPath() string {
Expand Down Expand Up @@ -290,18 +305,18 @@ func (e *Entity) updateTimestamp(headers http.Header) error {
return nil
}

func (e *Entity) insertOr(verb string) error {
uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.buildPath(), nil)
func (e *Entity) insertOr(verb string, options *EntityOptions) error {
query, headers := options.getParameters()
headers = mergeHeaders(headers, e.Table.tsc.client.getStandardHeaders())

body, err := json.Marshal(e)
if err != nil {
return err
}

headers := e.Table.tsc.client.getStandardHeaders()
headers = addBodyRelatedHeaders(headers, len(body))
headers = addReturnContentHeaders(headers, EmptyPayload)

uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.buildPath(), query)
resp, err := e.Table.tsc.client.exec(verb, uri, headers, bytes.NewReader(body), e.Table.tsc.auth)
if err != nil {
return err
Expand All @@ -315,19 +330,19 @@ func (e *Entity) insertOr(verb string) error {
return e.updateEtagAndTimestamp(resp.headers)
}

func (e *Entity) updateMerge(force bool, verb string) error {
uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.buildPath(), nil)
func (e *Entity) updateMerge(force bool, verb string, options *EntityOptions) error {
query, headers := options.getParameters()
headers = mergeHeaders(headers, e.Table.tsc.client.getStandardHeaders())

body, err := json.Marshal(e)
if err != nil {
return err
}

headers := e.Table.tsc.client.getStandardHeaders()
headers = addBodyRelatedHeaders(headers, len(body))
headers = addIfMatchHeader(headers, force, e.OdataEtag)
headers = addReturnContentHeaders(headers, EmptyPayload)

uri := e.Table.tsc.client.getEndpoint(tableServiceName, e.buildPath(), query)
resp, err := e.Table.tsc.client.exec(verb, uri, headers, bytes.NewReader(body), e.Table.tsc.auth)
if err != nil {
if resp.statusCode == http.StatusPreconditionFailed {
Expand All @@ -351,3 +366,13 @@ func stringFromMap(props map[string]interface{}, key string) string {
}
return ""
}

func (options *EntityOptions) getParameters() (url.Values, map[string]string) {
query := url.Values{}
headers := map[string]string{}
if options != nil {
query = addTimeout(query, options.Timeout)
headers = headersFromStruct(*options)
}
return query, headers
}

0 comments on commit a0cc775

Please sign in to comment.