detached 6af0a61

client.go RawHistoryFolder

1package http
2
3import (
4	"fmt"
5	"io"
6	nethttp "net/http"
7	"time"
8
9	"github.com/bnema/archup/internal/domain/ports"
10)
11
12// HTTPClient implements the HTTPClient port using the standard library
13type HTTPClient struct {
14	client *nethttp.Client
15}
16
17// NewHTTPClient creates a new HTTP client with a default timeout
18func NewHTTPClient() *HTTPClient {
19	return &HTTPClient{
20		client: &nethttp.Client{
21			Timeout: 30 * time.Second,
22		},
23	}
24}
25
26// NewHTTPClientWithTimeout creates a new HTTP client with a custom timeout
27func NewHTTPClientWithTimeout(timeout time.Duration) *HTTPClient {
28	return &HTTPClient{
29		client: &nethttp.Client{
30			Timeout: timeout,
31		},
32	}
33}
34
35// Get performs an HTTP GET request
36func (hc *HTTPClient) Get(url string) (ports.Response, error) {
37	resp, err := hc.client.Get(url)
38	if err != nil {
39		return nil, fmt.Errorf("GET request failed: %w", err)
40	}
41
42	// Read body into memory
43	body, err := io.ReadAll(resp.Body)
44	if err != nil {
45		if closeErr := resp.Body.Close(); closeErr != nil {
46			return nil, fmt.Errorf("failed to read response body: %w (close error: %v)", err, closeErr)
47		}
48		return nil, fmt.Errorf("failed to read response body: %w", err)
49	}
50
51	return &httpResponse{
52		statusCode: resp.StatusCode,
53		body:       body,
54		rawResp:    resp,
55	}, nil
56}
57
58// Post performs an HTTP POST request
59func (hc *HTTPClient) Post(url string, contentType string, body []byte) (ports.Response, error) {
60	resp, err := hc.client.Post(url, contentType, nil)
61	if err != nil {
62		return nil, fmt.Errorf("POST request failed: %w", err)
63	}
64
65	// Read body into memory
66	respBody, err := io.ReadAll(resp.Body)
67	if err != nil {
68		if closeErr := resp.Body.Close(); closeErr != nil {
69			return nil, fmt.Errorf("failed to read response body: %w (close error: %v)", err, closeErr)
70		}
71		return nil, fmt.Errorf("failed to read response body: %w", err)
72	}
73
74	return &httpResponse{
75		statusCode: resp.StatusCode,
76		body:       respBody,
77		rawResp:    resp,
78	}, nil
79}
80
81// httpResponse implements the Response port
82type httpResponse struct {
83	statusCode int
84	body       []byte
85	rawResp    *nethttp.Response
86}
87
88// StatusCode returns the HTTP status code
89func (hr *httpResponse) StatusCode() int {
90	return hr.statusCode
91}
92
93// Body returns the response body
94func (hr *httpResponse) Body() []byte {
95	return hr.body
96}
97
98// Close closes the response
99func (hr *httpResponse) Close() error {
100	if hr.rawResp != nil {
101		return hr.rawResp.Body.Close()
102	}
103	return nil
104}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 0

    ↑↓ moveenter openesc close