Skip to content

Commit

Permalink
chore: added client files
Browse files Browse the repository at this point in the history
  • Loading branch information
tiwarishubham635 committed Sep 2, 2024
1 parent e6d6376 commit 7ab686d
Show file tree
Hide file tree
Showing 41 changed files with 3,886 additions and 1 deletion.
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: test install test-integ test-docker
.PHONY: test install test-integ test-docker goimports

install:
go get -t -v ./...
Expand All @@ -13,3 +13,8 @@ version ?= latest
test-docker:
curl -s https://raw.githubusercontent.com/sendgrid/sendgrid-oai/HEAD/prism/prism.sh -o prism.sh
version=$(version) bash ./prism.sh

goimports:
go install golang.org/x/tools/cmd/goimports@latest
goimports -w .
go mod tidy
13 changes: 13 additions & 0 deletions client/base_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package client

import (
"net/http"
"net/url"
"time"
)

type BaseClient interface {
SetTimeout(timeout time.Duration)
SendRequest(method string, rawURL string, data url.Values,
headers map[string]interface{}, body ...byte) (*http.Response, error)
}
180 changes: 180 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// Package client provides internal utilities for the twilio-go client library.
package client

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
"regexp"
"runtime"
"strconv"
"strings"
"time"

"github.com/pkg/errors"
"github.com/sendgrid/sendgrid-go/client/form"
)

var alphanumericRegex *regexp.Regexp
var delimitingRegex *regexp.Regexp

func init() {
alphanumericRegex = regexp.MustCompile(`^[a-zA-Z0-9]*$`)
delimitingRegex = regexp.MustCompile(`\.\d+`)
}

// Credentials store user authentication credentials.
type Credentials struct {
Apikey string
}

func NewCredentials(apikey string) *Credentials {
return &Credentials{Apikey: apikey}
}

// Client encapsulates a standard HTTP backend with authorization.
type Client struct {
*Credentials
HTTPClient *http.Client
UserAgentExtensions []string
}

// default http Client should not follow redirects and return the most recent response.
func defaultHTTPClient() *http.Client {
return &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Timeout: time.Second * 10,
}
}

func (c *Client) basicAuth() (string, string) {
return c.Credentials.Apikey, ""
}

func (c *Client) bearerAuth() string {
return c.Credentials.Apikey
}

// SetTimeout sets the Timeout for HTTP requests.
func (c *Client) SetTimeout(timeout time.Duration) {
if c.HTTPClient == nil {
c.HTTPClient = defaultHTTPClient()
}
c.HTTPClient.Timeout = timeout
}

func extractContentTypeHeader(headers map[string]interface{}) (cType string) {
headerType, ok := headers["Content-Type"]
if !ok {
return urlEncodedContentType
}
return headerType.(string)
}

const (
urlEncodedContentType = "application/x-www-form-urlencoded"
jsonContentType = "application/json"
keepZeros = true
delimiter = '.'
escapee = '\\'
)

func (c *Client) doWithErr(req *http.Request) (*http.Response, error) {
client := c.HTTPClient

if client == nil {
client = defaultHTTPClient()
}

res, err := client.Do(req)
if err != nil {
return nil, err
}

// Note that 3XX response codes are allowed for fetches
if res.StatusCode < 200 || res.StatusCode >= 400 {
err = &SendgridRestError{}
decodeErrs := json.NewDecoder(res.Body)
decodeErr := decodeErrs.Decode(err)
if decodeErr != nil {
err = errors.Wrap(decodeErr, "error decoding the response for an HTTP error code: "+strconv.Itoa(res.StatusCode))
return nil, err
}

return nil, err
}
return res, nil
}

func setBearerToken(req *http.Request, token string) {
req.Header.Set("Authorization", "Bearer "+token)
}

// SendRequest verifies, constructs, and authorizes an HTTP request.
func (c *Client) SendRequest(method string, rawURL string, data url.Values,
headers map[string]interface{}, body ...byte) (*http.Response, error) {

contentType := extractContentTypeHeader(headers)

u, err := url.Parse(rawURL)
if err != nil {
return nil, err
}

valueReader := &strings.Reader{}
goVersion := runtime.Version()
var req *http.Request

//For HTTP GET Method there are no body parameters. All other parameters like query, path etc
// are added as information in the url itself. Also while Content-Type is json, we are sending
// json body. In that case, data variable contains all other parameters than body, which is the
//same case as GET method. In that case as well all parameters will be added to url
if method == http.MethodGet || contentType == jsonContentType {
if data != nil {
v, _ := form.EncodeToStringWith(data, delimiter, escapee, keepZeros)
s := delimitingRegex.ReplaceAllString(v, "")

u.RawQuery = s
}
}

//data is already processed and information will be added to u(the url) in the
//previous step. Now body will solely contain json payload
if contentType == jsonContentType {
req, err = http.NewRequest(method, u.String(), bytes.NewBuffer(body))
if err != nil {
return nil, err
}
} else {
//Here the HTTP POST methods which is not having json content type are processed
//All the values will be added in data and encoded (all body, query, path parameters)
if method == http.MethodPost || method == http.MethodPut {
valueReader = strings.NewReader(data.Encode())
}
req, err = http.NewRequest(method, u.String(), valueReader)
if err != nil {
return nil, err
}

}

setBearerToken(req, c.bearerAuth())

// E.g. "User-Agent": "twilio-go/1.0.0 (darwin amd64) go/go1.17.8"
userAgent := fmt.Sprintf("sendgrid-go/%s (%s %s) go/%s", LibraryVersion, runtime.GOOS, runtime.GOARCH, goVersion)

if len(c.UserAgentExtensions) > 0 {
userAgent += " " + strings.Join(c.UserAgentExtensions, " ")
}

req.Header.Add("User-Agent", userAgent)

for k, v := range headers {
req.Header.Add(k, fmt.Sprint(v))
}
return c.doWithErr(req)
}
Loading

0 comments on commit 7ab686d

Please sign in to comment.