diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 386d911b..78f9f840 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -29,6 +29,7 @@ jobs: - "log" - "orm" - "trace" + - "fxconfig" steps: - name: Checkout uses: actions/checkout@v3 diff --git a/.github/workflows/fxconfig-ci.yml b/.github/workflows/fxconfig-ci.yml new file mode 100644 index 00000000..9e180e0c --- /dev/null +++ b/.github/workflows/fxconfig-ci.yml @@ -0,0 +1,31 @@ +name: "fxconfig-ci" + +on: + push: + branches: + - "feat**" + - "fix**" + - "hotfix**" + - "chore**" + paths: + - "fxconfig/**.go" + - "fxconfig/go.mod" + - "fxconfig/go.sum" + pull_request: + types: + - opened + - synchronize + - reopened + branches: + - main + paths: + - "fxconfig/**.go" + - "fxconfig/go.mod" + - "fxconfig/go.sum" + +jobs: + ci: + uses: ./.github/workflows/common-ci.yml + secrets: inherit + with: + module: "fxconfig" diff --git a/README.md b/README.md index 905a05f5..f088a168 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,21 @@ Yokai's documentation will be available soon. +## Fx Modules + +Yokai is using [Fx](https://github.com/uber-go/fx) for its plugin system. + +`Yokai's Fx modules` are the plugins for your Yokai application, but they can be used in any [Fx](https://github.com/uber-go/fx) based Go application. + +| Fx Module | Description | +|----------------------|--------------------------------| +| [fxconfig](fxconfig) | Fx module for [config](config) | + + ## Modules +`Yokai's modules` are the foundation of the framework, but they can be used in any Go application (no Yokai dependencies). + | Module | Description | |----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------| | [config](config) | Config module based on [Viper](https://github.com/spf13/viper) | diff --git a/fxconfig/.golangci.yml b/fxconfig/.golangci.yml new file mode 100644 index 00000000..edf0e9ec --- /dev/null +++ b/fxconfig/.golangci.yml @@ -0,0 +1,66 @@ +run: + timeout: 5m + concurrency: 8 + +linters: + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + - cyclop + - decorder + - dogsled + - dupl + - durationcheck + - errcheck + - errchkjson + - errname + - errorlint + - exhaustive + - forbidigo + - forcetypeassert + - gocognit + - goconst + - gocritic + - gocyclo + - godot + - godox + - gofmt + - goheader + - gomoddirectives + - gomodguard + - goprintffuncname + - gosec + - gosimple + - govet + - grouper + - importas + - ineffassign + - interfacebloat + - logrlint + - maintidx + - makezero + - misspell + - nestif + - nilerr + - nilnil + - nlreturn + - nolintlint + - nosprintfhostport + - prealloc + - predeclared + - promlinter + - reassign + - staticcheck + - tenv + - thelper + - tparallel + - typecheck + - unconvert + - unparam + - unused + - usestdlibvars + - whitespace diff --git a/fxconfig/README.md b/fxconfig/README.md new file mode 100644 index 00000000..116cb046 --- /dev/null +++ b/fxconfig/README.md @@ -0,0 +1,107 @@ +# Fx Config Module + +[![ci](https://github.com/ankorstore/yokai/actions/workflows/fxconfig-ci.yml/badge.svg)](https://github.com/ankorstore/yokai/actions/workflows/fxconfig-ci.yml) +[![go report](https://goreportcard.com/badge/github.com/ankorstore/yokai/fxconfig)](https://goreportcard.com/report/github.com/ankorstore/yokai/fxconfig) +[![codecov](https://codecov.io/gh/ankorstore/yokai/graph/badge.svg?token=ghUBlFsjhR&flag=fxconfig)](https://app.codecov.io/gh/ankorstore/yokai/tree/main/fxconfig) +[![Deps](https://img.shields.io/badge/osi-deps-blue)](https://deps.dev/go/github.com%2Fankorstore%2Fyokai%2Ffxconfig) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/ankorstore/yokai/fxconfig)](https://pkg.go.dev/github.com/ankorstore/yokai/fxconfig) + +> [Fx](https://uber-go.github.io/fx/) module for [config](https://github.com/ankorstore/yokai/tree/main/config). + + +* [Installation](#installation) +* [Documentation](#documentation) + * [Loading](#loading) + * [Configuration files](#configuration-files) + * [Configuration usage](#configuration-usage) + * [Override](#override) + + +## Installation + +```shell +go get github.com/ankorstore/yokai/fxconfig +``` + +## Documentation + +### Loading + +To load the module in your Fx application: + +```go +package main + +import ( + "fmt" + + "github.com/ankorstore/yokai/config" + "github.com/ankorstore/yokai/fxconfig" + "go.uber.org/fx" +) + +func main() { + fx.New( + fxconfig.FxConfigModule, // load the module + fx.Invoke(func(cfg *config.Config) { // invoke the config + fmt.Printf("app name: %s", cfg.AppName()) + }), + ).Run() +} +``` + +### Configuration files + +The module expects configuration files to be present: +- in `.` (project root) +- or in the`./configs` directory +- or any directory referenced in the `APP_CONFIG_PATH` env var + +Check the [configuration files documentation](https://github.com/ankorstore/yokai/tree/main/config#configuration-files) for more details. + +### Configuration usage + +This module offers several features, such as: +- config helpers and typed accessors +- config dynamic environment overrides +- config env vars placeholders and runtime substitution + +Check the [configuration usage documentation](https://github.com/ankorstore/yokai/tree/main/config#configuration-usage) for more details. + +### Override + +By default, the `config.Config` is created by the [DefaultConfigFactory](https://github.com/ankorstore/yokai/blob/main/config/factory.go). + +If needed, you can provide your own factory and override the module: + +```go +package main + +import ( + "fmt" + + "github.com/ankorstore/yokai/config" + "github.com/ankorstore/yokai/fxconfig" + "go.uber.org/fx" +) + +type CustomConfigFactory struct{} + +func NewCustomConfigFactory() config.ConfigFactory { + return &CustomConfigFactory{} +} + +func (f *CustomConfigFactory) Create(options ...config.ConfigOption) (*config.Config, error) { + return &config.Config{...}, nil +} + +func main() { + fx.New( + fxconfig.FxConfigModule, // load the module + fx.Decorate(NewCustomConfigFactory), // decorate the module with a custom factory + fx.Invoke(func(cfg *config.Config) { // invoke the custom config + fmt.Printf("custom app name: %s", cfg.AppName()) + }), + ).Run() +} +``` diff --git a/fxconfig/go.mod b/fxconfig/go.mod new file mode 100644 index 00000000..7e1fc2c8 --- /dev/null +++ b/fxconfig/go.mod @@ -0,0 +1,36 @@ +module github.com/ankorstore/yokai/fxconfig + +go 1.20 + +require ( + github.com/ankorstore/yokai/config v1.1.0 + github.com/stretchr/testify v1.8.4 + go.uber.org/fx v1.20.1 +) + +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.18.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/dig v1.17.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/fxconfig/go.sum b/fxconfig/go.sum new file mode 100644 index 00000000..579554c3 --- /dev/null +++ b/fxconfig/go.sum @@ -0,0 +1,91 @@ +github.com/ankorstore/yokai/config v1.1.0 h1:z6xnsVXAbWhhjcb5kqVaw0VlaGZziGc7Di1bJlt5rf0= +github.com/ankorstore/yokai/config v1.1.0/go.mod h1:yDANaMWIOfAUkAMClG22Q4bzQk91NLwWK3WbL5IFnbg= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= +go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= +go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= +go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.20.0 h1:ZMC/pnRvhsthOZh9MZjMq5U8Or3mA9zBSPaLnzs3ihQ= +go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= +go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= +go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e h1:723BNChdd0c2Wk6WOE320qGBiPtYx0F0Bbm1kriShfE= +golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/fxconfig/module.go b/fxconfig/module.go new file mode 100644 index 00000000..63eeecba --- /dev/null +++ b/fxconfig/module.go @@ -0,0 +1,40 @@ +package fxconfig + +import ( + "os" + + "github.com/ankorstore/yokai/config" + "go.uber.org/fx" +) + +// ModuleName is the module name. +const ModuleName = "config" + +// FxConfigModule is the [Fx] config module. +// +// [Fx]: https://github.com/uber-go/fx +var FxConfigModule = fx.Module( + ModuleName, + fx.Provide( + config.NewDefaultConfigFactory, + NewFxConfig, + ), +) + +// FxConfigParam allows injection of the required dependencies in [NewFxConfig]. +type FxConfigParam struct { + fx.In + Factory config.ConfigFactory +} + +// NewFxConfig returns a [config.Config]. +func NewFxConfig(p FxConfigParam) (*config.Config, error) { + return p.Factory.Create( + config.WithFileName("config"), + config.WithFilePaths( + ".", + "./configs", + os.Getenv("APP_CONFIG_PATH"), + ), + ) +} diff --git a/fxconfig/module_test.go b/fxconfig/module_test.go new file mode 100644 index 00000000..9cf2ed10 --- /dev/null +++ b/fxconfig/module_test.go @@ -0,0 +1,108 @@ +package fxconfig_test + +import ( + "testing" + + "github.com/ankorstore/yokai/config" + "github.com/ankorstore/yokai/fxconfig" + "github.com/ankorstore/yokai/fxconfig/testdata/factory" + "github.com/stretchr/testify/assert" + "go.uber.org/fx" + "go.uber.org/fx/fxtest" +) + +func TestModule(t *testing.T) { + t.Setenv("APP_CONFIG_PATH", "testdata/config") + + var cfg *config.Config + + fxtest.New( + t, + fx.NopLogger, + fxconfig.FxConfigModule, + fx.Populate(&cfg), + ).RequireStart().RequireStop() + + assert.Equal(t, "default-app", cfg.AppName()) + assert.Equal(t, config.AppEnvDev, cfg.AppEnv()) + assert.True(t, cfg.IsDevEnv()) + assert.False(t, cfg.IsTestEnv()) + assert.False(t, cfg.IsProdEnv()) + assert.False(t, cfg.AppDebug()) + assert.Equal(t, "0.1.0", cfg.AppVersion()) + + assert.Equal(t, "default", cfg.GetString("config.values.string_value")) + assert.Equal(t, 0, cfg.GetInt("config.values.int_value")) + + assert.Equal(t, "foo--baz", cfg.GetString("config.substitution")) +} + +func TestModuleWithTestEnv(t *testing.T) { + t.Setenv("APP_ENV", "test") + t.Setenv("APP_CONFIG_PATH", "testdata/config") + t.Setenv("BAR", "bar") + + var cfg *config.Config + + fxtest.New( + t, + fx.NopLogger, + fxconfig.FxConfigModule, + fx.Populate(&cfg), + ).RequireStart().RequireStop() + + assert.Equal(t, "test-app", cfg.AppName()) + assert.Equal(t, config.AppEnvTest, cfg.AppEnv()) + assert.False(t, cfg.IsDevEnv()) + assert.True(t, cfg.IsTestEnv()) + assert.False(t, cfg.IsProdEnv()) + assert.True(t, cfg.AppDebug()) + assert.Equal(t, "0.1.0", cfg.AppVersion()) + + assert.Equal(t, "test", cfg.GetString("config.values.string_value")) + assert.Equal(t, 0, cfg.GetInt("config.values.int_value")) + + assert.Equal(t, "foo-bar-baz", cfg.GetString("config.substitution")) +} + +func TestModuleWithCustomEnv(t *testing.T) { + t.Setenv("APP_ENV", "custom") + t.Setenv("APP_CONFIG_PATH", "testdata/config") + t.Setenv("BAR", "bar") + + var cfg *config.Config + + fxtest.New( + t, + fx.NopLogger, + fxconfig.FxConfigModule, + fx.Populate(&cfg), + ).RequireStart().RequireStop() + + assert.Equal(t, "custom-app", cfg.AppName()) + assert.Equal(t, "custom", cfg.AppEnv()) + assert.False(t, cfg.IsDevEnv()) + assert.False(t, cfg.IsTestEnv()) + assert.False(t, cfg.IsProdEnv()) + assert.True(t, cfg.AppDebug()) + assert.Equal(t, "0.1.0", cfg.AppVersion()) + + assert.Equal(t, "custom", cfg.GetString("config.values.string_value")) + assert.Equal(t, 0, cfg.GetInt("config.values.int_value")) + + assert.Equal(t, "foo-bar-baz", cfg.GetString("config.substitution")) +} + +func TestModuleDecoration(t *testing.T) { + var cfg *config.Config + + fxtest.New( + t, + fx.NopLogger, + fxconfig.FxConfigModule, + fx.Decorate(factory.NewTestConfigFactory), + fx.Populate(&cfg), + ).RequireStart().RequireStop() + + assert.Equal(t, &config.Config{}, cfg) +} diff --git a/fxconfig/testdata/config/config.custom.yaml b/fxconfig/testdata/config/config.custom.yaml new file mode 100644 index 00000000..9ea088c7 --- /dev/null +++ b/fxconfig/testdata/config/config.custom.yaml @@ -0,0 +1,7 @@ +app: + name: custom-app + env: custom + debug: true +config: + values: + string_value: custom diff --git a/fxconfig/testdata/config/config.test.yaml b/fxconfig/testdata/config/config.test.yaml new file mode 100644 index 00000000..c6550c9f --- /dev/null +++ b/fxconfig/testdata/config/config.test.yaml @@ -0,0 +1,7 @@ +app: + name: test-app + env: test + debug: true +config: + values: + string_value: test diff --git a/fxconfig/testdata/config/config.yaml b/fxconfig/testdata/config/config.yaml new file mode 100644 index 00000000..323e37a6 --- /dev/null +++ b/fxconfig/testdata/config/config.yaml @@ -0,0 +1,10 @@ +app: + name: default-app + env: dev + version: 0.1.0 + debug: false +config: + values: + string_value: default + int_value: 0 + substitution: foo-${BAR}-baz diff --git a/fxconfig/testdata/factory/factory.go b/fxconfig/testdata/factory/factory.go new file mode 100644 index 00000000..b8f02616 --- /dev/null +++ b/fxconfig/testdata/factory/factory.go @@ -0,0 +1,15 @@ +package factory + +import ( + "github.com/ankorstore/yokai/config" +) + +type TestConfigFactory struct{} + +func NewTestConfigFactory() config.ConfigFactory { + return &TestConfigFactory{} +} + +func (f *TestConfigFactory) Create(options ...config.ConfigOption) (*config.Config, error) { + return &config.Config{}, nil +} diff --git a/release-please-config.json b/release-please-config.json index 5fbd64c7..d78d45f0 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -40,6 +40,11 @@ "release-type": "go", "component": "trace", "tag-separator": "/" + }, + "fxconfig": { + "release-type": "go", + "component": "fxconfig", + "tag-separator": "/" } } }