Skip to content

Commit 775b4f4

Browse files
committed
feat: refactor
1 parent d67adef commit 775b4f4

File tree

32 files changed

+710
-447
lines changed

32 files changed

+710
-447
lines changed

backend/.golangci.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
linters-settings:
2+
# from https://betterstack.com/community/guides/logging/logging-in-go/#best-practices-for-writing-and-storing-go-logs
3+
sloglint:
4+
# Enforce not mixing key-value pairs and attributes.
5+
# Default: true
6+
no-mixed-args: false
7+
# Enforce using key-value pairs only (overrides no-mixed-args, incompatible with attr-only).
8+
# Default: false
9+
kv-only: true
10+
# Enforce using attributes only (overrides no-mixed-args, incompatible with kv-only).
11+
# Default: false
12+
attr-only: true
13+
# Enforce using methods that accept a context.
14+
# Default: ""
15+
context: all
16+
# Enforce using static values for log messages.
17+
# Default: false
18+
static-msg: true
19+
# Enforce using constants instead of raw keys.
20+
# Default: false
21+
no-raw-keys: true
22+
# Enforce a single key naming convention.
23+
# Values: snake, kebab, camel, pascal
24+
# Default: ""
25+
key-naming-case: snake
26+
# Enforce putting arguments on separate lines.
27+
# Default: false
28+
args-on-sep-lines: true

backend/cmd/db/example/main.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package scripts
2+
3+
import (
4+
"context"
5+
"flag"
6+
"log/slog"
7+
8+
"github.com/GenerateNU/platemate/internal/config"
9+
"github.com/GenerateNU/platemate/internal/storage/mongo"
10+
"github.com/GenerateNU/platemate/internal/xslog"
11+
"github.com/joho/godotenv"
12+
)
13+
14+
/*
15+
Adds two example fields to the collection passed via
16+
the collection flag.
17+
Example usage: go run cmd/db/example/main.go -collection=collectionName
18+
Applies empty strings as default values
19+
*/
20+
func main() {
21+
ctx := context.Background()
22+
collection := flag.String("collection", "users", "collection to add example fields to")
23+
flag.Parse()
24+
if *collection == "" {
25+
fatal(ctx, "collection flag is required", nil)
26+
}
27+
28+
if err := godotenv.Load(); err != nil {
29+
fatal(ctx, "Failed to load .env", err)
30+
}
31+
config, err := config.Load()
32+
if err != nil {
33+
fatal(ctx, "Failed to load config", err)
34+
}
35+
36+
db, err := mongo.New(ctx, config.Atlas)
37+
38+
if err := db.CreateExampleFields(ctx, *collection); err != nil {
39+
fatal(ctx, "Failed to add example fields", err)
40+
}
41+
}
42+
43+
func fatal(ctx context.Context, msg string, err error) {
44+
slog.LogAttrs(
45+
ctx,
46+
slog.LevelError,
47+
msg,
48+
xslog.Error(err),
49+
)
50+
}

backend/cmd/server/main.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"fmt"
7+
"io"
8+
"log/slog"
9+
"os"
10+
"os/signal"
11+
"syscall"
12+
13+
"github.com/GenerateNU/platemate/internal/config"
14+
"github.com/GenerateNU/platemate/internal/server"
15+
"github.com/GenerateNU/platemate/internal/storage/mongo"
16+
"github.com/GenerateNU/platemate/internal/xslog"
17+
)
18+
19+
func main() {
20+
run(os.Stderr, os.Args[1:])
21+
}
22+
23+
func run(stderr io.Writer, args []string) {
24+
cmd := flag.NewFlagSet("", flag.ExitOnError)
25+
verboseFlag := cmd.Bool("v", false, "")
26+
logLevelFlag := cmd.String("log-level", "info", "")
27+
if err := cmd.Parse(args); err != nil {
28+
fmt.Fprint(stderr, err)
29+
os.Exit(1)
30+
}
31+
logger := newLogger(*logLevelFlag, *verboseFlag, stderr)
32+
slog.SetDefault(logger)
33+
34+
ctx := context.Background()
35+
// var for collection name
36+
37+
config, err := config.Load()
38+
if err != nil {
39+
fatal(ctx, "Failed to load config", err)
40+
}
41+
42+
db, err := mongo.New(ctx, config.Atlas)
43+
if err != nil {
44+
fatal(ctx, "Failed to connect to MongoDB", err)
45+
}
46+
47+
app := server.New(db.Collections)
48+
49+
go func() {
50+
if err := app.Listen(":8080"); err != nil {
51+
fatal(ctx, "Failed to start server", err)
52+
}
53+
}()
54+
55+
quit := make(chan os.Signal, 1)
56+
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
57+
58+
<-quit
59+
slog.LogAttrs(
60+
ctx,
61+
slog.LevelInfo,
62+
"Stopping server",
63+
)
64+
65+
if err := app.Shutdown(); err != nil {
66+
fatal(ctx, "Failed to shutdown server", err)
67+
}
68+
69+
slog.LogAttrs(
70+
ctx,
71+
slog.LevelInfo,
72+
"Server shutdown",
73+
)
74+
75+
return
76+
}
77+
78+
func newLogger(logLevel string, verbose bool, stderr io.Writer) *slog.Logger {
79+
if verbose {
80+
logLevel = "debug"
81+
}
82+
level := slog.LevelInfo.Level()
83+
switch logLevel {
84+
case "debug":
85+
level = slog.LevelDebug.Level()
86+
case "warn":
87+
level = slog.LevelWarn.Level()
88+
case "error":
89+
level = slog.LevelError.Level()
90+
}
91+
return slog.New(slog.NewJSONHandler(stderr, &slog.HandlerOptions{
92+
AddSource: logLevel == "debug",
93+
Level: level,
94+
}))
95+
}
96+
97+
func fatal(ctx context.Context, msg string, err error) {
98+
slog.LogAttrs(
99+
ctx,
100+
slog.LevelError,
101+
msg,
102+
xslog.Error(err),
103+
)
104+
}

backend/cmd/server/main_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"io"
6+
"net/http"
7+
"testing"
8+
9+
"github.com/GenerateNU/platemate/internal/config"
10+
"github.com/GenerateNU/platemate/internal/server"
11+
"github.com/GenerateNU/platemate/internal/storage/mongo"
12+
"github.com/gofiber/fiber/v2"
13+
"github.com/joho/godotenv"
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
func TestIndexRoute(t *testing.T) {
18+
t.Parallel()
19+
tests := []struct {
20+
name string
21+
desc string
22+
route string
23+
expectedError bool
24+
expectedCode int
25+
expectedBody string
26+
}{
27+
{
28+
name: "index",
29+
desc: "test index route",
30+
route: "/",
31+
expectedError: false,
32+
expectedCode: 200,
33+
expectedBody: "Welcome to PlateMate!",
34+
},
35+
}
36+
37+
app := setup(t)
38+
39+
for _, tt := range tests {
40+
t.Run(tt.name, func(t *testing.T) {
41+
t.Parallel()
42+
req, err := http.NewRequest(
43+
http.MethodGet,
44+
tt.route,
45+
nil,
46+
)
47+
assert.NoErrorf(t, err, tt.desc)
48+
49+
// Perform the request plain with the app. The -1 disables request latency.
50+
res, err := app.Test(req, -1)
51+
if !tt.expectedError {
52+
assert.NoErrorf(t, err, tt.desc)
53+
}
54+
55+
// As expected errors lead to broken responses, the next test case needs to be processed.
56+
if tt.expectedError {
57+
return
58+
}
59+
60+
assert.Equalf(t, tt.expectedCode, res.StatusCode, tt.desc)
61+
62+
body, err := io.ReadAll(res.Body)
63+
assert.NoErrorf(t, err, tt.desc)
64+
assert.Equalf(t, tt.expectedBody, string(body), tt.desc)
65+
})
66+
}
67+
68+
t.Cleanup(func() {
69+
if err := app.Shutdown(); err != nil {
70+
t.Fatalf("Failed to shutdown server: %v", err)
71+
}
72+
})
73+
}
74+
75+
func setup(t *testing.T) *fiber.App {
76+
t.Helper()
77+
if err := godotenv.Load(); err != nil {
78+
t.Fatal("Failed to load .env")
79+
}
80+
cfg := config.Atlas{
81+
User: "test",
82+
Pass: "platemate-test-pw",
83+
Cluster: "Development",
84+
Environment: "Test",
85+
}
86+
db, err := mongo.New(context.Background(), cfg)
87+
if err != nil {
88+
t.Fatalf("Failed to connect to MongoDB: %v", err)
89+
}
90+
return server.New(db.Collections)
91+
}

backend/errs/http.go

Lines changed: 0 additions & 19 deletions
This file was deleted.

backend/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212

1313
require (
1414
github.com/andybalholm/brotli v1.1.1 // indirect
15+
github.com/caarlos0/env/v11 v11.3.1
1516
github.com/davecgh/go-spew v1.1.1 // indirect
1617
github.com/golang/snappy v0.0.4 // indirect
1718
github.com/google/uuid v1.6.0 // indirect

backend/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
22
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
3+
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
4+
github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
35
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
46
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
57
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=

backend/internal/config/app.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package config
2+
3+
type App struct {
4+
Port string `env:"PORT" envDefault:"8080"`
5+
}

backend/internal/config/atlas.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package config
2+
3+
import "fmt"
4+
5+
type Atlas struct {
6+
User string `env:"USER"`
7+
Pass string `env:"PASS"`
8+
Cluster string `env:"CLUSTER"`
9+
Environment string `env:"ENVIRONMENT"`
10+
}
11+
12+
const placeholderURI string = "mongodb+srv://%s:%s@development.t8bgq.mongodb.net/?retryWrites=true&w=majority&appName=%s"
13+
14+
func (a *Atlas) URI() string {
15+
return fmt.Sprintf(placeholderURI, a.User, a.Pass, a.Cluster)
16+
}

backend/internal/config/config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package config
2+
3+
import "github.com/caarlos0/env/v11"
4+
5+
type Config struct {
6+
App `envPrefix:"APP_"`
7+
Atlas `envPrefix:"ATLAS_"`
8+
}
9+
10+
func Load() (Config, error) {
11+
return env.ParseAs[Config]()
12+
}

0 commit comments

Comments
 (0)