From 9485a1b6c2cce729baffaf594344f4094e15b2ee Mon Sep 17 00:00:00 2001 From: nvima Date: Sun, 23 Apr 2023 19:03:06 +0200 Subject: [PATCH 1/2] Implement Argument Support for httpcli Commands This commit introduces the ability to pass arguments to httpcli commands, allowing users to dynamically define values in their configuration files. With this new feature, users can now provide arguments when invoking httpcli, such as: httpcli functionname arg1 "arg2 is here" arg3 The changes include: - Implementing a replaceArgs function that replaces placeholders, such as ${ARG1}, ${ARG2}, etc., in the URL and headers with the respective command line arguments. - Refactoring the existing environment variable replacement logic into a separate replaceEnvVariables function for better code organization. - Updating the getJSONData function to accept the new args parameter and call the replaceVariables function, which in turn calls both replaceArgs and replaceEnvVariables. - Adding a new ReplaceArgs function in the util package to handle argument replacement within the input JSON data. --- cmd/tpl.go | 69 +++++++++++++++++++++++++++++--------------- util/errors.go | 1 + util/replaceStdIn.go | 14 +++++++++ 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/cmd/tpl.go b/cmd/tpl.go index 2948cc0..876ee96 100644 --- a/cmd/tpl.go +++ b/cmd/tpl.go @@ -34,7 +34,7 @@ func tplCommand(cmd *cobra.Command, args []string) error { return err } - output, err := fc.handleFunc(cmd) + output, err := fc.handleFunc(cmd, args) if err != nil { return err } @@ -72,8 +72,49 @@ func initFunctionConfig(cmd *cobra.Command, args []string) (FunctionConfig, erro return funcConfig, nil } -func (fc *FunctionConfig) handleFunc(cmd *cobra.Command) (string, error) { - jsonData, err := fc.getJSONData(cmd) +func (fc *FunctionConfig) replaceArgs(args []string) { + if len(args) <= 1 { + return + } + for i, arg := range args[:1] { + placeholder := fmt.Sprintf("${ARG%d}", i) + + fc.Url = strings.Replace(fc.Url, placeholder, arg, -1) + + for i, header := range fc.Header { + fc.Header[i] = strings.Replace(header, placeholder, arg, -1) + } + } +} + +func (fc *FunctionConfig) replaceEnvVariables() { + for _, env := range fc.Env { + fc.Url = strings.Replace(fc.Url, fmt.Sprintf("${%s}", env), os.Getenv(env), -1) + + for i, header := range fc.Header { + fc.Header[i] = strings.Replace(header, fmt.Sprintf("${%s}", env), os.Getenv(env), -1) + } + } +} + +func (fc *FunctionConfig) replaceVariables(cmd *cobra.Command, args []string, jsonData []byte) ([]byte, error) { + fc.replaceArgs(args) + fc.replaceEnvVariables() + + jsonData, err := util.ReplaceStdIn(jsonData) + if err != nil { + return nil, util.HandleError(cmd, err, util.REPLACE_STDIN_FAILED) + } + return util.ReplaceArgs(jsonData, args), nil +} + +func (fc *FunctionConfig) handleFunc(cmd *cobra.Command, args []string) (string, error) { + jsonData, err := fc.getJSONData(cmd, args) + if err != nil { + return "", err + } + + jsonData, err = fc.replaceVariables(cmd, args, jsonData) if err != nil { return "", err } @@ -92,15 +133,12 @@ func (fc *FunctionConfig) handleFunc(cmd *cobra.Command) (string, error) { } func (fc *FunctionConfig) makeHttpCall(jsonData []byte, cmd *cobra.Command) (map[string]interface{}, error) { - url := fc.replaceEnvVariables(fc.Url) - req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) + req, err := http.NewRequest("POST", fc.Url, bytes.NewBuffer(jsonData)) if err != nil { return nil, util.HandleError(cmd, err, util.INIT_HTTP_POST_REQUEST_FAILED) } for _, header := range fc.Header { - header = fc.replaceEnvVariables(header) - headerParts := strings.SplitN(header, ":", 2) if len(headerParts) == 2 { req.Header.Set(strings.TrimSpace(headerParts[0]), strings.TrimSpace(headerParts[1])) @@ -133,26 +171,11 @@ func (fc *FunctionConfig) makeHttpCall(jsonData []byte, cmd *cobra.Command) (map return responseData, nil } -func (fc *FunctionConfig) getJSONData(cmd *cobra.Command) ([]byte, error) { +func (fc *FunctionConfig) getJSONData(cmd *cobra.Command, args []string) ([]byte, error) { jsonData, err := json.Marshal(fc.Data) if err != nil { return nil, util.HandleError(cmd, err, util.MARSHAL_DATA_FAILED) } - jsonData, err = util.ReplaceStdIn(jsonData) - if err != nil { - return nil, util.HandleError(cmd, err, util.REPLACE_STDIN_FAILED) - } - return jsonData, nil } - -func (fc *FunctionConfig) replaceEnvVariables(value string) string { - for _, envVar := range fc.Env { - envValue := os.Getenv(envVar) - placeholder := fmt.Sprintf("${%s}", envVar) - value = strings.Replace(value, placeholder, envValue, -1) - } - - return value -} diff --git a/util/errors.go b/util/errors.go index 8f70444..6e48d58 100644 --- a/util/errors.go +++ b/util/errors.go @@ -19,3 +19,4 @@ var REPLACE_STDIN_FAILED = errors.New("Failed to replace stdin") var INIT_HTTP_POST_REQUEST_FAILED = errors.New("An error occurred while initializing the HTTP POST request. Possible causes could be an invalid URL or incorrect input parameters.") var SEND_HTTP_POST_REQUEST_FAILED = errors.New("An error occurred while sending the HTTP POST request. Possible causes could be network issues, server unavailability, or issues with the request payload.") var READ_RESPONSE_BODY_FAILED = errors.New("Failed to read the response body") +var REPLACE_ARGS_FAILED = errors.New("Failed to replace args") diff --git a/util/replaceStdIn.go b/util/replaceStdIn.go index 0fa731e..3d3ca79 100644 --- a/util/replaceStdIn.go +++ b/util/replaceStdIn.go @@ -45,6 +45,20 @@ func ReplaceStdIn(input []byte) ([]byte, error) { return input, nil } +func ReplaceArgs(input []byte, args []string) []byte { + if len(args) <= 1 { + return input + } + + inputStr := string(input) + for i, arg := range args[1:] { + arg = removeControlChars(arg) + inputStr = strings.Replace(inputStr, fmt.Sprintf("${ARG%d}", i+1), arg, -1) + } + + return []byte(inputStr) +} + func ParseJSONResponse(jsonData []byte) (map[string]interface{}, error) { var data map[string]interface{} err := json.Unmarshal(jsonData, &data) From cc70e85ddc768a69616690e4cec2fe728fcc377e Mon Sep 17 00:00:00 2001 From: nvima Date: Sun, 23 Apr 2023 19:18:41 +0200 Subject: [PATCH 2/2] Add example of passing command line arguments to API requests This commit adds documentation on how to pass dynamic values to API requests using command line arguments in the YAML configuration file. The placeholders ${ARG1}, ${ARG2}, and so on, can be used to represent the command line arguments provided when invoking `httpcli`. An example is provided showing how to use this feature with a translation API. Additionally, the commit reorders the table of contents for better organization and readability. --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 400cf73..f218c31 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,15 @@ You can easily define your API requests and customize the output to extract spec The tool supports environment variables, allowing you to store sensitive data, such as API keys, securely. You can also pipe the stdin to your API Response with ${STDIN}. -## Example YAML Configuration +- [Example YAML Configuration](#example-yaml-configuration) +- [Usage Examples](#usage-examples) + - [Translate Text](#translate-text) + - [Generate Git Commit Message](#generate-git-commit-message) +- [YAML Attributes](#yaml-attributes) +- [Passing Arguments to httpcli Commands](#passing-arguments-to-httpcli-commands) +- [CLI Flags](#cli-flags) +## Example YAML Configuration Here's an example of a YAML configuration file that includes two different API requests: `gitdiff` and `translate`. ~/.httpcli.yaml ``` @@ -75,6 +82,22 @@ In your YAML configuration file, the following attributes can be used to define This structure allows you to easily define and customize your API requests within the YAML configuration file. +## Passing Arguments to httpcli Commands + +With `httpcli`, you can pass dynamic values to your commands using the `${ARG1}`, `${ARG2}`, and so on, placeholders in your YAML configuration file. These placeholders will be replaced with the respective command line arguments provided when invoking `httpcli`. + +For example, let's say you have a translation API configured in your YAML file, and you want to translate a given word or phrase in a target_lang. You can use the `${ARG1}` and `${ARG2}` placeholder in your YAML configuration to represent the text you want to translate: + +```yaml +translate: + url: "https://api.example.com/translate?text=${ARG1}&target_lang={ARG2}" + output: "translations[0].text" +``` +Now, when you call httpcli with the translate command and provide a text argument, the ${ARG1} placeholder will be replaced with the provided text: +``` +httpcli translate "A green Apple" fr +``` + ## CLI Flags `httpcli` supports various flags to customize its behavior and provide additional functionality. These flags can be passed along with the command to modify the tool's behavior: