Fixed lint issues and missing documentation
This commit is contained in:
1 parent
7fd321df97
commit
6e6dec68c0
34 files changed
+346
-156
No files matched your search
+48
-25
@@ -18,19 +18,37 @@ import (
|
||||
resty "resty.dev/v3"
|
||||
)
|
||||
|
||||
var ErrNoNext = errors.New("no next link")
|
||||
var ErrNoReleases = errors.New("no releases")
|
||||
var ErrRateLimitExceeded = errors.New("rate limit has been exceeded")
|
||||
var ErrFetchCancelled = errors.New("fetch cancelled")
|
||||
var ErrUnexpectedStatus = errors.New("unexpected response status")
|
||||
// Errors returned when fetching from the API.
|
||||
var (
|
||||
// ErrNoNext means a release package has no next page link.
|
||||
ErrNoNext = errors.New("no next link")
|
||||
// ErrNoReleases means the API returned no releases.
|
||||
ErrNoReleases = errors.New("no releases")
|
||||
// ErrRateLimitExceeded means the API refused a request with 429 Too Many
|
||||
// Requests, and waiting for it was not allowed or did not help.
|
||||
ErrRateLimitExceeded = errors.New("rate limit has been exceeded")
|
||||
// ErrFetchCancelled means the fetch's context was cancelled while waiting
|
||||
// out a rate limit.
|
||||
ErrFetchCancelled = errors.New("fetch cancelled")
|
||||
// ErrUnexpectedStatus wraps an API response status other than 200 OK.
|
||||
ErrUnexpectedStatus = errors.New("unexpected response status")
|
||||
)
|
||||
|
||||
// RequestOptions selects the releases a fetch asks for.
|
||||
type RequestOptions struct {
|
||||
Since time.Time
|
||||
Limit int
|
||||
// Since asks for releases updated from this time. Zero means 24 hours ago.
|
||||
Since time.Time
|
||||
// Limit is the number of releases per page. Zero means 100.
|
||||
Limit int
|
||||
// AllowRateLimit waits out a 429 response and retries once, instead of
|
||||
// failing with ErrRateLimitExceeded.
|
||||
AllowRateLimit bool
|
||||
}
|
||||
|
||||
// ---------- Utility methods for handling API irregularities -----------
|
||||
|
||||
// safeReleasePackage is ocds.ReleasePackage with the releases left undecoded,
|
||||
// so safeUnpackReleases can repair or skip each one.
|
||||
type safeReleasePackage struct {
|
||||
URI string `json:"uri,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
@@ -127,7 +145,7 @@ func isDateField(name string) bool {
|
||||
strings.HasSuffix(name, "Deadline")
|
||||
}
|
||||
|
||||
// ReleaseIDs reads just the ocid and the release id from a release, without
|
||||
// releaseIDs reads just the ocid and the release id from a release, without
|
||||
// decoding the rest of it. It reads the top-level fields in order and stops
|
||||
// as soon as it has both, so an error later in the release (invalid JSON or a
|
||||
// value that wouldn't fit the release struct) doesn't matter. Fields before
|
||||
@@ -202,15 +220,16 @@ func missingIDsError(haveOCID, haveID bool) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Unpacks an API response into an OCDS ReleasePackage. Handles API
|
||||
// irregularities (malformed dates, missing fields, invalid releases, etc).
|
||||
// safeUnpackReleases unpacks an API response into an OCDS ReleasePackage,
|
||||
// handling API irregularities (malformed dates, missing fields, invalid
|
||||
// releases, etc).
|
||||
//
|
||||
// This method takes ownership of the response body and closes it when done.
|
||||
func safeUnpackReleases(response *resty.Response) (*ocds.ReleasePackage, error) {
|
||||
var pkg safeReleasePackage
|
||||
|
||||
// Take ownership of the response body.
|
||||
defer response.Body.Close()
|
||||
defer response.Body.Close() //nolint:errcheck // read only
|
||||
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
if err := decoder.Decode(&pkg); err != nil {
|
||||
@@ -255,9 +274,10 @@ func safeUnpackReleases(response *resty.Response) (*ocds.ReleasePackage, error)
|
||||
}
|
||||
|
||||
// ---------- API interaction methods -----------
|
||||
// Sleep the fetch worker until either the context is cancelled (shutdown) or
|
||||
// the duration has expired. The duration is usually provided by the API - a
|
||||
// nil duration will return immediately.
|
||||
|
||||
// rateSleep sleeps the fetch worker until either the context is cancelled
|
||||
// (shutdown), returning ctx.Err(), or the duration has expired. The duration
|
||||
// is usually provided by the API; a zero duration returns immediately.
|
||||
func rateSleep(ctx context.Context, duration time.Duration) error {
|
||||
timer := time.NewTimer(duration)
|
||||
defer timer.Stop()
|
||||
@@ -270,14 +290,16 @@ func rateSleep(ctx context.Context, duration time.Duration) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Check and close the response body - avoids double close issues
|
||||
// closeBody closes res's body if it has one, avoiding a double close.
|
||||
func closeBody(res *resty.Response) {
|
||||
if res.Body != nil {
|
||||
res.Body.Close()
|
||||
res.Body.Close() //nolint:errcheck,gosec // read only
|
||||
}
|
||||
}
|
||||
|
||||
// Actually perform a single fetch against the API
|
||||
// fetchURI performs a single GET of uri, using ft's context, and traces it at
|
||||
// debug level. Unlike the method of the same name it does not check the
|
||||
// response status.
|
||||
func fetchURI(ft *FindATender, uri string) (*resty.Response, error) {
|
||||
withTrace := ft.config.Logging.LogLevel() <= slog.LevelDebug
|
||||
|
||||
@@ -310,7 +332,7 @@ func fetchURI(ft *FindATender, uri string) (*resty.Response, error) {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Perform a fetch against the API. If the API returns with a rate limit
|
||||
// fetchURI performs a fetch against the API. If the API returns with a rate limit
|
||||
// reached response and allowRateLimit is true, then wait and retry like a good
|
||||
// client. If allowRateLimit is false, return to the caller informing them of
|
||||
// the rate limit exceeded error. If, after waiting, the API still thinks we
|
||||
@@ -367,7 +389,8 @@ func (ft *FindATender) fetchURI(
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Perform a fetch against the API with some sanitised default options.
|
||||
// fetchWithOpts performs a fetch against the API with some sanitised default
|
||||
// options (see RequestOptions).
|
||||
func (ft *FindATender) fetchWithOpts(
|
||||
opts RequestOptions,
|
||||
) (*resty.Response, error) {
|
||||
@@ -388,9 +411,9 @@ func (ft *FindATender) fetchWithOpts(
|
||||
return ft.fetchURI(uri, opts.AllowRateLimit)
|
||||
}
|
||||
|
||||
// If the response provided a "Next" link, perform another API call to get the
|
||||
// next page. Error if there is no "Next" link - the caller can decide if they
|
||||
// have attempted to fetch next in error or not.
|
||||
// fetchNext fetches the page pkg's "Next" link points to. It returns
|
||||
// ErrNoNext if there is no "Next" link; the caller can decide whether asking
|
||||
// for it was an error.
|
||||
func (ft *FindATender) fetchNext(
|
||||
pkg *ocds.ReleasePackage,
|
||||
allowRateLimit bool,
|
||||
@@ -401,7 +424,7 @@ func (ft *FindATender) fetchNext(
|
||||
return ft.fetchURI(pkg.Links.Next, allowRateLimit)
|
||||
}
|
||||
|
||||
// Make one call on the API. Do not follow any "Next" links.
|
||||
// fetchOnce makes one call on the API. It does not follow any "Next" links.
|
||||
func (ft *FindATender) fetchOnce(opts RequestOptions) ([]ocds.Release, error) {
|
||||
res, err := ft.fetchWithOpts(opts)
|
||||
if err != nil {
|
||||
@@ -420,8 +443,8 @@ func (ft *FindATender) fetchOnce(opts RequestOptions) ([]ocds.Release, error) {
|
||||
return pkg.Releases, nil
|
||||
}
|
||||
|
||||
// Make a call on the API. If there are any "Next" links, call them in
|
||||
// succession until the end has been reached.
|
||||
// fetchAll makes a call on the API. If there are any "Next" links, it calls
|
||||
// them in succession until the end has been reached.
|
||||
func (ft *FindATender) fetchAll(opts RequestOptions) ([]ocds.Release, error) {
|
||||
var releases []ocds.Release
|
||||
|
||||
|
||||
Reference in new issue
Block a user