Repository navigation
Expand file tree
/
Copy pathoptions.go
More file actions
135 lines (116 loc) · 2.96 KB
/
Copy pathoptions.go
File metadata and controls
135 lines (116 loc) · 2.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// SPDX-FileCopyrightText: Copyright 2025 Carabiner Systems, Inc
// SPDX-License-Identifier: Apache-2.0
package github
import (
"errors"
"fmt"
"time"
)
// Defaults for the native caller's HTTP behavior. Each attempt is bounded
// by DefaultTimeout; an attempt that fails on the transport, times out, or
// gets a 429 or a 5xx is retried DefaultRetries times, waiting
// DefaultRetryWait before the first retry and doubling after each.
const (
DefaultTimeout = 15 * time.Second
DefaultRetries = uint(3)
DefaultRetryWait = 2 * time.Second
)
type Options struct {
Host string
Token string
EnsureToken bool
TokenReader TokenReader
Caller Caller
// Timeout bounds each HTTP attempt, headers and body. Zero uses
// DefaultTimeout.
Timeout time.Duration
// Retries is how many times a failed attempt is retried: a transport
// error, a timeout, a 429 or a 5xx other than 501. Requests with a body
// are never retried, since the body has been consumed.
Retries uint
// RetryWait is the wait before the first retry; it doubles after each,
// up to a minute. Zero uses DefaultRetryWait.
RetryWait time.Duration
}
type fnOpt func(*Options)
func WithHost(h string) fnOpt {
return func(opts *Options) {
opts.Host = h
}
}
func WithToken(t string) fnOpt {
return func(opts *Options) {
opts.Token = t
}
}
func WithEnsureToken(e bool) fnOpt {
return func(opts *Options) {
opts.EnsureToken = e
}
}
func WithTokenReader(r TokenReader) fnOpt {
return func(opts *Options) {
opts.TokenReader = r
}
}
// WithTimeout bounds each HTTP attempt.
func WithTimeout(d time.Duration) fnOpt {
return func(opts *Options) {
opts.Timeout = d
}
}
// WithRetries sets how many times a failed attempt is retried.
func WithRetries(n uint) fnOpt {
return func(opts *Options) {
opts.Retries = n
}
}
// WithRetryWait sets the wait before the first retry.
func WithRetryWait(d time.Duration) fnOpt {
return func(opts *Options) {
opts.RetryWait = d
}
}
func WithCaller(c Caller) fnOpt {
return func(opts *Options) {
opts.Caller = c
}
}
var defaultOptions = Options{
Host: DefaultAPIHostname,
TokenReader: &EnvTokenReader{
VarName: "GITHUB_TOKEN",
},
Timeout: DefaultTimeout,
Retries: DefaultRetries,
RetryWait: DefaultRetryWait,
}
// ensureToken makes sure we have a token. If there is none set, we
// read it using the token reader
func (o *Options) ensureToken() error {
if o.Token != "" {
return nil
}
if o.TokenReader == nil {
return errors.New("no token set and no token reader configured")
}
token, err := o.TokenReader.ReadToken()
if err != nil {
return fmt.Errorf("reading token: %w", err)
}
if token == "" {
return fmt.Errorf("unable to get a token from the token reader")
}
o.Token = token
return nil
}
// Validate ensures the client options are sane
func (o *Options) Validate() error {
errs := []error{}
if err := o.ensureToken(); err != nil {
if o.EnsureToken {
errs = append(errs, err)
}
}
return errors.Join(errs...)
}