From 6d9033bddf90fc2ad37b3492fe4ccdb6184e2102 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Tue, 4 Jun 2024 19:33:21 +0700 Subject: [PATCH 01/33] chore: Rename variable to follow Go convention --- pkg/proxy/proxy.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go index 708325a..9442dce 100644 --- a/pkg/proxy/proxy.go +++ b/pkg/proxy/proxy.go @@ -214,14 +214,16 @@ func (p *Proxy) createSession(ctx context.Context) (hookdeck.Session, error) { TeamID: p.cfg.TeamID, } - var connection_ids []string + var connectionIDs []string for _, connection := range p.connections { - connection_ids = append(connection_ids, connection.Id) + connectionIDs = append(connectionIDs, connection.Id) } for i := 0; i <= 5; i++ { - session, err = client.CreateSession(hookdeck.CreateSessionInput{SourceId: p.source.Id, - ConnectionIds: connection_ids}) + session, err = client.CreateSession(hookdeck.CreateSessionInput{ + SourceId: p.source.Id, + ConnectionIds: connectionIDs, + }) if err == nil { return session, nil From 5d3abfec62dd98f6e75c2eb83c6d9104d0767603 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 5 Jun 2024 21:39:24 +0700 Subject: [PATCH 02/33] fix: Error message convention --- pkg/hookdeck/session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/hookdeck/session.go b/pkg/hookdeck/session.go index 8e6901f..b8d9df5 100644 --- a/pkg/hookdeck/session.go +++ b/pkg/hookdeck/session.go @@ -29,7 +29,7 @@ func (c *Client) CreateSession(input CreateSessionInput) (Session, error) { if res.StatusCode != http.StatusOK { defer res.Body.Close() body, _ := ioutil.ReadAll(res.Body) - return Session{}, fmt.Errorf("Unexpected http status code: %d %s", res.StatusCode, string(body)) + return Session{}, fmt.Errorf("unexpected http status code: %d %s", res.StatusCode, string(body)) } session := Session{} postprocessJsonResponse(res, &session) From 5c2160f5393e2c92f3e44cda080007e997ece36c Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 5 Jun 2024 22:11:25 +0700 Subject: [PATCH 03/33] feat: Display multiple sources & remove source dependency from Proxy package --- pkg/hookdeck/session.go | 1 - pkg/listen/listen.go | 49 +++++++------------------- pkg/listen/printer.go | 44 +++++++++++++++++++++++ pkg/proxy/proxy.go | 78 +++++------------------------------------ 4 files changed, 64 insertions(+), 108 deletions(-) create mode 100644 pkg/listen/printer.go diff --git a/pkg/hookdeck/session.go b/pkg/hookdeck/session.go index b8d9df5..31acda2 100644 --- a/pkg/hookdeck/session.go +++ b/pkg/hookdeck/session.go @@ -13,7 +13,6 @@ type Session struct { } type CreateSessionInput struct { - SourceId string `json:"source_id"` ConnectionIds []string `json:"webhook_ids"` } diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index 65c41f8..a098c32 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -21,10 +21,10 @@ import ( "net/url" "regexp" - "github.com/hookdeck/hookdeck-cli/pkg/ansi" "github.com/hookdeck/hookdeck-cli/pkg/config" "github.com/hookdeck/hookdeck-cli/pkg/login" "github.com/hookdeck/hookdeck-cli/pkg/proxy" + hookdecksdk "github.com/hookdeck/hookdeck-go-sdk" log "github.com/sirupsen/logrus" ) @@ -33,23 +33,24 @@ type Flags struct { } // listenCmd represents the listen command -func Listen(URL *url.URL, source_alias string, connectionQuery string, flags Flags, config *config.Config) error { +func Listen(URL *url.URL, sourceAlias string, connectionQuery string, flags Flags, config *config.Config) error { var err error - var guest_url string + var guestURL string if config.Profile.APIKey == "" { - guest_url, err = login.GuestLogin(config) - if guest_url == "" { + guestURL, err = login.GuestLogin(config) + if guestURL == "" { return err } } sdkClient := config.GetClient() - source, err := getSource(sdkClient, source_alias) + source, err := getSource(sdkClient, sourceAlias) if err != nil { return err } + sources := []*hookdecksdk.Source{source} connections, err := getConnections(sdkClient, source, connectionQuery) if err != nil { @@ -57,37 +58,11 @@ func Listen(URL *url.URL, source_alias string, connectionQuery string, flags Fla } fmt.Println() - fmt.Println(ansi.Bold("Dashboard")) - if guest_url != "" { - fmt.Println("👤 Console URL: " + guest_url) - fmt.Println("Sign up in the Console to make your webhook URL permanent.") - fmt.Println() - } else { - var url = config.DashboardBaseURL - if config.Profile.TeamID != "" { - url += "?team_id=" + config.Profile.TeamID - } - if config.Profile.TeamMode == "console" { - url = config.ConsoleBaseURL + "?source_id=" + source.Id - } - fmt.Println("👉 Inspect and replay events: " + url) - fmt.Println() - } - - fmt.Println(ansi.Bold(source.Name + " Source")) - fmt.Println("🔌 Event URL: " + source.Url) + printDashboardInformation(config, guestURL) fmt.Println() - - fmt.Println(ansi.Bold("Connections")) - for _, connection := range connections { - var connectionName string - if connection.Name != nil { - connectionName = *connection.Name - } else { - connectionName = connection.Destination.Name - } - fmt.Println(connectionName + " forwarding to " + *connection.Destination.CliPath) - } + printSources(config, sources) + fmt.Println() + printConnections(config, connections) fmt.Println() p := proxy.New(&proxy.Config{ @@ -103,7 +78,7 @@ func Listen(URL *url.URL, source_alias string, connectionQuery string, flags Fla URL: URL, Log: log.StandardLogger(), Insecure: config.Insecure, - }, source, connections) + }, connections) err = p.Run(context.Background()) if err != nil { diff --git a/pkg/listen/printer.go b/pkg/listen/printer.go new file mode 100644 index 0000000..404aece --- /dev/null +++ b/pkg/listen/printer.go @@ -0,0 +1,44 @@ +package listen + +import ( + "fmt" + + "github.com/hookdeck/hookdeck-cli/pkg/ansi" + "github.com/hookdeck/hookdeck-cli/pkg/config" + hookdecksdk "github.com/hookdeck/hookdeck-go-sdk" +) + +func printDashboardInformation(config *config.Config, guestURL string) { + fmt.Println(ansi.Bold("Dashboard")) + if guestURL != "" { + fmt.Println("👤 Console URL: " + guestURL) + fmt.Println("Sign up in the Console to make your webhook URL permanent.") + fmt.Println() + } else { + var url = config.DashboardBaseURL + if config.Profile.TeamID != "" { + url += "?team_id=" + config.Profile.TeamID + } + if config.Profile.TeamMode == "console" { + // TODO: how should we display this if there are multiple sources? + // url = config.ConsoleBaseURL + "?source_id=" + source.Id + url = config.ConsoleBaseURL + } + fmt.Println("👉 Inspect and replay events: " + url) + } +} + +func printSources(config *config.Config, sources []*hookdecksdk.Source) { + fmt.Println(ansi.Bold("Sources")) + + for _, source := range sources { + fmt.Printf("🔌 %s URL: %s\n", source.Name, source.Url) + } +} + +func printConnections(config *config.Config, connections []*hookdecksdk.Connection) { + fmt.Println(ansi.Bold("Connections")) + for _, connection := range connections { + fmt.Println(*connection.FullName + " forwarding to " + *connection.Destination.CliPath) + } +} diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go index 9442dce..b07a1fd 100644 --- a/pkg/proxy/proxy.go +++ b/pkg/proxy/proxy.go @@ -56,12 +56,10 @@ type Config struct { // webhook events, forwards them to the local endpoint and sends the response // back to Hookdeck. type Proxy struct { - cfg *Config - source *hookdecksdk.Source - connections []*hookdecksdk.Connection - connections_paths map[string]string - webSocketClient *websocket.Client - connectionTimer *time.Timer + cfg *Config + connections []*hookdecksdk.Connection + webSocketClient *websocket.Client + connectionTimer *time.Timer } func withSIGTERMCancel(ctx context.Context, onCancel func()) context.Context { @@ -221,7 +219,6 @@ func (p *Proxy) createSession(ctx context.Context) (hookdeck.Session, error) { for i := 0; i <= 5; i++ { session, err = client.CreateSession(hookdeck.CreateSessionInput{ - SourceId: p.source.Id, ConnectionIds: connectionIDs, }) @@ -345,8 +342,6 @@ func (p *Proxy) processEndpointResponse(webhookEvent *websocket.Attempt, resp *h return } - // body := truncate(string(buf), 5000, true) - if p.webSocketClient != nil { p.webSocketClient.SendMessage(&websocket.OutgoingMessage{ AttemptResponse: &websocket.AttemptResponse{ @@ -366,73 +361,16 @@ func (p *Proxy) processEndpointResponse(webhookEvent *websocket.Attempt, resp *h // // New creates a new Proxy -func New(cfg *Config, source *hookdecksdk.Source, connections []*hookdecksdk.Connection) *Proxy { +func New(cfg *Config, connections []*hookdecksdk.Connection) *Proxy { if cfg.Log == nil { cfg.Log = &log.Logger{Out: ioutil.Discard} } - connections_paths := make(map[string]string) - - for _, connection := range connections { - connections_paths[connection.Id] = *connection.Destination.CliPath - } - p := &Proxy{ - cfg: cfg, - connections: connections, - connections_paths: connections_paths, - source: source, - connectionTimer: time.NewTimer(0), // Defaults to no delay + cfg: cfg, + connections: connections, + connectionTimer: time.NewTimer(0), // Defaults to no delay } return p } - -// -// Private constants -// - -const ( - maxBodySize = 5000 - maxNumHeaders = 20 - maxHeaderKeySize = 50 - maxHeaderValueSize = 200 -) - -// -// Private functions -// - -// truncate will truncate str to be less than or equal to maxByteLength bytes. -// It will respect UTF8 and truncate the string at a code point boundary. -// If ellipsis is true, we'll append "..." to the truncated string if the string -// was in fact truncated, and if there's enough room. Note that the -// full string returned will always be <= maxByteLength bytes long, even with ellipsis. -func truncate(str string, maxByteLength int, ellipsis bool) string { - if len(str) <= maxByteLength { - return str - } - - bytes := []byte(str) - - if ellipsis && maxByteLength > 3 { - maxByteLength -= 3 - } else { - ellipsis = false - } - - for maxByteLength > 0 && maxByteLength < len(bytes) && isUTF8ContinuationByte(bytes[maxByteLength]) { - maxByteLength-- - } - - result := string(bytes[0:maxByteLength]) - if ellipsis { - result += "..." - } - - return result -} - -func isUTF8ContinuationByte(b byte) bool { - return (b & 0xC0) == 0x80 -} From 0b584355d8832c0cdbfc0baf8b17dc86fe52c313 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 5 Jun 2024 22:27:39 +0700 Subject: [PATCH 04/33] chore: Remove comment --- pkg/listen/printer.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/listen/printer.go b/pkg/listen/printer.go index 404aece..4b290d4 100644 --- a/pkg/listen/printer.go +++ b/pkg/listen/printer.go @@ -20,8 +20,6 @@ func printDashboardInformation(config *config.Config, guestURL string) { url += "?team_id=" + config.Profile.TeamID } if config.Profile.TeamMode == "console" { - // TODO: how should we display this if there are multiple sources? - // url = config.ConsoleBaseURL + "?source_id=" + source.Id url = config.ConsoleBaseURL } fmt.Println("👉 Inspect and replay events: " + url) From 3858dab5acc6c91e36c28ccff5bb26c02c5d58e0 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 5 Jun 2024 23:08:36 +0700 Subject: [PATCH 05/33] feat: Support query multiple sources --- pkg/cmd/listen.go | 13 +++++-- pkg/listen/connection.go | 4 ++- pkg/listen/listen.go | 8 ++--- pkg/listen/source.go | 73 +++++++++++++++++++++++++++------------- 4 files changed, 65 insertions(+), 33 deletions(-) diff --git a/pkg/cmd/listen.go b/pkg/cmd/listen.go index d770d42..8edd826 100644 --- a/pkg/cmd/listen.go +++ b/pkg/cmd/listen.go @@ -88,9 +88,9 @@ func newListenCmd() *listenCmd { // listenCmd represents the listen command func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error { - var sourceAlias, connectionQuery string + var sourceQueryString, connectionQuery string if len(args) > 1 { - sourceAlias = args[1] + sourceQueryString = args[1] } if len(args) > 2 { connectionQuery = args[2] @@ -112,7 +112,14 @@ func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error { url.Scheme = "http" } - return listen.Listen(url, sourceAlias, connectionQuery, listen.Flags{ + var sourceQuery []string + if sourceQueryString == "" { + sourceQuery = []string{} + } else { + sourceQuery = strings.Split(sourceQueryString, ",") + } + + return listen.Listen(url, sourceQuery, connectionQuery, listen.Flags{ NoWSS: lc.noWSS, }, &Config) } diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index ba9f02e..c81ec13 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -12,7 +12,9 @@ import ( hookdeckclient "github.com/hookdeck/hookdeck-go-sdk/client" ) -func getConnections(client *hookdeckclient.Client, source *hookdecksdk.Source, connectionQuery string) ([]*hookdecksdk.Connection, error) { +func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source, connectionQuery string) ([]*hookdecksdk.Connection, error) { + source := sources[0] + // TODO: Filter connections using connectionQuery var connections []*hookdecksdk.Connection connectionList, err := client.Connection.List(context.Background(), &hookdecksdk.ConnectionListRequest{ diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index a098c32..44a4f86 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -24,7 +24,6 @@ import ( "github.com/hookdeck/hookdeck-cli/pkg/config" "github.com/hookdeck/hookdeck-cli/pkg/login" "github.com/hookdeck/hookdeck-cli/pkg/proxy" - hookdecksdk "github.com/hookdeck/hookdeck-go-sdk" log "github.com/sirupsen/logrus" ) @@ -33,7 +32,7 @@ type Flags struct { } // listenCmd represents the listen command -func Listen(URL *url.URL, sourceAlias string, connectionQuery string, flags Flags, config *config.Config) error { +func Listen(URL *url.URL, sourceAliases []string, connectionQuery string, flags Flags, config *config.Config) error { var err error var guestURL string @@ -46,13 +45,12 @@ func Listen(URL *url.URL, sourceAlias string, connectionQuery string, flags Flag sdkClient := config.GetClient() - source, err := getSource(sdkClient, sourceAlias) + sources, err := getSources(sdkClient, sourceAliases) if err != nil { return err } - sources := []*hookdecksdk.Source{source} - connections, err := getConnections(sdkClient, source, connectionQuery) + connections, err := getConnections(sdkClient, sources, connectionQuery) if err != nil { return err } diff --git a/pkg/listen/source.go b/pkg/listen/source.go index c5039df..d5b5669 100644 --- a/pkg/listen/source.go +++ b/pkg/listen/source.go @@ -2,6 +2,7 @@ package listen import ( "context" + "errors" "fmt" "github.com/AlecAivazis/survey/v2" @@ -10,27 +11,31 @@ import ( hookdeckclient "github.com/hookdeck/hookdeck-go-sdk/client" ) -func getSource(sdkClient *hookdeckclient.Client, source_alias string) (*hookdecksdk.Source, error) { +func getSources(sdkClient *hookdeckclient.Client, sourceQuery []string) ([]*hookdecksdk.Source, error) { + limit := 100 var source *hookdecksdk.Source - if source_alias != "" { - sources, _ := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{ - Name: &source_alias, + if len(sourceQuery) == 1 && sourceQuery[0] == "*" { + sources, err := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{ + Limit: &limit, }) - if *sources.Count > 0 { - source = sources.Models[0] + if err != nil { + return []*hookdecksdk.Source{}, err } - if source == nil { - // TODO: Prompt here? - source, _ = sdkClient.Source.Create(context.Background(), &hookdecksdk.SourceCreateRequest{ - Name: slug.Make(source_alias), - }) + if sources == nil || *sources.Count == 0 { + return []*hookdecksdk.Source{}, errors.New("unable to find any matching sources") } + return sources.Models, nil + } else if len(sourceQuery) > 0 { + return listMultipleSources(sdkClient, sourceQuery) } else { - sources, _ := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{}) - if *sources.Count > 0 { - var sources_alias []string - for _, temp_source := range sources.Models { - sources_alias = append(sources_alias, temp_source.Name) + sources := []*hookdecksdk.Source{} + availableSources, _ := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{ + Limit: &limit, + }) + if *availableSources.Count > 0 { + var sourceAliases []string + for _, temp_source := range availableSources.Models { + sourceAliases = append(sourceAliases, temp_source.Name) } answers := struct { @@ -42,7 +47,7 @@ func getSource(sdkClient *hookdeckclient.Client, source_alias string) (*hookdeck Name: "source", Prompt: &survey.Select{ Message: "Select a source", - Options: append(sources_alias, "Create new source"), + Options: append(sourceAliases, "Create new source"), }, }, } @@ -50,19 +55,19 @@ func getSource(sdkClient *hookdeckclient.Client, source_alias string) (*hookdeck err := survey.Ask(qs, &answers) if err != nil { fmt.Println(err.Error()) - return source, err + return []*hookdecksdk.Source{}, err } if answers.SourceAlias != "Create new source" { - for _, temp_source := range sources.Models { - if temp_source.Name == answers.SourceAlias { - source = temp_source + for _, currentSource := range availableSources.Models { + if currentSource.Name == answers.SourceAlias { + sources = append(sources, currentSource) } } } } - if source == nil { + if len(sources) == 0 { answers := struct { Label string `survey:"label"` // or you can tag fields to match a specific name }{} @@ -76,13 +81,33 @@ func getSource(sdkClient *hookdeckclient.Client, source_alias string) (*hookdeck err := survey.Ask(qs, &answers) if err != nil { - return source, err + return []*hookdecksdk.Source{}, err } source, _ = sdkClient.Source.Create(context.Background(), &hookdecksdk.SourceCreateRequest{ Name: slug.Make(answers.Label), }) + sources = append(sources, source) + } + + return sources, nil + } +} + +func listMultipleSources(sdkClient *hookdeckclient.Client, sourceQuery []string) ([]*hookdecksdk.Source, error) { + sources := []*hookdecksdk.Source{} + + for _, sourceName := range sourceQuery { + sourceQuery, err := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{ + Name: &sourceName, + }) + if err != nil { + return []*hookdecksdk.Source{}, err + } + if len(sourceQuery.Models) > 0 { + sources = append(sources, sourceQuery.Models[0]) } } - return source, nil + + return sources, nil } From ebc8c8d802828dcacaf8996c786ecf562454e5ea Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 5 Jun 2024 23:19:50 +0700 Subject: [PATCH 06/33] feat: Get all connections --- pkg/listen/connection.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index c81ec13..9b9db6d 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -13,8 +13,20 @@ import ( ) func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source, connectionQuery string) ([]*hookdecksdk.Connection, error) { - source := sources[0] + connections := []*hookdecksdk.Connection{} + for _, source := range sources { + sourceConnections, err := getConnectionsPerSource(client, source, connectionQuery) + if err != nil { + return []*hookdecksdk.Connection{}, nil + } + connections = append(connections, sourceConnections...) + } + + return connections, nil +} + +func getConnectionsPerSource(client *hookdeckclient.Client, source *hookdecksdk.Source, connectionQuery string) ([]*hookdecksdk.Connection, error) { // TODO: Filter connections using connectionQuery var connections []*hookdecksdk.Connection connectionList, err := client.Connection.List(context.Background(), &hookdecksdk.ConnectionListRequest{ From 5264da4f50069e6f4ee3dbe903cbb47a9c4368c6 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Thu, 6 Jun 2024 13:54:38 +0700 Subject: [PATCH 07/33] chore: Temporarily enforce limit of 10 sources --- pkg/cmd/listen.go | 5 +++++ pkg/listen/source.go | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/listen.go b/pkg/cmd/listen.go index 8edd826..fc8393d 100644 --- a/pkg/cmd/listen.go +++ b/pkg/cmd/listen.go @@ -119,6 +119,11 @@ func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error { sourceQuery = strings.Split(sourceQueryString, ",") } + // TODO: remove once we can support better limit + if len(sourceQuery) > 10 { + return errors.New("max 10 sources supported") + } + return listen.Listen(url, sourceQuery, connectionQuery, listen.Flags{ NoWSS: lc.noWSS, }, &Config) diff --git a/pkg/listen/source.go b/pkg/listen/source.go index d5b5669..49f2872 100644 --- a/pkg/listen/source.go +++ b/pkg/listen/source.go @@ -15,8 +15,10 @@ func getSources(sdkClient *hookdeckclient.Client, sourceQuery []string) ([]*hook limit := 100 var source *hookdecksdk.Source if len(sourceQuery) == 1 && sourceQuery[0] == "*" { + // TODO: remove once we can support better limit + temporaryLimit := 10 sources, err := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{ - Limit: &limit, + Limit: &temporaryLimit, }) if err != nil { return []*hookdecksdk.Source{}, err From daa741455facc1694bab49bb0f7c5f9ac1ccfa32 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Thu, 6 Jun 2024 14:51:32 +0700 Subject: [PATCH 08/33] refactor: Source query logic --- pkg/listen/source.go | 181 +++++++++++++++++++++++++++++++------------ 1 file changed, 131 insertions(+), 50 deletions(-) diff --git a/pkg/listen/source.go b/pkg/listen/source.go index 49f2872..5ef4803 100644 --- a/pkg/listen/source.go +++ b/pkg/listen/source.go @@ -11,9 +11,26 @@ import ( hookdeckclient "github.com/hookdeck/hookdeck-go-sdk/client" ) +// There are 4 cases: +// +// 1. search all sources (query string == '*') +// 2. search multiple sources +// 3. search 1 source +// 4. no specific source +// +// For case 1 & 2, we'll simply query the sources data and return. +// If no source is found, we'll show an error message and exit. +// +// For case 3, we'll search for the 1 source. +// If that source is not found, we'll create it and move forward. +// +// For case 4, we'll get available sources and ask the user which ones +// they'd like to use. They will also have an option to create a new source. + func getSources(sdkClient *hookdeckclient.Client, sourceQuery []string) ([]*hookdecksdk.Source, error) { limit := 100 - var source *hookdecksdk.Source + + // case 1 if len(sourceQuery) == 1 && sourceQuery[0] == "*" { // TODO: remove once we can support better limit temporaryLimit := 10 @@ -26,73 +43,59 @@ func getSources(sdkClient *hookdeckclient.Client, sourceQuery []string) ([]*hook if sources == nil || *sources.Count == 0 { return []*hookdecksdk.Source{}, errors.New("unable to find any matching sources") } - return sources.Models, nil - } else if len(sourceQuery) > 0 { - return listMultipleSources(sdkClient, sourceQuery) + return validateSources(sources.Models) + + // case 2 + } else if len(sourceQuery) > 1 { + searchedSources, err := listMultipleSources(sdkClient, sourceQuery) + if err != nil { + return []*hookdecksdk.Source{}, err + } + return validateSources(searchedSources) + + // case 3 + } else if len(sourceQuery) == 1 { + searchedSources, err := listMultipleSources(sdkClient, sourceQuery) + if err != nil { + return []*hookdecksdk.Source{}, err + } + if len(searchedSources) > 0 { + return validateSources(searchedSources) + } + + // Create source with provided name + source, err := createSource(sdkClient, &sourceQuery[0]) + if err != nil { + return []*hookdecksdk.Source{}, err + } + + return validateSources([]*hookdecksdk.Source{source}) + + // case 4 } else { sources := []*hookdecksdk.Source{} + availableSources, _ := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{ Limit: &limit, }) - if *availableSources.Count > 0 { - var sourceAliases []string - for _, temp_source := range availableSources.Models { - sourceAliases = append(sourceAliases, temp_source.Name) - } - - answers := struct { - SourceAlias string `survey:"source"` - }{} - - var qs = []*survey.Question{ - { - Name: "source", - Prompt: &survey.Select{ - Message: "Select a source", - Options: append(sourceAliases, "Create new source"), - }, - }, - } - err := survey.Ask(qs, &answers) + if *availableSources.Count > 0 { + selectedSources, err := selectSources(availableSources.Models) if err != nil { - fmt.Println(err.Error()) return []*hookdecksdk.Source{}, err } - - if answers.SourceAlias != "Create new source" { - for _, currentSource := range availableSources.Models { - if currentSource.Name == answers.SourceAlias { - sources = append(sources, currentSource) - } - } - } + sources = append(sources, selectedSources...) } if len(sources) == 0 { - answers := struct { - Label string `survey:"label"` // or you can tag fields to match a specific name - }{} - var qs = []*survey.Question{ - { - Name: "label", - Prompt: &survey.Input{Message: "What should be your new source label?"}, - Validate: survey.Required, - }, - } - - err := survey.Ask(qs, &answers) + source, err := createSource(sdkClient, nil) if err != nil { return []*hookdecksdk.Source{}, err } - - source, _ = sdkClient.Source.Create(context.Background(), &hookdecksdk.SourceCreateRequest{ - Name: slug.Make(answers.Label), - }) sources = append(sources, source) } - return sources, nil + return validateSources(sources) } } @@ -113,3 +116,81 @@ func listMultipleSources(sdkClient *hookdeckclient.Client, sourceQuery []string) return sources, nil } + +func selectSources(availableSources []*hookdecksdk.Source) ([]*hookdecksdk.Source, error) { + sources := []*hookdecksdk.Source{} + + var sourceAliases []string + for _, temp_source := range availableSources { + sourceAliases = append(sourceAliases, temp_source.Name) + } + + answers := struct { + SourceAlias string `survey:"source"` + }{} + + var qs = []*survey.Question{ + { + Name: "source", + Prompt: &survey.Select{ + Message: "Select a source", + Options: append(sourceAliases, "Create new source"), + }, + }, + } + + err := survey.Ask(qs, &answers) + if err != nil { + fmt.Println(err.Error()) + return []*hookdecksdk.Source{}, err + } + + if answers.SourceAlias != "Create new source" { + for _, currentSource := range availableSources { + if currentSource.Name == answers.SourceAlias { + sources = append(sources, currentSource) + } + } + } + + return sources, nil +} + +func createSource(sdkClient *hookdeckclient.Client, name *string) (*hookdecksdk.Source, error) { + var sourceName string + + if name != nil { + sourceName = *name + } else { + answers := struct { + Label string `survey:"label"` // or you can tag fields to match a specific name + }{} + var qs = []*survey.Question{ + { + Name: "label", + Prompt: &survey.Input{Message: "What should be your new source label?"}, + Validate: survey.Required, + }, + } + + err := survey.Ask(qs, &answers) + if err != nil { + return nil, err + } + sourceName = answers.Label + } + + source, err := sdkClient.Source.Create(context.Background(), &hookdecksdk.SourceCreateRequest{ + Name: slug.Make(sourceName), + }) + + return source, err +} + +func validateSources(sources []*hookdecksdk.Source) ([]*hookdecksdk.Source, error) { + if len(sources) == 0 { + return []*hookdecksdk.Source{}, errors.New("unable to find any matching sources") + } + + return sources, nil +} From 43493c8b4535e98ad43856d91354d6c7a3225d8a Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Thu, 6 Jun 2024 15:02:49 +0700 Subject: [PATCH 09/33] chore: Add data validation before starting proxy --- pkg/listen/listen.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index 44a4f86..69e3eee 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -17,6 +17,7 @@ package listen import ( "context" + "errors" "fmt" "net/url" "regexp" @@ -24,6 +25,7 @@ import ( "github.com/hookdeck/hookdeck-cli/pkg/config" "github.com/hookdeck/hookdeck-cli/pkg/login" "github.com/hookdeck/hookdeck-cli/pkg/proxy" + hookdecksdk "github.com/hookdeck/hookdeck-go-sdk" log "github.com/sirupsen/logrus" ) @@ -45,6 +47,8 @@ func Listen(URL *url.URL, sourceAliases []string, connectionQuery string, flags sdkClient := config.GetClient() + // Prepare data + sources, err := getSources(sdkClient, sourceAliases) if err != nil { return err @@ -55,6 +59,12 @@ func Listen(URL *url.URL, sourceAliases []string, connectionQuery string, flags return err } + if err := validateData(sources, connections); err != nil { + return err + } + + // Start proxy + fmt.Println() printDashboardInformation(config, guestURL) fmt.Println() @@ -90,3 +100,11 @@ func isPath(value string) (bool, error) { is_path, err := regexp.MatchString(`^(\/)+([/a-zA-Z0-9-_%\.\-\_\~\!\$\&\'\(\)\*\+\,\;\=\:\@]*)$`, value) return is_path, err } + +func validateData(sources []*hookdecksdk.Source, connections []*hookdecksdk.Connection) error { + if len(connections) == 0 { + return errors.New("no connections provided") + } + + return nil +} From 1145fe2af46a73ee4270dfab0859042799f3bc0f Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Thu, 6 Jun 2024 15:07:21 +0700 Subject: [PATCH 10/33] fix: Skip connection creation when proxying multiple sources --- pkg/listen/connection.go | 8 ++++---- pkg/listen/listen.go | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index 9b9db6d..5041ede 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -12,11 +12,11 @@ import ( hookdeckclient "github.com/hookdeck/hookdeck-go-sdk/client" ) -func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source, connectionQuery string) ([]*hookdecksdk.Connection, error) { +func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source, connectionQuery string, isMultiSource bool) ([]*hookdecksdk.Connection, error) { connections := []*hookdecksdk.Connection{} for _, source := range sources { - sourceConnections, err := getConnectionsPerSource(client, source, connectionQuery) + sourceConnections, err := getConnectionsPerSource(client, source, connectionQuery, isMultiSource) if err != nil { return []*hookdecksdk.Connection{}, nil } @@ -26,7 +26,7 @@ func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source return connections, nil } -func getConnectionsPerSource(client *hookdeckclient.Client, source *hookdecksdk.Source, connectionQuery string) ([]*hookdecksdk.Connection, error) { +func getConnectionsPerSource(client *hookdeckclient.Client, source *hookdecksdk.Source, connectionQuery string, isMultiSource bool) ([]*hookdecksdk.Connection, error) { // TODO: Filter connections using connectionQuery var connections []*hookdecksdk.Connection connectionList, err := client.Connection.List(context.Background(), &hookdecksdk.ConnectionListRequest{ @@ -59,7 +59,7 @@ func getConnectionsPerSource(client *hookdeckclient.Client, source *hookdecksdk. connections = filteredConnections } - if len(connections) == 0 { + if len(connections) == 0 && !isMultiSource { answers := struct { Label string `survey:"label"` Path string `survey:"path"` diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index 69e3eee..3316504 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -38,6 +38,8 @@ func Listen(URL *url.URL, sourceAliases []string, connectionQuery string, flags var err error var guestURL string + isMultiSource := len(sourceAliases) > 1 || (len(sourceAliases) == 1 && sourceAliases[0] == "*") + if config.Profile.APIKey == "" { guestURL, err = login.GuestLogin(config) if guestURL == "" { @@ -54,7 +56,7 @@ func Listen(URL *url.URL, sourceAliases []string, connectionQuery string, flags return err } - connections, err := getConnections(sdkClient, sources, connectionQuery) + connections, err := getConnections(sdkClient, sources, connectionQuery, isMultiSource) if err != nil { return err } From 67204145bc3b83be8fd0ccd4e028d3a0644ff657 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Thu, 6 Jun 2024 21:26:26 +0700 Subject: [PATCH 11/33] feat: Only show relevant sources --- pkg/listen/listen.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index 3316504..b9922b4 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -61,6 +61,8 @@ func Listen(URL *url.URL, sourceAliases []string, connectionQuery string, flags return err } + sources = getRelevantSources(sources, connections) + if err := validateData(sources, connections); err != nil { return err } @@ -110,3 +112,21 @@ func validateData(sources []*hookdecksdk.Source, connections []*hookdecksdk.Conn return nil } + +func getRelevantSources(sources []*hookdecksdk.Source, connections []*hookdecksdk.Connection) []*hookdecksdk.Source { + relevantSourceId := map[string]bool{} + + for _, connection := range connections { + relevantSourceId[connection.Source.Id] = true + } + + relevantSources := []*hookdecksdk.Source{} + + for _, source := range sources { + if relevantSourceId[source.Id] { + relevantSources = append(relevantSources, source) + } + } + + return relevantSources +} From bcca2d45151c2c9e4aeed072f6976069a8a1e6a0 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Thu, 6 Jun 2024 21:30:37 +0700 Subject: [PATCH 12/33] refactor: Move source query parsing logic to listen package --- pkg/cmd/listen.go | 16 ++-------------- pkg/listen/listen.go | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/pkg/cmd/listen.go b/pkg/cmd/listen.go index fc8393d..87da4b7 100644 --- a/pkg/cmd/listen.go +++ b/pkg/cmd/listen.go @@ -88,9 +88,9 @@ func newListenCmd() *listenCmd { // listenCmd represents the listen command func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error { - var sourceQueryString, connectionQuery string + var sourceQuery, connectionQuery string if len(args) > 1 { - sourceQueryString = args[1] + sourceQuery = args[1] } if len(args) > 2 { connectionQuery = args[2] @@ -112,18 +112,6 @@ func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error { url.Scheme = "http" } - var sourceQuery []string - if sourceQueryString == "" { - sourceQuery = []string{} - } else { - sourceQuery = strings.Split(sourceQueryString, ",") - } - - // TODO: remove once we can support better limit - if len(sourceQuery) > 10 { - return errors.New("max 10 sources supported") - } - return listen.Listen(url, sourceQuery, connectionQuery, listen.Flags{ NoWSS: lc.noWSS, }, &Config) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index b9922b4..6350bf1 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -21,6 +21,7 @@ import ( "fmt" "net/url" "regexp" + "strings" "github.com/hookdeck/hookdeck-cli/pkg/config" "github.com/hookdeck/hookdeck-cli/pkg/login" @@ -34,10 +35,15 @@ type Flags struct { } // listenCmd represents the listen command -func Listen(URL *url.URL, sourceAliases []string, connectionQuery string, flags Flags, config *config.Config) error { +func Listen(URL *url.URL, sourceQuery string, connectionQuery string, flags Flags, config *config.Config) error { var err error var guestURL string + sourceAliases, err := parseSourceQuery(sourceQuery) + if err != nil { + return err + } + isMultiSource := len(sourceAliases) > 1 || (len(sourceAliases) == 1 && sourceAliases[0] == "*") if config.Profile.APIKey == "" { @@ -100,6 +106,22 @@ func Listen(URL *url.URL, sourceAliases []string, connectionQuery string, flags return nil } +func parseSourceQuery(sourceQuery string) ([]string, error) { + var sourceAliases []string + if sourceQuery == "" { + sourceAliases = []string{} + } else { + sourceAliases = strings.Split(sourceQuery, ",") + } + + // TODO: remove once we can support better limit + if len(sourceAliases) > 10 { + return []string{}, errors.New("max 10 sources supported") + } + + return sourceAliases, nil +} + func isPath(value string) (bool, error) { is_path, err := regexp.MatchString(`^(\/)+([/a-zA-Z0-9-_%\.\-\_\~\!\$\&\'\(\)\*\+\,\;\=\:\@]*)$`, value) return is_path, err From 63025c955217bf7c701a5d12b58c17edc1088273 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Thu, 6 Jun 2024 21:34:40 +0700 Subject: [PATCH 13/33] feat: Improve source query parsing --- pkg/listen/listen.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index 6350bf1..1be1aad 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -110,8 +110,16 @@ func parseSourceQuery(sourceQuery string) ([]string, error) { var sourceAliases []string if sourceQuery == "" { sourceAliases = []string{} - } else { + } else if strings.Contains(sourceQuery, ",") { sourceAliases = strings.Split(sourceQuery, ",") + } else if strings.Contains(sourceQuery, " ") { + sourceAliases = strings.Split(sourceQuery, " ") + } else { + sourceAliases = append(sourceAliases, sourceQuery) + } + + for i := range sourceAliases { + sourceAliases[i] = strings.TrimSpace(sourceAliases[i]) } // TODO: remove once we can support better limit From 830f17b1a4f4992f6b479518502906b5906365f7 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Fri, 7 Jun 2024 21:52:38 +0700 Subject: [PATCH 14/33] chore: Remove unnecessary comment --- pkg/listen/connection.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index 5041ede..2770136 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -27,7 +27,6 @@ func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source } func getConnectionsPerSource(client *hookdeckclient.Client, source *hookdecksdk.Source, connectionQuery string, isMultiSource bool) ([]*hookdecksdk.Connection, error) { - // TODO: Filter connections using connectionQuery var connections []*hookdecksdk.Connection connectionList, err := client.Connection.List(context.Background(), &hookdecksdk.ConnectionListRequest{ SourceId: &source.Id, From 2bc54c57d04defe230ea570c752554c0fe3e8e4a Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Fri, 7 Jun 2024 21:55:20 +0700 Subject: [PATCH 15/33] chore: Handle error --- pkg/listen/source.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/listen/source.go b/pkg/listen/source.go index 5ef4803..6593ad8 100644 --- a/pkg/listen/source.go +++ b/pkg/listen/source.go @@ -75,10 +75,14 @@ func getSources(sdkClient *hookdeckclient.Client, sourceQuery []string) ([]*hook } else { sources := []*hookdecksdk.Source{} - availableSources, _ := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{ + availableSources, err := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{ Limit: &limit, }) + if err != nil { + return []*hookdecksdk.Source{}, err + } + if *availableSources.Count > 0 { selectedSources, err := selectSources(availableSources.Models) if err != nil { From 8b417168bf03db4ed03c8874e56fd844a1721036 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 10 Jul 2024 19:24:35 +0700 Subject: [PATCH 16/33] chore: Upgrade Hookdeck Go SDK to v0.4.1 --- go.mod | 2 +- go.sum | 2 ++ pkg/listen/connection.go | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 5868e38..5d1eccc 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hookdeck/hookdeck-go-sdk v0.0.37 // indirect + github.com/hookdeck/hookdeck-go-sdk v0.4.1 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kr/pty v1.1.8 // indirect diff --git a/go.sum b/go.sum index a33bb0e..95f2ee9 100644 --- a/go.sum +++ b/go.sum @@ -128,6 +128,8 @@ github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDG github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hookdeck/hookdeck-go-sdk v0.0.37 h1:Y+QnwsWuJ6KMkpY2qJZDeGzcKc4GkzBrRaEnIb8zimc= github.com/hookdeck/hookdeck-go-sdk v0.0.37/go.mod h1:kfFn3/WEGcxuPkaaf8lAq9L+3nYg45GwGy4utH/Tnmg= +github.com/hookdeck/hookdeck-go-sdk v0.4.1 h1:r/rZJeBuDq31amTIB1LDHkA5lTAG2jAmZGqhgHRYKy8= +github.com/hookdeck/hookdeck-go-sdk v0.4.1/go.mod h1:kfFn3/WEGcxuPkaaf8lAq9L+3nYg45GwGy4utH/Tnmg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index 2770136..f415f08 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -29,7 +29,7 @@ func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source func getConnectionsPerSource(client *hookdeckclient.Client, source *hookdecksdk.Source, connectionQuery string, isMultiSource bool) ([]*hookdecksdk.Connection, error) { var connections []*hookdecksdk.Connection connectionList, err := client.Connection.List(context.Background(), &hookdecksdk.ConnectionListRequest{ - SourceId: &source.Id, + SourceId: []*string{&source.Id}, }) if err != nil { return nil, err From aed35e65ba1c0b581ec404ca550f5254fc598a27 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 10 Jul 2024 19:28:48 +0700 Subject: [PATCH 17/33] chore: Rename variable to follow Go styleguide --- pkg/listen/connection.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index f415f08..f9d1180 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -45,13 +45,13 @@ func getConnectionsPerSource(client *hookdeckclient.Client, source *hookdecksdk. connections = filteredConnections if connectionQuery != "" { - is_path, err := isPath(connectionQuery) + isPath, err := isPath(connectionQuery) if err != nil { return connections, err } var filteredConnections []*hookdecksdk.Connection for _, connection := range connections { - if (is_path && connection.Destination.CliPath != nil && strings.Contains(*connection.Destination.CliPath, connectionQuery)) || (connection.Name != nil && *connection.Name == connectionQuery) { + if (isPath && connection.Destination.CliPath != nil && strings.Contains(*connection.Destination.CliPath, connectionQuery)) || (connection.Name != nil && *connection.Name == connectionQuery) { filteredConnections = append(filteredConnections, connection) } } @@ -69,8 +69,8 @@ func getConnectionsPerSource(client *hookdeckclient.Client, source *hookdecksdk. Prompt: &survey.Input{Message: "What path should the events be forwarded to (ie: /webhooks)?"}, Validate: func(val interface{}) error { str, ok := val.(string) - is_path, err := isPath(str) - if !ok || !is_path || err != nil { + isPath, err := isPath(str) + if !ok || !isPath || err != nil { return errors.New("invalid path") } return nil From 65b815726d984726240fd1a8ce807f2f0c61eebc Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 10 Jul 2024 19:47:52 +0700 Subject: [PATCH 18/33] refactor: Improve connection query logic using new SDK function --- pkg/listen/connection.go | 145 ++++++++++++++++++++------------------- pkg/listen/listen.go | 4 +- 2 files changed, 77 insertions(+), 72 deletions(-) diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index f9d1180..8e16438 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -12,96 +12,101 @@ import ( hookdeckclient "github.com/hookdeck/hookdeck-go-sdk/client" ) -func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source, connectionQuery string, isMultiSource bool) ([]*hookdecksdk.Connection, error) { - connections := []*hookdecksdk.Connection{} +func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source, connectionFilterString string, isMultiSource bool) ([]*hookdecksdk.Connection, error) { + sourceIDs := []*string{} for _, source := range sources { - sourceConnections, err := getConnectionsPerSource(client, source, connectionQuery, isMultiSource) - if err != nil { - return []*hookdecksdk.Connection{}, nil - } - connections = append(connections, sourceConnections...) + sourceIDs = append(sourceIDs, &source.Id) + } + + connectionQuery, err := client.Connection.List(context.Background(), &hookdecksdk.ConnectionListRequest{ + SourceId: sourceIDs, + }) + if err != nil { + return []*hookdecksdk.Connection{}, err + } + + connections, err := filterConnections(connectionQuery.Models, connectionFilterString) + if err != nil { + return []*hookdecksdk.Connection{}, err + } + + connections, err = ensureConnections(client, connections, sources, isMultiSource) + if err != nil { + return []*hookdecksdk.Connection{}, err } return connections, nil } -func getConnectionsPerSource(client *hookdeckclient.Client, source *hookdecksdk.Source, connectionQuery string, isMultiSource bool) ([]*hookdecksdk.Connection, error) { - var connections []*hookdecksdk.Connection - connectionList, err := client.Connection.List(context.Background(), &hookdecksdk.ConnectionListRequest{ - SourceId: []*string{&source.Id}, - }) - if err != nil { - return nil, err +func filterConnections(connections []*hookdecksdk.Connection, connectionFilterString string) ([]*hookdecksdk.Connection, error) { + if connectionFilterString == "" { + return connections, nil } - connections = connectionList.Models + isPath, err := isPath(connectionFilterString) + if err != nil { + return connections, err + } var filteredConnections []*hookdecksdk.Connection for _, connection := range connections { - if connection.Destination.CliPath != nil && *connection.Destination.CliPath != "" { + if (isPath && connection.Destination.CliPath != nil && strings.Contains(*connection.Destination.CliPath, connectionFilterString)) || (connection.Name != nil && *connection.Name == connectionFilterString) { filteredConnections = append(filteredConnections, connection) } } - connections = filteredConnections - if connectionQuery != "" { - isPath, err := isPath(connectionQuery) - if err != nil { - return connections, err - } - var filteredConnections []*hookdecksdk.Connection - for _, connection := range connections { - if (isPath && connection.Destination.CliPath != nil && strings.Contains(*connection.Destination.CliPath, connectionQuery)) || (connection.Name != nil && *connection.Name == connectionQuery) { - filteredConnections = append(filteredConnections, connection) - } - } - connections = filteredConnections + return filteredConnections, nil +} + +// When users want to listen to a single source but there is no connection for that source, +// we can help user set up a new connection for it. +func ensureConnections(client *hookdeckclient.Client, connections []*hookdecksdk.Connection, sources []*hookdecksdk.Source, isMultiSource bool) ([]*hookdecksdk.Connection, error) { + if len(connections) > 0 || isMultiSource { + return connections, nil } - if len(connections) == 0 && !isMultiSource { - answers := struct { - Label string `survey:"label"` - Path string `survey:"path"` - }{} - var qs = []*survey.Question{ - { - Name: "path", - Prompt: &survey.Input{Message: "What path should the events be forwarded to (ie: /webhooks)?"}, - Validate: func(val interface{}) error { - str, ok := val.(string) - isPath, err := isPath(str) - if !ok || !isPath || err != nil { - return errors.New("invalid path") - } - return nil - }, - }, - { - Name: "label", - Prompt: &survey.Input{Message: "What's your connection label (ie: My API)?"}, - Validate: survey.Required, + answers := struct { + Label string `survey:"label"` + Path string `survey:"path"` + }{} + var qs = []*survey.Question{ + { + Name: "path", + Prompt: &survey.Input{Message: "What path should the events be forwarded to (ie: /webhooks)?"}, + Validate: func(val interface{}) error { + str, ok := val.(string) + isPath, err := isPath(str) + if !ok || !isPath || err != nil { + return errors.New("invalid path") + } + return nil }, - } + }, + { + Name: "label", + Prompt: &survey.Input{Message: "What's your connection label (ie: My API)?"}, + Validate: survey.Required, + }, + } - err := survey.Ask(qs, &answers) - if err != nil { - fmt.Println(err.Error()) - return connections, err - } - alias := slug.Make(answers.Label) - connection, err := client.Connection.Create(context.Background(), &hookdecksdk.ConnectionCreateRequest{ - Name: hookdecksdk.OptionalOrNull(&alias), - SourceId: hookdecksdk.OptionalOrNull(&source.Id), - Destination: hookdecksdk.OptionalOrNull(&hookdecksdk.ConnectionCreateRequestDestination{ - Name: alias, - CliPath: &answers.Path, - }), - }) - if err != nil { - return connections, err - } - connections = append(connections, connection) + err := survey.Ask(qs, &answers) + if err != nil { + fmt.Println(err.Error()) + return connections, err + } + alias := slug.Make(answers.Label) + connection, err := client.Connection.Create(context.Background(), &hookdecksdk.ConnectionCreateRequest{ + Name: hookdecksdk.OptionalOrNull(&alias), + SourceId: hookdecksdk.OptionalOrNull(&sources[0].Id), + Destination: hookdecksdk.OptionalOrNull(&hookdecksdk.ConnectionCreateRequestDestination{ + Name: alias, + CliPath: &answers.Path, + }), + }) + if err != nil { + return connections, err } + connections = append(connections, connection) return connections, nil } diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index 1be1aad..eea6bbc 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -35,7 +35,7 @@ type Flags struct { } // listenCmd represents the listen command -func Listen(URL *url.URL, sourceQuery string, connectionQuery string, flags Flags, config *config.Config) error { +func Listen(URL *url.URL, sourceQuery string, connectionFilterString string, flags Flags, config *config.Config) error { var err error var guestURL string @@ -62,7 +62,7 @@ func Listen(URL *url.URL, sourceQuery string, connectionQuery string, flags Flag return err } - connections, err := getConnections(sdkClient, sources, connectionQuery, isMultiSource) + connections, err := getConnections(sdkClient, sources, connectionFilterString, isMultiSource) if err != nil { return err } From fe018d7ab1290c532502f371503d975d378adc5f Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Thu, 11 Jul 2024 23:55:54 +0700 Subject: [PATCH 19/33] fix: Filter cli destination connection --- pkg/listen/connection.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index 8e16438..f5dc02d 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -39,17 +39,28 @@ func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source return connections, nil } +// 1. Filter to only include CLI destination +// 2. Apply connectionFilterString func filterConnections(connections []*hookdecksdk.Connection, connectionFilterString string) ([]*hookdecksdk.Connection, error) { + // 1. Filter to only include CLI destination + var cliDestinationConnections []*hookdecksdk.Connection + for _, connection := range connections { + if connection.Destination.CliPath != nil && *connection.Destination.CliPath != "" { + cliDestinationConnections = append(cliDestinationConnections, connection) + } + } + if connectionFilterString == "" { - return connections, nil + return cliDestinationConnections, nil } + // 2. Apply connectionFilterString isPath, err := isPath(connectionFilterString) if err != nil { return connections, err } var filteredConnections []*hookdecksdk.Connection - for _, connection := range connections { + for _, connection := range cliDestinationConnections { if (isPath && connection.Destination.CliPath != nil && strings.Contains(*connection.Destination.CliPath, connectionFilterString)) || (connection.Name != nil && *connection.Name == connectionFilterString) { filteredConnections = append(filteredConnections, connection) } From e96e719b97c09ecd66eff0837ad176e9014cf883 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Tue, 16 Jul 2024 18:17:39 +0700 Subject: [PATCH 20/33] chore: Add listen message for multi source mode --- pkg/listen/listen.go | 2 +- pkg/listen/printer.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index eea6bbc..ecb5e9d 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -74,7 +74,7 @@ func Listen(URL *url.URL, sourceQuery string, connectionFilterString string, fla } // Start proxy - + printListenMessage(config, sourceQuery, isMultiSource) fmt.Println() printDashboardInformation(config, guestURL) fmt.Println() diff --git a/pkg/listen/printer.go b/pkg/listen/printer.go index 4b290d4..7c9b3a5 100644 --- a/pkg/listen/printer.go +++ b/pkg/listen/printer.go @@ -8,6 +8,19 @@ import ( hookdecksdk "github.com/hookdeck/hookdeck-go-sdk" ) +func printListenMessage(config *config.Config, sourceQuery string, isMultiSource bool) { + if !isMultiSource { + return + } + + fmt.Println() + if sourceQuery == "*" { + fmt.Println("Listening for events on the first 10 Sources that have Connections with CLI Destinations") + } else { + fmt.Println("Listening for events on Sources that have Connections with CLI Destinations") + } +} + func printDashboardInformation(config *config.Config, guestURL string) { fmt.Println(ansi.Bold("Dashboard")) if guestURL != "" { From d3ee6b478ea08825122515c4b5affe618f5114cd Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 17 Jul 2024 00:37:53 +0700 Subject: [PATCH 21/33] chore: Update multi source listen message logic --- pkg/listen/printer.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pkg/listen/printer.go b/pkg/listen/printer.go index 7c9b3a5..8c809cc 100644 --- a/pkg/listen/printer.go +++ b/pkg/listen/printer.go @@ -9,16 +9,12 @@ import ( ) func printListenMessage(config *config.Config, sourceQuery string, isMultiSource bool) { - if !isMultiSource { + if !isMultiSource || sourceQuery == "*" { return } fmt.Println() - if sourceQuery == "*" { - fmt.Println("Listening for events on the first 10 Sources that have Connections with CLI Destinations") - } else { - fmt.Println("Listening for events on Sources that have Connections with CLI Destinations") - } + fmt.Println("Listening for events on Sources that have Connections with CLI Destinations") } func printDashboardInformation(config *config.Config, guestURL string) { From 5f06fa73429631af88bacaa13cd86eee489e7f64 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 17 Jul 2024 00:39:26 +0700 Subject: [PATCH 22/33] chore: Remove temporary 10 source limit for all source query --- pkg/listen/source.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pkg/listen/source.go b/pkg/listen/source.go index 6593ad8..cd600ad 100644 --- a/pkg/listen/source.go +++ b/pkg/listen/source.go @@ -28,15 +28,11 @@ import ( // they'd like to use. They will also have an option to create a new source. func getSources(sdkClient *hookdeckclient.Client, sourceQuery []string) ([]*hookdecksdk.Source, error) { - limit := 100 + limit := 255 // Hookdeck API limit // case 1 if len(sourceQuery) == 1 && sourceQuery[0] == "*" { - // TODO: remove once we can support better limit - temporaryLimit := 10 - sources, err := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{ - Limit: &temporaryLimit, - }) + sources, err := sdkClient.Source.List(context.Background(), &hookdecksdk.SourceListRequest{}) if err != nil { return []*hookdecksdk.Source{}, err } From 06cb5a003d111c936d75b1fd9afedde92ea41e67 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Wed, 17 Jul 2024 00:46:09 +0700 Subject: [PATCH 23/33] chore: Update multi source listen message logic --- pkg/listen/listen.go | 2 +- pkg/listen/printer.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index ecb5e9d..e98b03f 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -74,7 +74,7 @@ func Listen(URL *url.URL, sourceQuery string, connectionFilterString string, fla } // Start proxy - printListenMessage(config, sourceQuery, isMultiSource) + printListenMessage(config, isMultiSource) fmt.Println() printDashboardInformation(config, guestURL) fmt.Println() diff --git a/pkg/listen/printer.go b/pkg/listen/printer.go index 8c809cc..7708624 100644 --- a/pkg/listen/printer.go +++ b/pkg/listen/printer.go @@ -8,8 +8,8 @@ import ( hookdecksdk "github.com/hookdeck/hookdeck-go-sdk" ) -func printListenMessage(config *config.Config, sourceQuery string, isMultiSource bool) { - if !isMultiSource || sourceQuery == "*" { +func printListenMessage(config *config.Config, isMultiSource bool) { + if !isMultiSource { return } From c7b494babc0f0ecc2c464cbb4a0d94d1b5e7ddd4 Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Sat, 20 Jul 2024 20:44:10 -0400 Subject: [PATCH 24/33] feat: allow a --cli-path to be passed --- pkg/cmd/listen.go | 10 ++++--- pkg/listen/connection.go | 64 ++++++++++++++++++++++------------------ pkg/listen/listen.go | 38 ++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 34 deletions(-) diff --git a/pkg/cmd/listen.go b/pkg/cmd/listen.go index 87da4b7..29a6829 100644 --- a/pkg/cmd/listen.go +++ b/pkg/cmd/listen.go @@ -26,9 +26,9 @@ import ( ) type listenCmd struct { - cmd *cobra.Command - wsBaseURL string - noWSS bool + cmd *cobra.Command + noWSS bool + path string } func newListenCmd() *listenCmd { @@ -75,6 +75,7 @@ func newListenCmd() *listenCmd { RunE: lc.runListenCmd, } lc.cmd.Flags().BoolVar(&lc.noWSS, "no-wss", false, "Force unencrypted ws:// protocol instead of wss://") + lc.cmd.Flags().StringVar(&lc.path, "cli-path", "", "Sets the server path of that locally running web server the events will be forwarded to") lc.cmd.SetUsageTemplate( strings.Replace( @@ -113,6 +114,7 @@ func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error { } return listen.Listen(url, sourceQuery, connectionQuery, listen.Flags{ - NoWSS: lc.noWSS, + NoWSS: lc.noWSS, + CliPath: lc.path, }, &Config) } diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index f5dc02d..901b6e2 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -12,7 +12,7 @@ import ( hookdeckclient "github.com/hookdeck/hookdeck-go-sdk/client" ) -func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source, connectionFilterString string, isMultiSource bool) ([]*hookdecksdk.Connection, error) { +func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source, connectionFilterString string, isMultiSource bool, cliPath string) ([]*hookdecksdk.Connection, error) { sourceIDs := []*string{} for _, source := range sources { @@ -31,7 +31,7 @@ func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source return []*hookdecksdk.Connection{}, err } - connections, err = ensureConnections(client, connections, sources, isMultiSource) + connections, err = ensureConnections(client, connections, sources, isMultiSource, cliPath) if err != nil { return []*hookdecksdk.Connection{}, err } @@ -71,47 +71,55 @@ func filterConnections(connections []*hookdecksdk.Connection, connectionFilterSt // When users want to listen to a single source but there is no connection for that source, // we can help user set up a new connection for it. -func ensureConnections(client *hookdeckclient.Client, connections []*hookdecksdk.Connection, sources []*hookdecksdk.Source, isMultiSource bool) ([]*hookdecksdk.Connection, error) { +func ensureConnections(client *hookdeckclient.Client, connections []*hookdecksdk.Connection, sources []*hookdecksdk.Source, isMultiSource bool, cliPath string) ([]*hookdecksdk.Connection, error) { if len(connections) > 0 || isMultiSource { return connections, nil } - answers := struct { + connectionDetails := struct { Label string `survey:"label"` Path string `survey:"path"` }{} - var qs = []*survey.Question{ - { - Name: "path", - Prompt: &survey.Input{Message: "What path should the events be forwarded to (ie: /webhooks)?"}, - Validate: func(val interface{}) error { - str, ok := val.(string) - isPath, err := isPath(str) - if !ok || !isPath || err != nil { - return errors.New("invalid path") - } - return nil + + if len(cliPath) != 0 { + connectionDetails.Path = cliPath + connectionDetails.Label = "CLI" + } else { + var qs = []*survey.Question{ + { + Name: "path", + Prompt: &survey.Input{Message: "What path should the events be forwarded to (ie: /webhooks)?"}, + Validate: func(val interface{}) error { + str, ok := val.(string) + isPath, err := isPath(str) + if !ok || !isPath || err != nil { + return errors.New("invalid path") + } + return nil + }, }, - }, - { - Name: "label", - Prompt: &survey.Input{Message: "What's your connection label (ie: My API)?"}, - Validate: survey.Required, - }, - } + { + Name: "label", + Prompt: &survey.Input{Message: "What's your connection label (ie: My API)?"}, + Validate: survey.Required, + }, + } - err := survey.Ask(qs, &answers) - if err != nil { - fmt.Println(err.Error()) - return connections, err + err := survey.Ask(qs, &connectionDetails) + if err != nil { + fmt.Println(err.Error()) + return connections, err + } } - alias := slug.Make(answers.Label) + + alias := slug.Make(connectionDetails.Label) + connection, err := client.Connection.Create(context.Background(), &hookdecksdk.ConnectionCreateRequest{ Name: hookdecksdk.OptionalOrNull(&alias), SourceId: hookdecksdk.OptionalOrNull(&sources[0].Id), Destination: hookdecksdk.OptionalOrNull(&hookdecksdk.ConnectionCreateRequestDestination{ Name: alias, - CliPath: &answers.Path, + CliPath: &connectionDetails.Path, }), }) if err != nil { diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index e98b03f..f3a019f 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -31,7 +31,8 @@ import ( ) type Flags struct { - NoWSS bool + NoWSS bool + CliPath string } // listenCmd represents the listen command @@ -46,6 +47,17 @@ func Listen(URL *url.URL, sourceQuery string, connectionFilterString string, fla isMultiSource := len(sourceAliases) > 1 || (len(sourceAliases) == 1 && sourceAliases[0] == "*") + if flags.CliPath != "" { + if isMultiSource { + return errors.New("Can only set a CLI path when listening to a single source") + } + + _, err = isPath(flags.CliPath) + if err != nil { + return errors.New("The CLI path must be in a valid format") + } + } + if config.Profile.APIKey == "" { guestURL, err = login.GuestLogin(config) if guestURL == "" { @@ -62,11 +74,33 @@ func Listen(URL *url.URL, sourceQuery string, connectionFilterString string, fla return err } - connections, err := getConnections(sdkClient, sources, connectionFilterString, isMultiSource) + connections, err := getConnections(sdkClient, sources, connectionFilterString, isMultiSource, flags.CliPath) if err != nil { return err } + // If the "clii-path" flag has been passed and the destination has a current cli path value but it's different, update destination path + if len(flags.CliPath) != 0 && + len(connections) == 1 && + *connections[0].Destination.CliPath != "" && + *connections[0].Destination.CliPath != flags.CliPath { + + l := log.StandardLogger() + updateMsg := fmt.Sprintf("Updating destination CLI path from \"%s\" to \"%s\"", *connections[0].Destination.CliPath, flags.CliPath) + l.Debug(updateMsg) + + path := flags.CliPath + _, err := sdkClient.Destination.Update(context.Background(), connections[0].Destination.Id, &hookdecksdk.DestinationUpdateRequest{ + CliPath: hookdecksdk.Optional[string](path), + }) + + if err != nil { + return err + } + + connections[0].Destination.CliPath = &path + } + sources = getRelevantSources(sources, connections) if err := validateData(sources, connections); err != nil { From 6367c11af3f05a30069edc2db22e24efdd9d7a10 Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Tue, 23 Jul 2024 13:32:35 +0100 Subject: [PATCH 25/33] chore: remove interactive prompts --- pkg/cmd/listen.go | 56 ++++++++++++++++++++++++++++++++-------- pkg/listen/connection.go | 50 ++++++++++++++--------------------- pkg/listen/listen.go | 2 +- 3 files changed, 65 insertions(+), 43 deletions(-) diff --git a/pkg/cmd/listen.go b/pkg/cmd/listen.go index 29a6829..c3a012a 100644 --- a/pkg/cmd/listen.go +++ b/pkg/cmd/listen.go @@ -17,6 +17,7 @@ package cmd import ( "errors" + "fmt" "net/url" "strconv" "strings" @@ -26,9 +27,9 @@ import ( ) type listenCmd struct { - cmd *cobra.Command - noWSS bool - path string + cmd *cobra.Command + noWSS bool + cliPath string } func newListenCmd() *listenCmd { @@ -37,6 +38,12 @@ func newListenCmd() *listenCmd { lc.cmd = &cobra.Command{ Use: "listen", Short: "Forward events for a source to your local server", + Long: `Forward events for a source to your local server. + +This command will create a new Hookdeck Source if it doesn't exist. + +By default the Hookdeck Destination will be named "CLI", and the +Destination CLI path will be "/". To set the CLI path, use the "--cli-path" flag.`, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return errors.New("Requires a port or forwarding URL to forward the events to") @@ -75,14 +82,41 @@ func newListenCmd() *listenCmd { RunE: lc.runListenCmd, } lc.cmd.Flags().BoolVar(&lc.noWSS, "no-wss", false, "Force unencrypted ws:// protocol instead of wss://") - lc.cmd.Flags().StringVar(&lc.path, "cli-path", "", "Sets the server path of that locally running web server the events will be forwarded to") + lc.cmd.Flags().MarkHidden("no-wss") + lc.cmd.Flags().StringVar(&lc.cliPath, "cli-path", "", "Sets the server path of that locally running web server the events will be forwarded to") + + usage := lc.cmd.UsageTemplate() + + usage = strings.Replace( + usage, + "{{.UseLine}}", + `hookdeck listen [port or forwarding URL] [source] [connection] [flags] + +Arguments: + + - [port or forwarding URL]: Required. The port or forwarding URL to forward the events to e.g., "3000" or "http://localhost:3000" + - [source]: Required. The name of source to forward the events from e.g., "shopify", "stripe" + - [connection]: Optional. The name of the connection linking the Source and the Destination + `, 1) + + usage += fmt.Sprintf(` + +Examples: + + Forward events from a Hookdeck Source named "shopify" to a local server running on port %[1]d: + + hookdeck listen %[1]d shopify + + Forward events to a local server running on "http://myapp.test": + + hookdeck listen %[1]d http://myapp.test + + Forward events to the path "/webhooks" on local server running on port %[1]d: + + hookdeck listen %[1]d --cli-path /webhooks + `, 3000) - lc.cmd.SetUsageTemplate( - strings.Replace( - lc.cmd.UsageTemplate(), - "{{.UseLine}}", - "hookdeck listen [port or forwarding URL] [source] [connection] [flags]", 1), - ) + lc.cmd.SetUsageTemplate(usage) return lc } @@ -115,6 +149,6 @@ func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error { return listen.Listen(url, sourceQuery, connectionQuery, listen.Flags{ NoWSS: lc.noWSS, - CliPath: lc.path, + CliPath: lc.cliPath, }, &Config) } diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index 901b6e2..55eea6b 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -2,14 +2,13 @@ package listen import ( "context" - "errors" "fmt" "strings" - "github.com/AlecAivazis/survey/v2" "github.com/gosimple/slug" hookdecksdk "github.com/hookdeck/hookdeck-go-sdk" hookdeckclient "github.com/hookdeck/hookdeck-go-sdk/client" + log "github.com/sirupsen/logrus" ) func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source, connectionFilterString string, isMultiSource bool, cliPath string) ([]*hookdecksdk.Connection, error) { @@ -31,7 +30,7 @@ func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source return []*hookdecksdk.Connection{}, err } - connections, err = ensureConnections(client, connections, sources, isMultiSource, cliPath) + connections, err = ensureConnections(client, connections, sources, isMultiSource, connectionFilterString, cliPath) if err != nil { return []*hookdecksdk.Connection{}, err } @@ -71,45 +70,34 @@ func filterConnections(connections []*hookdecksdk.Connection, connectionFilterSt // When users want to listen to a single source but there is no connection for that source, // we can help user set up a new connection for it. -func ensureConnections(client *hookdeckclient.Client, connections []*hookdecksdk.Connection, sources []*hookdecksdk.Source, isMultiSource bool, cliPath string) ([]*hookdecksdk.Connection, error) { +func ensureConnections(client *hookdeckclient.Client, connections []*hookdecksdk.Connection, sources []*hookdecksdk.Source, isMultiSource bool, connectionFilterString string, cliPath string) ([]*hookdecksdk.Connection, error) { + l := log.StandardLogger() + if len(connections) > 0 || isMultiSource { + msg := fmt.Sprintf("Connection exists for Source \"%s\", Connection \"%s\", and CLI path \"%s\"", sources[0].Name, connectionFilterString, cliPath) + l.Debug(msg) + return connections, nil } + msg := fmt.Sprintf("No connection found. Creating a connection for Source \"%s\", Connection \"%s\", and CLI path \"%s\"", sources[0].Name, connectionFilterString, cliPath) + l.Debug(msg) + connectionDetails := struct { Label string `survey:"label"` Path string `survey:"path"` }{} - if len(cliPath) != 0 { - connectionDetails.Path = cliPath - connectionDetails.Label = "CLI" + if len(connectionFilterString) == 0 { + connectionDetails.Label = "cli" } else { - var qs = []*survey.Question{ - { - Name: "path", - Prompt: &survey.Input{Message: "What path should the events be forwarded to (ie: /webhooks)?"}, - Validate: func(val interface{}) error { - str, ok := val.(string) - isPath, err := isPath(str) - if !ok || !isPath || err != nil { - return errors.New("invalid path") - } - return nil - }, - }, - { - Name: "label", - Prompt: &survey.Input{Message: "What's your connection label (ie: My API)?"}, - Validate: survey.Required, - }, - } + connectionDetails.Label = connectionFilterString + } - err := survey.Ask(qs, &connectionDetails) - if err != nil { - fmt.Println(err.Error()) - return connections, err - } + if len(cliPath) == 0 { + connectionDetails.Path = "/" + } else { + connectionDetails.Path = cliPath } alias := slug.Make(connectionDetails.Label) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index f3a019f..bd228b2 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -79,7 +79,7 @@ func Listen(URL *url.URL, sourceQuery string, connectionFilterString string, fla return err } - // If the "clii-path" flag has been passed and the destination has a current cli path value but it's different, update destination path + // If the "cli-path" flag has been passed and the destination has a current cli path value but it's different, update destination path if len(flags.CliPath) != 0 && len(connections) == 1 && *connections[0].Destination.CliPath != "" && From b7e63d53475a437aff4fd15145b67ca96d982d25 Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Tue, 23 Jul 2024 17:36:03 +0100 Subject: [PATCH 26/33] chore: add multi-connection error for --cli-path --- pkg/listen/listen.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index bd228b2..65b096f 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -79,6 +79,13 @@ func Listen(URL *url.URL, sourceQuery string, connectionFilterString string, fla return err } + if len(flags.CliPath) != 0 && len(connections) > 1 { + return errors.New(fmt.Errorf(`Multiple CLI destinations found. Cannot set the CLI path on multiple destinations. +Specify a single destination to update the CLI path. For example, pass a connection name: + + hookdeck listen %s %s %s --cli-path %s`, URL.String(), sourceQuery, "connection-name", flags.CliPath).Error()) + } + // If the "cli-path" flag has been passed and the destination has a current cli path value but it's different, update destination path if len(flags.CliPath) != 0 && len(connections) == 1 && From e18b4d0036fb637d797ca026a0153ddbed2d797e Mon Sep 17 00:00:00 2001 From: Phil Leggetter Date: Mon, 29 Jul 2024 14:23:14 +0100 Subject: [PATCH 27/33] fix: use Source object in example CLI command --- pkg/listen/listen.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index 65b096f..dae5586 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -83,7 +83,7 @@ func Listen(URL *url.URL, sourceQuery string, connectionFilterString string, fla return errors.New(fmt.Errorf(`Multiple CLI destinations found. Cannot set the CLI path on multiple destinations. Specify a single destination to update the CLI path. For example, pass a connection name: - hookdeck listen %s %s %s --cli-path %s`, URL.String(), sourceQuery, "connection-name", flags.CliPath).Error()) + hookdeck listen %s %s %s --cli-path %s`, URL.String(), sources[0].Name, "connection-name", flags.CliPath).Error()) } // If the "cli-path" flag has been passed and the destination has a current cli path value but it's different, update destination path From f5d0f3bbe6a28b07bd90cac90e401e556b895049 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Mon, 29 Jul 2024 20:24:11 +0700 Subject: [PATCH 28/33] chore: Fix error formatting warnings --- pkg/cmd/listen.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/cmd/listen.go b/pkg/cmd/listen.go index c3a012a..7513636 100644 --- a/pkg/cmd/listen.go +++ b/pkg/cmd/listen.go @@ -39,14 +39,14 @@ func newListenCmd() *listenCmd { Use: "listen", Short: "Forward events for a source to your local server", Long: `Forward events for a source to your local server. - + This command will create a new Hookdeck Source if it doesn't exist. -By default the Hookdeck Destination will be named "CLI", and the +By default the Hookdeck Destination will be named "CLI", and the Destination CLI path will be "/". To set the CLI path, use the "--cli-path" flag.`, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { - return errors.New("Requires a port or forwarding URL to forward the events to") + return errors.New("requires a port or forwarding URL to forward the events to") } _, err_port := strconv.ParseInt(args[0], 10, 64) @@ -60,21 +60,21 @@ Destination CLI path will be "/". To set the CLI path, use the "--cli-path" flag } if err_port != nil && err_url != nil { - return errors.New("Argument is not a valid port or forwading URL") + return errors.New("argument is not a valid port or forwading URL") } if err_port != nil { if parsed_url.Host == "" { - return errors.New("Forwarding URL must contain a host.") + return errors.New("forwarding URL must contain a host") } if parsed_url.RawQuery != "" { - return errors.New("Forwarding URL cannot contain query params.") + return errors.New("forwarding URL cannot contain query params") } } if len(args) > 3 { - return errors.New("Invalid extra argument provided") + return errors.New("invalid extra argument provided") } return nil From b48c7bcf3191d9e0419852f8c40c5172fe31a229 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Mon, 29 Jul 2024 20:24:47 +0700 Subject: [PATCH 29/33] chore: Remove unused flag --- pkg/cmd/whoami.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/cmd/whoami.go b/pkg/cmd/whoami.go index 1c667f7..64678a4 100644 --- a/pkg/cmd/whoami.go +++ b/pkg/cmd/whoami.go @@ -11,8 +11,7 @@ import ( ) type whoamiCmd struct { - cmd *cobra.Command - interactive bool + cmd *cobra.Command } func newWhoamiCmd() *whoamiCmd { From 59d5bdc18db51ccf37d29e284e075f310ac743eb Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Mon, 29 Jul 2024 20:29:34 +0700 Subject: [PATCH 30/33] chore: Fix other warnings --- pkg/cmd/completion.go | 2 +- pkg/cmd/root.go | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/cmd/completion.go b/pkg/cmd/completion.go index a99842d..cc6408f 100644 --- a/pkg/cmd/completion.go +++ b/pkg/cmd/completion.go @@ -110,7 +110,7 @@ func selectShell(shell string) error { } return err default: - return fmt.Errorf("Could not automatically detect your shell. Please run the command with the `--shell` flag for either bash or zsh") + return fmt.Errorf("could not automatically detect your shell. Please run the command with the `--shell` flag for either bash or zsh") } } diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 167e0f1..6534bbc 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -28,8 +28,6 @@ import ( "github.com/spf13/cobra" ) -var cfgFile string - var Config config.Config var rootCmd = &cobra.Command{ From 55622a6c65eca0726b0f345a4dca763e6aff55b0 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Mon, 29 Jul 2024 20:34:30 +0700 Subject: [PATCH 31/33] fix: isPath check --- pkg/listen/listen.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index dae5586..888e48e 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -52,8 +52,11 @@ func Listen(URL *url.URL, sourceQuery string, connectionFilterString string, fla return errors.New("Can only set a CLI path when listening to a single source") } - _, err = isPath(flags.CliPath) + flagIsPath, err := isPath(flags.CliPath) if err != nil { + return err + } + if !flagIsPath { return errors.New("The CLI path must be in a valid format") } } From a9e3513d5846d239caf8215437660eae3ad76885 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Mon, 29 Jul 2024 20:42:13 +0700 Subject: [PATCH 32/33] refactor: Use logger directly instead of creating a new one --- pkg/listen/connection.go | 8 ++------ pkg/listen/listen.go | 3 +-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/pkg/listen/connection.go b/pkg/listen/connection.go index 55eea6b..9952417 100644 --- a/pkg/listen/connection.go +++ b/pkg/listen/connection.go @@ -71,17 +71,13 @@ func filterConnections(connections []*hookdecksdk.Connection, connectionFilterSt // When users want to listen to a single source but there is no connection for that source, // we can help user set up a new connection for it. func ensureConnections(client *hookdeckclient.Client, connections []*hookdecksdk.Connection, sources []*hookdecksdk.Source, isMultiSource bool, connectionFilterString string, cliPath string) ([]*hookdecksdk.Connection, error) { - l := log.StandardLogger() - if len(connections) > 0 || isMultiSource { - msg := fmt.Sprintf("Connection exists for Source \"%s\", Connection \"%s\", and CLI path \"%s\"", sources[0].Name, connectionFilterString, cliPath) - l.Debug(msg) + log.Debug(fmt.Sprintf("Connection exists for Source \"%s\", Connection \"%s\", and CLI path \"%s\"", sources[0].Name, connectionFilterString, cliPath)) return connections, nil } - msg := fmt.Sprintf("No connection found. Creating a connection for Source \"%s\", Connection \"%s\", and CLI path \"%s\"", sources[0].Name, connectionFilterString, cliPath) - l.Debug(msg) + log.Debug(fmt.Sprintf("No connection found. Creating a connection for Source \"%s\", Connection \"%s\", and CLI path \"%s\"", sources[0].Name, connectionFilterString, cliPath)) connectionDetails := struct { Label string `survey:"label"` diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index 888e48e..7b660c7 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -95,9 +95,8 @@ Specify a single destination to update the CLI path. For example, pass a connect *connections[0].Destination.CliPath != "" && *connections[0].Destination.CliPath != flags.CliPath { - l := log.StandardLogger() updateMsg := fmt.Sprintf("Updating destination CLI path from \"%s\" to \"%s\"", *connections[0].Destination.CliPath, flags.CliPath) - l.Debug(updateMsg) + log.Debug(updateMsg) path := flags.CliPath _, err := sdkClient.Destination.Update(context.Background(), connections[0].Destination.Id, &hookdecksdk.DestinationUpdateRequest{ From 6dd2063a87c59766f6310dc86bd20d24631a0518 Mon Sep 17 00:00:00 2001 From: Alex Luong Date: Mon, 29 Jul 2024 20:44:04 +0700 Subject: [PATCH 33/33] chore: Unnecessary type assertion --- pkg/listen/listen.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/listen/listen.go b/pkg/listen/listen.go index 7b660c7..edde953 100644 --- a/pkg/listen/listen.go +++ b/pkg/listen/listen.go @@ -100,7 +100,7 @@ Specify a single destination to update the CLI path. For example, pass a connect path := flags.CliPath _, err := sdkClient.Destination.Update(context.Background(), connections[0].Destination.Id, &hookdecksdk.DestinationUpdateRequest{ - CliPath: hookdecksdk.Optional[string](path), + CliPath: hookdecksdk.Optional(path), }) if err != nil {