Skip to content

Commit

Permalink
Update azcosmos with the latest azcore (#16787)
Browse files Browse the repository at this point in the history
* Update azcosmos with the latest azcore

* Export CosmosError and update fields

* return azcore.ResponseError, removed CosmosError

* fix changelog
  • Loading branch information
jhendrixMSFT committed Jan 13, 2022
1 parent 3c495ee commit d15088e
Show file tree
Hide file tree
Showing 15 changed files with 100 additions and 114 deletions.
8 changes: 3 additions & 5 deletions sdk/data/azcosmos/CHANGELOG.md
@@ -1,14 +1,12 @@
# Release History

## 0.1.1 (Unreleased)
## 0.2.0 (2022-01-13)

### Features Added
* Failed API calls will now return an `*azcore.ResponseError` type.

### Breaking Changes

### Bugs Fixed

### Other Changes
* Updated to latest `azcore`. Public surface area is unchanged. However, the `azcore.HTTPResponse` interface has been removed.

## 0.1.0 (2021-11-09)
* This is the initial preview release of the `azcosmos` library
14 changes: 8 additions & 6 deletions sdk/data/azcosmos/cosmos_client.go
Expand Up @@ -40,12 +40,14 @@ func newPipeline(cred KeyCredential, options *ClientOptions) azruntime.Pipeline
}

return azruntime.NewPipeline("azcosmos", serviceLibVersion,
[]policy.Policy{
newSharedKeyCredPolicy(cred),
&headerPolicies{
enableContentResponseOnWrite: options.EnableContentResponseOnWrite,
}},
nil,
azruntime.PipelineOptions{
PerCall: []policy.Policy{
newSharedKeyCredPolicy(cred),
&headerPolicies{
enableContentResponseOnWrite: options.EnableContentResponseOnWrite,
},
},
},
&options.ClientOptions)
}

Expand Down
29 changes: 15 additions & 14 deletions sdk/data/azcosmos/cosmos_client_test.go
Expand Up @@ -10,6 +10,7 @@ import (
"net/http"
"testing"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
azruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/internal/mock"
Expand All @@ -31,7 +32,7 @@ func TestEnsureErrorIsGeneratedOnResponse(t *testing.T) {
mock.WithBody(jsonString),
mock.WithStatusCode(404))

pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand All @@ -42,9 +43,9 @@ func TestEnsureErrorIsGeneratedOnResponse(t *testing.T) {
t.Fatal("Expected error")
}

asError := err.(*cosmosError)
if asError.ErrorCode() != someError.Code {
t.Errorf("Expected %v, but got %v", someError.Code, asError.ErrorCode())
asError := err.(*azcore.ResponseError)
if asError.ErrorCode != someError.Code {
t.Errorf("Expected %v, but got %v", someError.Code, asError.ErrorCode)
}

if err.Error() != asError.Error() {
Expand All @@ -58,7 +59,7 @@ func TestEnsureErrorIsNotGeneratedOnResponse(t *testing.T) {
srv.SetResponse(
mock.WithStatusCode(200))

pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand All @@ -76,7 +77,7 @@ func TestRequestEnricherIsCalled(t *testing.T) {
srv.SetResponse(
mock.WithStatusCode(200))

pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand All @@ -103,7 +104,7 @@ func TestNoOptionsIsCalled(t *testing.T) {
srv.SetResponse(
mock.WithStatusCode(200))

pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand All @@ -120,7 +121,7 @@ func TestAttachContent(t *testing.T) {
srv, close := mock.NewTLSServer()
defer close()

pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand Down Expand Up @@ -171,7 +172,7 @@ func TestAttachContent(t *testing.T) {
func TestCreateRequest(t *testing.T) {
srv, close := mock.NewTLSServer()
defer close()
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand Down Expand Up @@ -211,7 +212,7 @@ func TestSendDelete(t *testing.T) {
srv.SetResponse(
mock.WithStatusCode(200))
verifier := pipelineVerifier{}
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{&verifier}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand All @@ -234,7 +235,7 @@ func TestSendGet(t *testing.T) {
srv.SetResponse(
mock.WithStatusCode(200))
verifier := pipelineVerifier{}
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{&verifier}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand All @@ -257,7 +258,7 @@ func TestSendPut(t *testing.T) {
srv.SetResponse(
mock.WithStatusCode(200))
verifier := pipelineVerifier{}
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{&verifier}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand Down Expand Up @@ -290,7 +291,7 @@ func TestSendPost(t *testing.T) {
srv.SetResponse(
mock.WithStatusCode(200))
verifier := pipelineVerifier{}
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{&verifier}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand Down Expand Up @@ -323,7 +324,7 @@ func TestSendQuery(t *testing.T) {
srv.SetResponse(
mock.WithStatusCode(200))
verifier := pipelineVerifier{}
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{&verifier}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}
operationContext := pipelineRequestOptions{
resourceType: resourceTypeDatabase,
Expand Down
2 changes: 1 addition & 1 deletion sdk/data/azcosmos/cosmos_container_response_test.go
Expand Up @@ -50,7 +50,7 @@ func TestContainerResponseParsing(t *testing.T) {
t.Fatal(err)
}

pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
resp, _ := pl.Do(req)
parsedResponse, err := newContainerResponse(resp)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion sdk/data/azcosmos/cosmos_database_response_test.go
Expand Up @@ -46,7 +46,7 @@ func TestDatabaseResponseParsing(t *testing.T) {
t.Fatal(err)
}

pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
resp, _ := pl.Do(req)
parsedResponse, err := newDatabaseResponse(resp)
if err != nil {
Expand Down
54 changes: 5 additions & 49 deletions sdk/data/azcosmos/cosmos_error.go
Expand Up @@ -5,77 +5,33 @@ package azcosmos

import (
"encoding/json"
"fmt"
"net/http"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
azruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
)

// cosmosError is used as base error for any error response from the Cosmos service.
type cosmosError struct {
body string
code string
rawResponse *http.Response
}

func (e *cosmosError) StatusCode() int {
if e.rawResponse == nil {
return 0
}
return e.rawResponse.StatusCode
}

func (e *cosmosError) ErrorCode() string {
return e.code
}

func (e *cosmosError) RawResponse() *http.Response {
return e.rawResponse
}

func newCosmosError(response *http.Response) error {
bytesRead, err := azruntime.Payload(response)
if err != nil {
return err
}

cError := cosmosError{
rawResponse: response,
body: string(bytesRead),
cError := azcore.ResponseError{
StatusCode: response.StatusCode,
RawResponse: response,
}

// Attempt to extract Code from body
var cErrorResponse cosmosErrorResponse
err = json.Unmarshal(bytesRead, &cErrorResponse)
if err == nil {
cError.code = cErrorResponse.Code
cError.ErrorCode = cErrorResponse.Code
}

return &cError
}

func newCosmosErrorWithStatusCode(statusCode int, requestCharge *float32) error {
rawResponse := &http.Response{
StatusCode: statusCode,
Header: http.Header{},
}

if requestCharge != nil {
rawResponse.Header.Add(cosmosHeaderRequestCharge, fmt.Sprint(*requestCharge))
}

return &cosmosError{
rawResponse: rawResponse,
}
}

func (e *cosmosError) Error() string {
if e.body == "" {
return "response contained no body"
}
return e.body
}

type cosmosErrorResponse struct {
Code string `json:"Code"`
}
65 changes: 46 additions & 19 deletions sdk/data/azcosmos/cosmos_error_test.go
Expand Up @@ -6,9 +6,12 @@ package azcosmos
import (
"context"
"encoding/json"
"errors"
"net/http"
"strings"
"testing"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
azruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/internal/mock"
Expand All @@ -25,12 +28,21 @@ func TestCosmosErrorOnEmptyResponse(t *testing.T) {
t.Fatal(err)
}

pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
resp, _ := pl.Do(req)

cError := newCosmosError(resp)
if cError.Error() != "response contained no body" {
t.Errorf("Expected response contained no body, but got %v", cError)
var azErr *azcore.ResponseError
if err := newCosmosError(resp); !errors.As(err, &azErr) {
t.Fatalf("unexpected error type %T", err)
}
if azErr.StatusCode != http.StatusNotFound {
t.Errorf("unexpected status code %d", azErr.StatusCode)
}
if azErr.ErrorCode != "" {
t.Errorf("unexpected error code %s", azErr.ErrorCode)
}
if azErr.RawResponse == nil {
t.Error("unexpected nil RawResponse")
}
}

Expand All @@ -46,12 +58,24 @@ func TestCosmosErrorOnNonJsonBody(t *testing.T) {
t.Fatal(err)
}

pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
resp, _ := pl.Do(req)

cError := newCosmosError(resp)
if cError.Error() != "This is not JSON" {
t.Errorf("Expected This is not JSON, but got %v", cError)
var azErr *azcore.ResponseError
if err := newCosmosError(resp); !errors.As(err, &azErr) {
t.Fatalf("unexpected error type %T", err)
}
if azErr.StatusCode != http.StatusNotFound {
t.Errorf("unexpected status code %d", azErr.StatusCode)
}
if azErr.ErrorCode != "" {
t.Errorf("unexpected error code %s", azErr.ErrorCode)
}
if azErr.RawResponse == nil {
t.Error("unexpected nil RawResponse")
}
if !strings.Contains(azErr.Error(), "This is not JSON") {
t.Error("missing error message")
}
}

Expand All @@ -76,20 +100,23 @@ func TestCosmosErrorOnJsonBody(t *testing.T) {
t.Fatal(err)
}

pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", []policy.Policy{}, []policy.Policy{}, &policy.ClientOptions{Transport: srv})
pl := azruntime.NewPipeline("azcosmostest", "v1.0.0", azruntime.PipelineOptions{}, &policy.ClientOptions{Transport: srv})
resp, _ := pl.Do(req)

cError := newCosmosError(resp)
asError := cError.(*cosmosError)
if asError.ErrorCode() != someError.Code {
t.Errorf("Expected %v, but got %v", someError.Code, asError.ErrorCode())
var azErr *azcore.ResponseError
if err := newCosmosError(resp); !errors.As(err, &azErr) {
t.Fatalf("unexpected error type %T", err)
}

if asError.StatusCode() != 404 {
t.Errorf("Expected 404 Not Found, but got %v", asError.StatusCode())
if azErr.StatusCode != http.StatusNotFound {
t.Errorf("unexpected status code %d", azErr.StatusCode)
}

if asError.Error() != string(jsonString) {
t.Errorf("Expected %v, but got %v", string(jsonString), asError.Error())
if azErr.ErrorCode != someError.Code {
t.Errorf("unexpected error code %s", azErr.ErrorCode)
}
if azErr.RawResponse == nil {
t.Error("unexpected nil RawResponse")
}
if !strings.Contains(azErr.Error(), `"Code": "SomeCode"`) {
t.Error("missing error JSON")
}
}

0 comments on commit d15088e

Please sign in to comment.