mirror of
https://github.com/eduardolat/pgbackweb.git
synced 2026-01-07 04:40:19 -06:00
Add config package with environment validation and logging utilities
This commit is contained in:
7
.env.example
Normal file
7
.env.example
Normal file
@@ -0,0 +1,7 @@
|
||||
# Encryption key is used to encrypt and decrypt the sensitive data stored
|
||||
# in the database such as database credentials, secret keys, etc.
|
||||
PBW_ENCRYPTION_KEY=""
|
||||
|
||||
# Database connection string for a PostgreSQL database where the pgbackweb
|
||||
# will store its data.
|
||||
PBW_POSTGRES_CONN_STRING=""
|
||||
34
internal/config/env.go
Normal file
34
internal/config/env.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
type Env struct {
|
||||
PBW_ENCRYPTION_KEY *string
|
||||
PBW_POSTGRES_CONN_STRING *string
|
||||
}
|
||||
|
||||
// GetEnv returns the environment variables.
|
||||
//
|
||||
// If there is an error, it will log it and exit the program.
|
||||
func GetEnv() *Env {
|
||||
err := godotenv.Load()
|
||||
if err == nil {
|
||||
logInfo("using .env file")
|
||||
}
|
||||
|
||||
env := &Env{
|
||||
PBW_ENCRYPTION_KEY: getEnvAsString(getEnvAsStringParams{
|
||||
name: "PBW_ENCRYPTION_KEY",
|
||||
isRequired: true,
|
||||
}),
|
||||
PBW_POSTGRES_CONN_STRING: getEnvAsString(getEnvAsStringParams{
|
||||
name: "PBW_POSTGRES_CONN_STRING",
|
||||
isRequired: true,
|
||||
}),
|
||||
}
|
||||
|
||||
validateEnv(env)
|
||||
return env
|
||||
}
|
||||
4
internal/config/env_validate.go
Normal file
4
internal/config/env_validate.go
Normal file
@@ -0,0 +1,4 @@
|
||||
package config
|
||||
|
||||
// validateEnv runs additional validations on the environment variables.
|
||||
func validateEnv(env *Env) {}
|
||||
154
internal/config/helpers.go
Normal file
154
internal/config/helpers.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type getEnvAsStringParams struct {
|
||||
name string
|
||||
defaultValue *string
|
||||
isRequired bool
|
||||
}
|
||||
|
||||
// defaultValue returns a pointer to the given value.
|
||||
func newDefaultValue[T any](value T) *T {
|
||||
return &value
|
||||
}
|
||||
|
||||
// getEnvAsString returns the value of the environment variable with the given name.
|
||||
func getEnvAsString(params getEnvAsStringParams) *string { //nolint:all
|
||||
value, err := getEnvAsStringFunc(params)
|
||||
|
||||
if err != nil {
|
||||
logFatalError(
|
||||
"error getting env variable",
|
||||
"name", params.name,
|
||||
"error", err,
|
||||
)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// getEnvAsStringFunc is the outlying function for getEnvAsString.
|
||||
func getEnvAsStringFunc(params getEnvAsStringParams) (*string, error) {
|
||||
if params.defaultValue != nil && params.isRequired {
|
||||
return nil, errors.New("cannot have both a default value and be required")
|
||||
}
|
||||
|
||||
value, exists := os.LookupEnv(params.name)
|
||||
|
||||
if !exists && params.isRequired {
|
||||
return nil, errors.New("required env variable does not exist")
|
||||
}
|
||||
|
||||
if !exists {
|
||||
if params.defaultValue != nil {
|
||||
return params.defaultValue, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
type getEnvAsIntParams struct {
|
||||
name string
|
||||
defaultValue *int
|
||||
isRequired bool
|
||||
}
|
||||
|
||||
// getEnvAsInt returns the value of the environment variable with the given name.
|
||||
func getEnvAsInt(params getEnvAsIntParams) *int { //nolint:all
|
||||
value, err := getEnvAsIntFunc(params)
|
||||
|
||||
if err != nil {
|
||||
logFatalError(
|
||||
"error getting env variable",
|
||||
"name", params.name,
|
||||
"error", err,
|
||||
)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// getEnvAsIntFunc is the outlying function for getEnvAsInt.
|
||||
func getEnvAsIntFunc(params getEnvAsIntParams) (*int, error) {
|
||||
if params.defaultValue != nil && params.isRequired {
|
||||
return nil, errors.New("cannot have both a default value and be required")
|
||||
}
|
||||
|
||||
valueStr, exists := os.LookupEnv(params.name)
|
||||
|
||||
if !exists && params.isRequired {
|
||||
return nil, errors.New("required env variable does not exist")
|
||||
}
|
||||
|
||||
if !exists {
|
||||
if params.defaultValue != nil {
|
||||
return params.defaultValue, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(valueStr)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.New("env variable is not an integer")
|
||||
}
|
||||
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
type getEnvAsBoolParams struct {
|
||||
name string
|
||||
defaultValue *bool
|
||||
isRequired bool
|
||||
}
|
||||
|
||||
// getEnvAsBool returns the value of the environment variable with the given name.
|
||||
func getEnvAsBool(params getEnvAsBoolParams) *bool { //nolint:all
|
||||
value, err := getEnvAsBoolFunc(params)
|
||||
|
||||
if err != nil {
|
||||
logFatalError(
|
||||
"error getting env variable",
|
||||
"name", params.name,
|
||||
"error", err,
|
||||
)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// getEnvAsBoolFunc is the outlying function for getEnvAsBool.
|
||||
func getEnvAsBoolFunc(params getEnvAsBoolParams) (*bool, error) {
|
||||
if params.defaultValue != nil && params.isRequired {
|
||||
return nil, errors.New("cannot have both a default value and be required")
|
||||
}
|
||||
|
||||
valueStr, exists := os.LookupEnv(params.name)
|
||||
|
||||
if !exists && params.isRequired {
|
||||
return nil, errors.New("required env variable does not exist")
|
||||
}
|
||||
|
||||
if !exists {
|
||||
if params.defaultValue != nil {
|
||||
return params.defaultValue, nil
|
||||
}
|
||||
f := false
|
||||
return &f, nil
|
||||
}
|
||||
|
||||
value, err := strconv.ParseBool(valueStr)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.New("env variable is not a boolean, must be true or false")
|
||||
}
|
||||
|
||||
return &value, nil
|
||||
}
|
||||
190
internal/config/helpers_test.go
Normal file
190
internal/config/helpers_test.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetEnvAsStringFunc(t *testing.T) {
|
||||
// Test when environment variable exists
|
||||
os.Setenv("TEST_ENV", "test_value")
|
||||
value, err := getEnvAsStringFunc(getEnvAsStringParams{
|
||||
name: "TEST_ENV",
|
||||
isRequired: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test_value", *value)
|
||||
os.Unsetenv("TEST_ENV")
|
||||
|
||||
// Test when environment variable does not exist, default value is provided, and is not required
|
||||
value, err = getEnvAsStringFunc(getEnvAsStringParams{
|
||||
name: "NON_EXISTENT_ENV",
|
||||
defaultValue: newDefaultValue("default_value"),
|
||||
isRequired: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "default_value", *value)
|
||||
|
||||
// Test when environment variable does not exist, no default value is provided, and is required
|
||||
// This should return an error
|
||||
value, err = getEnvAsStringFunc(getEnvAsStringParams{
|
||||
name: "NON_EXISTENT_ENV",
|
||||
isRequired: true,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, value)
|
||||
|
||||
// Test when environment variable exists, default value is provided, and is required
|
||||
os.Setenv("TEST_ENV", "test_value")
|
||||
value, err = getEnvAsStringFunc(getEnvAsStringParams{
|
||||
name: "TEST_ENV",
|
||||
defaultValue: newDefaultValue("default_value"),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test_value", *value)
|
||||
os.Unsetenv("TEST_ENV")
|
||||
|
||||
// Test when environment variable exists, is not required, and no default value is provided
|
||||
os.Setenv("TEST_ENV", "test_value")
|
||||
value, err = getEnvAsStringFunc(getEnvAsStringParams{
|
||||
name: "TEST_ENV",
|
||||
isRequired: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test_value", *value)
|
||||
os.Unsetenv("TEST_ENV")
|
||||
|
||||
// Test when environment variable does not exist, is not required, and no default value is provided
|
||||
value, err = getEnvAsStringFunc(getEnvAsStringParams{
|
||||
name: "NON_EXISTENT_ENV",
|
||||
isRequired: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, value)
|
||||
|
||||
// Test when default value and required are both present
|
||||
// This should return an error
|
||||
_, err = getEnvAsStringFunc(getEnvAsStringParams{
|
||||
name: "NON_EXISTENT_ENV",
|
||||
defaultValue: newDefaultValue("default_value"),
|
||||
isRequired: true,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetEnvAsIntFunc(t *testing.T) {
|
||||
// Test when environment variable exists and is an integer
|
||||
os.Setenv("TEST_ENV", "123")
|
||||
value, err := getEnvAsIntFunc(getEnvAsIntParams{
|
||||
name: "TEST_ENV",
|
||||
isRequired: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 123, *value)
|
||||
os.Unsetenv("TEST_ENV")
|
||||
|
||||
// Test when environment variable does not exist, default value is provided, and is not required
|
||||
value, err = getEnvAsIntFunc(getEnvAsIntParams{
|
||||
name: "NON_EXISTENT_ENV",
|
||||
defaultValue: newDefaultValue(456),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 456, *value)
|
||||
|
||||
// Test when environment variable does not exist, no default value is provided, and is required
|
||||
// This should return an error
|
||||
value, err = getEnvAsIntFunc(getEnvAsIntParams{
|
||||
name: "NON_EXISTENT_ENV",
|
||||
isRequired: true,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, value)
|
||||
|
||||
// Test when environment variable exists, is not an integer, no default value is provided, and is required
|
||||
// This should return an error
|
||||
os.Setenv("TEST_ENV", "not_an_integer")
|
||||
value, err = getEnvAsIntFunc(getEnvAsIntParams{
|
||||
name: "TEST_ENV",
|
||||
isRequired: true,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, value)
|
||||
os.Unsetenv("TEST_ENV")
|
||||
|
||||
// Test when environment variable exists, is not required, and no default value is provided
|
||||
os.Setenv("TEST_ENV", "123")
|
||||
value, err = getEnvAsIntFunc(getEnvAsIntParams{
|
||||
name: "TEST_ENV",
|
||||
isRequired: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 123, *value)
|
||||
os.Unsetenv("TEST_ENV")
|
||||
|
||||
// Test when environment variable does not exist, is not required, and no default value is provided
|
||||
value, err = getEnvAsIntFunc(getEnvAsIntParams{
|
||||
name: "NON_EXISTENT_ENV",
|
||||
isRequired: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, value)
|
||||
|
||||
// Test when default value and required are both present
|
||||
// This should return an error
|
||||
_, err = getEnvAsIntFunc(getEnvAsIntParams{
|
||||
name: "NON_EXISTENT_ENV",
|
||||
defaultValue: newDefaultValue(1),
|
||||
isRequired: true,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetEnvAsBoolFunc(t *testing.T) {
|
||||
// Test when environment variable exists and is a boolean
|
||||
os.Setenv("TEST_ENV", "true")
|
||||
value, err := getEnvAsBoolFunc(getEnvAsBoolParams{
|
||||
name: "TEST_ENV",
|
||||
isRequired: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, *value)
|
||||
os.Unsetenv("TEST_ENV")
|
||||
|
||||
// Test when environment variable exists, is not a boolean, and is required
|
||||
os.Setenv("TEST_ENV", "not_a_boolean")
|
||||
_, err = getEnvAsBoolFunc(getEnvAsBoolParams{
|
||||
name: "TEST_ENV",
|
||||
isRequired: true,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
os.Unsetenv("TEST_ENV")
|
||||
|
||||
// Test when environment variable exists, is not required, and no default value is provided
|
||||
os.Setenv("TEST_ENV", "true")
|
||||
value, err = getEnvAsBoolFunc(getEnvAsBoolParams{
|
||||
name: "TEST_ENV",
|
||||
isRequired: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, *value)
|
||||
os.Unsetenv("TEST_ENV")
|
||||
|
||||
// Test when environment variable does not exist, is not required, and no default value is provided
|
||||
value, err = getEnvAsBoolFunc(getEnvAsBoolParams{
|
||||
name: "NON_EXISTENT_ENV",
|
||||
isRequired: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, false, *value)
|
||||
|
||||
// Test when default value and required are both present
|
||||
// This should return an error
|
||||
_, err = getEnvAsBoolFunc(getEnvAsBoolParams{
|
||||
name: "NON_EXISTENT_ENV",
|
||||
defaultValue: newDefaultValue(true),
|
||||
isRequired: true,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
15
internal/config/logger.go
Normal file
15
internal/config/logger.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
)
|
||||
|
||||
func logFatalError(msg string, args ...any) {
|
||||
slog.Error(msg, args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func logInfo(msg string, args ...any) {
|
||||
slog.Info(msg, args...)
|
||||
}
|
||||
Reference in New Issue
Block a user