Giter Site home page Giter Site logo

sonirico / withttp Goto Github PK

View Code? Open in Web Editor NEW
19.0 1.0 0.0 109 KB

go build http ๐ŸŒ requests with fluency and wit โœจ

License: MIT License

Makefile 3.19% Go 96.81%
builder fluent-api golang golang-library http http-client http-requests generics https hacktoberfest

withttp's Introduction

build

withttp

Build http requests and parse their responses with fluent syntax and wit. This package aims to quickly configure http roundtrips by covering common scenarios, while leaving all details of http requests and responses open for developers to allow maximum flexibility.

Supported underlying http implementations are:

Query Restful endpoints

type GithubRepoInfo struct {
  ID  int    `json:"id"`
  URL string `json:"html_url"`
}

func GetRepoInfo(user, repo string) (GithubRepoInfo, error) {

  call := withttp.NewCall[GithubRepoInfo](withttp.WithFasthttp()).
    WithURL(fmt.Sprintf("https://api.github.com/repos/%s/%s", user, repo)).
    WithMethod(http.MethodGet).
    WithHeader("User-Agent", "withttp/0.5.1 See https://github.com/sonirico/withttp", false).
    WithParseJSON().
    WithExpectedStatusCodes(http.StatusOK)

  err := call.Call(context.Background())

  return call.BodyParsed, err
}

func main() {
  info, _ := GetRepoInfo("sonirico", "withttp")
  log.Println(info)
}

Stream data to server (from a slice)

See full example

type metric struct {
  Time time.Time `json:"t"`
  Temp float32   `json:"T"`
}

func CreateStream() error {
  points := []metric{
    {
      Time: time.Unix(time.Now().Unix()-1, 0),
      Temp: 39,
    },
    {
      Time: time.Now(),
      Temp: 40,
    },
  }

  stream := withttp.Slice[metric](points)

  testEndpoint := withttp.NewEndpoint("webhook-site-request-stream-example").
    Request(
      withttp.WithBaseURL("https://webhook.site/24e84e8f-75cf-4239-828e-8bed244c0afb"),
    )

  call := withttp.NewCall[any](withttp.WithFasthttp()).
    WithMethod(http.MethodPost).
    WithContentType(withttp.ContentTypeJSONEachRow).
    WithRequestSniffed(func(data []byte, err error) {
      fmt.Printf("recv: '%s', err: %v", string(data), err)
    }).
    WithRequestStreamBody(
      withttp.WithRequestStreamBody[any, metric](stream),
    ).
    WithExpectedStatusCodes(http.StatusOK)

  return call.CallEndpoint(context.Background(), testEndpoint)
}

Stream data to server (from a channel)

See full example

func CreateStreamChannel() error {
  points := make(chan metric, 2)

  go func() {
    points <- metric{
      Time: time.Unix(time.Now().Unix()-1, 0),
      Temp: 39,
    }

    points <- metric{
      Time: time.Now(),
      Temp: 40,
    }

    close(points)
  }()

  stream := withttp.Channel[metric](points)

  testEndpoint := withttp.NewEndpoint("webhook-site-request-stream-example").
    Request(
      withttp.WithBaseURL("https://webhook.site/24e84e8f-75cf-4239-828e-8bed244c0afb"),
    )

  call := withttp.NewCall[any](withttp.WithFasthttp()).
    WithMethod(http.MethodPost).
    WithContentType(withttp.ContentTypeJSONEachRow).
    WithRequestSniffed(func(data []byte, err error) {
      fmt.Printf("recv: '%s', err: %v", string(data), err)
    }).
    WithRequestStreamBody(
      withttp.WithRequestStreamBody[any, metric](stream),
    ).
    WithExpectedStatusCodes(http.StatusOK)

  return call.CallEndpoint(context.Background(), testEndpoint)
}

Stream data to server (from a reader)

See full example

func CreateStreamReader() error {
  buf := bytes.NewBuffer(nil)

  go func() {
    buf.WriteString("{\"t\":\"2022-09-01T00:58:15+02:00\"")
    buf.WriteString(",\"T\":39}\n{\"t\":\"2022-09-01T00:59:15+02:00\",\"T\":40}\n")
  }()

  streamFactory := withttp.NewProxyStreamFactory(1 << 10)

  stream := withttp.NewStreamFromReader(buf, streamFactory)

  testEndpoint := withttp.NewEndpoint("webhook-site-request-stream-example").
    Request(
      withttp.WithBaseURL("https://webhook.site/24e84e8f-75cf-4239-828e-8bed244c0afb"),
    )

  call := withttp.NewCall[any](withttp.WithNetHttp()).
    WithMethod(http.MethodPost).
    WithRequestSniffed(func(data []byte, err error) {
      fmt.Printf("recv: '%s', err: %v", string(data), err)
    }).
    WithContentType(withttp.ContentTypeJSONEachRow).
    WithRequestStreamBody(
      withttp.WithRequestStreamBody[any, []byte](stream),
    ).
    WithExpectedStatusCodes(http.StatusOK)

  return call.CallEndpoint(context.Background(), testEndpoint)
}

