Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
39 changes: 39 additions & 0 deletions .github/workflows/go.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Go

on:
pull_request:
types:
- opened
- synchronize
- reopened
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5

- name: Download dependencies
run: go mod download

- name: Test
env:
GOTESTSUM_JUNITFILE: junit.xml
run: make

- name: Upload results to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}

- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target/
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*@heppu
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM alpine:3.12
COPY target/demo-srv /demo-srv
ENTRYPOINT ["/demo-srv"]
25 changes: 25 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
BUILD_TARGETS = $(wildcard cmd/*)
TARGET_NAMES = $(BUILD_TARGETS:cmd/%=%)
BIN_DIR = target/bin
COVERAGE_TXT = target/coverage/coverage.txt

.PHONY: test clean ${TARGET_NAMES}

all: test ${TARGET_NAMES}

${TARGET_NAMES}:
@echo "Building ${@}..."
@mkdir -p ${BIN_DIR}
go build -o ${BIN_DIR}/${@} ./cmd/${@}/
@echo "Building ${@} done"

test-unit:
mkdir -p target
go run gotest.tools/gotestsum@v1.12.0 -- -coverprofile=coverage.txt ./...

test-integration:
mkdir -p target
go run gotest.tools/gotestsum@v1.12.0 -- -coverprofile=coverage.txt ./...

clean:
git clean -Xdf
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# demo
Demo project

Exmaple projects demontsrating usage of tstr package.
23 changes: 23 additions & 0 deletions cmd/demo-cli/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"fmt"
"log/slog"
"os"

"github.com/go-tstr/demo/db"
"github.com/go-tstr/demo/handler"
)

func main() {
dsn := os.Getenv("DSN")

d, err := db.New(dsn)
if err != nil {
slog.Error("db.New failed", slog.String("error", err.Error()))
os.Exit(1)
}

h := handler.New(d)
fmt.Println(h.Hello())
}
34 changes: 34 additions & 0 deletions cmd/demo-srv/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

import (
"context"
"log/slog"
"os"
"os/signal"

"github.com/go-tstr/demo/db"
"github.com/go-tstr/demo/handler"
"github.com/go-tstr/demo/server"
)

func main() {
addr := os.Getenv("ADDR")
dsn := os.Getenv("DSN")

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()

d, err := db.New(dsn)
if err != nil {
slog.Error("db.New failed", slog.String("error", err.Error()))
os.Exit(1)
}
h := handler.New(d)

slog.Info("Starting server", slog.String("addr", addr))
if err := server.Run(ctx, addr, h); err != nil {
slog.Error("server.Run failed", slog.String("error", err.Error()))
os.Exit(1)
}
slog.Info("Server stopped successfully")
}
31 changes: 31 additions & 0 deletions coverage.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
mode: set
github.com/go-tstr/demo/cmd/demo-cli/main.go:12.13,16.16 3 0
github.com/go-tstr/demo/cmd/demo-cli/main.go:16.16,19.3 2 0
github.com/go-tstr/demo/cmd/demo-cli/main.go:21.2,22.24 2 0
github.com/go-tstr/demo/server/server.go:16.61,26.21 5 0
github.com/go-tstr/demo/server/server.go:26.21,29.3 2 0
github.com/go-tstr/demo/server/server.go:30.2,30.21 1 0
github.com/go-tstr/demo/server/server.go:30.21,33.3 2 0
github.com/go-tstr/demo/server/server.go:35.2,35.18 1 0
github.com/go-tstr/demo/server/server.go:38.57,39.76 1 0
github.com/go-tstr/demo/server/server.go:39.76,41.17 2 0
github.com/go-tstr/demo/server/server.go:41.17,44.4 2 0
github.com/go-tstr/demo/server/server.go:45.3,45.19 1 0
github.com/go-tstr/demo/server/server.go:47.2,47.12 1 0
github.com/go-tstr/demo/server/server.go:50.34,52.42 2 0
github.com/go-tstr/demo/server/server.go:52.42,54.3 1 0
github.com/go-tstr/demo/server/server.go:55.2,55.12 1 0
github.com/go-tstr/demo/db/db.go:13.35,15.16 2 0
github.com/go-tstr/demo/db/db.go:15.16,17.3 1 0
github.com/go-tstr/demo/db/db.go:19.2,19.25 1 0
github.com/go-tstr/demo/db/db.go:23.39,27.2 3 0
github.com/go-tstr/demo/handler/handler.go:20.30,22.2 1 0
github.com/go-tstr/demo/handler/handler.go:24.43,26.16 2 0
github.com/go-tstr/demo/handler/handler.go:26.16,28.3 1 0
github.com/go-tstr/demo/handler/handler.go:29.2,29.22 1 0
github.com/go-tstr/demo/handler/handler.go:32.32,34.2 1 0
github.com/go-tstr/demo/cmd/demo-srv/main.go:14.13,22.16 6 0
github.com/go-tstr/demo/cmd/demo-srv/main.go:22.16,25.3 2 0
github.com/go-tstr/demo/cmd/demo-srv/main.go:26.2,29.49 3 0
github.com/go-tstr/demo/cmd/demo-srv/main.go:29.49,32.3 2 0
github.com/go-tstr/demo/cmd/demo-srv/main.go:33.2,33.42 1 0
27 changes: 27 additions & 0 deletions db/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package db

import (
"time"

"github.com/jmoiron/sqlx"
)

type DB struct {
db *sqlx.DB
}

func New(dsn string) (*DB, error) {
db, err := sqlx.Open("postgres", dsn)
if err != nil {
return nil, err
}

return &DB{db: db}, nil
}

// Now returns the current time from the database.
func (d *DB) Now() (time.Time, error) {
var now time.Time
err := d.db.Get(&now, "SELECT NOW()")
return now, err
}
10 changes: 10 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
db:
image: postgres:16
container_name: postgres
environment:
POSTGRES_USER: example
POSTGRES_PASSWORD: example
POSTGRES_DB: example
ports:
- "5432:5432"
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module github.com/go-tstr/demo

go 1.23.4

require (
github.com/go-tstr/tstr v0.0.1
github.com/heppu/errgroup v1.0.0
github.com/jmoiron/sqlx v1.4.0
)
22 changes: 22 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-tstr/tstr v0.0.1 h1:UZVRncSDm5Zh1RvyGwAc2SxJ5Ab2NzbVm/9z8bbo6xs=
github.com/go-tstr/tstr v0.0.1/go.mod h1:Xr8fr9FwDNM602fS/zIGHNDlL22pJAdNgiTHkCQ3OSc=
github.com/heppu/errgroup v1.0.0 h1:Th073WwEpGARMkxWQnybOfcMuvozkBr7Kqvn2tmx7iU=
github.com/heppu/errgroup v1.0.0/go.mod h1:eiBTIbuHZPfUsa978/V4HmR1p1oSqtNTpc8XiqetgIg=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
34 changes: 34 additions & 0 deletions handler/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package handler

import (
"fmt"
"time"

"github.com/go-tstr/tstr/strerr"
)

const ErrFailedToGetTime = strerr.Error("failed to get time from storage")

type Storage interface {
Now() (time.Time, error)
}

type Handler struct {
s Storage
}

func New(s Storage) *Handler {
return &Handler{s: s}
}

func (d *Handler) Hello() (string, error) {
t, err := d.s.Now()
if err != nil {
return "", fmt.Errorf("%w: %w", ErrFailedToGetTime, err)
}
return hello(t), nil
}

func hello(t time.Time) string {
return fmt.Sprintf("Hello world at %s", t)
}
56 changes: 56 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package server

import (
"context"
"errors"
"fmt"
"net/http"

"github.com/heppu/errgroup"
)

type Handler interface {
Hello() (string, error)
}

func Run(ctx context.Context, addr string, h Handler) error {
srv := http.Server{
Addr: addr,
Handler: Bind(http.NewServeMux(), h),
}

ctx, cancel := context.WithCancel(ctx)
defer cancel()

eg := &errgroup.ErrGroup{}
eg.Go(func() error {
<-ctx.Done()
return srv.Close()
})
eg.Go(func() error {
defer cancel()
return serve(&srv)
})

return eg.Wait()
}

func Bind(mux *http.ServeMux, d Handler) *http.ServeMux {
mux.HandleFunc("GET /hello", func(w http.ResponseWriter, r *http.Request) {
h, err := d.Hello()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprint(w, h)
})
return mux
}

func serve(s *http.Server) error {
err := s.ListenAndServe()
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return err
}
Loading