From 4899fe95f47dfb8ce1f309c5ba267a7ecd0e08ba Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sun, 23 Jan 2022 01:21:31 +0200 Subject: [PATCH] more improvements to the x/client new sub-package --- _examples/README.md | 2 + .../http-client/weatherapi/client/client.go | 46 ++++++++++++++++++ .../http-client/weatherapi/client/response.go | 43 +++++++++++++++++ _examples/http-client/weatherapi/main.go | 21 +++++++++ x/client/option.go | 47 +++++++++++++++++++ 5 files changed, 159 insertions(+) create mode 100644 _examples/http-client/weatherapi/client/client.go create mode 100644 _examples/http-client/weatherapi/client/response.go create mode 100644 _examples/http-client/weatherapi/main.go diff --git a/_examples/README.md b/_examples/README.md index 792eef11..070610d3 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -30,6 +30,8 @@ * [Multi Instances](http-server/custom-httpserver/multi/main.go) * [HTTP/3 Quic](http-server/http3-quic) * [Timeout](http-server/timeout/main.go) +* HTTP Client + * [Weather Client](http-client/weatherapi) * Configuration * [Functional](configuration/functional/main.go) * [Configuration Struct](configuration/from-configuration-structure/main.go) diff --git a/_examples/http-client/weatherapi/client/client.go b/_examples/http-client/weatherapi/client/client.go new file mode 100644 index 00000000..9e518e0a --- /dev/null +++ b/_examples/http-client/weatherapi/client/client.go @@ -0,0 +1,46 @@ +package client + +import ( + "context" + "net/url" + + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/x/client" +) + +// The BaseURL of our API client. +const BaseURL = "https://api.weatherapi.com/v1" + +type ( + Options struct { + APIKey string `json:"api_key" yaml:"APIKey" toml:"APIKey"` + } + + Client struct { + *client.Client + } +) + +func NewClient(opts Options) *Client { + apiKeyParameterSetter := client.RequestParam("key", opts.APIKey) + + c := client.New( + client.Debug, + client.BaseURL(BaseURL), + client.PersistentRequestOptions(apiKeyParameterSetter), + ) + + return &Client{c} +} + +func (c *Client) GetCurrentByCity(ctx context.Context, city string) (resp Response, err error) { + urlpath := "/current.json" + // ?q=Athens&aqi=no + params := client.RequestQuery(url.Values{ + "q": []string{city}, + "aqi": []string{"no"}, + }) + + err = c.Client.ReadJSON(ctx, &resp, iris.MethodGet, urlpath, nil, params) + return +} diff --git a/_examples/http-client/weatherapi/client/response.go b/_examples/http-client/weatherapi/client/response.go new file mode 100644 index 00000000..ff624807 --- /dev/null +++ b/_examples/http-client/weatherapi/client/response.go @@ -0,0 +1,43 @@ +package client + +type Response struct { + Location struct { + Name string `json:"name"` + Region string `json:"region"` + Country string `json:"country"` + Lat float64 `json:"lat"` + Lon float64 `json:"lon"` + TzID string `json:"tz_id"` + LocaltimeEpoch int `json:"localtime_epoch"` + Localtime string `json:"localtime"` + } `json:"location"` + Current struct { + LastUpdatedEpoch int `json:"last_updated_epoch"` + LastUpdated string `json:"last_updated"` + TempC float64 `json:"temp_c"` + TempF float64 `json:"temp_f"` + IsDay int `json:"is_day"` + Condition struct { + Text string `json:"text"` + Icon string `json:"icon"` + Code int `json:"code"` + } `json:"condition"` + WindMph float64 `json:"wind_mph"` + WindKph float64 `json:"wind_kph"` + WindDegree int `json:"wind_degree"` + WindDir string `json:"wind_dir"` + PressureMb float64 `json:"pressure_mb"` + PressureIn float64 `json:"pressure_in"` + PrecipMm float64 `json:"precip_mm"` + PrecipIn float64 `json:"precip_in"` + Humidity int `json:"humidity"` + Cloud int `json:"cloud"` + FeelslikeC float64 `json:"feelslike_c"` + FeelslikeF float64 `json:"feelslike_f"` + VisKm float64 `json:"vis_km"` + VisMiles float64 `json:"vis_miles"` + Uv float64 `json:"uv"` + GustMph float64 `json:"gust_mph"` + GustKph float64 `json:"gust_kph"` + } `json:"current"` +} diff --git a/_examples/http-client/weatherapi/main.go b/_examples/http-client/weatherapi/main.go new file mode 100644 index 00000000..c6850466 --- /dev/null +++ b/_examples/http-client/weatherapi/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "context" + "fmt" + + "github.com/kataras/iris/v12/_examples/http-client/weatherapi/client" +) + +func main() { + c := client.NewClient(client.Options{ + APIKey: "{YOUR_API_KEY_HERE}", + }) + + resp, err := c.GetCurrentByCity(context.Background(), "Xanthi/GR") + if err != nil { + panic(err) + } + + fmt.Printf("Temp: %.2f(C), %.2f(F)\n", resp.Current.TempC, resp.Current.TempF) +} diff --git a/x/client/option.go b/x/client/option.go index 4a69b109..188d169c 100644 --- a/x/client/option.go +++ b/x/client/option.go @@ -1,8 +1,13 @@ package client import ( + "context" + "fmt" + "net/http" + "strings" "time" + "github.com/kataras/golog" "golang.org/x/time/rate" ) @@ -46,3 +51,45 @@ func RateLimit(requestsPerSecond int) Option { c.rateLimiter = rate.NewLimiter(rate.Limit(requestsPerSecond), requestsPerSecond) } } + +// Debug enables the client's debug logger. +func Debug(c *Client) { + handler := &debugRequestHandler{ + logger: golog.Child("Iris HTTP Client: ").SetLevel("debug"), + } + c.requestHandlers = append(c.requestHandlers, handler) +} + +type debugRequestHandler struct { + logger *golog.Logger +} + +func (h *debugRequestHandler) getHeadersLine(headers http.Header) (headersLine string) { + for k, v := range headers { + headersLine += fmt.Sprintf("%s(%s), ", k, strings.Join(v, ",")) + } + + headersLine = strings.TrimRight(headersLine, ", ") + return +} + +func (h *debugRequestHandler) BeginRequest(ctx context.Context, req *http.Request) error { + format := "%s: %s: content length: %d: headers: %s" + headersLine := h.getHeadersLine(req.Header) + + h.logger.Debugf(format, req.Method, req.URL.String(), req.ContentLength, headersLine) + return nil +} + +func (h *debugRequestHandler) EndRequest(ctx context.Context, resp *http.Response, err error) error { + if err != nil { + h.logger.Debugf("%s: %s: ERR: %s", resp.Request.Method, resp.Request.URL.String(), err.Error()) + } else { + format := "%s: %s: content length: %d: headers: %s" + headersLine := h.getHeadersLine(resp.Header) + + h.logger.Debugf(format, resp.Request.Method, resp.Request.URL.String(), resp.ContentLength, headersLine) + } + + return err +}