Skip to content

Commit e2214ac

Browse files
[Tables] Adding error codes (Azure#17524)
* examples for table error codes * more * adding error code parsing * changelog * testing improvements * better changelog description * adding test for xml failure * using latest core * corrupted recordings * removing func * using latest azcore
1 parent 3c6770e commit e2214ac

22 files changed

+490
-178
lines changed

sdk/data/aztables/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
## 0.7.1 (Unreleased)
44

55
### Features Added
6+
* Added `TableErrorCode` to help recover from and understand error responses
67

78
### Breaking Changes
9+
* Renamed `InsertEntityResponse/Options` to `UpsertEntityResponse/Options`
810
* Renamed `PossibleGeoReplicationStatusTypeValues` to `PossibleGeoReplicationStatusValues`
911

1012
### Bugs Fixed

sdk/data/aztables/access_policy_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"testing"
99
"time"
1010

11+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
12+
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
1113
"github.com/Azure/azure-sdk-for-go/sdk/internal/recording"
1214
"github.com/stretchr/testify/require"
1315
)
@@ -140,7 +142,7 @@ func TestSetTooManyAccessPolicies(t *testing.T) {
140142
param := SetAccessPolicyOptions{TableACL: signedIdentifiers}
141143

142144
_, err := client.SetAccessPolicy(ctx, &param)
143-
require.NotNil(t, err, "Set access policy succeeded but should have failed")
145+
require.Error(t, err)
144146
require.Contains(t, err.Error(), errTooManyAccessPoliciesError.Error())
145147
}
146148

@@ -166,3 +168,26 @@ func TestSetNullAccessPolicy(t *testing.T) {
166168
require.NoError(t, err)
167169
require.Equal(t, len(resp.SignedIdentifiers), 1)
168170
}
171+
172+
func TestSetInvalidAccessPolicy(t *testing.T) {
173+
client, delete := initClientTest(t, "storage", true)
174+
defer delete()
175+
176+
signedIdentifiers := make([]*SignedIdentifier, 0)
177+
signedIdentifiers = append(signedIdentifiers, &SignedIdentifier{
178+
AccessPolicy: &AccessPolicy{
179+
Expiry: to.Ptr(time.Date(2042, 1, 1, 1, 1, 1, 1, time.UTC)),
180+
},
181+
})
182+
183+
param := SetAccessPolicyOptions{
184+
TableACL: signedIdentifiers,
185+
}
186+
187+
_, err := client.SetAccessPolicy(ctx, &param)
188+
require.Error(t, err)
189+
var httpErr *azcore.ResponseError
190+
require.ErrorAs(t, err, &httpErr)
191+
require.Equal(t, "InvalidXmlDocument", httpErr.ErrorCode)
192+
require.Contains(t, PossibleTableErrorCodeValues(), TableErrorCode(httpErr.ErrorCode))
193+
}

sdk/data/aztables/client.go

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ func (t *Client) CreateTable(ctx context.Context, options *CreateTableOptions) (
125125
options = &CreateTableOptions{}
126126
}
127127
resp, err := t.client.Create(ctx, generated.Enum1Three0, generated.TableProperties{TableName: &t.name}, options.toGenerated(), &generated.QueryOptions{})
128+
if err != nil {
129+
return CreateTableResponse{}, err
130+
}
128131
return createTableResponseFromGen(&resp), err
129132
}
130133

@@ -373,6 +376,9 @@ func (t *Client) DeleteEntity(ctx context.Context, partitionKey string, rowKey s
373376
options.IfMatch = &nilEtag
374377
}
375378
resp, err := t.client.DeleteEntity(ctx, generated.Enum1Three0, t.name, partitionKey, rowKey, string(*options.IfMatch), options.toGenerated(), &generated.QueryOptions{})
379+
if err != nil {
380+
return DeleteEntityResponse{}, err
381+
}
376382
return deleteEntityResponseFromGenerated(&resp), err
377383
}
378384

