Naming, layout and comments: descriptive full-word names, one item per line in wrapped calls and literals, and doc comments on exported names

Short and single-letter names in application code become full words (tests
keep theirs). Wrapped calls, signatures and composite literals put one item
per line, and one-line function bodies are expanded. Exported names and
groups of constants get doc comments, and comments refer to the renamed
identifiers.
This commit is contained in:
bwilliams committed 2026-10-15 16:12:56 +01:00
1 parent 043e307663
commit 465358609a
78 files changed
+2374 -1402

No files matched your search

+80 -61
View File
@@ -14,11 +14,17 @@ import (
"git.avsdev.uk/Go/job-search/internal/mock/findatender/ocds"
)
// Paging and request parameters of the release package endpoint.
const (
// defaultLimit is the page size when the request sets no limit.
defaultLimit = 100
maxLimit = 100
timeLayout = "2006-01-02T15:04:05"
BasePath = "/api/1.0/ocdsReleasePackages"
// maxLimit is the largest page size a request may ask for.
maxLimit = 100
// timeLayout is the documented layout of updatedFrom and updatedTo; see
// parseTime for the others accepted.
timeLayout = "2006-01-02T15:04:05"
// BasePath is the path the endpoint is served at.
BasePath = "/api/1.0/ocdsReleasePackages"
)
// server serves release packages from a Store; see NewHandler.
@@ -36,10 +42,18 @@ type server struct {
// Stored release dates are UTC; comparisons are on absolute instants. It
// logs to slog.Default() unless given WithLogger.
func NewHandler(
store *Store, baseURL string, loc *time.Location, opts ...Option,
store *Store,
baseURL string,
location *time.Location,
opts ...Option,
) http.HandlerFunc {
s := &server{store: store, baseURL: baseURL, loc: loc, opts: applyOptions(opts)}
return s.handle
handler := &server{
store: store,
baseURL: baseURL,
loc: location,
opts: applyOptions(opts),
}
return handler.handle
}
// encodeCursor makes the opaque cursor for a next link that resumes at store
@@ -50,18 +64,18 @@ func encodeCursor(i int) string {
// decodeCursor returns the release index in a cursor made by encodeCursor.
// Anything else, including a negative index, is an error.
func decodeCursor(c string) (int, error) {
b, err := base64.RawURLEncoding.DecodeString(c)
if err != nil || !strings.HasPrefix(string(b), "idx:") {
func decodeCursor(cursor string) (int, error) {
decoded, err := base64.RawURLEncoding.DecodeString(cursor)
if err != nil || !strings.HasPrefix(string(decoded), "idx:") {
return 0, fmt.Errorf("invalid cursor")
}
n, err := strconv.Atoi(strings.TrimPrefix(string(b), "idx:"))
if err != nil || n < 0 {
index, err := strconv.Atoi(strings.TrimPrefix(string(decoded), "idx:"))
if err != nil || index < 0 {
return 0, fmt.Errorf("invalid cursor")
}
return n, nil
return index, nil
}
func badRequest(w http.ResponseWriter, msg string) {
@@ -72,20 +86,20 @@ func badRequest(w http.ResponseWriter, msg string) {
}
// parseTime reads a query timestamp. Values without an offset (the documented
// yyyy-MM-ddTHH:mm:ss, or a plain date) are local time in loc; values with an
// yyyy-MM-ddTHH:mm:ss, or a plain date) are local time in location; values with an
// offset or Z (RFC 3339) mean exactly what they say.
func parseTime(v string, loc *time.Location) (time.Time, error) {
if loc == nil {
loc = time.UTC
func parseTime(value string, location *time.Location) (time.Time, error) {
if location == nil {
location = time.UTC
}
if t, err := time.Parse(time.RFC3339, v); err == nil {
return t, nil
if parsed, err := time.Parse(time.RFC3339, value); err == nil {
return parsed, nil
}
for _, layout := range []string{timeLayout, "2006-01-02"} {
if t, err := time.ParseInLocation(layout, v, loc); err == nil {
return t, nil
if parsed, err := time.ParseInLocation(layout, value, location); err == nil {
return parsed, nil
}
}
@@ -107,15 +121,15 @@ func (s *server) handle(w http.ResponseWriter, req *http.Request) {
return
}
q := req.URL.Query()
pq, err := s.parseQuery(q)
query := req.URL.Query()
page, err := s.parseQuery(query)
if err != nil {
badRequest(w, err.Error())
return
}
releases, next, more := s.store.Page(pq.from, pq.upper, pq.start, pq.limit)
pkg := ocds.ReleasePackage{
releases, next, more := s.store.Page(page.from, page.upper, page.start, page.limit)
releasePackage := ocds.ReleasePackage{
URI: s.baseURL + req.URL.RequestURI(),
Version: "1.1",
Extensions: ocds.DeclaredExtensions,
@@ -127,73 +141,78 @@ func (s *server) handle(w http.ResponseWriter, req *http.Request) {
Releases: releases,
}
if more {
pkg.Links = &ocds.ReleasePackageLinks{Next: s.nextLink(q, next)}
releasePackage.Links = &ocds.ReleasePackageLinks{
Next: s.nextLink(query, next),
}
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
enc.SetEscapeHTML(false)
if err := enc.Encode(pkg); err != nil {
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
encoder.SetEscapeHTML(false)
if err := encoder.Encode(releasePackage); err != nil {
s.opts.logger("handler").Warn("writing response failed", "error", err)
}
}
// parseQuery reads and checks the query parameters. Its errors are meant for
// the client.
func (s *server) parseQuery(q url.Values) (pageQuery, error) {
pq := pageQuery{limit: defaultLimit, upper: time.Now().UTC()}
if v := q.Get("limit"); v != "" {
n, err := strconv.Atoi(v)
if err != nil || n < 1 || n > maxLimit {
return pq, fmt.Errorf("limit must be an integer between 1 and %d", maxLimit)
}
pq.limit = n
func (s *server) parseQuery(query url.Values) (pageQuery, error) {
page := pageQuery{
limit: defaultLimit,
upper: time.Now().UTC(),
}
if v := q.Get("updatedFrom"); v != "" {
t, err := parseTime(v, s.loc)
if value := query.Get("limit"); value != "" {
number, err := strconv.Atoi(value)
if err != nil || number < 1 || number > maxLimit {
return page, fmt.Errorf("limit must be an integer between 1 and %d", maxLimit)
}
page.limit = number
}
if value := query.Get("updatedFrom"); value != "" {
from, err := parseTime(value, s.loc)
if err != nil {
return pq, fmt.Errorf("updatedFrom %w", err)
return page, fmt.Errorf("updatedFrom %w", err)
}
pq.from = t
page.from = from
}
if v := q.Get("updatedTo"); v != "" {
t, err := parseTime(v, s.loc)
if value := query.Get("updatedTo"); value != "" {
to, err := parseTime(value, s.loc)
if err != nil {
return pq, fmt.Errorf("updatedTo %w", err)
return page, fmt.Errorf("updatedTo %w", err)
}
if q.Get("updatedFrom") != "" && t.Before(pq.from) {
return pq, errors.New("updatedTo must not be before updatedFrom")
if query.Get("updatedFrom") != "" && to.Before(page.from) {
return page, errors.New("updatedTo must not be before updatedFrom")
}
if t.Before(pq.upper) {
pq.upper = t
if to.Before(page.upper) {
page.upper = to
}
}
if c := q.Get("cursor"); c != "" {
n, err := decodeCursor(c)
if cursor := query.Get("cursor"); cursor != "" {
index, err := decodeCursor(cursor)
if err != nil {
return pq, err
return page, err
}
pq.start = n
page.start = index
}
return pq, nil
return page, nil
}
// nextLink is the URL of the page starting at release index next: the same
// query with a new cursor.
func (s *server) nextLink(q url.Values, next int) string {
nq := url.Values{}
for k, vs := range q {
if k != "cursor" {
nq[k] = vs
func (s *server) nextLink(query url.Values, next int) string {
nextQuery := url.Values{}
for key, values := range query {
if key != "cursor" {
nextQuery[key] = values
}
}
nq.Set("cursor", encodeCursor(next))
return s.baseURL + BasePath + "?" + nq.Encode()
nextQuery.Set("cursor", encodeCursor(next))
return s.baseURL + BasePath + "?" + nextQuery.Encode()
}