Skip to content

Commit

Permalink
use log/slog, update deps, markdown lint
Browse files Browse the repository at this point in the history
  • Loading branch information
gerbenjacobs committed Nov 2, 2024
1 parent b133294 commit ae9d03d
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 539 deletions.
30 changes: 16 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Note: This is **not** an actual framework; however you are free to use, adapt an
## Rationale

There are 4 main directories of which 3 are synonymous to a layer:

- `cmd`: contains executables, most likely just an application (http, grpc), but also CLI tools
- `handler`: contains all files related to handling incoming requests or triggers
- `services`: contains your business logic
Expand All @@ -16,7 +17,7 @@ Domain models live in the root of the project, although people have created a `m
before.

The last 3 folders each contain a self-titled go file that is the entry point of their layer.
For example `handler/handler.go` has the actual endpoints and the mux router,
For example `handler/handler.go` has the actual endpoints and the mux router,
`services/services.go` and `storage/storage.go` contain the interfaces that are being used
throughout the application.

Expand All @@ -25,24 +26,24 @@ Use the interfaces instead of actual implementations.

Every layers knows about the *Domain Models* and you should have these be the types that are
transferred between the Service and Storage layer. It's perfectly ok to then create custom
storage DAOs and custom input or output models for dealing with HTTP (i.e. an almost exact
copy of app.User but without the Password field). Just make sure that the service and storage
storage DAOs and custom input or output models for dealing with HTTP (i.e. an almost exact
copy of app.User but without the Password field). Just make sure that the service and storage
methods do the transformation back and forth.

Separation of concern and an explicit clarity is what this structure gives you.

_Throughout the code I've written comments prefixed with `Rationale:` to explain a bit about the code._
*Throughout the code I've written comments prefixed with `Rationale:` to explain a bit about the code.*

## Running this example

- Copy the config.yml.example to config.yml
- `cp config.yml.example config.yml`
- `cp config.yml.example config.yml`
- Create a secret token
- `echo -n "my secret, make sure it's not too short" | openssl dgst -sha256`
- `echo -n "my secret, make sure it's not too short" | openssl dgst -sha256`
- Set up the database with Docker Compose
- `docker-compose up -d`
- `docker-compose up -d`
- Run the application (make sure the database container is up and running)
- `go run cmd/app/main.go`
- `go run cmd/app/main.go`

## Examples

Expand All @@ -52,7 +53,7 @@ Change the token when retrieving your user to the one you received when creating
### Creating a user

```shell script
curl -v -X POST -d '{"name": "Gerben"}' http://127.0.0.1:8000/v1/user
curl -i -X POST -d '{"name": "Gerben"}' http://127.0.0.1:8000/v1/user
```

```json
Expand All @@ -65,10 +66,10 @@ curl -v -X POST -d '{"name": "Gerben"}' http://127.0.0.1:8000/v1/user
}
```

### Retrieving your user

### Retrieving your user
```shell script
curl -v -X GET -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiI5M2Q1YmFlZC0zZjJkLTQ0YTYtYjNlMC0wMjg0MWZjZjMwYjkiLCJuYmYiOjE1NzY2NjU5ODd9.UtIQNBpLBCRVH65LriP9uqKds-jrKJzmIcILQv082yc" http://127.0.0.1:8000/v1/user
curl -i -X GET -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiI5M2Q1YmFlZC0zZjJkLTQ0YTYtYjNlMC0wMjg0MWZjZjMwYjkiLCJuYmYiOjE1NzY2NjU5ODd9.UtIQNBpLBCRVH65LriP9uqKds-jrKJzmIcILQv082yc" http://127.0.0.1:8000/v1/user
```

```json
Expand All @@ -82,12 +83,13 @@ curl -v -X GET -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ey
```

### Converting our users to a JSON output file using the CLI
```shell script

```shell
go run cmd/cli/main.go
```

```
```shell
2019/12/18 12:58:23 Found 1 users
2019/12/18 12:58:23 [93d5baed-3f2d-44a6-b3e0-02841fcf30b9] Gerben - 2019-12-18T10:46:27Z
2019/12/18 12:58:23 Finished writing to output.json
```
```
27 changes: 12 additions & 15 deletions cmd/app/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package main

import (
"context"
"log"
"log/slog"
"net/http"
"os"
"os/signal"
Expand All @@ -12,35 +14,30 @@ import (
"github.com/gerbenjacobs/svc/internal"
"github.com/gerbenjacobs/svc/services"
"github.com/gerbenjacobs/svc/storage"

stackdriver "github.com/TV4/logrus-stackdriver-formatter"
_ "github.com/go-sql-driver/mysql"
"github.com/mattn/go-colorable"
log "github.com/sirupsen/logrus"
"github.com/lmittmann/tint"
)

func main() {
// handle shutdown signals
shutdown := make(chan os.Signal, 3)
signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM)

// set output logging (specifically for windows)
log.SetFormatter(&log.TextFormatter{ForceColors: true})
log.SetOutput(colorable.NewColorableStdout())
log.SetLevel(log.DebugLevel)

// load configuration
c := internal.NewConfig()

// set stackdriver formatter
// set output logging
level := slog.LevelDebug
if c.Svc.Env != "dev" {
log.SetLevel(log.InfoLevel)
log.SetFormatter(stackdriver.NewFormatter(
stackdriver.WithService(c.Svc.Name),
stackdriver.WithVersion("v"+c.Svc.Version),
))
level = slog.LevelInfo
}

slog.SetDefault(
slog.New(
tint.NewHandler(os.Stdout, &tint.Options{Level: level}),
),
)

// set up and check database
db, err := internal.NewDB(c)
if err != nil {
Expand Down
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: '3.3'
services:
db:
image: mariadb:10.4
Expand Down
43 changes: 23 additions & 20 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
module github.com/gerbenjacobs/svc

go 1.19
go 1.23

toolchain go1.23.2

require (
github.com/TV4/logrus-stackdriver-formatter v0.1.0
github.com/go-sql-driver/mysql v1.7.0
github.com/go-sql-driver/mysql v1.8.1
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.3.0
github.com/gorilla/handlers v1.5.1
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2
github.com/julienschmidt/httprouter v1.3.0
github.com/justinas/alice v1.2.0
github.com/mattn/go-colorable v0.1.13
github.com/sirupsen/logrus v1.9.0
github.com/spf13/viper v1.15.0
github.com/lmittmann/tint v1.0.5
github.com/spf13/viper v1.19.0
)

require (
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-stack/stack v1.8.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit ae9d03d

Please sign in to comment.