Skip to content

Commit

Permalink
Merge pull request #15 from StephanHCB/issue-14-mime-uploads
Browse files Browse the repository at this point in the history
feat(#14): support any custom body reader and content type
  • Loading branch information
StephanHCB committed Jan 18, 2023
2 parents f31460e + f600a4f commit f83d7ec
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 1 deletion.
11 changes: 10 additions & 1 deletion api/interface.go
Expand Up @@ -2,6 +2,7 @@ package aurestclientapi

import (
"context"
"io"
"net/http"
"time"
)
Expand All @@ -18,6 +19,14 @@ type ParsedResponse struct {
Time time.Time
}

// CustomRequestBody allows you the greatest amount of control over the request by directly supplying the
// body io.Reader, the length, and the content type header.
type CustomRequestBody struct {
BodyReader io.Reader // Tip: &bytes.Buffer{} implements io.Reader
BodyLength int
ContentType string
}

// Client is a utility class representing a http client.
//
// We provide multiple stackable implementations, typical stacking order is
Expand Down Expand Up @@ -104,4 +113,4 @@ type CacheKeyFunction func(ctx context.Context, method string, url string, reque
//
// Not all parameters will always be set. For example, the latency is only known at the request logging level,
// and the request/response body size is only known while that is being processed.
type MetricsCallbackFunction func(ctx context.Context, method string, url string, status int, err error, latency time.Duration, size int)
type MetricsCallbackFunction func(ctx context.Context, method string, url string, status int, err error, latency time.Duration, size int)
70 changes: 70 additions & 0 deletions example/fullstack/fullstack.go
@@ -1,6 +1,7 @@
package examplefullstack

import (
"bytes"
"context"
aulogging "github.com/StephanHCB/go-autumn-logging"
aurestclientapi "github.com/StephanHCB/go-autumn-restclient/api"
Expand All @@ -11,6 +12,8 @@ import (
aurestrecorder "github.com/StephanHCB/go-autumn-restclient/implementation/recorder"
aurestlogging "github.com/StephanHCB/go-autumn-restclient/implementation/requestlogging"
aurestretry "github.com/StephanHCB/go-autumn-restclient/implementation/retry"
"io"
"mime/multipart"
"net/http"
"time"
)
Expand Down Expand Up @@ -112,3 +115,70 @@ func testingExample() {
// also assert on the recording what requests were made
_ = aurestcapture.GetRecording(requestCaptureClient)
}

func fileUploadExample() {
fileContents := &bytes.Buffer{} // here you'd read the file, or open a reader for the file

// construct custom response body

body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, _ := writer.CreateFormFile("file", "filename.txt")
_, _ = io.Copy(part, fileContents)
_ = writer.Close()

request := aurestclientapi.CustomRequestBody{
BodyReader: body,
BodyLength: body.Len(),
ContentType: writer.FormDataContentType(),
}

// assumes you set up logging by importing one of the go-autumn-logging-xxx dependencies
//
// for this example, let's set up a logger that does nothing, so we don't pull in these dependencies here
//
// This of course makes the requestLoggingClient not work.
aulogging.SetupNoLoggerForTesting()

// ----- setup (can be done once during application startup) ----

// 1. set up http client
var timeout time.Duration = 0
var customCACert []byte = nil
var requestManipulator aurestclientapi.RequestManipulatorCallback = nil

httpClient, _ := auresthttpclient.New(timeout, customCACert, requestManipulator)

// 2. recording (for use with 1a)
recorderClient := aurestrecorder.New(httpClient)

// 3. request logging
requestLoggingClient := aurestlogging.New(recorderClient)

// 4. circuit breaker (see https://github.com/StephanHCB/go-autumn-restclient-circuitbreaker)
// not included here because it has extra dependencies

// 5. retry
var repeatCount uint8 = 2 // 0 means only try once (but then why use this at all?)
var condition aurestclientapi.RetryConditionCallback = func(ctx context.Context, response *aurestclientapi.ParsedResponse, err error) bool {
return response.Status == http.StatusRequestTimeout
}
var beforeRetry aurestclientapi.BeforeRetryCallback = nil

retryingClient := aurestretry.New(requestLoggingClient, repeatCount, condition, beforeRetry)

// ----- now make a request -----

bodyDto := make(map[string]interface{})

response := aurestclientapi.ParsedResponse{
Body: &bodyDto,
}
err := retryingClient.Perform(context.Background(), http.MethodPost, "https://some.rest.api/fileupload", request, &response)
if err != nil {
return
}

// now bodyDto is filled with the response and response.Status and response.Header are also set.

}
3 changes: 3 additions & 0 deletions implementation/httpclient/httpclient.go
Expand Up @@ -170,6 +170,9 @@ func (c *HttpClientImpl) requestBodyReader(requestBody interface{}) (io.Reader,
if requestBody == nil {
return nil, 0, "", nil
}
if asCustom, ok := requestBody.(aurestclientapi.CustomRequestBody); ok {
return asCustom.BodyReader, asCustom.BodyLength, asCustom.ContentType, nil
}
if asString, ok := requestBody.(string); ok {
return strings.NewReader(asString), len(asString), aurestclientapi.ContentTypeApplicationJson, nil
}
Expand Down

0 comments on commit f83d7ec

Please sign in to comment.