@@ -476,6 +482,9 @@ func (t *Client) UpdateEntity(ctx context.Context, entity []byte, options *Updat
476482
options.toGeneratedMergeEntity(mapEntity),
477483
&generated.QueryOptions{},
478484
)
485+
if err != nil {
486+
return UpdateEntityResponse{}, err
487+
}
479488
return updateEntityResponseFromMergeGenerated(&resp), err
480489
case UpdateModeReplace:
481490
resp, err := t.client.UpdateEntity(
@@ -487,6 +496,9 @@ func (t *Client) UpdateEntity(ctx context.Context, entity []byte, options *Updat
487496
options.toGeneratedUpdateEntity(mapEntity),
488497
&generated.QueryOptions{},
489498
)
499+
if err != nil {
500+
return UpdateEntityResponse{}, err
501+
}
490502
return updateEntityResponseFromUpdateGenerated(&resp), err
491503
}
492504
if pk == "" || rk == "" {
@@ -495,8 +507,8 @@ func (t *Client) UpdateEntity(ctx context.Context, entity []byte, options *Updat
495507
return UpdateEntityResponse{}, errInvalidUpdateMode
496508
}
497509

498-
// InsertEntityOptions contains optional parameters for Client.InsertEntity
499-
type InsertEntityOptions struct {
510+
// UpsertEntityOptions contains optional parameters for Client.InsertEntity
511+
type UpsertEntityOptions struct {
500512
// ETag is the optional etag for the Table
501513
ETag azcore.ETag
502514

@@ -505,35 +517,35 @@ type InsertEntityOptions struct {
505517
UpdateMode UpdateMode
506518
}
507519

508-
// InsertEntityResponse contains response fields for Client.InsertEntity
509-
type InsertEntityResponse struct {
520+
// UpsertEntityResponse contains response fields for Client.InsertEntity
521+
type UpsertEntityResponse struct {
510522
ETag azcore.ETag
511523
}
512524

513-
func insertEntityFromGeneratedMerge(g *generated.TableClientMergeEntityResponse) InsertEntityResponse {
525+
func insertEntityFromGeneratedMerge(g *generated.TableClientMergeEntityResponse) UpsertEntityResponse {
514526
if g == nil {
515-
return InsertEntityResponse{}
527+
return UpsertEntityResponse{}
516528
}
517529

518530
var ETag azcore.ETag
519531
if g.ETag != nil {
520532
ETag = azcore.ETag(*g.ETag)
521533
}
522-
return InsertEntityResponse{
534+
return UpsertEntityResponse{
523535
ETag: ETag,
524536
}
525537
}
526538

527-
func insertEntityFromGeneratedUpdate(g *generated.TableClientUpdateEntityResponse) InsertEntityResponse {
539+
func insertEntityFromGeneratedUpdate(g *generated.TableClientUpdateEntityResponse) UpsertEntityResponse {
528540
if g == nil {
529-
return InsertEntityResponse{}
541+
return UpsertEntityResponse{}
530542
}
531543

532544
var ETag azcore.ETag
533545
if g.ETag != nil {
534546
ETag = azcore.ETag(*g.ETag)
535547
}
536-
return InsertEntityResponse{
548+
return UpsertEntityResponse{
537549
ETag: ETag,
538550
}
539551
}
@@ -544,16 +556,16 @@ func insertEntityFromGeneratedUpdate(g *generated.TableClientUpdateEntityRespons
544556
// The response type will be TableEntityMergeResponse if updateMode is Merge and TableEntityUpdateResponse if updateMode is Replace.
545557
// If the service returns a non-successful HTTP status code, the function returns an *azcore.ResponseError type.
546558
// Specify nil for options if you want to use the default options.
547-
func (t *Client) UpsertEntity(ctx context.Context, entity []byte, options *InsertEntityOptions) (InsertEntityResponse, error) {
559+
func (t *Client) UpsertEntity(ctx context.Context, entity []byte, options *UpsertEntityOptions) (UpsertEntityResponse, error) {
548560
if options == nil {
549-
options = &InsertEntityOptions{
561+
options = &UpsertEntityOptions{
550562
UpdateMode: UpdateModeMerge,
551563
}
552564
}
553565
var mapEntity map[string]interface{}
554566
err := json.Unmarshal(entity, &mapEntity)
555567
if err != nil {
556-
return InsertEntityResponse{}, err
568+
return UpsertEntityResponse{}, err
557569
}
558570

559571
pk := mapEntity[partitionKey]
@@ -573,6 +585,9 @@ func (t *Client) UpsertEntity(ctx context.Context, entity []byte, options *Inser
573585
&generated.TableClientMergeEntityOptions{TableEntityProperties: mapEntity},
574586
&generated.QueryOptions{},
575587
)
588+
if err != nil {
589+
return UpsertEntityResponse{}, err
590+
}
576591
return insertEntityFromGeneratedMerge(&resp), err
577592
case UpdateModeReplace:
578593
resp, err := t.client.UpdateEntity(
@@ -584,12 +599,15 @@ func (t *Client) UpsertEntity(ctx context.Context, entity []byte, options *Inser
584599
&generated.TableClientUpdateEntityOptions{TableEntityProperties: mapEntity},
585600
&generated.QueryOptions{},
586601
)
602+
if err != nil {
603+
return UpsertEntityResponse{}, err
604+
}
587605
return insertEntityFromGeneratedUpdate(&resp), err
588606
}
589607
if pk == "" || rk == "" {
590-
return InsertEntityResponse{}, errPartitionKeyRowKeyError
608+
return UpsertEntityResponse{}, errPartitionKeyRowKeyError
591609
}
592-
return InsertEntityResponse{}, errInvalidUpdateMode
610+
return UpsertEntityResponse{}, errInvalidUpdateMode
593611
}
594612

595613
// GetAccessPolicyOptions contains optional parameters for Client.GetAccessPolicy
@@ -625,6 +643,9 @@ func getAccessPolicyResponseFromGenerated(g *generated.TableClientGetAccessPolic
625643
// Specify nil for options if you want to use the default options.
626644
func (t *Client) GetAccessPolicy(ctx context.Context, options *GetAccessPolicyOptions) (GetAccessPolicyResponse, error) {
627645
resp, err := t.client.GetAccessPolicy(ctx, t.name, generated.Enum4ACL, options.toGenerated())
646+
if err != nil {
647+
return GetAccessPolicyResponse{}, err
648+
}
628649
return getAccessPolicyResponseFromGenerated(&resp), err
629650
}
630651

@@ -663,6 +684,9 @@ func (t *Client) SetAccessPolicy(ctx context.Context, options *SetAccessPolicyOp
663684
if err != nil && len(options.TableACL) > 5 {
664685
err = errTooManyAccessPoliciesError
665686
}
687+
if err != nil {
688+
return SetAccessPolicyResponse{}, err
689+
}
666690
return setAccessPolicyResponseFromGenerated(&response), err
667691
}
668692

sdk/data/aztables/client_test.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"testing"
1111
"time"
1212

13+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
1314
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
1415
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
1516
"github.com/stretchr/testify/require"
@@ -26,6 +27,10 @@ func TestServiceErrors(t *testing.T) {
2627
// Create a duplicate table to produce an error
2728
_, err := client.CreateTable(ctx, nil)
2829
require.Error(t, err)
30+
var httpErr *azcore.ResponseError
31+
require.ErrorAs(t, err, &httpErr)
32+
require.Equal(t, string(TableAlreadyExists), httpErr.ErrorCode)
33+
require.Contains(t, PossibleTableErrorCodeValues(), TableErrorCode(httpErr.ErrorCode))
2934
})
3035
}
3136
}
@@ -117,6 +122,9 @@ func TestDeleteEntityWithETag(t *testing.T) {
117122

118123
_, err = client.DeleteEntity(ctx, simpleEntity2.PartitionKey, simpleEntity2.RowKey, &DeleteEntityOptions{IfMatch: &oldETag})
119124
require.Error(t, err)
125+
var httpErr *azcore.ResponseError
126+
require.ErrorAs(t, err, &httpErr)
127+
require.Contains(t, PossibleTableErrorCodeValues(), TableErrorCode(httpErr.ErrorCode))
120128

121129
_, err = client.DeleteEntity(ctx, simpleEntity.PartitionKey, simpleEntity.RowKey, &DeleteEntityOptions{IfMatch: &oldETag})
122130
require.NoError(t, err)
@@ -193,6 +201,10 @@ func TestMergeEntityDoesNotExist(t *testing.T) {
193201

194202
_, updateErr := client.UpdateEntity(ctx, marshalled, &UpdateEntityOptions{UpdateMode: UpdateModeMerge})
195203
require.Error(t, updateErr)
204+
var httpErr *azcore.ResponseError
205+
require.ErrorAs(t, updateErr, &httpErr)
206+
require.Equal(t, string(ResourceNotFound), httpErr.ErrorCode)
207+
require.Contains(t, PossibleTableErrorCodeValues(), TableErrorCode(httpErr.ErrorCode))
196208
})
197209
}
198210
}
@@ -208,7 +220,7 @@ func TestInsertEntity(t *testing.T) {
208220
marshalled, err := json.Marshal(entityToCreate)
209221
require.NoError(t, err)
210222

211-
_, err = client.UpsertEntity(ctx, marshalled, &InsertEntityOptions{UpdateMode: UpdateModeReplace})
223+
_, err = client.UpsertEntity(ctx, marshalled, &UpsertEntityOptions{UpdateMode: UpdateModeReplace})
212224
require.NoError(t, err)
213225

214226
filter := "RowKey eq '1'"
@@ -230,7 +242,7 @@ func TestInsertEntity(t *testing.T) {
230242
require.NoError(t, err)
231243

232244
// 4. Replace Entity with "bool"-less entity
233-
_, err = client.UpsertEntity(ctx, reMarshalled, &InsertEntityOptions{UpdateMode: UpdateModeReplace})
245+
_, err = client.UpsertEntity(ctx, reMarshalled, &UpsertEntityOptions{UpdateMode: UpdateModeReplace})
234246
require.Nil(t, err)
235247

236248
// 5. Query for new entity
@@ -265,10 +277,10 @@ func TestInsertEntityTwice(t *testing.T) {
265277
marshalled, err := json.Marshal(entityToCreate)
266278
require.NoError(t, err)
267279

268-
_, err = client.UpsertEntity(ctx, marshalled, &InsertEntityOptions{UpdateMode: UpdateModeReplace})
280+
_, err = client.UpsertEntity(ctx, marshalled, &UpsertEntityOptions{UpdateMode: UpdateModeReplace})
269281
require.NoError(t, err)
270282

271-
_, err = client.UpsertEntity(ctx, marshalled, &InsertEntityOptions{UpdateMode: UpdateModeReplace})
283+
_, err = client.UpsertEntity(ctx, marshalled, &UpsertEntityOptions{UpdateMode: UpdateModeReplace})
272284
require.NoError(t, err)
273285
})
274286
}

sdk/data/aztables/error_codes.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package aztables
5+
6+
// TableErrorCode is the error code returned by the service on failed operations. For more
7+
// information about Table service error codes: https://docs.microsoft.com/en-us/rest/api/storageservices/table-service-error-codes
8+
type TableErrorCode string
9+
10+
const (
11+
DuplicatePropertiesSpecified TableErrorCode = "DuplicatePropertiesSpecified"
12+
EntityAlreadyExists TableErrorCode = "EntityAlreadyExists"
13+
EntityNotFound TableErrorCode = "EntityNotFound"
14+
EntityTooLarge TableErrorCode = "EntityTooLarge"
15+
HostInformationNotPresent TableErrorCode = "HostInformationNotPresent"
16+
InvalidDuplicateRow TableErrorCode = "InvalidDuplicateRow"
17+
InvalidInput TableErrorCode = "InvalidInput"
18+
InvalidValueType TableErrorCode = "InvalidValueType"
19+
InvalidXmlDocument TableErrorCode = "InvalidXmlDocument"
20+
JSONFormatNotSupported TableErrorCode = "JsonFormatNotSupported"
21+
MethodNotAllowed TableErrorCode = "MethodNotAllowed"
22+
NotImplemented TableErrorCode = "NotImplemented"
23+
OutOfRangeInput TableErrorCode = "OutOfRangeInput"
24+
PropertiesNeedValue TableErrorCode = "PropertiesNeedValue"
25+
PropertyNameInvalid TableErrorCode = "PropertyNameInvalid"
26+
PropertyNameTooLong TableErrorCode = "PropertyNameTooLong"
27+
PropertyValueTooLarge TableErrorCode = "PropertyValueTooLarge"
28+
ResourceNotFound TableErrorCode = "ResourceNotFound"
29+
TableAlreadyExists TableErrorCode = "TableAlreadyExists"
30+
TableBeingDeleted TableErrorCode = "TableBeingDeleted"
31+
TableNotFound TableErrorCode = "TableNotFound"
32+
TooManyProperties TableErrorCode = "TooManyProperties"
33+
UpdateConditionNotSatisfied TableErrorCode = "UpdateConditionNotSatisfied"
34+
XMethodIncorrectCount TableErrorCode = "XMethodIncorrectCount"
35+
XMethodIncorrectValue TableErrorCode = "XMethodIncorrectValue"
36+
XMethodNotUsingPost TableErrorCode = "XMethodNotUsingPost"
37+
)
38+
39+
// PossibleTableErrorCodeValues returns a slice of all possible TableErrorCode values
40+
func PossibleTableErrorCodeValues() []TableErrorCode {
41+
return []TableErrorCode{
42+
DuplicatePropertiesSpecified,
43+
EntityAlreadyExists,
44+
EntityNotFound,
45+
EntityTooLarge,
46+
HostInformationNotPresent,
47+
InvalidDuplicateRow,
48+
InvalidInput,
49+
InvalidValueType,
50+
InvalidXmlDocument,
51+
JSONFormatNotSupported,
52+
MethodNotAllowed,
53+
NotImplemented,
54+
OutOfRangeInput,
55+
PropertiesNeedValue,
56+
PropertyNameInvalid,
57+
PropertyNameTooLong,
58+
PropertyValueTooLarge,
59+
ResourceNotFound,
60+
TableAlreadyExists,
61+
TableBeingDeleted,
62+
TableNotFound,
63+
TooManyProperties,
64+
UpdateConditionNotSatisfied,
65+
XMethodIncorrectCount,
66+
XMethodIncorrectValue,
67+
XMethodNotUsingPost,
68+
}
69+
}

sdk/data/aztables/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/Azure/azure-sdk-for-go/sdk/data/aztables
33
go 1.18
44

55
require (
6-
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.23.0
6+
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.23.1
77
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.13.0
88
github.com/Azure/azure-sdk-for-go/sdk/internal v0.9.2
99
github.com/stretchr/testify v1.7.0

sdk/data/aztables/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.0/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM=
2-
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.23.0 h1:D7l5jspkc4kwBYRWoZE4DQnu6LVpLwDsMZjBKS4wZLQ=
3-
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.23.0/go.mod h1:w5pDIZuawUmY3Bj4tVx3Xb8KS96ToB0j315w9rqpAg0=
2+
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.23.1 h1:3CVsSo4mp8NDWO11tHzN/mdo2zP0CtaSK5IcwBjfqRA=
3+
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.23.1/go.mod h1:w5pDIZuawUmY3Bj4tVx3Xb8KS96ToB0j315w9rqpAg0=
44
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.13.0 h1:bLRntPH25SkY1uZ/YZW+dmxNky9r1fAHvDFrzluo+4Q=
55
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.13.0/go.mod h1:TmXReXZ9yPp5D5TBRMTAtyz+UyOl15Py4hL5E5p6igQ=
66
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I=

0 commit comments

Comments
 (0)