Skip to content

Commit bcebadb

Browse files
authored
feat: Finalize workspace management (#54)
* chore: gofmt * chore: remove debugging log * fix: Issue where local config directory doesn't exist * feat: Validate API key before whoami and workspace commands * chore: Change "Verifying CLI key..." to "Verifying credentials..." * chore: Deprecate cli-key flag * docs: Add documentation about workspace local flag
1 parent 4a01477 commit bcebadb

File tree

15 files changed

+85
-37
lines changed

15 files changed

+85
-37
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,21 @@ Using profile default
243243
Logged in as Me in workspace Yet Another One
244244
```
245245

246+
You can also pin an active workspace in the current working directory with the `--local` flag.
247+
248+
```sh-session
249+
$ hookdeck workspace use --local
250+
Use the arrow keys to navigate: ↓ ↑ → ←
251+
? Select Workspace:
252+
My Workspace
253+
Another Workspace
254+
▸ Yet Another One
255+
256+
Selecting workspace Yet Another One
257+
```
258+
259+
This will create a local config file in your current directory at `myproject/.hookdeck/config.toml`. Depending on your team's Hookdeck usage and workspace setup, you may or may not want to commit this configuration file to version control.
260+
246261
## Developing
247262

248263
Build from source by running:

pkg/ansi/init_windows.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build windows
12
// +build windows
23

34
package ansi

pkg/cmd/listen.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,6 @@ func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error {
106106
}
107107

108108
return listen.Listen(url, source_alias, connection_query, listen.Flags{
109-
NoWSS: lc.noWSS,
109+
NoWSS: lc.noWSS,
110110
}, &Config)
111111
}

pkg/cmd/root.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ func init() {
9090
cobra.OnInitialize(Config.InitConfig)
9191

9292
rootCmd.PersistentFlags().StringVarP(&Config.Profile.Name, "profile", "p", "", fmt.Sprintf("profile name (default \"%s\")", hookdeck.DefaultProfileName))
93-
rootCmd.PersistentFlags().StringVar(&Config.Profile.APIKey, "cli-key", "", "Your CLI key to use for the command")
93+
rootCmd.PersistentFlags().StringVar(&Config.Profile.APIKey, "cli-key", "", "(deprecated) Your API key to use for the command")
94+
rootCmd.PersistentFlags().StringVar(&Config.Profile.APIKey, "api-key", "", "Your API key to use for the command")
9495
rootCmd.PersistentFlags().StringVar(&Config.Color, "color", "", "turn on/off color output (on, off, auto)")
9596
rootCmd.PersistentFlags().StringVar(&Config.LocalConfigFile, "config", "", "config file (default is $HOME/.config/hookdeck/config.toml)")
9697
rootCmd.PersistentFlags().StringVar(&Config.DeviceName, "device-name", "", "device name")

pkg/cmd/whoami.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ func newWhoamiCmd() *whoamiCmd {
2929
}
3030

3131
func (lc *whoamiCmd) runWhoamiCmd(cmd *cobra.Command, args []string) error {
32+
if err := Config.Profile.ValidateAPIKey(); err != nil {
33+
return err
34+
}
35+
3236
color := ansi.Color(os.Stdout)
3337

34-
fmt.Printf( "Using profile %s\n", color.Bold(Config.Profile.Name))
38+
fmt.Printf("Using profile %s\n", color.Bold(Config.Profile.Name))
3539

3640
response, err := login.ValidateKey(Config.APIBaseURL, Config.Profile.APIKey, Config.Profile.TeamID)
3741
if err != nil {

pkg/cmd/workspace_list.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ func newWorkspaceListCmd() *workspaceListCmd {
2828
return lc
2929
}
3030

31-
func (lc *workspaceListCmd) runWorkspaceListCmd(cmd *cobra.Command, args []string) error {
32-
// TODO: validate API key ??
31+
func (lc *workspaceListCmd) runWorkspaceListCmd(cmd *cobra.Command, args []string) error {
32+
if err := Config.Profile.ValidateAPIKey(); err != nil {
33+
return err
34+
}
3335

3436
workspaces, err := workspace.ListWorkspaces(&Config)
3537
if err != nil {

pkg/cmd/workspace_use.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ func newWorkspaceUseCmd() *workspaceUseCmd {
2727
return lc
2828
}
2929

30-
func (lc *workspaceUseCmd) runWorkspaceUseCmd(cmd *cobra.Command, args []string) error {
30+
func (lc *workspaceUseCmd) runWorkspaceUseCmd(cmd *cobra.Command, args []string) error {
31+
if err := Config.Profile.ValidateAPIKey(); err != nil {
32+
return err
33+
}
34+
3135
workspaces, err := workspace.ListWorkspaces(&Config)
3236
if err != nil {
3337
return err
@@ -45,8 +49,8 @@ func (lc *workspaceUseCmd) runWorkspaceUseCmd(cmd *cobra.Command, args []string)
4549
}
4650

4751
prompt := promptui.Select{
48-
Label: "Select Workspace",
49-
Items: workspaces,
52+
Label: "Select Workspace",
53+
Items: workspaces,
5054
Templates: templates,
5155
}
5256

pkg/config/config.go

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ const ColorAuto = "auto"
3131

3232
// Config handles all overall configuration for the CLI
3333
type Config struct {
34-
Profile Profile
35-
Color string
36-
LogLevel string
37-
DeviceName string
34+
Profile Profile
35+
Color string
36+
LogLevel string
37+
DeviceName string
3838

3939
// Helpers
4040
APIBaseURL string
@@ -45,9 +45,9 @@ type Config struct {
4545

4646
// Config
4747
GlobalConfigFile string
48-
GlobalConfig *viper.Viper
48+
GlobalConfig *viper.Viper
4949
LocalConfigFile string
50-
LocalConfig *viper.Viper
50+
LocalConfig *viper.Viper
5151
}
5252

5353
// GetConfigFolder retrieves the folder where the profiles file is stored
@@ -246,23 +246,34 @@ func (c *Config) RemoveAllProfiles() error {
246246
return c.GlobalConfig.WriteConfig()
247247
}
248248

249+
func (c *Config) SaveLocalConfig() error {
250+
if err := ensureDirectoy(filepath.Dir(c.LocalConfigFile)); err != nil {
251+
return err
252+
}
253+
return c.LocalConfig.WriteConfig()
254+
}
255+
256+
func ensureDirectoy(path string) error {
257+
return os.MkdirAll(path, os.ModePerm)
258+
}
259+
249260
// Construct the config struct from flags > local config > global config
250261
func (c *Config) constructConfig() {
251-
c.Color = getStringConfig([]string{c.Color , c.LocalConfig.GetString("color") , c.GlobalConfig.GetString(("color")) , "auto"})
252-
c.LogLevel = getStringConfig([]string{c.LogLevel , c.LocalConfig.GetString("log") , c.GlobalConfig.GetString(("log")) , "info"})
253-
c.APIBaseURL = getStringConfig([]string{c.APIBaseURL , c.LocalConfig.GetString("api_base") , c.GlobalConfig.GetString(("api_base")) , hookdeck.DefaultAPIBaseURL})
254-
c.DashboardBaseURL = getStringConfig([]string{c.DashboardBaseURL , c.LocalConfig.GetString("dashboard_base") , c.GlobalConfig.GetString(("dashboard_base")) , hookdeck.DefaultDashboardBaseURL})
255-
c.ConsoleBaseURL = getStringConfig([]string{c.ConsoleBaseURL , c.LocalConfig.GetString("console_base") , c.GlobalConfig.GetString(("console_base")) , hookdeck.DefaultConsoleBaseURL})
256-
c.WSBaseURL = getStringConfig([]string{c.WSBaseURL , c.LocalConfig.GetString("ws_base") , c.GlobalConfig.GetString(("ws_base")) , hookdeck.DefaultWebsocektURL})
257-
c.Profile.Name = getStringConfig([]string{c.Profile.Name , c.LocalConfig.GetString("profile") , c.GlobalConfig.GetString(("profile")) , hookdeck.DefaultProfileName})
258-
c.Profile.APIKey = getStringConfig([]string{c.Profile.APIKey , c.LocalConfig.GetString("api_key") , c.GlobalConfig.GetString((c.Profile.GetConfigField("api_key"))) , ""})
259-
c.Profile.TeamID = getStringConfig([]string{c.Profile.TeamID , c.LocalConfig.GetString("workspace_id") , c.LocalConfig.GetString("team_id") , c.GlobalConfig.GetString((c.Profile.GetConfigField("workspace_id"))) , c.GlobalConfig.GetString((c.Profile.GetConfigField("team_id"))) , ""})
260-
c.Profile.TeamMode = getStringConfig([]string{c.Profile.TeamMode , c.LocalConfig.GetString("workspace_mode") , c.LocalConfig.GetString("team_mode") , c.GlobalConfig.GetString((c.Profile.GetConfigField("workspace_mode"))) , c.GlobalConfig.GetString((c.Profile.GetConfigField("team_mode"))) , ""})
262+
c.Color = getStringConfig([]string{c.Color, c.LocalConfig.GetString("color"), c.GlobalConfig.GetString(("color")), "auto"})
263+
c.LogLevel = getStringConfig([]string{c.LogLevel, c.LocalConfig.GetString("log"), c.GlobalConfig.GetString(("log")), "info"})
264+
c.APIBaseURL = getStringConfig([]string{c.APIBaseURL, c.LocalConfig.GetString("api_base"), c.GlobalConfig.GetString(("api_base")), hookdeck.DefaultAPIBaseURL})
265+
c.DashboardBaseURL = getStringConfig([]string{c.DashboardBaseURL, c.LocalConfig.GetString("dashboard_base"), c.GlobalConfig.GetString(("dashboard_base")), hookdeck.DefaultDashboardBaseURL})
266+
c.ConsoleBaseURL = getStringConfig([]string{c.ConsoleBaseURL, c.LocalConfig.GetString("console_base"), c.GlobalConfig.GetString(("console_base")), hookdeck.DefaultConsoleBaseURL})
267+
c.WSBaseURL = getStringConfig([]string{c.WSBaseURL, c.LocalConfig.GetString("ws_base"), c.GlobalConfig.GetString(("ws_base")), hookdeck.DefaultWebsocektURL})
268+
c.Profile.Name = getStringConfig([]string{c.Profile.Name, c.LocalConfig.GetString("profile"), c.GlobalConfig.GetString(("profile")), hookdeck.DefaultProfileName})
269+
c.Profile.APIKey = getStringConfig([]string{c.Profile.APIKey, c.LocalConfig.GetString("api_key"), c.GlobalConfig.GetString((c.Profile.GetConfigField("api_key"))), ""})
270+
c.Profile.TeamID = getStringConfig([]string{c.Profile.TeamID, c.LocalConfig.GetString("workspace_id"), c.LocalConfig.GetString("team_id"), c.GlobalConfig.GetString((c.Profile.GetConfigField("workspace_id"))), c.GlobalConfig.GetString((c.Profile.GetConfigField("team_id"))), ""})
271+
c.Profile.TeamMode = getStringConfig([]string{c.Profile.TeamMode, c.LocalConfig.GetString("workspace_mode"), c.LocalConfig.GetString("team_mode"), c.GlobalConfig.GetString((c.Profile.GetConfigField("workspace_mode"))), c.GlobalConfig.GetString((c.Profile.GetConfigField("team_mode"))), ""})
261272
}
262273

263274
func getStringConfig(values []string) string {
264275
for _, str := range values {
265-
if str != "" {
276+
if str != "" {
266277
return str
267278
}
268279
}

pkg/config/profile.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package config
22

3+
import "github.com/hookdeck/hookdeck-cli/pkg/validators"
4+
35
type Profile struct {
46
Name string // profile name
5-
APIKey string
6-
TeamID string
7+
APIKey string
8+
TeamID string
79
TeamMode string
810

911
Config *Config
@@ -18,13 +20,13 @@ func (p *Profile) SaveProfile(local bool) error {
1820
// in local, we're d setting mode because it should always be inbound
1921
// as a user can't have both inbound & console teams (i think)
2022
// and we don't need to expose it to the end user
21-
if (local) {
23+
if local {
2224
p.Config.GlobalConfig.Set(p.GetConfigField("api_key"), p.APIKey)
2325
if err := p.Config.GlobalConfig.WriteConfig(); err != nil {
2426
return err
2527
}
2628
p.Config.LocalConfig.Set("workspace_id", p.TeamID)
27-
return p.Config.LocalConfig.WriteConfig()
29+
return p.Config.SaveLocalConfig()
2830
} else {
2931
p.Config.GlobalConfig.Set(p.GetConfigField("api_key"), p.APIKey)
3032
p.Config.GlobalConfig.Set(p.GetConfigField("workspace_id"), p.TeamID)
@@ -37,11 +39,11 @@ func (p *Profile) RemoveProfile() error {
3739
var err error
3840
runtimeViper := p.Config.GlobalConfig
3941

40-
runtimeViper, err = removeKey(runtimeViper, "profile");
42+
runtimeViper, err = removeKey(runtimeViper, "profile")
4143
if err != nil {
4244
return err
4345
}
44-
runtimeViper, err = removeKey(runtimeViper, p.Name);
46+
runtimeViper, err = removeKey(runtimeViper, p.Name)
4547
if err != nil {
4648
return err
4749
}
@@ -56,3 +58,10 @@ func (p *Profile) UseProfile() error {
5658
p.Config.GlobalConfig.Set("profile", p.Name)
5759
return p.Config.GlobalConfig.WriteConfig()
5860
}
61+
62+
func (p *Profile) ValidateAPIKey() error {
63+
if p.APIKey == "" {
64+
return validators.ErrAPIKeyNotConfigured
65+
}
66+
return nil
67+
}

pkg/listen/listen.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import (
3030
)
3131

3232
type Flags struct {
33-
NoWSS bool
33+
NoWSS bool
3434
}
3535

3636
// listenCmd represents the listen command
@@ -94,8 +94,6 @@ func Listen(URL *url.URL, source_alias string, connection_query string, flags Fl
9494
}
9595
fmt.Println()
9696

97-
fmt.Println(config.WSBaseURL)
98-
9997
p := proxy.New(&proxy.Config{
10098
DeviceName: config.DeviceName,
10199
Key: config.Profile.APIKey,

0 commit comments

Comments
 (0)