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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Collapse files in vendor directory in PRs
.app/vendor/** linguist-generated=true
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# No binaries
app/dist/

# No IDE settings
.vscode

# No Mac OS-specific
.DS_Store
22 changes: 22 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM golang:1.24

ARG USERNAME=dev
ARG USER_UID=1000
ARG USER_GID=1000

# Install tools and create user
RUN apt-get update && apt-get install -y \
bash curl git vim less sudo \
&& groupadd --gid $USER_GID $USERNAME \
&& useradd --uid $USER_UID --gid $USER_GID -m -s /bin/bash $USERNAME

USER $USERNAME

RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.1.6 && \
go install github.com/vektra/mockery/v3@v3.3.2 && \
go install github.com/cweill/gotests/gotests@v1.6.0 && \
go install -v github.com/go-delve/delve/cmd/dlv@latest && \
go install github.com/google/wire/cmd/wire@latest

WORKDIR /home/dev/ws
CMD ["bash"]
19 changes: 19 additions & 0 deletions app/.mockery.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
all: false
dir: './mocks/{{.SrcPackageName}}'
filename: 'mock_{{.InterfaceName}}.go'
force-file-write: true
formatter: goimports
log-level: info
structname: '{{.InterfaceName}}'
pkgname: 'mocks_{{.SrcPackageName}}'
recursive: false
require-template-schema-exists: true
template: testify
template-schema: '{{.Template}}.schema.json'
packages:
app/internal/adapters/rest/routes:
config:
all: true
app/internal/domain/repositories:
config:
all: true
22 changes: 21 additions & 1 deletion app/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@


## vendor: Download dependencies
vendor: go mod vendor
vendor:
go mod vendor

## lint: Lint code
lint:
golangci-lint run

wire:
wire ./cmd/cloud-file-rest

build:
go build -o ./dist/cloud-file-rest ./cmd/cloud-file-rest

run:
./dist/cloud-file-rest

mocks:
mockery

unit:
go test ./internal/... -timeout 30s
35 changes: 35 additions & 0 deletions app/cmd/cloud-file-rest/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"app/internal/drivers/otel"
"app/internal/drivers/restserver"
"log/slog"
"time"
)

type AppConfig struct {
ShutdownTimeoutSecs uint64
}

type App struct {
config *Config
server *restserver.Server
logger *slog.Logger
tracerProviderShutdown otel.TracerProviderShutdown
shutdownTimeout time.Duration
}

func NewAppFromConfig(
config *Config,
server *restserver.Server,
logger *slog.Logger,
tracerProviderShutdown otel.TracerProviderShutdown,
) *App {
return &App{
config: config,
server: server,
logger: logger,
tracerProviderShutdown: tracerProviderShutdown,
shutdownTimeout: time.Duration(config.App.ShutdownTimeoutSecs) * time.Second,
}
}
32 changes: 32 additions & 0 deletions app/cmd/cloud-file-rest/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"app/internal/adapters/rest/middleware"
"app/internal/drivers/restserver"

"github.com/spf13/viper"
)

type Config struct {
App AppConfig
Server restserver.ServerConfig
Tracing middleware.TracingMiddlewareConfig
}

// LoadConfig reads configuration from ./config.yaml file.
func LoadConfig() (*Config, error) {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("./cmd/cloud-file-rest")

if err := viper.ReadInConfig(); err != nil {
return nil, err
}

var config Config
if err := viper.Unmarshal(&config); err != nil {
return nil, err
}

return &config, nil
}
8 changes: 8 additions & 0 deletions app/cmd/cloud-file-rest/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
server:
address: localhost:3000

tracing:
serviceName: cloud-file-rest

app:
shutdownTimeoutSecs: 30
63 changes: 63 additions & 0 deletions app/cmd/cloud-file-rest/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
)

const (
EXIT_FAILURE = 1
EXIT_SUCCESS = 0
)

func main() {
os.Exit(run(context.Background()))
}

func run(ctx context.Context) int {
app, err := SetupApp()
if err != nil {
log.Printf("failed to initialize the app: %v", err)
return EXIT_FAILURE
}

ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
defer stop()

app.logger.InfoContext(ctx, "starting cloud-file-rest")

// Run the REST server.
go func() {
if err := app.server.Run(); err != http.ErrServerClosed {
app.logger.ErrorContext(ctx, "failed to start REST server", "error", err)
stop()
}
}()

<-ctx.Done()

// Start a fresh context derived from the parent context (ignore cancellation) as the old one will have already
// been canceled to avoid immediate exit.
ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), app.shutdownTimeout)
defer cancel()

app.logger.InfoContext(ctx, "started graceful shutdown of cloud-file-rest")

err = app.server.Shutdown(ctx)
if err != nil {
app.logger.ErrorContext(ctx, "failed to shutdown REST server", "error", err)
}

err = app.tracerProviderShutdown(ctx)
if err != nil {
app.logger.ErrorContext(ctx, "failed to shutdown tracer provider", "error", err)
}

app.logger.InfoContext(ctx, "completed graceful shut down of cloud-file-rest")

return EXIT_SUCCESS
}
35 changes: 35 additions & 0 deletions app/cmd/cloud-file-rest/wire.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//go:build wireinject
// +build wireinject

package main

import (
"github.com/google/wire"

"app/internal/adapters/rest/handlers"
"app/internal/adapters/rest/routes"
"app/internal/drivers/otel"
"app/internal/drivers/restserver"
"app/internal/drivers/wireproviders"
)

func SetupApp() (*App, error) {
wire.Build(
LoadConfig,
wire.FieldsOf(new(*Config), "Server", "Tracing"),

wireproviders.ProvideOtelInstrumentedSlogLogger,
otel.SetupOtel,

handlers.NewHandlerFactory,
wire.Bind(new(routes.HandlerFactory), new(*handlers.HandlerFactory)),

wireproviders.ProvideMiddlewares,
routes.NewRouter,
restserver.NewServerFromConfig,

NewAppFromConfig,
)

return &App{}, nil
}
37 changes: 37 additions & 0 deletions app/cmd/cloud-file-rest/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 38 additions & 1 deletion app/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,41 @@ module app

go 1.24.2

require github.com/google/uuid v1.6.0 // indirect
require (
github.com/go-chi/chi/v5 v5.2.1
github.com/go-slog/otelslog v0.3.0
github.com/google/uuid v1.6.0
github.com/google/wire v0.6.0
github.com/riandyrn/otelchi v0.12.1
github.com/spf13/viper v1.20.1
github.com/stretchr/testify v1.10.0
go.opentelemetry.io/otel v1.36.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0
go.opentelemetry.io/otel/sdk v1.36.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading