Skip to content

Commit

Permalink
feat: allow setting Reason in Context (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisgacsal committed Mar 14, 2023
1 parent db51a7e commit a3f54dd
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 28 deletions.
38 changes: 22 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,30 @@ import (
)

func main() {
// Initializing Cruise Control client with default configuration
cruisecontrol, err := client.NewDefaultClient()
if err != nil {
panic(err)
}
// Initializing Cruise Control client with default configuration
cruisecontrol, err := client.NewDefaultClient()
if err != nil {
panic(err)
}

// Create Context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30 * time.Second) │ │
ctx, cancel := context.WithTimeout(context.Background(), 30 * time.Second)
defer cancel()

// Assembling the request using default values
req := api.StateRequestWithDefaults()
// Sending the request to the State API
resp, err := cruisecontrol.State(ctx, req)
if err != nil {
panic(err)
}
// Getting the state of the Executor
fmt.Println(resp.Result.ExecutorState.State)

// Optionally set request Reason to Context which will sent to Cruise Control as part of the HTTP request
ctx = client.ContextWithReason("example")

// Assembling the request using default values
req := api.StateRequestWithDefaults()

// Sending the request to the State API
resp, err := cruisecontrol.State(ctx, req)
if err != nil {
panic(err)
}

// Getting the state of the Executor
fmt.Println(resp.Result.ExecutorState.State)
}
```

Expand Down
4 changes: 4 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ func (c Client) request(ctx context.Context, req interface{}, resp types.APIResp
WithContext(ctx),
}

if _, ok := req.(types.RequestReasoner); ok {
opts = append(opts, WithReasonFromContext(ctx))
}

httpResp, err := c.send(ctx, r, opts...)
if err != nil {
return err
Expand Down
38 changes: 26 additions & 12 deletions pkg/client/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const (
HTTPHeaderContentType = "Content-Type"
MIMETypeJSON = "application/json"
ChartSetUTF8 = "utf-8"
JSONQueryParam = "json"
ReasonQueryParam = "reason"
)

type RequestOptions interface {
Expand Down Expand Up @@ -87,13 +89,13 @@ func WithMethod(m string) RequestOptionApplier {
}

func WithHeader(h string, v string) RequestOptionApplier {
return RequestOptionApplier(func(r *http.Request) error {
return func(r *http.Request) error {
if r.Header == nil {
r.Header = make(http.Header)
}
r.Header.Set(h, v)
return nil
})
}
}

func WithUserAgent(agent string) RequestOptionApplier {
Expand All @@ -109,24 +111,36 @@ func WithContentTypeJSON() RequestOptionApplier {
}

func WithJSONQuery() RequestOptionApplier {
return WithQuery(JSONQueryParam, "true")
}

func WithContext(ctx context.Context) RequestOptionApplier {
return func(r *http.Request) error {
r2 := r.WithContext(ctx)
*r = *r2
return nil
}
}

func WithReasonFromContext(ctx context.Context) RequestOptionApplier {
reason, ok := ReasonFromContext(ctx)
if !ok {
return func(r *http.Request) error { return nil }
}
return WithQuery(ReasonQueryParam, reason)
}

func WithQuery(k, v string) RequestOptionApplier {
return func(r *http.Request) error {
if r.URL == nil {
r.URL = &url.URL{}
}
q, err := url.ParseQuery(r.URL.RawQuery)
if err != nil {
return fmt.Errorf("failed to apply JSON query parameter to HTTP request: %w", err)
return fmt.Errorf("failed to apply %s query parameter to HTTP request: %w", k, err)
}
q.Set("json", "true")
q.Set(k, v)
r.URL.RawQuery = q.Encode()
return nil
}
}

func WithContext(ctx context.Context) RequestOptionApplier {
return func(r *http.Request) error {
r2 := r.WithContext(ctx)
*r = *r2
return nil
}
}
18 changes: 18 additions & 0 deletions pkg/client/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package client

import (
"context"
"fmt"
"net/http"
"net/url"
Expand All @@ -28,6 +29,10 @@ import (
"github.com/banzaicloud/go-cruise-control/pkg/internal/encoder"
)

const reasonContextKey reasonContextKeyType = "CruiseControlRequestReason"

type reasonContextKeyType string

// RequestMarshaler is the interface implemented by types that can marshal themselves into valid http.Request
type RequestMarshaler interface {
MarshalRequest() (*http.Request, error)
Expand Down Expand Up @@ -87,3 +92,16 @@ func marshal(v interface{}) (*http.Request, error) {

return r, nil
}

func ContextWithReason(ctx context.Context, reason string) context.Context {
return context.WithValue(ctx, reasonContextKey, reason)
}

func ReasonFromContext(ctx context.Context) (string, bool) {
if r := ctx.Value(reasonContextKey); r != nil {
if rr, ok := r.(string); ok {
return rr, true
}
}
return "", false
}
10 changes: 10 additions & 0 deletions pkg/types/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,23 @@ type GenericRequest struct {
DoAs string `param:"doAs,omitempty"`
}

// RequestReasoner is a dummy interface to help identifying APIRequests
// which have the `Reason` field without the need for using reflection
type RequestReasoner interface {
requestReason() string
}

type GenericRequestWithReason struct {
GenericRequest

// Reason for request
Reason string `param:"reason,omitempty"`
}

func (r GenericRequestWithReason) requestReason() string {
return r.Reason
}

type APIResponse interface {
UnmarshalResponse(*http.Response) error
InProgress() bool
Expand Down

0 comments on commit a3f54dd

Please sign in to comment.