Skip to content
Open

V2 #145

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
name: Go

on: [ push, pull_request ]
on: [push, pull_request]
permissions:
contents: read
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable
- name: Get dependencies
run: go mod download
- name: Check go.mod
run: go mod tidy -diff
- name: Test
run: go test -race -v -coverprofile=profile.cov ./...
- uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: profile.cov
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "stable"
- name: Download modules
run: go mod download
- name: Check go.mod
run: go mod tidy -diff
- name: Test
run: go test -race -v -coverprofile=profile.cov ./...
- uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: profile.cov
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

Requests also comes with tools for building custom http transports, include a request recorder and replayer for testing.

*[See this note on the canonical project URL.](https://gist.github.com/earthboundkid/8915002ae0e531cecdfc58bc6453ac80)*

## Features

- Simplifies HTTP client usage compared to net/http
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ Only the most recently tagged version of this repository is supported.

## Reporting a Vulnerability

Email me@carlmjohnson.net with any security concerns or vulnerabilities.
Email me@carlana.net with any security concerns or vulnerabilities.
24 changes: 12 additions & 12 deletions body.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
// BodyGetter provides a Builder with a source for a request body.
type BodyGetter = func() (io.ReadCloser, error)

// BodyReader is a BodyGetter that returns an io.Reader.
func BodyReader(r io.Reader) BodyGetter {
// bodyReader is a BodyGetter that returns an io.Reader.
func bodyReader(r io.Reader) BodyGetter {
return func() (io.ReadCloser, error) {
if rc, ok := r.(io.ReadCloser); ok {
return rc, nil
Expand All @@ -21,8 +21,8 @@ func BodyReader(r io.Reader) BodyGetter {
}
}

// BodyWriter is a BodyGetter that pipes writes into a request body.
func BodyWriter(f func(w io.Writer) error) BodyGetter {
// bodyWriter is a BodyGetter that pipes writes into a request body.
func bodyWriter(f func(w io.Writer) error) BodyGetter {
return func() (io.ReadCloser, error) {
r, w := io.Pipe()
go func() {
Expand All @@ -36,8 +36,8 @@ func BodyWriter(f func(w io.Writer) error) BodyGetter {
}
}

// BodyBytes is a BodyGetter that returns the provided raw bytes.
func BodyBytes(b []byte) BodyGetter {
// bodyBytes is a BodyGetter that returns the provided raw bytes.
func bodyBytes(b []byte) BodyGetter {
return func() (io.ReadCloser, error) {
return rc(bytes.NewReader(b)), nil
}
Expand All @@ -56,21 +56,21 @@ func BodySerializer(s Serializer, v any) BodyGetter {
}
}

// BodyJSON is a [BodySerializer]
// bodyJSON is a [BodySerializer]
// that uses [JSONSerializer] to marshal the object.
func BodyJSON(v any) BodyGetter {
func bodyJSON(v any) BodyGetter {
return BodySerializer(JSONSerializer, v)
}

// BodyForm is a BodyGetter that builds an encoded form body.
func BodyForm(data url.Values) BodyGetter {
// bodyForm is a BodyGetter that builds an encoded form body.
func bodyForm(data url.Values) BodyGetter {
return func() (r io.ReadCloser, err error) {
return rc(strings.NewReader(data.Encode())), nil
}
}

// BodyFile is a BodyGetter that reads the provided file path.
func BodyFile(name string) BodyGetter {
// bodyFile is a BodyGetter that reads the provided file path.
func bodyFile(name string) BodyGetter {
return func() (r io.ReadCloser, err error) {
return os.Open(name)
}
Expand Down
8 changes: 5 additions & 3 deletions builder_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"net/url"
"slices"

"github.com/carlmjohnson/requests/internal/minitrue"
"github.com/gophertools/requests/internal/minitrue"
)

// Builder is a convenient way to build, send, and handle HTTP requests.
Expand Down Expand Up @@ -59,7 +59,7 @@ import (
//
// Set a handler for a response with [Builder.Handle]
// or use the built in [Builder.ToHeaders], [Builder.ToJSON], [Builder.ToString],
// [Builder.ToBytesBuffer], or [Builder.ToWriter].
// or [Builder.ToWriter].
//
// [Builder.Fetch] creates an http.Request with [Builder.Request]
// and validates and handles it with [Builder.Do].
Expand Down Expand Up @@ -241,6 +241,8 @@ func (rb *Builder) Request(ctx context.Context) (req *http.Request, err error) {
return req, nil
}

func noOp(res *http.Response) (err error) { return nil }

// Do calls the underlying http.Client and validates and handles any resulting response. The response body is closed after all validators and the handler run.
func (rb *Builder) Do(req *http.Request) (err error) {
cl := cmp.Or(rb.cl, http.DefaultClient)
Expand All @@ -255,7 +257,7 @@ func (rb *Builder) Do(req *http.Request) (err error) {
}
h := minitrue.Cond(rb.handler != nil,
rb.handler,
consumeBody)
noOp)

code, err := do(cl, req, validators, h)
switch code {
Expand Down
39 changes: 22 additions & 17 deletions builder_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@ import (
"path/filepath"
"strings"

"github.com/carlmjohnson/requests"
"github.com/gophertools/requests"
"github.com/gophertools/requests/reqtest"
)

func init() {
http.DefaultClient.Transport = requests.Replay("testdata")
}

func Example() {
// Simple GET into a string
var s string
Expand Down Expand Up @@ -77,12 +74,12 @@ func Example_postJSON() {
// {101 foo baz 1}
}

func ExampleBuilder_ToBytesBuffer() {
func ExampleToBytesBuffer() {
// Simple GET into a buffer
var buf bytes.Buffer
err := requests.
URL("http://example.com").
ToBytesBuffer(&buf).
Handle(requests.ToBytesBuffer(&buf)).
Fetch(context.Background())
if err != nil {
fmt.Println("could not connect to example.com:", err)
Expand Down Expand Up @@ -148,7 +145,7 @@ func ExampleBuilder_ToFile() {
}

type placeholder struct {
ID int `json:"id,omitempty"`
ID int `json:"id,omitzero"`
Title string `json:"title"`
Body string `json:"body"`
UserID int `json:"userId"`
Expand Down Expand Up @@ -310,8 +307,8 @@ func ExampleBuilder_Bearer() {
}
// But our response is accepted when we provide a bearer token
var res struct {
Authenticated bool
Token string
Authenticated bool `json:"authenticated"`
Token string `json:"token"`
}
err = requests.
URL("http://httpbin.org/bearer").
Expand Down Expand Up @@ -541,12 +538,16 @@ func ExampleBuilder_Transport() {

func ExampleBuilder_ErrorJSON() {
{
trans := requests.ReplayString(`HTTP/1.1 200 OK
trans := reqtest.ReplayString(`HTTP/1.1 200 OK

{"x": 1}`)

var goodJSON struct{ X int }
var errJSON struct{ Error string }
var goodJSON struct {
X int `json:"x"`
}
var errJSON struct {
Error string `json:"error"`
}
err := requests.
URL("http://example.com/").
Transport(trans).
Expand All @@ -560,12 +561,16 @@ func ExampleBuilder_ErrorJSON() {
}
}
{
trans := requests.ReplayString(`HTTP/1.1 418 I'm a teapot
trans := reqtest.ReplayString(`HTTP/1.1 418 I'm a teapot

{"error": "brewing"}`)

var goodJSON struct{ X int }
var errJSON struct{ Error string }
var goodJSON struct {
X int `json:"x"`
}
var errJSON struct {
Error string `json:"error"`
}
err := requests.
URL("http://example.com/").
Transport(trans).
Expand Down Expand Up @@ -620,7 +625,7 @@ func ExampleBuilder_BodySerializer() {
}

func ExampleBuilder_ToDeserializer() {
trans := requests.ReplayString(
trans := reqtest.ReplayString(
"HTTP/1.1 200 OK\r\n\r\nXYZ\x00\xde\xca\xff",
)
// Have some binary structure
Expand Down
37 changes: 16 additions & 21 deletions builder_extras.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package requests

import (
"bytes"
"encoding/base64"
"fmt"
"io"
Expand Down Expand Up @@ -120,17 +119,17 @@ func (rb *Builder) Bearer(token string) *Builder {

// BodyReader sets the Builder's request body to r.
func (rb *Builder) BodyReader(r io.Reader) *Builder {
return rb.Body(BodyReader(r))
return rb.Body(bodyReader(r))
}

// BodyWriter pipes writes from w to the Builder's request body.
func (rb *Builder) BodyWriter(f func(w io.Writer) error) *Builder {
return rb.Body(BodyWriter(f))
return rb.Body(bodyWriter(f))
}

// BodyBytes sets the Builder's request body to b.
func (rb *Builder) BodyBytes(b []byte) *Builder {
return rb.Body(BodyBytes(b))
return rb.Body(bodyBytes(b))
}

// BodySerializer sets the Builder's request body
Expand All @@ -146,21 +145,22 @@ func (rb *Builder) BodySerializer(s Serializer, v any) *Builder {
// if it is not otherwise set.
func (rb *Builder) BodyJSON(v any) *Builder {
return rb.
Body(BodyJSON(v)).
Body(bodyJSON(v)).
HeaderOptional("Content-Type", "application/json")
}

// BodyForm sets the Builder's request body to the encoded form.
// It also sets the ContentType to "application/x-www-form-urlencoded".
// It also sets the ContentType to "application/x-www-form-urlencoded"
// if it is not otherwise set.
func (rb *Builder) BodyForm(data url.Values) *Builder {
return rb.
Body(BodyForm(data)).
ContentType("application/x-www-form-urlencoded")
Body(bodyForm(data)).
HeaderOptional("Content-Type", "application/x-www-form-urlencoded")
}

// BodyFile sets the Builder's request body to read from the given file path.
func (rb *Builder) BodyFile(name string) *Builder {
return rb.Body(BodyFile(name))
return rb.Body(bodyFile(name))
}

// CheckStatus adds a validator for status code of a response.
Expand Down Expand Up @@ -189,29 +189,24 @@ func (rb *Builder) ToDeserializer(d Deserializer, v any) *Builder {
//
// It uses [JSONDeserializer] to unmarshal the object.
func (rb *Builder) ToJSON(v any) *Builder {
return rb.Handle(ToJSON(v))
return rb.Handle(toJSON(v))
}

// ToString sets the Builder to write the response body to the provided string pointer.
func (rb *Builder) ToString(sp *string) *Builder {
return rb.Handle(ToString(sp))
}

// ToBytesBuffer sets the Builder to write the response body to the provided bytes.Buffer.
func (rb *Builder) ToBytesBuffer(buf *bytes.Buffer) *Builder {
return rb.Handle(ToBytesBuffer(buf))
}

// ToWriter sets the Builder to copy the response body into w.
func (rb *Builder) ToWriter(w io.Writer) *Builder {
return rb.Handle(ToWriter(w))
return rb.Handle(toWriter(w))
}

// ToFile sets the Builder to write the response body to the given file name.
// The file and its parent directories are created automatically.
// For more advanced use cases, use ToWriter.
// For more advanced use cases, use [Builder.ToWriter].
func (rb *Builder) ToFile(name string) *Builder {
return rb.Handle(ToFile(name))
return rb.Handle(toFile(name))
}

// CopyHeaders adds a validator which copies the response headers to h.
Expand All @@ -220,15 +215,15 @@ func (rb *Builder) ToFile(name string) *Builder {
// if status code validation is desired.
func (rb *Builder) CopyHeaders(h map[string][]string) *Builder {
return rb.
AddValidator(CopyHeaders(h))
AddValidator(copyHeaders(h))
}

// ToHeaders sets the method to HEAD and adds a handler which copies the response headers to h.
// To just copy headers, see Builder.CopyHeaders.
// To just copy headers, see [Builder.CopyHeaders].
func (rb *Builder) ToHeaders(h map[string][]string) *Builder {
return rb.
Head().
Handle(ChainHandlers(CopyHeaders(h), consumeBody))
Handle(copyHeaders(h))
}

// ErrorJSON adds a validator that applies DefaultValidator
Expand Down
Loading
Loading