Refactor backend code to remove duplicate functions and factorize service startup
parent
7505a4256e
commit
c03ae3f0f0
@ -0,0 +1,46 @@
|
|||||||
|
// Package envutil provides environment variable parsing helpers with fallback values.
|
||||||
|
package envutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the environment variable value or fallback when empty.
|
||||||
|
func String(key string, fallback string) string {
|
||||||
|
if v := os.Getenv(key); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int returns the parsed integer environment variable or fallback on parse error.
|
||||||
|
func Int(key string, fallback int) int {
|
||||||
|
v := os.Getenv(key)
|
||||||
|
if v == "" {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration returns the parsed duration environment variable or fallback on parse error.
|
||||||
|
func Duration(key string, fallback time.Duration) time.Duration {
|
||||||
|
v := os.Getenv(key)
|
||||||
|
if v == "" {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := time.ParseDuration(v)
|
||||||
|
if err != nil {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package envutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestString(t *testing.T) {
|
||||||
|
t.Setenv("APP_VALUE", "configured")
|
||||||
|
require.Equal(t, "configured", String("APP_VALUE", "fallback"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringFallback(t *testing.T) {
|
||||||
|
t.Setenv("APP_VALUE", "")
|
||||||
|
require.Equal(t, "fallback", String("APP_VALUE", "fallback"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInt(t *testing.T) {
|
||||||
|
t.Setenv("APP_PORT", "8080")
|
||||||
|
require.Equal(t, 8080, Int("APP_PORT", 3000))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntFallbackInvalid(t *testing.T) {
|
||||||
|
t.Setenv("APP_PORT", "oops")
|
||||||
|
require.Equal(t, 3000, Int("APP_PORT", 3000))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDuration(t *testing.T) {
|
||||||
|
t.Setenv("APP_TIMEOUT", "7s")
|
||||||
|
require.Equal(t, 7*time.Second, Duration("APP_TIMEOUT", time.Second))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDurationFallbackInvalid(t *testing.T) {
|
||||||
|
t.Setenv("APP_TIMEOUT", "oops")
|
||||||
|
require.Equal(t, 2*time.Second, Duration("APP_TIMEOUT", 2*time.Second))
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
// Package serviceboot provides shared service bootstrap helpers.
|
||||||
|
package serviceboot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
|
||||||
|
"knowfoolery/backend/shared/infra/utils/envutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config defines basic service runtime settings.
|
||||||
|
type Config struct {
|
||||||
|
AppName string
|
||||||
|
ServiceSlug string
|
||||||
|
PortEnv string
|
||||||
|
DefaultPort int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFiberApp creates a Fiber app with shared defaults.
|
||||||
|
func NewFiberApp(cfg Config) *fiber.App {
|
||||||
|
return fiber.New(fiber.Config{
|
||||||
|
AppName: cfg.AppName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterHealth registers a standard health endpoint.
|
||||||
|
func RegisterHealth(app *fiber.App, serviceSlug string) {
|
||||||
|
app.Get("/health", func(c fiber.Ctx) error {
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"status": "healthy",
|
||||||
|
"service": serviceSlug,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAddress resolves the listen port from environment with fallback.
|
||||||
|
func ListenAddress(portEnv string, defaultPort int) string {
|
||||||
|
port := envutil.Int(portEnv, defaultPort)
|
||||||
|
if port < 1 || port > 65535 {
|
||||||
|
port = defaultPort
|
||||||
|
}
|
||||||
|
if port < 1 || port > 65535 {
|
||||||
|
port = 8080
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(":%d", port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run starts the Fiber app.
|
||||||
|
func Run(app *fiber.App, addr string) error {
|
||||||
|
return app.Listen(addr)
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
package serviceboot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRegisterHealth(t *testing.T) {
|
||||||
|
app := NewFiberApp(Config{AppName: "test-service"})
|
||||||
|
RegisterHealth(app, "svc")
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/health", nil)
|
||||||
|
resp, err := app.Test(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var body map[string]string
|
||||||
|
require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
|
||||||
|
require.Equal(t, "healthy", body["status"])
|
||||||
|
require.Equal(t, "svc", body["service"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListenAddressFromEnv(t *testing.T) {
|
||||||
|
t.Setenv("SERVICE_PORT", "9090")
|
||||||
|
require.Equal(t, ":9090", ListenAddress("SERVICE_PORT", 8080))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListenAddressFallback(t *testing.T) {
|
||||||
|
t.Setenv("SERVICE_PORT", "bad")
|
||||||
|
require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListenAddressOutOfRangeFallback(t *testing.T) {
|
||||||
|
t.Setenv("SERVICE_PORT", "70000")
|
||||||
|
require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080))
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue