Skip to content

Commit

Permalink
feat: support use of Context with RequestBuilder
Browse files Browse the repository at this point in the history
Fixes arf/planning-sdk-squad#2230
Fixes #77

This commit adds support for setting a context.Context
instance on the RequestBuilder struct.  This Context instance
will then be associated with the http.Request instance constructed
by the RequestBuilder.Build() method.

BREAKING CHANGE: Minimum supported Go version is now 1.13.
The minimum version of Go supported by this library has been changed
to Go version 1.13. To use this and future versions of the
Go core library, please make sure that you are using
Go version 1.13 or higher.
  • Loading branch information
padamstx committed Oct 13, 2020
1 parent aadc46c commit e9e71cb
Show file tree
Hide file tree
Showing 45 changed files with 123 additions and 44 deletions.
8 changes: 1 addition & 7 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,10 @@ current_version = 4.6.1
commit = True
message = Update version numbers from {current_version} -> {new_version} [skip ci]

[bumpversion:file:v4/core/version.go]
[bumpversion:file:v5/core/version.go]
search = __VERSION__ = "{current_version}"
replace = __VERSION__ = "{new_version}"

[bumpversion:file:v4/go.mod]
parse = (?P<major>\d+)
serialize = {major}
search = module github.com/IBM/go-sdk-core/v{current_version}
replace = module github.com/IBM/go-sdk-core/v{new_version}

[bumpversion:file:Makefile]
parse = (?P<major>\d+)
serialize = {major}
Expand Down
8 changes: 2 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,19 @@ language: go
dist: xenial

go:
- 1.12.x
- 1.13.x

notifications:
email: false

env:
global:
- GO111MODULE=on

before_install:
- nvm install 12
- npm install -g npm@6.x
- sudo apt-get update
- sudo apt-get install python

install:
- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.21.0
- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.31.0

script:
- make all
Expand Down
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ If you want to contribute to the repository, here's a quick guide:

4. Install the `golangci-lint` tool:
```sh
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.21.0
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.31.0
```
* Note: As of this writing, the 1.21.0 version of `golangci-lint` is being used by this project.
* Note: As of this writing, the 1.31.0 version of `golangci-lint` is being used by this project.
Please check the `curl` command found in the `.travis.yml` file to see the version of this tool that is currently
being used at the time you are planning to commit changes. This will ensure that you are using the same version
of the linter as the Travis build automation, which will ensure that you are using the same set of linter checks
Expand All @@ -42,12 +42,12 @@ If you want to contribute to the repository, here's a quick guide:

6. Test your changes:
```sh
go test ./...
make test
```

7. Check your code for lint issues
```sh
golangci-lint run
make lint
```

8. Commit your changes:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Makefile to build go-sdk-core library

VDIR=v4
VDIR=v5

all: build test lint tidy

Expand Down
12 changes: 3 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ go get -u github.com/IBM/go-sdk-core/...
```

## Prerequisites
- Go version 1.12 or newer
- Go version 1.13 or newer

## Authentication
The go-sdk-core project supports the following types of authentication:
Expand All @@ -32,15 +32,9 @@ Before opening a new issue, please search for similar issues. It's possible that

## Tests

Run all test suites:
To build, test and lint-check the project:
```bash
go test ./...
```

Get code coverage for each test suite:
```bash
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
make all
```

## Contributing
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
45 changes: 30 additions & 15 deletions v4/core/request_builder.go → v5/core/request_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package core

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -76,6 +77,13 @@ type RequestBuilder struct {
// the "Content-Encoding" header will be added to the request with the
// value "gzip".
EnableGzipCompression bool

// RequestContext can be used by users of RequestBuilder to provide an
// optional context.Context instance to be associated with the http.Request
// instance that will be built by the Build() method.
// If specified, then http.NewRequestWithContext() is used to create the
// http.Request instance. If nil, then http.NewRequest() is used instead.
RequestContext *context.Context
}

// NewRequestBuilder initiates a new request.
Expand Down Expand Up @@ -275,7 +283,7 @@ func (requestBuilder *RequestBuilder) SetBodyContentForMultipart(contentType str
}

// Build builds an HTTP Request object from this RequestBuilder instance.
func (requestBuilder *RequestBuilder) Build() (*http.Request, error) {
func (requestBuilder *RequestBuilder) Build() (req *http.Request, err error) {
// Create multipart form data
if len(requestBuilder.Form) > 0 {
// handle both application/x-www-form-urlencoded or multipart/form-data
Expand All @@ -287,29 +295,31 @@ func (requestBuilder *RequestBuilder) Build() (*http.Request, error) {
data.Add(fieldName, v.contents.(string))
}
}
_, err := requestBuilder.SetBodyContentString(data.Encode())
_, err = requestBuilder.SetBodyContentString(data.Encode())
if err != nil {
return nil, err
return
}
} else {
formWriter := requestBuilder.createMultipartWriter()
for fieldName, l := range requestBuilder.Form {
for _, v := range l {
dataPartWriter, err := createFormFile(formWriter, fieldName, v.fileName, v.contentType)
var dataPartWriter io.Writer
dataPartWriter, err = createFormFile(formWriter, fieldName, v.fileName, v.contentType)
if err != nil {
return nil, err
return
}
if err = requestBuilder.SetBodyContentForMultipart(v.contentType,
v.contents, dataPartWriter); err != nil {
return nil, err

err = requestBuilder.SetBodyContentForMultipart(v.contentType, v.contents, dataPartWriter)
if err != nil {
return
}
}
}

requestBuilder.AddHeader("Content-Type", formWriter.FormDataContentType())
err := formWriter.Close()
err = formWriter.Close()
if err != nil {
return nil, err
return
}
}
}
Expand All @@ -318,18 +328,23 @@ func (requestBuilder *RequestBuilder) Build() (*http.Request, error) {
// and add the "Content-Encoding: gzip" request header.
if !IsNil(requestBuilder.Body) && requestBuilder.EnableGzipCompression &&
!SliceContains(requestBuilder.Header[CONTENT_ENCODING], "gzip") {
newBody, err := NewGzipCompressionReader(requestBuilder.Body)
var newBody io.Reader
newBody, err = NewGzipCompressionReader(requestBuilder.Body)
if err != nil {
return nil, err
return
}
requestBuilder.Body = newBody
requestBuilder.Header.Add(CONTENT_ENCODING, "gzip")
}

// Create the request
req, err := http.NewRequest(requestBuilder.Method, requestBuilder.URL.String(), requestBuilder.Body)
if requestBuilder.RequestContext != nil {
req, err = http.NewRequestWithContext(*requestBuilder.RequestContext, requestBuilder.Method, requestBuilder.URL.String(), requestBuilder.Body)
} else {
req, err = http.NewRequest(requestBuilder.Method, requestBuilder.URL.String(), requestBuilder.Body)
}
if err != nil {
return nil, err
return
}

// Headers
Expand All @@ -345,7 +360,7 @@ func (requestBuilder *RequestBuilder) Build() (*http.Request, error) {
// Encode query
req.URL.RawQuery = query.Encode()

return req, nil
return
}

// SetBodyContent sets the body content from one of three different sources.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ package core

import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"

assert "github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -593,3 +598,78 @@ func TestBuild(t *testing.T) {
assert.Equal(t, wantURL, request.URL.String())
assert.Equal(t, "Application/json", request.Header["Content-Type"][0])
}

func TestRequestWithContext(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{"name": "wonder woman"}`)
}))
defer server.Close()

ctx, cancelFunc := context.WithTimeout(context.Background(), 3*time.Second)
defer cancelFunc()

builder := NewRequestBuilder("GET")
builder.RequestContext = &ctx
_, err := builder.ConstructHTTPURL(server.URL, nil, nil)
assert.Nil(t, err)
req, _ := builder.Build()

authenticator, _ := NewNoAuthAuthenticator()

options := &ServiceOptions{
URL: server.URL,
Authenticator: authenticator,
}
service, err := NewBaseService(options)
assert.Nil(t, err)

var foo *Foo
detailedResponse, err := service.Request(req, &foo)
assert.Nil(t, err)
assert.NotNil(t, detailedResponse)
assert.Equal(t, http.StatusOK, detailedResponse.StatusCode)
assert.Equal(t, "application/json", detailedResponse.Headers.Get("Content-Type"))

result, ok := detailedResponse.Result.(*Foo)
assert.Equal(t, true, ok)
assert.NotNil(t, result)
assert.NotNil(t, foo)
assert.Equal(t, "wonder woman", *(result.Name))
}

func TestRequestWithContextTimeout(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-type", "application/json")
w.WriteHeader(http.StatusOK)
time.Sleep(3 * time.Second)
fmt.Fprint(w, `{"name": "wonder woman"}`)
}))
defer server.Close()

ctx, cancelFunc := context.WithTimeout(context.Background(), 2*time.Second)
defer cancelFunc()

builder := NewRequestBuilder("GET")
builder.RequestContext = &ctx
_, err := builder.ConstructHTTPURL(server.URL, nil, nil)
assert.Nil(t, err)
req, _ := builder.Build()

authenticator, _ := NewNoAuthAuthenticator()

options := &ServiceOptions{
URL: server.URL,
Authenticator: authenticator,
}
service, err := NewBaseService(options)
assert.Nil(t, err)

var foo *Foo
detailedResponse, err := service.Request(req, &foo)
assert.NotNil(t, err)
t.Logf("Expected error: %s\n", err.Error())
assert.Contains(t, err.Error(), "context deadline exceeded")
assert.Nil(t, detailedResponse)
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions v4/go.mod → v5/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/IBM/go-sdk-core/v4
module github.com/IBM/go-sdk-core/v5

go 1.12
go 1.13

require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit e9e71cb

Please sign in to comment.