From a68716a44780864a8869cc73fdb55b136897923f Mon Sep 17 00:00:00 2001 From: Pius Alfred Date: Fri, 25 Oct 2024 20:08:45 +0200 Subject: [PATCH] refactor (#49) --- auth/auth.go | 14 +++--- business/business.go | 18 ++++---- flow/flow.go | 37 ++++++++-------- flow/metrics.go | 16 ++++--- media/media.go | 81 +++++++++++++++++++---------------- pkg/http/http.go | 100 +++++++++++++++++++++++++++++++++++++++++++ qrcode/qrcode.go | 14 +++--- 7 files changed, 198 insertions(+), 82 deletions(-) diff --git a/auth/auth.go b/auth/auth.go index 18987ce..1543684 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -274,13 +274,15 @@ func (c *Client) RefreshAccessToken(ctx context.Context, if params.SetTokenExpiresIn60 { queryParams["set_token_expires_in_60_days"] = "true" } - req := &whttp.Request[any]{ - Type: whttp.RequestTypeRefreshToken, - BaseURL: c.baseURL, - Method: http.MethodGet, - Endpoints: []string{c.apiVersion, "/oauth/access_token"}, - QueryParams: queryParams, + + opts := []whttp.RequestOption[any]{ + whttp.WithRequestType[any](whttp.RequestTypeRefreshToken), + whttp.WithRequestEndpoints[any](c.apiVersion, "/oauth/access_token"), + whttp.WithRequestQueryParams[any](queryParams), } + + req := whttp.MakeRequest[any](http.MethodGet, c.baseURL, opts...) + res := &RefreshAccessTokenResponse{} decoder := whttp.ResponseDecoderJSON(res, whttp.DecodeOptions{ DisallowUnknownFields: false, diff --git a/business/business.go b/business/business.go index 9231cd7..769c1a5 100644 --- a/business/business.go +++ b/business/business.go @@ -95,19 +95,21 @@ func (s *BaseSender) Send(ctx context.Context, config *config.Config, request *B params["fields"] = fields } - req := &whttp.Request[any]{ - Type: request.Type, - Method: request.Method, - BaseURL: config.BaseURL, - Endpoints: []string{config.APIVersion, config.PhoneNumberID, Endpoint}, - QueryParams: params, - Bearer: config.AccessToken, + opts := []whttp.RequestOption[any]{ + whttp.WithRequestEndpoints[any](config.APIVersion, config.PhoneNumberID, Endpoint), + whttp.WithRequestQueryParams[any](params), + whttp.WithRequestBearer[any](config.AccessToken), + whttp.WithRequestType[any](request.Type), + whttp.WithRequestAppSecret[any](config.AppSecret), + whttp.WithRequestSecured[any](config.SecureRequests), } if request.Payload != nil { - req.Message = &request.Payload + opts = append(opts, whttp.WithRequestMessage[any](&request.Payload)) } + req := whttp.MakeRequest[any](request.Method, config.BaseURL, opts...) + response := &Response{} decoder := whttp.ResponseDecoderJSON(response, whttp.DecodeOptions{ diff --git a/flow/flow.go b/flow/flow.go index 2c20d98..45e555a 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -401,27 +401,28 @@ func (client *BaseClient) UpdateFlowJSON(ctx context.Context, return nil, fmt.Errorf("read config: %w", err) } - req := &whttp.Request[any]{ - Type: whttp.RequestTypeUploadMedia, - Method: http.MethodPost, - Bearer: conf.AccessToken, - QueryParams: nil, - BaseURL: conf.BaseURL, - Endpoints: []string{conf.APIVersion, conf.PhoneNumberID, "media"}, - Metadata: nil, - Message: nil, - Form: &whttp.RequestForm{ - Fields: map[string]string{ - "name": request.Name, - "asset_type": "FLOW_JSON", - }, - FormFile: &whttp.FormFile{ - Name: "file", - Path: request.File, - }, + form := &whttp.RequestForm{ + Fields: map[string]string{ + "name": request.Name, + "asset_type": "FLOW_JSON", }, + FormFile: &whttp.FormFile{ + Name: "file", + Path: request.File, + }, + } + + opts := []whttp.RequestOption[any]{ + whttp.WithRequestType[any](whttp.RequestTypeUploadMedia), + whttp.WithRequestBearer[any](conf.AccessToken), + whttp.WithRequestForm[any](form), + whttp.WithRequestEndpoints[any](conf.APIVersion, conf.PhoneNumberID, "media"), + whttp.WithRequestAppSecret[any](conf.AppSecret), + whttp.WithRequestSecured[any](conf.SecureRequests), } + req := whttp.MakeRequest[any](http.MethodPost, conf.BaseURL, opts...) + var resp UpdateFlowJSONResponse decoder := whttp.ResponseDecoderJSON(&resp, whttp.DecodeOptions{ DisallowUnknownFields: true, diff --git a/flow/metrics.go b/flow/metrics.go index 6ecf13b..f4bb5b7 100644 --- a/flow/metrics.go +++ b/flow/metrics.go @@ -69,15 +69,17 @@ func (client *BaseClient) GetFlowMetrics(ctx context.Context, request *MetricsRe request.MetricName, request.Granularity, request.Since.Format(time.DateOnly), request.Until.Format(time.DateOnly)), } - req := &whttp.Request[any]{ - Type: whttp.RequestTypeGetFlowMetrics, - Method: http.MethodGet, - Bearer: conf.AccessToken, - QueryParams: queryParams, - BaseURL: conf.BaseURL, - Endpoints: []string{conf.APIVersion, request.FlowID}, + opts := []whttp.RequestOption[any]{ + whttp.WithRequestType[any](whttp.RequestTypeGetFlowMetrics), + whttp.WithRequestBearer[any](conf.AccessToken), + whttp.WithRequestQueryParams[any](queryParams), + whttp.WithRequestSecured[any](conf.SecureRequests), + whttp.WithRequestAppSecret[any](conf.AppSecret), + whttp.WithRequestEndpoints[any](conf.APIVersion, request.FlowID), } + req := whttp.MakeRequest[any](http.MethodGet, conf.BaseURL, opts...) + var resp MetricsAPIResponse decoder := whttp.ResponseDecoderJSON(&resp, whttp.DecodeOptions{ DisallowUnknownFields: true, diff --git a/media/media.go b/media/media.go index 5116648..0f2b404 100644 --- a/media/media.go +++ b/media/media.go @@ -278,13 +278,16 @@ func (s *BaseClient) Download(ctx context.Context, request *DownloadRequest, dec if err != nil { return fmt.Errorf("%w: config read: %w", ErrMediaDownload, err) } - req := &whttp.Request[any]{ - Type: whttp.RequestTypeDownloadMedia, - Method: http.MethodGet, - BaseURL: request.URL, - Bearer: conf.AccessToken, + + opts := []whttp.RequestOption[any]{ + whttp.WithRequestAppSecret[any](conf.AppSecret), + whttp.WithRequestSecured[any](conf.SecureRequests), + whttp.WithRequestBearer[any](conf.AccessToken), + whttp.WithRequestType[any](whttp.RequestTypeDownloadMedia), } + req := whttp.MakeRequest[any](http.MethodGet, request.URL, opts...) + for i := 0; i <= request.Retries; i++ { if err := s.Sender.Send(ctx, req, decoder); err != nil { if i < request.Retries { @@ -316,15 +319,17 @@ func (s *BaseClient) Delete(ctx context.Context, req *BaseRequest) (*DeleteMedia queryParams["phone_number_id"] = phoneNumberID } - request := &whttp.Request[any]{ - Type: whttp.RequestTypeDeleteMedia, - Method: http.MethodDelete, - Bearer: conf.AccessToken, - BaseURL: conf.BaseURL, - QueryParams: queryParams, - Endpoints: []string{conf.APIVersion, req.MediaID}, + opts := []whttp.RequestOption[any]{ + whttp.WithRequestAppSecret[any](conf.AppSecret), + whttp.WithRequestSecured[any](conf.SecureRequests), + whttp.WithRequestBearer[any](conf.AccessToken), + whttp.WithRequestType[any](whttp.RequestTypeDeleteMedia), + whttp.WithRequestQueryParams[any](queryParams), + whttp.WithRequestEndpoints[any](conf.APIVersion, req.MediaID), } + request := whttp.MakeRequest[any](http.MethodDelete, conf.BaseURL, opts...) + var resp DeleteMediaResponse decoder := whttp.ResponseDecoderJSON(&resp, whttp.DecodeOptions{ DisallowUnknownFields: true, @@ -354,15 +359,16 @@ func (s *BaseClient) GetInfo(ctx context.Context, req *BaseRequest) (*Informatio queryParams["phone_number_id"] = phoneNumberID } - request := &whttp.Request[any]{ - Type: whttp.RequestTypeGetMedia, - Method: http.MethodGet, - Bearer: conf.AccessToken, - QueryParams: queryParams, - BaseURL: conf.BaseURL, - Endpoints: []string{conf.APIVersion, req.MediaID}, + opts := []whttp.RequestOption[any]{ + whttp.WithRequestAppSecret[any](conf.AppSecret), + whttp.WithRequestSecured[any](conf.SecureRequests), + whttp.WithRequestType[any](whttp.RequestTypeGetMedia), + whttp.WithRequestQueryParams[any](queryParams), + whttp.WithRequestEndpoints[any](conf.APIVersion, req.MediaID), } + request := whttp.MakeRequest[any](http.MethodGet, conf.BaseURL, opts...) + var info Information decoder := whttp.ResponseDecoderJSON(&info, whttp.DecodeOptions{ DisallowUnknownFields: true, @@ -388,27 +394,28 @@ func (s *BaseClient) Upload(ctx context.Context, req *UploadRequest) (*UploadMed return nil, fmt.Errorf("%w: media type not supported", ErrMediaUpload) } - request := &whttp.Request[any]{ - Type: whttp.RequestTypeUploadMedia, - Method: http.MethodPost, - Bearer: conf.AccessToken, - QueryParams: nil, - BaseURL: conf.BaseURL, - Endpoints: []string{conf.APIVersion, conf.PhoneNumberID, "media"}, - Metadata: nil, - Message: nil, - Form: &whttp.RequestForm{ - Fields: map[string]string{ - "type": string(req.MediaType), - "messaging_product": "whatsapp", - }, - FormFile: &whttp.FormFile{ - Name: "file", - Path: req.Filename, - }, + form := &whttp.RequestForm{ + Fields: map[string]string{ + "type": string(req.MediaType), + "messaging_product": "whatsapp", }, + FormFile: &whttp.FormFile{ + Name: "file", + Path: req.Filename, + }, + } + + opts := []whttp.RequestOption[any]{ + whttp.WithRequestAppSecret[any](conf.AppSecret), + whttp.WithRequestSecured[any](conf.SecureRequests), + whttp.WithRequestBearer[any](conf.AccessToken), + whttp.WithRequestType[any](whttp.RequestTypeUploadMedia), + whttp.WithRequestEndpoints[any](conf.APIVersion, conf.PhoneNumberID, "media"), + whttp.WithRequestForm[any](form), } + request := whttp.MakeRequest[any](http.MethodPost, conf.BaseURL, opts...) + var resp UploadMediaResponse decoder := whttp.ResponseDecoderJSON(&resp, whttp.DecodeOptions{ DisallowUnknownFields: true, diff --git a/pkg/http/http.go b/pkg/http/http.go index 7439862..af52202 100644 --- a/pkg/http/http.go +++ b/pkg/http/http.go @@ -262,8 +262,108 @@ type ( Name string Path string } + + RequestOption[T any] func(request *Request[T]) ) +// MakeRequest creates a new request with the provided options. +func MakeRequest[T any](method, baseURL string, options ...RequestOption[T]) *Request[T] { + req := &Request[T]{ + Method: method, + BaseURL: baseURL, + Headers: make(map[string]string), + QueryParams: make(map[string]string), + } + + for _, option := range options { + if option != nil { + option(req) + } + } + + return req +} + +// NewRequestWithContext ... +func NewRequestWithContext[T any](ctx context.Context, method, baseURL string, + options ...RequestOption[T], +) (*http.Request, error) { + req := MakeRequest[T](method, baseURL, options...) + + return RequestWithContext(ctx, req) +} + +// WithRequestType sets the request type for the request. +func WithRequestType[T any](requestType RequestType) RequestOption[T] { + return func(request *Request[T]) { + request.Type = requestType + } +} + +// WithRequestBearer sets the bearer token for the request. +func WithRequestBearer[T any](bearer string) RequestOption[T] { + return func(request *Request[T]) { + request.Bearer = bearer + } +} + +// WithRequestEndpoints sets the endpoints for the request. +func WithRequestEndpoints[T any](endpoints ...string) RequestOption[T] { + return func(request *Request[T]) { + request.Endpoints = endpoints + } +} + +// WithRequestMetadata sets the metadata for the request. +func WithRequestMetadata[T any](metadata types.Metadata) RequestOption[T] { + return func(request *Request[T]) { + request.Metadata = metadata + } +} + +// WithRequestHeaders sets the headers for the request. +func WithRequestHeaders[T any](headers map[string]string) RequestOption[T] { + return func(request *Request[T]) { + request.Headers = headers + } +} + +// WithRequestQueryParams sets the query parameters for the request. +func WithRequestQueryParams[T any](queryParams map[string]string) RequestOption[T] { + return func(request *Request[T]) { + request.QueryParams = queryParams + } +} + +// WithRequestMessage sets the message for the request. +func WithRequestMessage[T any](message *T) RequestOption[T] { + return func(request *Request[T]) { + request.Message = message + } +} + +func WithRequestForm[T any](form *RequestForm) RequestOption[T] { + return func(request *Request[T]) { + request.Form = form + } +} + +// WithRequestAppSecret sets the app secret for the request and turns on secure requests. +func WithRequestAppSecret[T any](appSecret string) RequestOption[T] { + return func(request *Request[T]) { + if request.AppSecret != "" { + request.AppSecret = appSecret + } + } +} + +// WithRequestSecured sets the request to be secure. +func WithRequestSecured[T any](secured bool) RequestOption[T] { + return func(request *Request[T]) { + request.SecureRequests = secured + } +} + var errNilRequest = errors.New("nil request provided") func RequestWithContext[T any](ctx context.Context, req *Request[T]) (*http.Request, error) { diff --git a/qrcode/qrcode.go b/qrcode/qrcode.go index 80e7cdc..be2dfbb 100644 --- a/qrcode/qrcode.go +++ b/qrcode/qrcode.go @@ -341,14 +341,16 @@ func (sender *BaseSender) Send(ctx context.Context, conf *config.Config, req *Ba endpoints = append(endpoints, req.QRCodeID) } - request := &whttp.Request[any]{ - Type: req.Type, - Method: req.Method, - BaseURL: conf.BaseURL, - Endpoints: endpoints, - QueryParams: req.QueryParams, + opts := []whttp.RequestOption[any]{ + whttp.WithRequestType[any](req.Type), + whttp.WithRequestEndpoints[any](endpoints...), + whttp.WithRequestQueryParams[any](req.QueryParams), + whttp.WithRequestAppSecret[any](conf.AppSecret), + whttp.WithRequestSecured[any](conf.SecureRequests), } + request := whttp.MakeRequest[any](req.Method, conf.BaseURL, opts...) + response := &Response{} decoder := whttp.ResponseDecoderJSON(response, whttp.DecodeOptions{