Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ✨ integrating with goose and atlas migration tools #88

Merged
merged 4 commits into from
Sep 20, 2023
Merged
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
8 changes: 0 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ out/
.idea/
local/data/
portainer-data/
.env
.husky/pre-commit

# Logs
Expand Down Expand Up @@ -138,13 +137,6 @@ web_modules/
# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
Expand Down
22 changes: 22 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,28 @@ lint:
@./scripts/lint.sh order_service
@./scripts/lint.sh pkg

# https://github.com/golang-migrate/migrate/blob/856ea12df9d230b0145e23d951b7dbd6b86621cb/database/postgres/TUTORIAL.md
# https://github.com/golang-migrate/migrate/blob/856ea12df9d230b0145e23d951b7dbd6b86621cb/GETTING_STARTED.md
# https://github.com/golang-migrate/migrate/blob/856ea12df9d230b0145e23d951b7dbd6b86621cb/MIGRATIONS.md
# https://github.com/golang-migrate/migrate/tree/856ea12df9d230b0145e23d951b7dbd6b86621cb/cmd/migrate#usage
.PHONY: go-migrate
go-migrate:
@./scripts/go-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/go-migrate -c create -n create_product_table
@./scripts/go-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/go-migrate -c up -o postgres://postgres:postgres@localhost:5432/catalogs_service?sslmode=disable
@./scripts/go-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/go-migrate -c down -o postgres://postgres:postgres@localhost:5432/catalogs_service?sslmode=disable

# https://github.com/pressly/goose#usage
.PHONY: goose-migrate
goose-migrate:
@./scripts/goose-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/goose-migrate -c create -n create_product_table
@./scripts/goose-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/goose-migrate -c up -o "user=postgres password=postgres dbname=catalogs_service sslmode=disable"
@./scripts/goose-migrate.sh -p ./internal/services/catalog_write_service/db/migrations/goose-migrate -c down -o "user=postgres password=postgres dbname=catalogs_service sslmode=disable"

# https://atlasgo.io/guides/orms/gorm
.PHONY: atlas
atlas:
@./scripts/atlas-migrate.sh -c gorm-sync -p "./internal/services/catalog_write_service"
@./scripts/atlas-migrate.sh -c apply -p "./internal/services/catalog_write_service" -o "postgres://postgres:postgres@localhost:5432/catalogs_service?sslmode=disable"

#.PHONY: c4
#c4:
Expand Down
8 changes: 5 additions & 3 deletions internal/pkg/config/config_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ func BindConfigKey[T any](configKey string, environments ...environemnt.Environm
}

