From f84aa26003512526badc9822099d061ef9a5a3dc Mon Sep 17 00:00:00 2001 From: Miguel Prieto Date: Wed, 8 Oct 2025 12:00:06 -0300 Subject: [PATCH 1/2] Added commands to get, created and update webhooks --- cmd/webhook_metadata.go | 204 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 198 insertions(+), 6 deletions(-) diff --git a/cmd/webhook_metadata.go b/cmd/webhook_metadata.go index aa6304b..1b0a06e 100644 --- a/cmd/webhook_metadata.go +++ b/cmd/webhook_metadata.go @@ -4,8 +4,12 @@ import ( "context" "encoding/json" "fmt" + "github.com/conductor-sdk/conductor-go/sdk/model" "github.com/orkes-io/conductor-cli/internal" "github.com/spf13/cobra" + "os" + "strconv" + "strings" ) var webhookCmd = &cobra.Command{ @@ -22,10 +26,35 @@ var ( } deleteWebHookMetadataCmd = &cobra.Command{ - Use: "delete", + Use: "delete ", Short: "Delete Webhook", RunE: delete, SilenceUsage: true, + Example: "webhook delete ", + } + + getWebHookMetadataCmd = &cobra.Command{ + Use: "get ", + Short: "Get Webhook", + RunE: get, + SilenceUsage: true, + Example: "webhook get ", + } + + createWebHookMetadataCmd = &cobra.Command{ + Use: "create", + Short: "Create Webhook", + RunE: create, + SilenceUsage: true, + Example: "webhook create --name my-webhook --file webhook.json", + } + + updateWebHookMetadataCmd = &cobra.Command{ + Use: "update ", + Short: "Update Webhook", + RunE: update, + SilenceUsage: true, + Example: "webhook update --file webhook.json", } ) @@ -50,28 +79,191 @@ func list(cmd *cobra.Command, args []string) error { } func delete(cmd *cobra.Command, args []string) error { + var id string + + // Check if reading from stdin (pipe) + stat, _ := os.Stdin.Stat() + if (stat.Mode() & os.ModeCharDevice) == 0 { + // Reading from pipe + data, err := os.ReadFile("/dev/stdin") + if err != nil { + return fmt.Errorf("error reading from stdin: %v", err) + } + id = strings.TrimSpace(string(data)) + } else if len(args) > 0 { + id = args[0] + } else { + return cmd.Usage() + } + webhookClient := internal.GetWebhooksConfigClient() + _, err := webhookClient.DeleteWebhook(context.Background(), id) + if err != nil { + return err + } + + fmt.Printf("Webhook %s deleted successfully\n", id) + return nil +} +func get(cmd *cobra.Command, args []string) error { if len(args) == 0 { return cmd.Usage() } - for i := 0; i < len(args); i++ { - id := args[i] - fmt.Println(id) - _, err := webhookClient.DeleteWebhook(context.Background(), id) + + webhookClient := internal.GetWebhooksConfigClient() + webhook, _, err := webhookClient.GetWebhook(context.Background(), args[0]) + if err != nil { + return err + } + + data, _ := json.MarshalIndent(webhook, "", " ") + fmt.Println(string(data)) + return nil +} + +func create(cmd *cobra.Command, args []string) error { + name, _ := cmd.Flags().GetString("name") + file, _ := cmd.Flags().GetString("file") + workflowsToStart, _ := cmd.Flags().GetString("workflows-to-start") + receiverWorkflows, _ := cmd.Flags().GetString("receiver-workflows") + + var webhookConfig model.WebhookConfig + var data []byte + var err error + + // Check if reading from stdin (pipe) + stat, _ := os.Stdin.Stat() + if (stat.Mode() & os.ModeCharDevice) == 0 { + // Reading from pipe + data, err = os.ReadFile("/dev/stdin") + if err != nil { + return fmt.Errorf("error reading from stdin: %v", err) + } + err = json.Unmarshal(data, &webhookConfig) + if err != nil { + return fmt.Errorf("error parsing JSON from stdin: %v", err) + } + } else if file != "" { + // Read from file + data, err = os.ReadFile(file) + if err != nil { + return fmt.Errorf("error reading file: %v", err) + } + err = json.Unmarshal(data, &webhookConfig) if err != nil { - return err + return fmt.Errorf("error parsing JSON: %v", err) } + } else { + // Build from flags + webhookConfig = model.WebhookConfig{ + Name: name, + } + + if workflowsToStart != "" { + webhookConfig.WorkflowsToStart = parseWorkflowMap(workflowsToStart) + } + + if receiverWorkflows != "" { + webhookConfig.ReceiverWorkflowNamesToVersions = parseWorkflowMap(receiverWorkflows) + } + } + + webhookClient := internal.GetWebhooksConfigClient() + result, _, err := webhookClient.CreateWebhook(context.Background(), webhookConfig) + if err != nil { + return fmt.Errorf("error creating webhook: %v", err) } + data, _ = json.MarshalIndent(result, "", " ") + fmt.Println(string(data)) return nil } +func update(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return cmd.Usage() + } + + id := args[0] + file, _ := cmd.Flags().GetString("file") + + var data []byte + var err error + + // Check if reading from stdin (pipe) + stat, _ := os.Stdin.Stat() + if (stat.Mode() & os.ModeCharDevice) == 0 { + // Reading from pipe + data, err = os.ReadFile("/dev/stdin") + if err != nil { + return fmt.Errorf("error reading from stdin: %v", err) + } + } else if file != "" { + // Read from file + data, err = os.ReadFile(file) + if err != nil { + return fmt.Errorf("error reading file: %v", err) + } + } else { + return fmt.Errorf("--file is required or pipe JSON to stdin") + } + + var webhookConfig model.WebhookConfig + err = json.Unmarshal(data, &webhookConfig) + if err != nil { + return fmt.Errorf("error parsing JSON: %v", err) + } + + webhookClient := internal.GetWebhooksConfigClient() + result, _, err := webhookClient.UpdateWebhook(context.Background(), webhookConfig, id) + if err != nil { + return fmt.Errorf("error updating webhook: %v", err) + } + + data, _ = json.MarshalIndent(result, "", " ") + fmt.Println(string(data)) + return nil +} + +// parseWorkflowMap parses a string like "workflow1:1,workflow2:2" into a map +func parseWorkflowMap(input string) map[string]int32 { + result := make(map[string]int32) + if input == "" { + return result + } + + pairs := strings.Split(input, ",") + for _, pair := range pairs { + parts := strings.Split(strings.TrimSpace(pair), ":") + if len(parts) == 2 { + version, err := strconv.Atoi(parts[1]) + if err == nil { + result[parts[0]] = int32(version) + } + } + } + return result +} + func init() { rootCmd.AddCommand(webhookCmd) + listWebHookMetadataCmd.Flags().Bool("json", false, "print json") + + createWebHookMetadataCmd.Flags().String("name", "", "Webhook name") + createWebHookMetadataCmd.Flags().String("file", "", "JSON file containing webhook configuration") + createWebHookMetadataCmd.Flags().String("workflows-to-start", "", "Workflows to start (format: workflow1:version1,workflow2:version2)") + createWebHookMetadataCmd.Flags().String("receiver-workflows", "", "Receiver workflows (format: workflow1:version1,workflow2:version2)") + + updateWebHookMetadataCmd.Flags().String("file", "", "JSON file containing webhook configuration (required)") + updateWebHookMetadataCmd.MarkFlagRequired("file") + webhookCmd.AddCommand( listWebHookMetadataCmd, + getWebHookMetadataCmd, + createWebHookMetadataCmd, + updateWebHookMetadataCmd, deleteWebHookMetadataCmd, ) } From a4cadedb2ff42059b1150e709b561af2ed305b63 Mon Sep 17 00:00:00 2001 From: Miguel Prieto Date: Wed, 8 Oct 2025 14:38:01 -0300 Subject: [PATCH 2/2] Added missing flags to create webhook + E2E tests --- cmd/webhook_metadata.go | 35 ++++++++++ test/e2e/webhook.bats | 147 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 test/e2e/webhook.bats diff --git a/cmd/webhook_metadata.go b/cmd/webhook_metadata.go index 1b0a06e..233b13e 100644 --- a/cmd/webhook_metadata.go +++ b/cmd/webhook_metadata.go @@ -127,6 +127,9 @@ func create(cmd *cobra.Command, args []string) error { file, _ := cmd.Flags().GetString("file") workflowsToStart, _ := cmd.Flags().GetString("workflows-to-start") receiverWorkflows, _ := cmd.Flags().GetString("receiver-workflows") + sourcePlatform, _ := cmd.Flags().GetString("source-platform") + verifier, _ := cmd.Flags().GetString("verifier") + headers, _ := cmd.Flags().GetString("headers") var webhookConfig model.WebhookConfig var data []byte @@ -160,6 +163,18 @@ func create(cmd *cobra.Command, args []string) error { Name: name, } + if sourcePlatform != "" { + webhookConfig.SourcePlatform = sourcePlatform + } + + if verifier != "" { + webhookConfig.Verifier = verifier + } + + if headers != "" { + webhookConfig.Headers = parseHeaderMap(headers) + } + if workflowsToStart != "" { webhookConfig.WorkflowsToStart = parseWorkflowMap(workflowsToStart) } @@ -246,6 +261,23 @@ func parseWorkflowMap(input string) map[string]int32 { return result } +// parseHeaderMap parses a string like "key1:value1,key2:value2" into a map +func parseHeaderMap(input string) map[string]string { + result := make(map[string]string) + if input == "" { + return result + } + + pairs := strings.Split(input, ",") + for _, pair := range pairs { + parts := strings.SplitN(strings.TrimSpace(pair), ":", 2) + if len(parts) == 2 { + result[parts[0]] = parts[1] + } + } + return result +} + func init() { rootCmd.AddCommand(webhookCmd) @@ -255,6 +287,9 @@ func init() { createWebHookMetadataCmd.Flags().String("file", "", "JSON file containing webhook configuration") createWebHookMetadataCmd.Flags().String("workflows-to-start", "", "Workflows to start (format: workflow1:version1,workflow2:version2)") createWebHookMetadataCmd.Flags().String("receiver-workflows", "", "Receiver workflows (format: workflow1:version1,workflow2:version2)") + createWebHookMetadataCmd.Flags().String("source-platform", "", "Source platform (e.g., Custom, GitHub, Slack)") + createWebHookMetadataCmd.Flags().String("verifier", "", "Verifier type (e.g., HEADER_BASED)") + createWebHookMetadataCmd.Flags().String("headers", "", "Headers as key:value pairs (format: key1:value1,key2:value2)") updateWebHookMetadataCmd.Flags().String("file", "", "JSON file containing webhook configuration (required)") updateWebHookMetadataCmd.MarkFlagRequired("file") diff --git a/test/e2e/webhook.bats b/test/e2e/webhook.bats new file mode 100644 index 0000000..cb4a3a0 --- /dev/null +++ b/test/e2e/webhook.bats @@ -0,0 +1,147 @@ +#!/usr/bin/env bats + +# E2E tests for webhook functionality + +WEBHOOK_FILE="test/e2e/webhook.json" +WEBHOOK_NAME="custom_webhook_1" +WEBHOOK_ID="" + +setup() { + # Ensure the CLI binary exists + if [ ! -f "./orkes" ]; then + echo "ERROR: orkes binary not found. Please build it first." + exit 1 + fi +} + +# Helper function to extract webhook ID from JSON output +get_webhook_id() { + echo "$1" | grep -o '"id"[[:space:]]*:[[:space:]]*"[^"]*"' | grep -o '"[a-zA-Z0-9_-]*"$' | tr -d '"' +} + +@test "1. Create webhook using command flags" { + run ./orkes webhook create \ + --name custom_webhook_1 \ + --source-platform Custom \ + --verifier HEADER_BASED \ + --headers "Authorization:BB12346789" \ + --receiver-workflows hello_world:1 + echo "Output: $output" + [ "$status" -eq 0 ] + + # Extract and save webhook ID + WEBHOOK_ID=$(get_webhook_id "$output") + [ -n "$WEBHOOK_ID" ] + echo "$WEBHOOK_ID" > /tmp/webhook_test_id.txt + echo "Created webhook ID: $WEBHOOK_ID" + + # Verify the webhook name is in the output + echo "$output" | grep -q "$WEBHOOK_NAME" +} + +@test "2. Get webhook by ID" { + WEBHOOK_ID=$(cat /tmp/webhook_test_id.txt) + [ -n "$WEBHOOK_ID" ] + + run ./orkes webhook get "$WEBHOOK_ID" + echo "Output: $output" + [ "$status" -eq 0 ] + + # Verify webhook details + echo "$output" | grep -q "$WEBHOOK_NAME" + echo "$output" | grep -q "BB12346789" + echo "$output" | grep -q "hello_world" +} + +@test "3. List webhooks (should include created webhook)" { + WEBHOOK_ID=$(cat /tmp/webhook_test_id.txt) + [ -n "$WEBHOOK_ID" ] + + run ./orkes webhook list + echo "Output: $output" + [ "$status" -eq 0 ] + + # Verify the webhook is in the list + echo "$output" | grep -q "$WEBHOOK_NAME" + echo "$output" | grep -q "$WEBHOOK_ID" +} + +@test "4. List webhooks with JSON output" { + WEBHOOK_ID=$(cat /tmp/webhook_test_id.txt) + [ -n "$WEBHOOK_ID" ] + + run ./orkes webhook list --json + echo "Output: $output" + [ "$status" -eq 0 ] + + # Verify JSON format and webhook presence + echo "$output" | grep -q "$WEBHOOK_NAME" + echo "$output" | grep -q "$WEBHOOK_ID" +} + +@test "5. Update webhook - change Authorization header" { + WEBHOOK_ID=$(cat /tmp/webhook_test_id.txt) + [ -n "$WEBHOOK_ID" ] + + # Create updated webhook JSON with new Authorization value + cat > /tmp/webhook_update.json <