Several endpoints

In case of a wide range catalog of endpoints, predefined parameters and behaviours can be defined by employing an endpoint definition.

var (
  githubApi = withttp.NewEndpoint("GithubAPI").
    Request(withttp.WithBaseURL("https://api.github.com/"))
)

type GithubRepoInfo struct {
  ID  int    `json:"id"`
  URL string `json:"html_url"`
}

func GetRepoInfo(user, repo string) (GithubRepoInfo, error) {
  call := withttp.NewCall[GithubRepoInfo](withttp.WithFasthttp()).
    WithURI(fmt.Sprintf("repos/%s/%s", user, repo)).
    WithMethod(http.MethodGet).
    WithHeader("User-Agent", "withttp/0.5.1 See https://github.com/sonirico/withttp", false).
    WithHeaderFunc(func() (key, value string, override bool) {
      key = "X-Date"
      value = time.Now().String()
      override = true
      return
    }).
    WithParseJSON().
    WithExpectedStatusCodes(http.StatusOK)

  err := call.CallEndpoint(context.Background(), githubApi)

  return call.BodyParsed, err
}

type GithubCreateIssueResponse struct {
  ID  int    `json:"id"`
  URL string `json:"url"`
}

func CreateRepoIssue(user, repo, title, body, assignee string) (GithubCreateIssueResponse, error) {
  type payload struct {
    Title    string `json:"title"`
    Body     string `json:"body"`
    Assignee string `json:"assignee"`
  }

  p := payload{
    Title:    title,
    Body:     body,
    Assignee: assignee,
  }

  call := withttp.NewCall[GithubCreateIssueResponse](
    withttp.WithFasthttp(),
  ).
    WithURI(fmt.Sprintf("repos/%s/%s/issues", user, repo)).
    WithMethod(http.MethodPost).
    WithContentType("application/vnd+github+json").
    WithBody(p).
    WithHeaderFunc(func() (key, value string, override bool) {
      key = "Authorization"
      value = fmt.Sprintf("Bearer %s", "S3cret")
      override = true
      return
    }).
    WithExpectedStatusCodes(http.StatusCreated)

  err := call.CallEndpoint(context.Background(), githubApi)

  log.Println("req body", string(call.Req.Body()))

  return call.BodyParsed, err
}

func main() {
  // Fetch repo info
  info, _ := GetRepoInfo("sonirico", "withttp")
  log.Println(info)

  // Create an issue
  res, err := CreateRepoIssue("sonirico", "withttp", "test",
    "This is a test", "sonirico")
  log.Println(res, err)
}

Test your calls again a mock endpoint

Quickly test your calls by creating a mock endpoint

var (
  exchangeListOrders = withttp.NewEndpoint("ListOrders").
        Request(withttp.WithBaseURL("http://example.com")).
        Response(
      withttp.WithMockedRes(func(res withttp.Response) {
        res.SetBody(io.NopCloser(bytes.NewReader(mockResponse)))
        res.SetStatus(http.StatusOK)
      }),
    )
  mockResponse = []byte(strings.TrimSpace(`
    {"amount": 234, "pair": "BTC/USDT"}
    {"amount": 123, "pair": "ETH/USDT"}`))
)

func main() {
  type Order struct {
    Amount float64 `json:"amount"`
    Pair   string  `json:"pair"`
  }

  res := make(chan Order)

  call := withttp.NewCall[Order](withttp.WithFasthttp()).
    WithURL("https://github.com/").
    WithMethod(http.MethodGet).
    WithHeader("User-Agent", "withttp/0.5.1 See https://github.com/sonirico/withttp", false).
    WithParseJSONEachRowChan(res).
    WithExpectedStatusCodes(http.StatusOK)

  go func() {
    for order := range res {
      log.Println(order)
    }
  }()

  err := call.CallEndpoint(context.Background(), exchangeListOrders)

  if err != nil {
    panic(err)
  }
}

TODO:

  • Form-data content type codecs
  • More quality-of-life methods for auth
  • Able to parse more content types:
    • tabular separated
    • xml
    • gRPC

withttp's People

Contributors

sonirico avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

withttp's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.