// https://articles.wesionary.team/environment-variable-configuration-in-your-golang-project-using-viper-4e8289ef664d
configPathFromEnv := viper.Get(constants.ConfigPath)
if configPathFromEnv != nil {
configPath = configPathFromEnv.(string)
// when we `Set` a viper with string value, we should get it from viper with `viper.GetString`, elsewhere we get empty string
// load `config path` from environment variable or viper internal registry
configPathFromEnv := viper.GetString(constants.ConfigPath)
if configPathFromEnv != "" {
configPath = configPathFromEnv
} else {
// https://stackoverflow.com/questions/31873396/is-it-possible-to-get-the-current-root-of-package-structure-as-a-string-in-golan
// https://stackoverflow.com/questions/18537257/how-to-get-the-directory-of-the-currently-running-file
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/config/configfx.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var ModuleFunc = func(e environemnt.Environment) fx.Option {
return fx.Module(
"configfx",
fx.Provide(func() environemnt.Environment {
return e
return environemnt.ConfigAppEnv(e)
}),
)
}
75 changes: 73 additions & 2 deletions internal/pkg/config/environemnt/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package environemnt
import (
"log"
"os"
"path/filepath"
"strings"
"syscall"

"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants"

"emperror.dev/errors"
"github.com/joho/godotenv"
"github.com/spf13/viper"
)
Expand All @@ -32,11 +35,13 @@ func ConfigAppEnv(environments ...Environment) Environment {

// https://articles.wesionary.team/environment-variable-configuration-in-your-golang-project-using-viper-4e8289ef664d
// load environment variables form .env files to system environment variables, it just find `.env` file in our current `executing working directory` in our app for example `catalogs_service`
err := godotenv.Load()
err := loadEnvFilesRecursive()
if err != nil {
log.Println(".env file cannot be found.")
log.Printf(".env file cannot be found, err: %v", err)
}

setRootWorkingDirectoryEnvironment()

manualEnv := os.Getenv(constants.AppEnv)

if manualEnv != "" {
Expand Down Expand Up @@ -64,3 +69,69 @@ func EnvString(key, fallback string) string {
}
return fallback
}

func loadEnvFilesRecursive() error {
// Start from the current working directory
dir, err := os.Getwd()
if err != nil {
return err
}

// Keep traversing up the directory hierarchy until you find an ".env" file
for {
envFilePath := filepath.Join(dir, ".env")
err := godotenv.Load(envFilePath)

if err == nil {
// .env file found and loaded
return nil
}

// Move up one directory level
parentDir := filepath.Dir(dir)
if parentDir == dir {
// Reached the root directory, stop searching
break
}

dir = parentDir
}

return errors.New(".env file not found in the project hierarchy")
}

func setRootWorkingDirectoryEnvironment() {
// https://articles.wesionary.team/environment-variable-configuration-in-your-golang-project-using-viper-4e8289ef664d
// when we `Set` a viper with string value, we should get it from viper with `viper.GetString`, elsewhere we get empty string
// viper will get it from `os env` or a .env file
pn := viper.GetString(constants.PROJECT_NAME_ENV)
if pn == "" {
log.Fatalf(
"can't find environment variable `%s` in the system environment variables or a .env file",
constants.PROJECT_NAME_ENV,
)
}

// set root working directory of our app in the viper
// https://stackoverflow.com/a/47785436/581476
wd, _ := os.Getwd()

for !strings.HasSuffix(wd, pn) {
wd = filepath.Dir(wd)
}

absoluteRootWorkingDirectory, _ := filepath.Abs(wd)

// when we `Set` a viper with string value, we should get it from viper with `viper.GetString`, elsewhere we get empty string
viper.Set(constants.AppRootPath, absoluteRootWorkingDirectory)

configPath := viper.GetString(constants.ConfigPath)

// check for existing `CONFIG_PATH` variable in system environment variables - we can set it directly in .env file or system environments
if configPath == "" {
configPath := filepath.Join(absoluteRootWorkingDirectory, "config")

// when we `Set` a viper with string value, we should get it from viper with `viper.GetString`, elsewhere we get empty string
viper.Set(constants.ConfigPath, configPath)
}
}
33 changes: 0 additions & 33 deletions internal/pkg/fxapp/application_builder.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
package fxapp

import (
"os"
"path/filepath"
"strings"

"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt"
"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants"
"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts"
"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger"
loggerConfig "github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/config"
"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/logrous"
"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/models"
"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger/zap"

"github.com/spf13/viper"
"go.uber.org/fx"
)

Expand All @@ -29,8 +23,6 @@ type applicationBuilder struct {
func NewApplicationBuilder(environments ...environemnt.Environment) contracts.ApplicationBuilder {
env := environemnt.ConfigAppEnv(environments...)

setConfigPath()

var logger logger.Logger
logoption, err := loggerConfig.ProvideLogConfig(env)
if err != nil || logoption == nil {
Expand All @@ -44,31 +36,6 @@ func NewApplicationBuilder(environments ...environemnt.Environment) contracts.Ap
return &applicationBuilder{logger: logger, environment: env}
}

func setConfigPath() {
// https://stackoverflow.com/a/47785436/581476
wd, _ := os.Getwd()

// https://articles.wesionary.team/environment-variable-configuration-in-your-golang-project-using-viper-4e8289ef664d
// get from `os env` or viper `internal registry`
pn := viper.Get(constants.PROJECT_NAME_ENV)
if pn == nil {
return
}
for !strings.HasSuffix(wd, pn.(string)) {
wd = filepath.Dir(wd)
}

// Get the absolute path of the executed project directory
absCurrentDir, _ := filepath.Abs(wd)

viper.Set(constants.AppRootPath, absCurrentDir)

// Get the path to the "config" folder within the project directory
configPath := filepath.Join(absCurrentDir, "config")

viper.Set(constants.ConfigPath, configPath)
}

func (a *applicationBuilder) ProvideModule(module fx.Option) {
a.options = append(a.options, module)
}
Expand Down
2 changes: 2 additions & 0 deletions internal/pkg/fxapp/contracts/application_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
)

type ApplicationBuilder interface {
// ProvideModule register modules directly instead and modules should not register with `provided` function
ProvideModule(module fx.Option)
// Provide register functions constructors as dependency resolver
Provide(constructors ...interface{})
Decorate(constructors ...interface{})
Build() Application
Expand Down
51 changes: 38 additions & 13 deletions internal/pkg/fxapp/test/test_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package test

import (
"context"
"os"

"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/config/environemnt"
"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/constants"
"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/fxapp/contracts"
"github.com/mehdihadeli/go-ecommerce-microservices/internal/pkg/logger"

"github.com/spf13/viper"
"go.uber.org/fx"
"go.uber.org/fx/fxtest"
)
Expand Down Expand Up @@ -61,16 +64,8 @@ func (a *testApplication) RegisterHook(function interface{}) {
}

func (a *testApplication) Run() {
// build phase of container will do in this stage, containing provides and invokes but app not started yet and will be started in the future with `fxApp.Register`
fxTestApp := CreateFxTestApp(
a.tb,
a.provides,
a.decorates,
a.invokes,
a.options,
a.logger,
a.env,
)
fxTestApp := a.createFxTest()

// running phase will do in this stage and all register event hooks like OnStart and OnStop
// instead of run for handling start and stop and create a ctx and cancel we can handle them manually with appconfigfx.start and appconfigfx.stop
// https://github.com/uber-go/fx/blob/v1.20.0/app.go#L573
Expand All @@ -79,10 +74,9 @@ func (a *testApplication) Run() {

func (a *testApplication) Start(ctx context.Context) error {
// build phase of container will do in this stage, containing provides and invokes but app not started yet and will be started in the future with `fxApp.Register`
fxApp := CreateFxTestApp(a.tb, a.provides, a.decorates, a.invokes, a.options, a.logger, a.env)
a.fxtestApp = fxApp
fxTestApp := a.createFxTest()

return fxApp.Start(ctx)
return fxTestApp.Start(ctx)
}

func (a *testApplication) Stop(ctx context.Context) error {
Expand All @@ -98,3 +92,34 @@ func (a *testApplication) Wait() <-chan fx.ShutdownSignal {
}
return a.fxtestApp.Wait()
}

func (a *testApplication) createFxTest() *fxtest.App {
a.fixTestEnvironmentWorkingDirectory()

// build phase of container will do in this stage, containing provides and invokes but app not started yet and will be started in the future with `fxApp.Register`
fxTestApp := CreateFxTestApp(
a.tb,
a.provides,
a.decorates,
a.invokes,
a.options,
a.logger,
a.env,
)
a.fxtestApp = fxTestApp

return fxTestApp
}

func (a *testApplication) fixTestEnvironmentWorkingDirectory() {
currentWD, _ := os.Getwd()
a.logger.Infof("Current test working directory is: %s", currentWD)

rootDir := viper.GetString(constants.AppRootPath)
if rootDir != "" {
_ = os.Chdir(rootDir)

newWD, _ := os.Getwd()
a.logger.Infof("New test working directory is: %s", newWD)
}
}
6 changes: 5 additions & 1 deletion internal/pkg/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/avast/retry-go v3.0.0+incompatible
github.com/caarlos0/env/v8 v8.0.0
github.com/docker/docker v24.0.5+incompatible
github.com/docker/go-connections v0.4.0
github.com/doug-martin/goqu/v9 v9.18.0
github.com/elastic/go-elasticsearch/v8 v8.9.0
Expand All @@ -33,11 +34,13 @@ require (
github.com/lib/pq v1.10.9
github.com/mcuadros/go-defaults v1.2.0
github.com/mehdihadeli/go-mediatr v1.1.10
github.com/michaelklishin/rabbit-hole v1.5.0
github.com/mitchellh/mapstructure v1.5.0
github.com/nolleh/caption_json_formatter v0.2.2
github.com/orlangure/gnomock v0.30.0
github.com/ory/dockertest/v3 v3.10.0
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
github.com/pressly/goose/v3 v3.15.0
github.com/prometheus/client_golang v1.16.0
github.com/rabbitmq/amqp091-go v1.8.1
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5
Expand Down Expand Up @@ -90,7 +93,6 @@ require (
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/docker/cli v24.0.5+incompatible // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v24.0.5+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/elastic/elastic-transport-go/v8 v8.3.0 // indirect
github.com/fatih/color v1.15.0 // indirect
Expand Down Expand Up @@ -137,6 +139,7 @@ require (
github.com/moby/term v0.5.0 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
github.com/opencontainers/runc v1.1.9 // indirect
Expand All @@ -157,6 +160,7 @@ require (
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/streadway/amqp v1.1.0 // indirect
github.com/stretchr/objx v0.5.1 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
Expand Down
Loading
Loading