Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates for aztables metadata #22472

Merged
merged 1 commit into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion sdk/data/aztables/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# Release History

## 1.1.1 (Unreleased)
## 1.2.0 (Unreleased)

### Features Added
* Methods `Client.AddEntity` and `ServiceClient.NewListTablesPager` now include OData metadata in their responses.
* The amount of OData metadata returned has been made configurable for the following methods:
* `Client.AddEntity`, `Client.GetEntity`, `Client.NewListEntitiesPager`, and `ServiceClient.NewListTablesPager`.
* Use one of the following constants to specify the amount: `MetadataFormatFull`, `MetadataFormatMinimal`, or `MetadataFormatNone`.

### Breaking Changes

Expand Down
2 changes: 1 addition & 1 deletion sdk/data/aztables/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "go",
"TagPrefix": "go/data/aztables",
"Tag": "go/data/aztables_45893b48dc"
"Tag": "go/data/aztables_ec73894009"
}
22 changes: 17 additions & 5 deletions sdk/data/aztables/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
generated "github.com/Azure/azure-sdk-for-go/sdk/data/aztables/internal"
)

Expand Down Expand Up @@ -220,8 +219,9 @@ func (t *Client) GetEntity(ctx context.Context, partitionKey string, rowKey stri
options = &GetEntityOptions{}
}

genOptions, queryOptions := options.toGenerated()
resp, err := t.client.QueryEntityWithPartitionAndRowKey(ctx, t.name, prepareKey(partitionKey), prepareKey(rowKey), genOptions, queryOptions)
resp, err := t.client.QueryEntityWithPartitionAndRowKey(ctx, t.name, prepareKey(partitionKey), prepareKey(rowKey), nil, &generated.QueryOptions{
Format: options.Format,
})
if err != nil {
return GetEntityResponse{}, err
}
Expand Down Expand Up @@ -254,18 +254,30 @@ func (t *Client) AddEntity(ctx context.Context, entity []byte, options *AddEntit
if err != nil {
return AddEntityResponse{}, err
}
resp, err := t.client.InsertEntity(ctx, t.name, &generated.TableClientInsertEntityOptions{TableEntityProperties: mapEntity, ResponsePreference: to.Ptr(generated.ResponseFormatReturnNoContent)}, nil)

if options == nil {
options = &AddEntityOptions{}
}

resp, err := t.client.InsertEntity(ctx, t.name, &generated.TableClientInsertEntityOptions{TableEntityProperties: mapEntity}, &generated.QueryOptions{
Format: options.Format,
})
if err != nil {
err = checkEntityForPkRk(&mapEntity, err)
return AddEntityResponse{}, err
}
marshalledValue, err := json.Marshal(resp.Value)
if err != nil {
return AddEntityResponse{}, err
}

var ETag azcore.ETag
if resp.ETag != nil {
ETag = azcore.ETag(*resp.ETag)
}
return AddEntityResponse{
ETag: ETag,
ETag: ETag,
Value: marshalledValue,
}, nil
}

Expand Down
67 changes: 62 additions & 5 deletions sdk/data/aztables/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ func TestCreateTable(t *testing.T) {
}
}

type mdforAddGet struct {
Metadata string `json:"odata.metadata"`
Type string `json:"odata.type"` // only for full metadata
}

