Skip to content

Commit

Permalink
feat: Add full-message feature and validate output writer flags
Browse files Browse the repository at this point in the history
- Introduced a `full-message` flag to print the entire RabbitMQ message, including headers, exchange, timestamp, and body.
- Added a `writer` flag to specify the output type, which can be either "file" or "console".
- Implemented validation for:
  - The `writer` flag must be "file" or "console".
  - The `output` flag is required when the writer is set to "file".
  - The `file-mode` flag must be either "append" or "overwrite" when the writer is "file".
- Moved flag validation into a new `validateFlags` function.
- Removed redundant output file validation from `validateRequiredFields`.
- Updated flag descriptions in the documentation.
  • Loading branch information
marianozunino committed Nov 27, 2024
1 parent bbeede3 commit a665c8f
Show file tree
Hide file tree
Showing 16 changed files with 602 additions and 362 deletions.
46 changes: 31 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,34 @@ Usage:
goq [command]
Available Commands:
configure Generate a sample configuration file for goq.
dump Dump messages from a RabbitMQ queue to a file.
monitor Monitor RabbitMQ messages using routing keys and a temporary queue.
update Update the goq tool to the latest available version.
version Display the current version of the goq tool.
Additional Commands:
completion Generate the autocompletion script for the specified shell
configure Create a sample configuration file
dump Dump RabbitMQ messages to a file
help Help about any command
monitor Create a temporary queue and consume messages from it having the specified routing keys
update Update GOQ to the latest version
version Print the version number of GOQ
Flags:
-s, --amqps Use AMQPS instead of AMQP
--config string Config file (default is $XDG_CONFIG_HOME/goq/goq.yaml)
-e, --exchange string RabbitMQ exchange name
-m, --file-mode string File mode (append or overwrite) (default "overwrite")
-h, --help Help for GOQ
-o, --output string Output file name (default "messages.txt")
-k, --skip-tls-verify Skip TLS certificate verification (insecure)
-u, --url string RabbitMQ URL (e.g., localhost:5672)
-v, --virtualhost string RabbitMQ virtual host
-p, --pretty-print Pretty print JSON messages
-s, --amqps Use AMQPS instead of AMQP
--config string config file (default "/home/forbi/.config/goq/goq.yaml")
-e, --exchange string RabbitMQ exchange name
-x, --exclude-patterns strings Exclude messages containing these patterns
-m, --file-mode string File mode (append or overwrite, only valid for file writer) (default "overwrite")
-f, --full-message Print full message
-h, --help help for goq
-i, --include-patterns strings Include messages containing these patterns
-j, --json-filter string JSON filter expression
-z, --max-message-size int Maximum message size in bytes (default -1)
-o, --output string Output file name (required when writer is 'file')
-p, --pretty-print Pretty print JSON messages
-R, --regex-filter string Regex pattern to filter messages
-k, --skip-tls-verify Skip TLS certificate verification (insecure)
-u, --url string RabbitMQ URL (e.g., localhost:5672)
-v, --virtualhost string RabbitMQ virtual host
-w, --writer string Output writer type (console or file) (default "file")
Use "goq [command] --help" for more information about a command.
```
Expand Down Expand Up @@ -71,6 +80,10 @@ Use "goq [command] --help" for more information about a command.
```bash
goq dump -u localhost:5672 -q my_queue -m append -o existing_output.txt
```
6. **Print the full message including headers, exchange, and timestamp:**
```bash
goq dump -u localhost:5672 -q my_queue -f
```

## Configuration
GOQ can be configured using a YAML file. By default, it looks for a configuration file at `$XDG_CONFIG_HOME/goq/goq.yaml`.
Expand Down Expand Up @@ -102,6 +115,9 @@ file-mode: "overwrite"

# Pretty print JSON messages
pretty-print: true

# Print full message including headers, exchange, timestamp, and body
full-message: false
```
When using a config file, the flags will override the values in the config file. It's useful to combine the config file with command-line flags for more flexibility.
Expand Down
10 changes: 9 additions & 1 deletion cmd/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,27 @@ This command provides options for automatically acknowledging messages, controll
Messages can be captured from an AMQP or AMQPS RabbitMQ server, with flexible TLS and virtual host settings.`,
Example: `goq dump -q my_queue -o output.txt -a -c`,
GroupID: "available-commands",
PreRunE: validateFlags,
RunE: func(cmd *cobra.Command, args []string) error {
cfg := config.New(
config.WithRabbitMQURL(fmt.Sprintf("%s://%s/%s", getProtocol(), viper.GetString("url"), viper.GetString("virtualhost"))),
config.WithExchange(viper.GetString("exchange")),

config.WithOutputFile(viper.GetString("output")),
config.WithFileMode(viper.GetString("file-mode")),

config.WithWriter(viper.GetString("writer")),
config.WithOutputFile(viper.GetString("output")),
config.WithFileMode(viper.GetString("file-mode")),

config.WithUseAMQPS(viper.GetBool("amqps")),
config.WithVirtualHost(viper.GetString("virtualhost")),
config.WithSkipTLSVerification(viper.GetBool("skip-tls-verify")),
config.WithFileMode(viper.GetString("file-mode")),
config.WithPrettyPrint(viper.GetBool("pretty-print")),
config.WithQueue(queue),
config.WithStopAfterConsume(stopAfterConsume),
config.WithAutoAck(autoAck),
config.WithFullMessage(viper.GetBool("full-message")),

config.WithIncludePatterns(viper.GetStringSlice("include-patterns")),
config.WithExcludePatterns(viper.GetStringSlice("exclude-patterns")),
Expand Down
7 changes: 6 additions & 1 deletion cmd/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,23 @@ func NewMonitorCmd() *cobra.Command {
Long: `Monitor RabbitMQ messages by consuming from a temporary queue that listens to specified routing keys.
This command captures and dumps the received messages to a file for analysis or processing.`,
Example: `goq monitor -r "key1,key2" -o output.txt -s -v my_vhost`,
PreRunE: validateFlags,
RunE: func(cmd *cobra.Command, args []string) error {
cfg := config.New(
config.WithRabbitMQURL(fmt.Sprintf("%s://%s/%s", getProtocol(), viper.GetString("url"), viper.GetString("virtualhost"))),
config.WithExchange(viper.GetString("exchange")),

config.WithWriter(viper.GetString("writer")),
config.WithOutputFile(viper.GetString("output")),
config.WithFileMode(viper.GetString("file-mode")),

config.WithUseAMQPS(viper.GetBool("amqps")),
config.WithVirtualHost(viper.GetString("virtualhost")),
config.WithSkipTLSVerification(viper.GetBool("skip-tls-verify")),
config.WithAutoAck(viper.GetBool("auto-ack")),
config.WithFileMode(viper.GetString("file-mode")),
config.WithPrettyPrint(viper.GetBool("pretty-print")),
config.WithRoutingKeys(routingKeys),
config.WithFullMessage(viper.GetBool("full-message")),

config.WithIncludePatterns(viper.GetStringSlice("include-patterns")),
config.WithExcludePatterns(viper.GetStringSlice("exclude-patterns")),
Expand Down
66 changes: 47 additions & 19 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ var logo = `
\ \_____\ \ \_____\ \ \___\_\
\/_____/ \/_____/ \/___/_/ ` + VersionFromBuild()

var validWriters = []string{"file", "console"}

var rootCmd = &cobra.Command{
Use: "goq",
Short: "A tool to dump RabbitMQ messages to a file",
Expand All @@ -50,7 +52,6 @@ var rootCmd = &cobra.Command{
This application connects to a RabbitMQ server, consumes messages from a specified queue,
and writes them to a file while keeping the messages in the queue.`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
// You can bind cobra and viper in a few locations, but PersistencePreRunE on the root command works well
run(cmd, args)
return nil
},
Expand All @@ -59,16 +60,21 @@ and writes them to a file while keeping the messages in the queue.`,
func init() {
cobra.OnInitialize(initConfig)

// Define the flags
rootCmd.PersistentFlags().StringP("url", "u", "", "RabbitMQ URL (e.g., localhost:5672)")
rootCmd.PersistentFlags().StringP("exchange", "e", "", "RabbitMQ exchange name")
rootCmd.PersistentFlags().StringP("output", "o", "messages.txt", "Output file name")
rootCmd.PersistentFlags().BoolP("amqps", "s", false, "Use AMQPS instead of AMQP")
rootCmd.PersistentFlags().StringP("virtualhost", "v", "", "RabbitMQ virtual host")
rootCmd.PersistentFlags().BoolP("skip-tls-verify", "k", false, "Skip TLS certificate verification (insecure)")
rootCmd.PersistentFlags().StringP("file-mode", "m", "overwrite", "File mode (append or overwrite)")

rootCmd.PersistentFlags().StringP("writer", "w", "file", "Output writer type (console or file)")
rootCmd.PersistentFlags().StringP("output", "o", "", "Output file name (required when writer is 'file')")
rootCmd.PersistentFlags().StringP("file-mode", "m", "overwrite", "File mode (append or overwrite, only valid for file writer)")

rootCmd.PersistentFlags().BoolP("pretty-print", "p", false, "Pretty print JSON messages")
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", xdg.ConfigHome+"/goq/goq.yaml", "config file")

rootCmd.PersistentFlags().BoolP("full-message", "f", false, "Print full message")
rootCmd.PersistentFlags().StringSliceP("include-patterns", "i", []string{}, "Include messages containing these patterns")
rootCmd.PersistentFlags().StringSliceP("exclude-patterns", "x", []string{}, "Exclude messages containing these patterns")
rootCmd.PersistentFlags().StringP("json-filter", "j", "", "JSON filter expression")
Expand All @@ -80,10 +86,47 @@ func init() {
Title: "Available Commands:",
})

// allow to attatch to multiple
// Add flag validation
rootCmd.PreRunE = validateFlags

// Allow viper to bind flags
viper.BindPFlags(rootCmd.PersistentFlags())
}

// validateFlags ensures all flags are correctly set
func validateFlags(cmd *cobra.Command, args []string) error {
writer, _ := cmd.Flags().GetString("writer")
output, _ := cmd.Flags().GetString("output")
fileMode, _ := cmd.Flags().GetString("file-mode")

// Validate writer
if !isValidWriter(writer) {
return fmt.Errorf("invalid writer type '%s', must be one of: %v", writer, validWriters)
}

// Validate output file for file writer
if writer == "file" && output == "" {
return fmt.Errorf("output file is required when using file writer")
}

// Validate file mode
if fileMode != "append" && fileMode != "overwrite" {
return fmt.Errorf("invalid file mode '%s': must be 'append' or 'overwrite'", fileMode)
}

return nil
}

// isValidWriter checks if the writer is valid
func isValidWriter(writer string) bool {
for _, valid := range validWriters {
if writer == valid {
return true
}
}
return false
}

func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
Expand All @@ -100,7 +143,6 @@ func initConfig() {
}

func run(cmd *cobra.Command, args []string) {
// Validate required fields
switch cmd.Use {
case "dump", "monitor":
if err := validateRequiredFields(); err != nil {
Expand All @@ -111,7 +153,6 @@ func run(cmd *cobra.Command, args []string) {
}

func validateRequiredFields() error {
// Validate URL
urlStr := viper.GetString("url")
if urlStr == "" {
return fmt.Errorf("RabbitMQ URL is required")
Expand All @@ -120,19 +161,6 @@ func validateRequiredFields() error {
return fmt.Errorf("invalid RabbitMQ URL: %v", err)
}

// Validate output file
output := viper.GetString("output")
if output == "" {
return fmt.Errorf("output file name is required")
}

// Validate file mode
fileMode := viper.GetString("file-mode")
if fileMode != "append" && fileMode != "overwrite" {
return fmt.Errorf("invalid file mode: must be 'append' or 'overwrite'")
}

// Validate virtual host (optional, but if provided, shouldn't start with '/')
virtualHost := viper.GetString("virtualhost")
if virtualHost != "" && strings.HasPrefix(virtualHost, "/") {
return fmt.Errorf("virtual host should not start with '/'")
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/marianozunino/goq
go 1.23.0

require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/itchyny/gojq v0.12.16
github.com/marianozunino/selfupdater v1.0.1
github.com/spf13/cobra v1.8.1
Expand Down
Loading

0 comments on commit a665c8f

Please sign in to comment.