Skip to content
Merged
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
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly

- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
65 changes: 65 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: CI

on:
pull_request:
push:
branches:
- master
- main
tags:
- "v*"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
test:
name: Go ${{ matrix.go-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
go-version:
- "1.17.x"
- "1.26.x"
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go-version }}
cache: true

- name: Download modules
run: go mod download

- name: Test
run: go test -v ./...

quality:
name: Quality
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.26.x"
cache: true

- name: Race test
run: go test -race -count=1 ./...

- name: Lint
uses: golangci/golangci-lint-action@v9
with:
version: v2.11.3
21 changes: 0 additions & 21 deletions .github/workflows/go-test.yml

This file was deleted.

15 changes: 0 additions & 15 deletions .github/workflows/golangci-lint.yml

This file was deleted.

6 changes: 0 additions & 6 deletions .travis.yml

This file was deleted.

3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
[![Golang CI Linter](https://github.com/putdotio/go-putio/actions/workflows/golangci-lint.yml/badge.svg)](https://github.com/putdotio/go-putio/actions/workflows/golangci-lint.yml)
[![Golang Tests](https://github.com/putdotio/go-putio/actions/workflows/go-test.yml/badge.svg)](https://github.com/putdotio/go-putio/actions/workflows/go-test.yml)
[![CI](https://github.com/putdotio/go-putio/actions/workflows/ci.yml/badge.svg)](https://github.com/putdotio/go-putio/actions/workflows/ci.yml)


# putio
Expand Down
5 changes: 2 additions & 3 deletions account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package putio

import (
"context"
"fmt"
"net/http"
"testing"
)
Expand Down Expand Up @@ -59,7 +58,7 @@ func TestAccount_Info(t *testing.T) {
`
mux.HandleFunc("/v2/account/info", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
fmt.Fprintln(w, fixture)
writeResponse(t, w, fixture)
})

info, err := client.Account.Info(context.Background())
Expand Down Expand Up @@ -107,7 +106,7 @@ func TestAccount_Settings(t *testing.T) {
`
mux.HandleFunc("/v2/account/settings", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
fmt.Fprintln(w, fixture)
writeResponse(t, w, fixture)
})

settings, err := client.Account.Settings(context.Background())
Expand Down
33 changes: 31 additions & 2 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,25 +148,43 @@ func (c *Client) NewRequest(ctx context.Context, method, relURL string, body io.
// v is nil. If v is nil, response body is not closed and the body can be used
// for streaming.
func (c *Client) Do(r *http.Request, v interface{}) (*http.Response, error) {
var cancel context.CancelFunc
if c.Timeout > 0 {
ctx, cancel := context.WithTimeout(r.Context(), c.Timeout)
defer cancel()
ctx, timeoutCancel := context.WithTimeout(r.Context(), c.Timeout)
cancel = timeoutCancel
defer func() {
if v != nil {
cancel()
}
}()
r = r.WithContext(ctx)
}

resp, err := c.client.Do(r)
if err != nil {
if cancel != nil {
cancel()
}
return nil, fmt.Errorf("%w", err)
}

err = checkResponse(resp)
if err != nil {
// close the body at all times if there is an http error
_ = resp.Body.Close()
if cancel != nil {
cancel()
}
return resp, err
}

if v == nil {
if cancel != nil {
resp.Body = &cancelReadCloser{
ReadCloser: resp.Body,
cancel: cancel,
}
}
return resp, nil
}

Expand All @@ -183,6 +201,17 @@ func (c *Client) Do(r *http.Request, v interface{}) (*http.Response, error) {
return resp, nil
}

type cancelReadCloser struct {
io.ReadCloser
cancel context.CancelFunc
}

func (c *cancelReadCloser) Close() error {
err := c.ReadCloser.Close()
c.cancel()
return err
}

// checkResponse is the entrypoint to reading the API response. If the response
// status code is not in success range, it will try to return a structured
// error.
Expand Down
8 changes: 8 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package putio

import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
Expand Down Expand Up @@ -39,6 +40,13 @@ func testHeader(t *testing.T, r *http.Request, key, value string) { // nolint
}
}

func writeResponse(t *testing.T, w http.ResponseWriter, body string) {
t.Helper()
if _, err := fmt.Fprintln(w, body); err != nil {
t.Errorf("write response: %v", err)
}
}

func TestNewClient(t *testing.T) {
cl := NewClient(nil)
if cl.BaseURL.String() != defaultBaseURL {
Expand Down
9 changes: 6 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@ func (f *ConfigService) SetAll(ctx context.Context, config interface{}) error {
return fmt.Errorf("%w", err)
}
req.Header.Set("content-type", "application/json")
_, err = f.client.Do(req, nil) // nolint:bodyclose
resp, err := f.client.Do(req, nil)
if err != nil {
return fmt.Errorf("%w", err)
}
_ = resp.Body.Close()
return nil
}

Expand All @@ -95,10 +96,11 @@ func (f *ConfigService) Set(ctx context.Context, key string, value interface{})
return fmt.Errorf("%w", err)
}
req.Header.Set("content-type", "application/json")
_, err = f.client.Do(req, nil) // nolint:bodyclose
resp, err := f.client.Do(req, nil)
if err != nil {
return fmt.Errorf("%w", err)
}
_ = resp.Body.Close()
return nil

}
Expand All @@ -109,9 +111,10 @@ func (f *ConfigService) Del(ctx context.Context, key string) error {
if err != nil {
return fmt.Errorf("%w", err)
}
_, err = f.client.Do(req, nil) // nolint:bodyclose
resp, err := f.client.Do(req, nil)
if err != nil {
return fmt.Errorf("%w", err)
}
_ = resp.Body.Close()
return nil
}
72 changes: 72 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package putio

import (
"context"
"net/http"
"strings"
"testing"
)

func TestConfig_WriteMethodsCloseResponseBody(t *testing.T) {
tests := []struct {
name string
call func(*Client) error
}{
{
name: "set all",
call: func(client *Client) error {
return client.Config.SetAll(context.Background(), map[string]string{"key": "value"})
},
},
{
name: "set",
call: func(client *Client) error {
return client.Config.Set(context.Background(), "key", "value")
},
},
{
name: "del",
call: func(client *Client) error {
return client.Config.Del(context.Background(), "key")
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
body := &trackedReadCloser{Reader: strings.NewReader(`{"status":"OK"}`)}
client := NewClient(&http.Client{
Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: http.StatusOK,
Body: body,
Header: make(http.Header),
}, nil
}),
})

if err := tt.call(client); err != nil {
t.Fatal(err)
}
if !body.closed {
t.Fatal("response body was not closed")
}
})
}
}

type roundTripFunc func(*http.Request) (*http.Response, error)

func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
return f(r)
}

type trackedReadCloser struct {
*strings.Reader
closed bool
}

func (r *trackedReadCloser) Close() error {
r.closed = true
return nil
}
5 changes: 2 additions & 3 deletions events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package putio

import (
"context"
"fmt"
"net/http"
"testing"
)
Expand Down Expand Up @@ -36,7 +35,7 @@ func TestEvents_List(t *testing.T) {
`
mux.HandleFunc("/v2/events/list", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodGet)
fmt.Fprintln(w, fixture)
writeResponse(t, w, fixture)
})

events, err := client.Events.List(context.Background())
Expand All @@ -60,7 +59,7 @@ func TestEvents_Delete(t *testing.T) {
mux.HandleFunc("/v2/events/delete", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, http.MethodPost)
testHeader(t, r, "Content-Type", "application/x-www-form-urlencoded")
fmt.Fprintln(w, `{"status":"OK"}`)
writeResponse(t, w, `{"status":"OK"}`)
})

err := client.Events.Delete(context.Background())
Expand Down
Loading