func TestAddEntity(t *testing.T) {
for _, service := range services {
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
Expand All @@ -66,8 +71,13 @@ func TestAddEntity(t *testing.T) {

marshalledEntity, err := json.Marshal(simpleEntity)
require.NoError(t, err)
_, err = client.AddEntity(ctx, marshalledEntity, nil)
resp, err := client.AddEntity(ctx, marshalledEntity, nil)
require.NoError(t, err)
require.NotEmpty(t, resp.Value)
var md mdforAddGet
require.NoError(t, json.Unmarshal(resp.Value, &md))
require.NotEmpty(t, md.Metadata)
require.Empty(t, md.Type)
})
}
}
Expand All @@ -83,8 +93,18 @@ func TestAddComplexEntity(t *testing.T) {
marshalledEntity, err := json.Marshal(entity)
require.NoError(t, err)

_, err = client.AddEntity(ctx, marshalledEntity, nil)
resp, err := client.AddEntity(ctx, marshalledEntity, &AddEntityOptions{
Format: to.Ptr(MetadataFormatFull),
})
require.NoError(t, err)
require.NotEmpty(t, resp.Value)
var md mdforAddGet
require.NoError(t, json.Unmarshal(resp.Value, &md))
require.NotEmpty(t, md.Metadata)
if service == "storage" {
// cosmos doesn't send full metadata
require.NotEmpty(t, md.Type)
}
})
}
}
Expand Down Expand Up @@ -165,6 +185,11 @@ func TestMergeEntity(t *testing.T) {

preMerge, err := client.GetEntity(ctx, entityToCreate.PartitionKey, entityToCreate.RowKey, nil)
require.NoError(t, err)
require.NotEmpty(t, preMerge.Value)
var md mdforAddGet
require.NoError(t, json.Unmarshal(preMerge.Value, &md))
require.NotEmpty(t, md.Metadata)
require.Empty(t, md.Type)

var unMarshalledPreMerge map[string]any
err = json.Unmarshal(preMerge.Value, &unMarshalledPreMerge)
Expand Down Expand Up @@ -245,8 +270,18 @@ func TestInsertEntity(t *testing.T) {
list := &ListEntitiesOptions{Filter: &filter}

// 2. Query for basic Entity
preMerge, err := client.GetEntity(ctx, entityToCreate.PartitionKey, entityToCreate.RowKey, nil)
preMerge, err := client.GetEntity(ctx, entityToCreate.PartitionKey, entityToCreate.RowKey, &GetEntityOptions{
Format: to.Ptr(MetadataFormatFull),
})
require.NoError(t, err)
require.NotEmpty(t, preMerge.Value)
var md mdforAddGet
require.NoError(t, json.Unmarshal(preMerge.Value, &md))
require.NotEmpty(t, md.Metadata)
if service == "storage" {
// cosmos doesn't send full metadata
require.NotEmpty(t, md.Type)
}

var unMarshalledPreMerge map[string]any
err = json.Unmarshal(preMerge.Value, &unMarshalledPreMerge)
Expand Down Expand Up @@ -304,6 +339,11 @@ func TestInsertEntityTwice(t *testing.T) {
}
}

type mdForListEntities struct {
Timestamp time.Time `json:"Timestamp"`
ID string `json:"odata.id"` // only for full metadata
}

func TestQuerySimpleEntity(t *testing.T) {
for _, service := range services {
t.Run(fmt.Sprintf("%v_%v", t.Name(), service), func(t *testing.T) {
Expand All @@ -328,7 +368,8 @@ func TestQuerySimpleEntity(t *testing.T) {
var resp ListEntitiesResponse
pager := client.NewListEntitiesPager(list)
for pager.More() {
resp, err := pager.NextPage(ctx)
var err error
resp, err = pager.NextPage(ctx)
require.NoError(t, err)
require.Equal(t, len(resp.Entities), expectedCount)
}
Expand All @@ -353,6 +394,11 @@ func TestQuerySimpleEntity(t *testing.T) {
require.Equal(t, b.String, (*entitiesToCreate)[i].String)
require.Equal(t, b.Integer, (*entitiesToCreate)[i].Integer)
require.Equal(t, b.Bool, (*entitiesToCreate)[i].Bool)

var md mdForListEntities
require.NoError(t, json.Unmarshal(e, &md))
require.False(t, md.Timestamp.IsZero())
require.Empty(t, md.ID)
}
})
}
Expand All @@ -375,7 +421,10 @@ func TestQueryComplexEntity(t *testing.T) {

filter := "RowKey lt '5'"
expectedCount := 4
options := &ListEntitiesOptions{Filter: &filter}
options := &ListEntitiesOptions{
Filter: &filter,
Format: to.Ptr(MetadataFormatFull),
}

pager := client.NewListEntitiesPager(options)
for pager.More() {
Expand All @@ -396,6 +445,14 @@ func TestQueryComplexEntity(t *testing.T) {
require.Equal(t, model.Float, (entitiesToCreate)[idx].Float)
require.Equal(t, model.DateTime, (entitiesToCreate)[idx].DateTime)
require.Equal(t, model.Byte, (entitiesToCreate)[idx].Byte)

var md mdForListEntities
require.NoError(t, json.Unmarshal(entity, &md))
require.False(t, md.Timestamp.IsZero())
if service == "storage" {
// cosmos doesn't send full metadata
require.NotEmpty(t, md.ID)
}
}
}
})
Expand Down
10 changes: 10 additions & 0 deletions sdk/data/aztables/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ func toGeneratedStatusType(g *generated.GeoReplicationStatusType) *GeoReplicatio
return nil
}

// MetadataFormat specifies the level of OData metadata returned with an entity.
// https://learn.microsoft.com/rest/api/storageservices/payload-format-for-table-service-operations#json-format-applicationjson-versions-2013-08-15-and-later
type MetadataFormat = generated.ODataMetadataFormat

const (
MetadataFormatFull MetadataFormat = generated.ODataMetadataFormatApplicationJSONODataFullmetadata
MetadataFormatMinimal MetadataFormat = generated.ODataMetadataFormatApplicationJSONODataMinimalmetadata
MetadataFormatNone MetadataFormat = generated.ODataMetadataFormatApplicationJSONODataNometadata
)

// SASProtocol indicates the SAS protocol
type SASProtocol string

Expand Down
2 changes: 1 addition & 1 deletion sdk/data/aztables/internal/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ package internal

const (
ModuleName = "github.com/Azure/azure-sdk-for-go/sdk/data/aztables"
Version = "v1.1.1"
Version = "v1.2.0"
)
3 changes: 3 additions & 0 deletions sdk/data/aztables/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ func (t *ServiceProperties) toGenerated() *generated.TableServiceProperties {
type TableProperties struct {
// The name of the table.
Name *string `json:"TableName,omitempty"`

// The OData properties of the table in JSON format.
Value []byte
}

// RetentionPolicy - The retention policy.
Expand Down
25 changes: 16 additions & 9 deletions sdk/data/aztables/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ package aztables

import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
generated "github.com/Azure/azure-sdk-for-go/sdk/data/aztables/internal"
)

// AddEntityOptions contains optional parameters for Client.AddEntity
type AddEntityOptions struct {
// placeholder for future optional parameters
// Format specifies the amount of metadata returned.
// The default is MetadataFormatMinimal.
Format *MetadataFormat
}

// CreateTableOptions contains optional parameters for Client.Create and ServiceClient.CreateTable
Expand Down Expand Up @@ -52,11 +53,9 @@ func (g *GetAccessPolicyOptions) toGenerated() *generated.TableClientGetAccessPo

// GetEntityOptions contains optional parameters for Client.GetEntity
type GetEntityOptions struct {
// placeholder for future optional parameters
}

func (g *GetEntityOptions) toGenerated() (*generated.TableClientQueryEntityWithPartitionAndRowKeyOptions, *generated.QueryOptions) {
return &generated.TableClientQueryEntityWithPartitionAndRowKeyOptions{}, &generated.QueryOptions{Format: to.Ptr(generated.ODataMetadataFormatApplicationJSONODataMinimalmetadata)}
// Format specifies the amount of metadata returned.
// The default is MetadataFormatMinimal.
Format *MetadataFormat
}

// GetPropertiesOptions contains optional parameters for Client.GetProperties
Expand Down Expand Up @@ -94,6 +93,10 @@ type ListEntitiesOptions struct {

// The NextRowKey to start paging from
NextRowKey *string

// Format specifies the amount of metadata returned.
// The default is MetadataFormatMinimal.
Format *MetadataFormat
}

func (l *ListEntitiesOptions) toQueryOptions() *generated.QueryOptions {
Expand All @@ -103,7 +106,7 @@ func (l *ListEntitiesOptions) toQueryOptions() *generated.QueryOptions {

return &generated.QueryOptions{
Filter: l.Filter,
Format: to.Ptr(generated.ODataMetadataFormatApplicationJSONODataMinimalmetadata),
Format: l.Format,
Select: l.Select,
Top: l.Top,
}
Expand All @@ -122,6 +125,10 @@ type ListTablesOptions struct {

// NextTableName is the continuation token for the next table to page from
NextTableName *string

// Format specifies the amount of metadata returned.
// The default is MetadataFormatMinimal.
Format *MetadataFormat
}

func (l *ListTablesOptions) toQueryOptions() *generated.QueryOptions {
Expand All @@ -131,7 +138,7 @@ func (l *ListTablesOptions) toQueryOptions() *generated.QueryOptions {

return &generated.QueryOptions{
Filter: l.Filter,
Format: to.Ptr(generated.ODataMetadataFormatApplicationJSONODataMinimalmetadata),
Format: l.Format,
Select: l.Select,
Top: l.Top,
}
Expand Down
6 changes: 5 additions & 1 deletion sdk/data/aztables/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import "github.com/Azure/azure-sdk-for-go/sdk/azcore"

// AddEntityResponse contains response fields for Client.AddEntityResponse
type AddEntityResponse struct {
// ETag contains the information returned from the ETag header response.
ETag azcore.ETag

// The OData properties of the table entity in JSON format.
Value []byte
}

// CreateTableResponse contains response fields for Client.Create and ServiceClient.CreateTable
Expand Down Expand Up @@ -36,7 +40,7 @@ type GetEntityResponse struct {
// ETag contains the information returned from the ETag header response.
ETag azcore.ETag

// The properties of the table entity.
// The OData properties of the table entity in JSON format.
Value []byte
}

Expand Down
21 changes: 20 additions & 1 deletion sdk/data/aztables/service_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package aztables

import (
"context"
"encoding/json"
"errors"
"strings"
"time"
Expand Down Expand Up @@ -162,8 +163,26 @@ func (t *ServiceClient) NewListTablesPager(listOptions *ListTablesOptions) *runt

tableProps := make([]*TableProperties, len(resp.Value))
for i := range resp.Value {
odataValues := map[string]any{}
if resp.Value[i].ODataEditLink != nil {
odataValues["odata.editLink"] = *resp.Value[i].ODataEditLink
}
if resp.Value[i].ODataID != nil {
odataValues["odata.id"] = *resp.Value[i].ODataID
}
if resp.Value[i].ODataType != nil {
odataValues["odata.type"] = *resp.Value[i].ODataType
}
var odataJSON []byte
if len(odataValues) > 0 {
odataJSON, err = json.Marshal(odataValues)
if err != nil {
return ListTablesResponse{}, err
}
}
tableProps[i] = &TableProperties{
Name: resp.Value[i].TableName,
Name: resp.Value[i].TableName,
Value: odataJSON,
}
}

Expand Down
Loading