Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

v0.2.0 #84

Merged
merged 34 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6d9033b
chore: Rename variable to follow Go convention
alexluong Jun 4, 2024
5d3abfe
fix: Error message convention
alexluong Jun 5, 2024
5c2160f
feat: Display multiple sources & remove source dependency from Proxy …
alexluong Jun 5, 2024
0b58435
chore: Remove comment
alexluong Jun 5, 2024
3858dab
feat: Support query multiple sources
alexluong Jun 5, 2024
ebc8c8d
feat: Get all connections
alexluong Jun 5, 2024
5264da4
chore: Temporarily enforce limit of 10 sources
alexluong Jun 6, 2024
daa7414
refactor: Source query logic
alexluong Jun 6, 2024
43493c8
chore: Add data validation before starting proxy
alexluong Jun 6, 2024
1145fe2
fix: Skip connection creation when proxying multiple sources
alexluong Jun 6, 2024
6720414
feat: Only show relevant sources
alexluong Jun 6, 2024
bcca2d4
refactor: Move source query parsing logic to listen package
alexluong Jun 6, 2024
63025c9
feat: Improve source query parsing
alexluong Jun 6, 2024
830f17b
chore: Remove unnecessary comment
alexluong Jun 7, 2024
2bc54c5
chore: Handle error
alexluong Jun 7, 2024
8b41716
chore: Upgrade Hookdeck Go SDK to v0.4.1
alexluong Jul 10, 2024
aed35e6
chore: Rename variable to follow Go styleguide
alexluong Jul 10, 2024
65b8157
refactor: Improve connection query logic using new SDK function
alexluong Jul 10, 2024
fe018d7
fix: Filter cli destination connection
alexluong Jul 11, 2024
e96e719
chore: Add listen message for multi source mode
alexluong Jul 16, 2024
d3ee6b4
chore: Update multi source listen message logic
alexluong Jul 16, 2024
5f06fa7
chore: Remove temporary 10 source limit for all source query
alexluong Jul 16, 2024
06cb5a0
chore: Update multi source listen message logic
alexluong Jul 16, 2024
c7b494b
feat: allow a --cli-path to be passed
leggetter Jul 21, 2024
6367c11
chore: remove interactive prompts
leggetter Jul 23, 2024
b7e63d5
chore: add multi-connection error for --cli-path
leggetter Jul 23, 2024
42fb1e0
Merge pull request #92 from hookdeck/feat/cli-path-non-interactive
leggetter Jul 25, 2024
e18b4d0
fix: use Source object in example CLI command
leggetter Jul 29, 2024
f5d0f3b
chore: Fix error formatting warnings
alexluong Jul 29, 2024
b48c7bc
chore: Remove unused flag
alexluong Jul 29, 2024
59d5bdc
chore: Fix other warnings
alexluong Jul 29, 2024
55622a6
fix: isPath check
alexluong Jul 29, 2024
a9e3513
refactor: Use logger directly instead of creating a new one
alexluong Jul 29, 2024
6dd2063
chore: Unnecessary type assertion
alexluong Jul 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}

Expand Down
72 changes: 54 additions & 18 deletions pkg/cmd/listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package cmd

import (
"errors"
"fmt"
"net/url"
"strconv"
"strings"
Expand All @@ -26,9 +27,9 @@ import (
)

type listenCmd struct {
cmd *cobra.Command
wsBaseURL string
noWSS bool
cmd *cobra.Command
noWSS bool
cliPath string
}

func newListenCmd() *listenCmd {
Expand All @@ -37,9 +38,15 @@ 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")
return errors.New("requires a port or forwarding URL to forward the events to")
}

_, err_port := strconv.ParseInt(args[0], 10, 64)
Expand All @@ -53,44 +60,72 @@ func newListenCmd() *listenCmd {
}

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
},
RunE: lc.runListenCmd,
}
lc.cmd.Flags().BoolVar(&lc.noWSS, "no-wss", false, "Force unencrypted ws:// protocol instead of wss://")
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
}

// listenCmd represents the listen command
func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error {
var sourceAlias, connectionQuery string
var sourceQuery, connectionQuery string
if len(args) > 1 {
sourceAlias = args[1]
sourceQuery = args[1]
}
if len(args) > 2 {
connectionQuery = args[2]
Expand All @@ -112,7 +147,8 @@ func (lc *listenCmd) runListenCmd(cmd *cobra.Command, args []string) error {
url.Scheme = "http"
}

return listen.Listen(url, sourceAlias, connectionQuery, listen.Flags{
NoWSS: lc.noWSS,
return listen.Listen(url, sourceQuery, connectionQuery, listen.Flags{
NoWSS: lc.noWSS,
CliPath: lc.cliPath,
}, &Config)
}
2 changes: 0 additions & 2 deletions pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import (
"github.com/spf13/cobra"
)

var cfgFile string

var Config config.Config

var rootCmd = &cobra.Command{
Expand Down
3 changes: 1 addition & 2 deletions pkg/cmd/whoami.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import (
)

type whoamiCmd struct {
cmd *cobra.Command
interactive bool
cmd *cobra.Command
}

func newWhoamiCmd() *whoamiCmd {
Expand Down
3 changes: 1 addition & 2 deletions pkg/hookdeck/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ type Session struct {
}

type CreateSessionInput struct {
SourceId string `json:"source_id"`
ConnectionIds []string `json:"webhook_ids"`
}

Expand All @@ -29,7 +28,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)
Expand Down
151 changes: 86 additions & 65 deletions pkg/listen/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,93 +2,114 @@ 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, 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{
SourceId: &source.Id,
func getConnections(client *hookdeckclient.Client, sources []*hookdecksdk.Source, connectionFilterString string, isMultiSource bool, cliPath string) ([]*hookdecksdk.Connection, error) {
sourceIDs := []*string{}

for _, source := range sources {
sourceIDs = append(sourceIDs, &source.Id)
}

connectionQuery, err := client.Connection.List(context.Background(), &hookdecksdk.ConnectionListRequest{
SourceId: sourceIDs,
})
if err != nil {
return nil, err
return []*hookdecksdk.Connection{}, err
}
connections = connectionList.Models

var filteredConnections []*hookdecksdk.Connection
connections, err := filterConnections(connectionQuery.Models, connectionFilterString)
if err != nil {
return []*hookdecksdk.Connection{}, err
}

connections, err = ensureConnections(client, connections, sources, isMultiSource, connectionFilterString, cliPath)
if err != nil {
return []*hookdecksdk.Connection{}, err
}

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 != "" {
filteredConnections = append(filteredConnections, connection)
cliDestinationConnections = append(cliDestinationConnections, connection)
}
}
connections = filteredConnections

if connectionQuery != "" {
is_path, 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) {
filteredConnections = append(filteredConnections, connection)
}
}
connections = filteredConnections
if connectionFilterString == "" {
return cliDestinationConnections, nil
}

if len(connections) == 0 {
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)
is_path, err := isPath(str)
if !ok || !is_path || 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,
},
// 2. Apply connectionFilterString
isPath, err := isPath(connectionFilterString)
if err != nil {
return connections, err
}
var filteredConnections []*hookdecksdk.Connection
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)
}
}

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)
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, connectionFilterString string, cliPath string) ([]*hookdecksdk.Connection, error) {
if len(connections) > 0 || isMultiSource {
log.Debug(fmt.Sprintf("Connection exists for Source \"%s\", Connection \"%s\", and CLI path \"%s\"", sources[0].Name, connectionFilterString, cliPath))

return connections, nil
}

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"`
Path string `survey:"path"`
}{}

if len(connectionFilterString) == 0 {
connectionDetails.Label = "cli"
} else {
connectionDetails.Label = connectionFilterString
}

if len(cliPath) == 0 {
connectionDetails.Path = "/"
} else {
connectionDetails.Path = cliPath
}

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: &connectionDetails.Path,
}),
})
if err != nil {
return connections, err
}
connections = append(connections, connection)

return connections, nil
}
Loading
Loading