Skip to content

Commit c36307b

Browse files
committed
refactor: handle config with viper
1 parent 059abce commit c36307b

File tree

9 files changed

+220
-121
lines changed

9 files changed

+220
-121
lines changed

cli/auth.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"syscall"
77
"time"
88

9+
"github.com/compiuta-origin/connhex-cli/internal/config"
910
"github.com/compiuta-origin/connhex-cli/internal/sdk"
1011
"github.com/spf13/cobra"
1112
"golang.org/x/term"
@@ -23,7 +24,6 @@ func NewLoginCmd() *cobra.Command {
2324
cmd := cobra.Command{
2425
Use: "login [connhex-instance user password]",
2526
Short: "Login to Connhex instance",
26-
// We use `RunE` beacause this command is used in the main PersistentPreRun
2727
RunE: func(cmd *cobra.Command, args []string) error {
2828
var connhexInstance, user, password string
2929

@@ -59,31 +59,30 @@ func NewLoginCmd() *cobra.Command {
5959
return ErrInvalidInput
6060
}
6161

62-
connhexInstance, err := sanitizeConnhexInstance(connhexInstance)
62+
connhexInstance, err := config.SanitizeConnhexInstance(connhexInstance)
6363
if err != nil {
6464
return err
6565
}
6666

6767
// Initialize the SDK if needed, since the login command
6868
// doesn't require authentication when first executed
6969
if chxsdk == nil {
70-
s := sdk.NewSDK(connhexInstance)
71-
SetSDK(s)
70+
SetSDK(sdk.NewSDK(connhexInstance))
7271
}
7372

7473
token, err := chxsdk.Login(user, password)
7574
if err != nil {
7675
return err
7776
}
7877

79-
config := Config{
78+
cfg := config.Config{
8079
ConnhexInstance: connhexInstance,
8180
Token: token,
8281
User: user,
8382
ExpiresAt: time.Now().Add(tokenExpirationTime),
8483
}
8584

86-
if err := config.Save(); err != nil {
85+
if err := config.Save(cfg); err != nil {
8786
return err
8887
}
8988

cli/config.go

Lines changed: 3 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -3,95 +3,11 @@ package cli
33
import (
44
"encoding/json"
55
"fmt"
6-
"os"
7-
"path/filepath"
8-
"time"
96

7+
"github.com/compiuta-origin/connhex-cli/internal/config"
108
"github.com/spf13/cobra"
119
)
1210

13-
var (
14-
configDirname = ".connhex"
15-
configFilename = "config.json"
16-
)
17-
18-
type Config struct {
19-
ConnhexInstance string `json:"connhex_instance"`
20-
Token string `json:"token"`
21-
User string `json:"user"`
22-
ExpiresAt time.Time `json:"expires_at"`
23-
}
24-
25-
func (c *Config) sanitize() error {
26-
sanitizedInstance, err := sanitizeConnhexInstance(c.ConnhexInstance)
27-
if err != nil {
28-
return err
29-
}
30-
c.ConnhexInstance = sanitizedInstance
31-
32-
return nil
33-
}
34-
35-
func (c *Config) Save() error {
36-
if err := c.sanitize(); err != nil {
37-
return err
38-
}
39-
40-
configFile, err := getConfigFilePath()
41-
if err != nil {
42-
return err
43-
}
44-
45-
configDir := filepath.Dir(configFile)
46-
47-
// Create config directory if it doesn't exist
48-
if err := os.MkdirAll(configDir, 0755); err != nil {
49-
return err
50-
}
51-
52-
data, err := json.Marshal(c)
53-
if err != nil {
54-
return err
55-
}
56-
57-
return os.WriteFile(configFile, data, 0600)
58-
}
59-
60-
func (c *Config) IsUserAuthenticated() bool {
61-
return c.Token != "" && time.Now().Before(c.ExpiresAt)
62-
}
63-
64-
func getConfigFilePath() (string, error) {
65-
homeDir, err := os.UserHomeDir()
66-
if err != nil {
67-
return "", err
68-
}
69-
configDir := filepath.Join(homeDir, configDirname)
70-
return filepath.Join(configDir, configFilename), nil
71-
}
72-
73-
func LoadConfig() (Config, error) {
74-
config := Config{}
75-
76-
configFile, err := getConfigFilePath()
77-
if err != nil {
78-
return config, err
79-
}
80-
81-
// Check if config file exists
82-
if _, err := os.Stat(configFile); os.IsNotExist(err) {
83-
return config, nil
84-
}
85-
86-
data, err := os.ReadFile(configFile)
87-
if err != nil {
88-
return config, err
89-
}
90-
91-
err = json.Unmarshal(data, &config)
92-
return config, err
93-
}
94-
9511
func NewConfigCmd() *cobra.Command {
9612
cmd := &cobra.Command{
9713
Use: "config",
@@ -103,13 +19,13 @@ func NewConfigCmd() *cobra.Command {
10319
return
10420
}
10521

106-
config, err := LoadConfig()
22+
cfg, err := config.Load()
10723
if err != nil {
10824
logError(fmt.Errorf("failed to load configuration: %w", err))
10925
return
11026
}
11127

112-
output, err := json.MarshalIndent(config, "", " ")
28+
output, err := json.MarshalIndent(cfg, "", " ")
11329
if err != nil {
11430
logError(fmt.Errorf("failed to format configuration: %w", err))
11531
return

cli/provision.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"encoding/csv"
1212
"strings"
1313

14+
"github.com/compiuta-origin/connhex-cli/internal/config"
1415
"github.com/compiuta-origin/connhex-cli/internal/sdk"
1516
"github.com/spf13/cobra"
1617
)
@@ -233,27 +234,27 @@ func getManufacturingData(devices []Device, connectables []sdk.Connectable, mdc
233234
}
234235

235236
func provisionDevices(devices []Device, mdc ManufacturingDeviceConfig) error {
236-
config, err := LoadConfig()
237+
cfg, err := config.Load()
237238
if err != nil {
238239
return err
239240
}
240241

241242
provisionData := getProvisionData(devices)
242-
provisionResult, err := chxsdk.BulkProvision(provisionData, config.Token)
243+
provisionResult, err := chxsdk.BulkProvision(provisionData, cfg.Token)
243244
if err != nil {
244245
return err
245246
}
246247

247248
manufacturingData := getManufacturingData(devices, provisionResult.Things, mdc)
248249

249-
if _, err := chxsdk.CreateResources(manufacturingData, "manufacturing", mdc.schema, config.Token); err != nil {
250+
if _, err := chxsdk.CreateResources(manufacturingData, "manufacturing", mdc.schema, cfg.Token); err != nil {
250251
ids := make([]string, len(provisionResult.Things))
251252
for i := range ids {
252253
ids[i] = provisionResult.Things[i].ID
253254
}
254255

255256
// Ignoring unprovisioning errors
256-
chxsdk.BulkUnprovision(ids, config.Token)
257+
chxsdk.BulkUnprovision(ids, cfg.Token)
257258

258259
return err
259260
}

cli/utils.go

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,11 @@
11
package cli
22

33
import (
4-
"errors"
54
"fmt"
6-
"net/url"
7-
"strings"
85

96
"github.com/fatih/color"
107
)
118

12-
func sanitizeConnhexInstance(instance string) (string, error) {
13-
instance = strings.TrimSpace(instance)
14-
15-
if strings.HasPrefix(instance, "http://") {
16-
return "", errors.New("http protocol not supported")
17-
}
18-
19-
if strings.HasPrefix(instance, "https://") {
20-
parsedURL, err := url.Parse(instance)
21-
if err != nil {
22-
return "", fmt.Errorf("invalid URL: %w", err)
23-
}
24-
instance = parsedURL.Host
25-
}
26-
27-
return instance, nil
28-
}
29-
309
func logUsage(u string) {
3110
fmt.Printf(color.GreenString("\nusage: %s\n\n"), u)
3211
}

cmd/main.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,47 @@ import (
55
"slices"
66

77
"github.com/compiuta-origin/connhex-cli/cli"
8+
"github.com/compiuta-origin/connhex-cli/internal/config"
89
"github.com/compiuta-origin/connhex-cli/internal/sdk"
910
"github.com/spf13/cobra"
1011
)
1112

1213
func main() {
14+
if err := config.Setup(); err != nil {
15+
log.Fatal(err)
16+
}
17+
1318
rootCmd := &cobra.Command{
1419
Use: "connhex-cli",
1520
Short: "Connhex CLI",
1621
PersistentPreRun: func(cmd *cobra.Command, args []string) {
17-
// TODO: add more commands if needed
1822
skipCommands := []string{"login"}
1923
if slices.Contains(skipCommands, cmd.Name()) || cmd.CalledAs() == "help" {
2024
return
2125
}
2226

23-
config, err := cli.LoadConfig()
27+
cfg, err := config.Load()
2428
if err != nil {
2529
log.Fatal(err)
2630
}
2731

2832
if !config.IsUserAuthenticated() {
2933
log.Println("You're not authenticated or you token has expired. Logging in...")
34+
3035
loginCmd := cli.NewLoginCmd()
3136
loginCmd.SetArgs([]string{})
3237
if err := loginCmd.Execute(); err != nil {
3338
log.Fatal(err)
3439
}
40+
41+
// We need to reload the config since it has been updated during the login
42+
cfg, err = config.Load()
43+
if err != nil {
44+
log.Fatal(err)
45+
}
3546
}
3647

37-
s := sdk.NewSDK(config.ConnhexInstance)
48+
s := sdk.NewSDK(cfg.ConnhexInstance)
3849
cli.SetSDK(s)
3950
},
4051
}

go.mod

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,28 @@ go 1.24
44

55
require (
66
github.com/fatih/color v1.18.0
7+
github.com/mitchellh/mapstructure v1.5.0
78
github.com/spf13/cobra v1.9.1
9+
github.com/spf13/viper v1.20.1
810
golang.org/x/term v0.31.0
911
)
1012

1113
require (
14+
github.com/fsnotify/fsnotify v1.8.0 // indirect
15+
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
1216
github.com/inconshreveable/mousetrap v1.1.0 // indirect
1317
github.com/mattn/go-colorable v0.1.13 // indirect
1418
github.com/mattn/go-isatty v0.0.20 // indirect
19+
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
20+
github.com/sagikazarmark/locafero v0.7.0 // indirect
21+
github.com/sourcegraph/conc v0.3.0 // indirect
22+
github.com/spf13/afero v1.12.0 // indirect
23+
github.com/spf13/cast v1.7.1 // indirect
1524
github.com/spf13/pflag v1.0.6 // indirect
25+
github.com/subosito/gotenv v1.6.0 // indirect
26+
go.uber.org/atomic v1.9.0 // indirect
27+
go.uber.org/multierr v1.9.0 // indirect
1628
golang.org/x/sys v0.32.0 // indirect
29+
golang.org/x/text v0.21.0 // indirect
30+
gopkg.in/yaml.v3 v3.0.1 // indirect
1731
)

go.sum

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,71 @@
11
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
2+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
25
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
36
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
7+
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
8+
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
9+
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
10+
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
11+
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
12+
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
13+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
14+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
415
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
516
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
17+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
18+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
19+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
20+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
621
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
722
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
823
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
924
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
1025
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
26+
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
27+
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
28+
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
29+
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
30+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
31+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
32+
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
33+
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
1134
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
35+
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
36+
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
37+
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
38+
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
39+
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
40+
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
41+
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
42+
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
1243
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
1344
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
1445
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
1546
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
47+
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
48+
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
49+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
50+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
51+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
52+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
53+
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
54+
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
55+
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
56+
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
57+
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
58+
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
1659
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1760
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1861
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
1962
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
2063
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
2164
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
65+
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
66+
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
2267
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
68+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
69+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
70+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
2371
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)