Skip to content

Commit

Permalink
fix all lint issues
Browse files Browse the repository at this point in the history
  • Loading branch information
bgentry committed May 3, 2024
1 parent 06b5d46 commit ab81407
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 23 deletions.
92 changes: 92 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
issues:
exclude:
- 'Error return value of .(\w+\.Rollback(.*)). is not checked'

linters:
presets:
- bugs
- comment
- format
- performance
- style
- test
- unused

disable:
# disabled, but which we should enable with discussion
- wrapcheck # checks that errors are wrapped; currently not done anywhere

# disabled because we're not compliant, but which we should think about
- exhaustruct # checks that properties in structs are exhaustively defined; may be a good idea
- testpackage # requires tests in test packages like `river_test`

# disabled because they're annoying/bad
- interfacebloat # we do in fact want >10 methods on the Adapter interface or wherever we see fit.
- godox # bans TODO statements; total non-starter at the moment
- goerr113 # wants all errors to be defined as variables at the package level; quite obnoxious
- gomnd # detects "magic numbers", which it defines as any number; annoying
- ireturn # bans returning interfaces; questionable as is, but also buggy as hell; very, very annoying
- lll # restricts maximum line length; annoying
- nlreturn # requires a blank line before returns; annoying
- wsl # a bunch of style/whitespace stuff; annoying

linters-settings:
depguard:
rules:
all:
files: ["$all"]
allow:
deny:
- desc: "Use `github.com/google/uuid` package for UUIDs instead."
pkg: "github.com/xtgo/uuid"

forbidigo:
forbid:
- msg: "Use `require` variants instead."
p: '^assert\.'
- msg: "Use `Func` suffix for function variables instead."
p: 'Fn\b'
- msg: "Use built-in `max` function instead."
p: '\bmath\.Max\b'
- msg: "Use built-in `min` function instead."
p: '\bmath\.Min\b'

gci:
sections:
- Standard
- Default
- Prefix(github.com/riverqueue)

gomoddirectives:
replace-local: true

revive:
rules:
- name: unused-parameter
disabled: true

tagliatelle:
case:
rules:
json: snake

testifylint:
enable-all: true
disable:
- go-require

varnamelen:
ignore-names:
- db
- eg
- f
- i
- id
- j
- mu
- rw # common for http.ResponseWriter
- sb # common convention for string builder
- t
- tt # common convention for table tests
- tx
- wg
33 changes: 20 additions & 13 deletions api_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"net/http"
"strconv"
"time"
Expand All @@ -14,7 +16,6 @@ import (

"github.com/riverqueue/river"
"github.com/riverqueue/river/rivertype"

"github.com/riverqueue/riverui/internal/db"
)

Expand Down Expand Up @@ -49,7 +50,7 @@ func (a *apiHandler) JobCancel(rw http.ResponseWriter, req *http.Request) {
for _, jobID := range jobIDs {
job, err := a.client.JobCancelTx(ctx, tx, jobID)
if err != nil {
if err == river.ErrNotFound {
if errors.Is(err, river.ErrNotFound) {
fmt.Printf("job %d not found\n", jobID)
}
return err
Expand All @@ -63,15 +64,21 @@ func (a *apiHandler) JobCancel(rw http.ResponseWriter, req *http.Request) {
}

// TODO: return jobs in response, use in frontend instead of invalidating
rw.Write([]byte("{\"status\": \"ok\"}"))
writeResponse(ctx, rw, []byte("{\"status\": \"ok\"}"))
}

func writeResponse(ctx context.Context, rw http.ResponseWriter, data []byte) {
if _, err := rw.Write(data); err != nil {
logger.ErrorContext(ctx, "error writing response", slog.String("error", err.Error()))
}
}

func stringIDsToInt64s(strs []string) ([]int64, error) {
ints := make([]int64, len(strs))
for i, str := range strs {
intVal, err := strconv.ParseInt(str, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid job id: %s", err)
return nil, fmt.Errorf("invalid job id: %w", err)
}
ints[i] = intVal
}
Expand Down Expand Up @@ -100,7 +107,7 @@ func (a *apiHandler) JobRetry(rw http.ResponseWriter, req *http.Request) {
if err := pgx.BeginFunc(ctx, a.dbPool, func(tx pgx.Tx) error {
for _, jobID := range jobIDs {
if _, err := a.client.JobRetryTx(ctx, tx, jobID); err != nil {
if err == river.ErrNotFound {
if errors.Is(err, river.ErrNotFound) {
fmt.Printf("job %d not found\n", jobID)
}
return err
Expand All @@ -112,7 +119,7 @@ func (a *apiHandler) JobRetry(rw http.ResponseWriter, req *http.Request) {
return
}

rw.Write([]byte("{\"status\": \"ok\"}"))
writeResponse(ctx, rw, []byte("{\"status\": \"ok\"}"))
}

func (a *apiHandler) JobGet(rw http.ResponseWriter, req *http.Request) {
Expand All @@ -132,7 +139,7 @@ func (a *apiHandler) JobGet(rw http.ResponseWriter, req *http.Request) {
}

job, err := a.client.JobGet(ctx, jobID)
if err == river.ErrNotFound {
if errors.Is(err, river.ErrNotFound) {
http.Error(rw, "{\"error\": {\"msg\": \"job not found\"}}", http.StatusNotFound)
return
}
Expand Down Expand Up @@ -164,7 +171,7 @@ func (a *apiHandler) JobList(rw http.ResponseWriter, req *http.Request) {
params = params.States(rivertype.JobStateRunning).OrderBy(river.JobListOrderByTime, river.SortOrderAsc)
case rivertype.JobStateCancelled, rivertype.JobStateCompleted, rivertype.JobStateDiscarded:
params = params.States(state).OrderBy(river.JobListOrderByTime, river.SortOrderDesc)
case rivertype.JobStateAvailable, rivertype.JobStateRetryable, rivertype.JobStateRunning, rivertype.JobStateScheduled:
case rivertype.JobStateAvailable, rivertype.JobStateRetryable, rivertype.JobStatePending, rivertype.JobStateRunning, rivertype.JobStateScheduled:
params = params.States(state)
default:
http.Error(rw, "invalid state", http.StatusBadRequest)
Expand Down Expand Up @@ -194,7 +201,7 @@ func (a *apiHandler) QueueGet(rw http.ResponseWriter, req *http.Request) {
}

queue, err := a.client.QueueGet(ctx, name)
if err == river.ErrNotFound {
if errors.Is(err, river.ErrNotFound) {
http.Error(rw, "{\"error\": {\"msg\": \"queue not found\"}}", http.StatusNotFound)
return
}
Expand Down Expand Up @@ -269,15 +276,15 @@ func (a *apiHandler) QueuePause(rw http.ResponseWriter, req *http.Request) {
}

if err := a.client.QueuePause(ctx, queue, nil); err != nil {
if err == river.ErrNotFound {
if errors.Is(err, river.ErrNotFound) {
http.Error(rw, "{\"error\": {\"msg\": \"queue not found\"}}", http.StatusNotFound)
return
}
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}

rw.Write([]byte("{\"status\": \"ok\"}"))
writeResponse(ctx, rw, []byte("{\"status\": \"ok\"}"))
}

func (a *apiHandler) QueueResume(rw http.ResponseWriter, req *http.Request) {
Expand All @@ -291,15 +298,15 @@ func (a *apiHandler) QueueResume(rw http.ResponseWriter, req *http.Request) {
}

if err := a.client.QueueResume(ctx, queue, nil); err != nil {
if err == river.ErrNotFound {
if errors.Is(err, river.ErrNotFound) {
http.Error(rw, "{\"error\": {\"msg\": \"queue not found\"}}", http.StatusNotFound)
return
}
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}

rw.Write([]byte("{\"status\": \"ok\"}"))
writeResponse(ctx, rw, []byte("{\"status\": \"ok\"}"))
}

func (a *apiHandler) StatesAndCounts(rw http.ResponseWriter, req *http.Request) {
Expand Down
37 changes: 27 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package main

import (
"context"
"errors"
"fmt"
"io/fs"
"log"
"log/slog"
"net/http"
"os"
"strings"
"time"

"github.com/jackc/pgx/v5/pgxpool"
"github.com/joho/godotenv"
Expand All @@ -21,26 +23,37 @@ import (
"github.com/riverqueue/riverui/ui"
)

var logger *slog.Logger
var logger *slog.Logger //nolint:gochecknoglobals

func main() {
ctx := context.Background()
godotenv.Load()
if err := godotenv.Load(); err != nil {
log.Fatal("Error loading .env file")
}
logger = slog.New(slog.NewTextHandler(os.Stdout, nil))

os.Exit(initAndServe(ctx))
}

func initAndServe(ctx context.Context) int {
ctx, cancel := context.WithCancel(ctx)
defer cancel()

corsOriginString := os.Getenv("CORS_ORIGINS")
corsOrigins := strings.Split(corsOriginString, ",")
dbURL := mustEnv("DATABASE_URL")
otelEnabled := os.Getenv("OTEL_ENABLED") == "true"

frontendIndex, err := fs.Sub(ui.Index, "dist")
if err != nil {
panic(err)
logger.ErrorContext(ctx, "error getting frontend index", slog.String("error", err.Error()))
return 1
}

dbPool, err := getDBPool(ctx, dbURL)
if err != nil {
log.Fatal(err)
logger.ErrorContext(ctx, "error connecting to db", slog.String("error", err.Error()))
return 1
}
defer dbPool.Close()

Expand All @@ -51,7 +64,8 @@ func main() {

client, err := river.NewClient(riverpgxv5.New(dbPool), &river.Config{})
if err != nil {
log.Fatal(err)
logger.ErrorContext(ctx, "error creating river client", slog.String("error", err.Error()))
return 1
}
handler := &apiHandler{client: client, dbPool: dbPool, queries: db.New(dbPool)}

Expand All @@ -77,16 +91,19 @@ func main() {
wrappedHandler := sloghttp.NewWithConfig(logger, config)(corsHandler.Handler(logHandler))

srv := &http.Server{
Addr: ":8080",
Handler: wrappedHandler,
Addr: ":8080",
Handler: wrappedHandler,
ReadHeaderTimeout: 5 * time.Second,
}

log.Printf("starting server on %s", srv.Addr)

err = srv.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
log.Fatal(err)
if err = srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
logger.ErrorContext(ctx, "error from ListenAndServe", slog.String("error", err.Error()))
return 1
}

return 0
}

func getDBPool(ctx context.Context, dbURL string) (*pgxpool.Pool, error) {
Expand Down

0 comments on commit ab81407

Please sign in to comment.