diff --git a/cmd/testdata/config.yaml b/cmd/testdata/config.yaml index c4e25c6..9e73832 100644 --- a/cmd/testdata/config.yaml +++ b/cmd/testdata/config.yaml @@ -9,6 +9,7 @@ gitdiff: - role: "user" content: "Hello" output: "choices[0].message.content" + statuscode: 200 env: - "TEST_API_KEY" - "TEST_SERVER_URL" diff --git a/cmd/tpl.go b/cmd/tpl.go index 5a18a62..778529e 100644 --- a/cmd/tpl.go +++ b/cmd/tpl.go @@ -16,51 +16,61 @@ import ( ) type FunctionConfig struct { - Header []string `mapstructure:"header"` - Data map[string]interface{} `mapstructure:"data"` - Env []string `mapstructure:"env"` - Url string `mapstructure:"url"` - Output string `mapstructure:"output"` + Header []string `mapstructure:"header"` + Data map[string]interface{} `mapstructure:"data"` + Env []string `mapstructure:"env"` + Url string `mapstructure:"url"` + Output string `mapstructure:"output"` + StatusCode int `mapstructure:"statuscode"` } type AppConfig struct { Functions map[string]FunctionConfig } +//TODO Better error handling for testing func tplCommand(cmd *cobra.Command, args []string) { + fc := initFunctionConfig(cmd, args) + output := fc.handleFunc(cmd) + fmt.Fprintf(cmd.OutOrStdout(), output) +} + +func initFunctionConfig(cmd *cobra.Command, args []string) FunctionConfig { + fc := FunctionConfig{} config := viper.AllSettings() + if len(config) == 0 { - panic("No config found") + util.HandleError(cmd, util.NO_FUNC_NAME_ERR.Err(), util.NO_CONFIG_FILE_ERR) } if len(args) == 0 { - panic("No function name provided") + util.HandleError(cmd, util.NO_FUNC_NAME_ERR.Err(), util.NO_FUNC_NAME_ERR) } var appConfig AppConfig err := mapstructure.Decode(config, &appConfig.Functions) if err != nil { - panic("Failed to decode config: " + err.Error()) + util.HandleError(cmd, err, util.INVALID_CONFIG_ERR) } fc, ok := appConfig.Functions[args[0]] if !ok { - panic("No config found for function: " + args[0]) + util.HandleError(cmd, util.NO_FUNC_FOUND_ERR.Err(), util.NO_FUNC_FOUND_ERR) } - - fmt.Fprintf(cmd.OutOrStdout(), fc.handleFunc()) -} -func (fc *FunctionConfig) handleFunc() string { - jsonData := fc.getJSONData() + return fc +} - req, err := http.NewRequest("POST", fc.replaceEnvVariables(fc.Url), bytes.NewBuffer(jsonData)) +func (fc *FunctionConfig) makeHttpCall(jsonData []byte, cmd *cobra.Command) ([]byte, error) { + url := fc.replaceEnvVariables(fc.Url) + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) if err != nil { - panic("Failed to create request: " + err.Error()) + return nil, err } 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])) @@ -70,42 +80,59 @@ func (fc *FunctionConfig) handleFunc() string { client := &http.Client{} resp, err := client.Do(req) if err != nil { - panic("Failed to send request: " + err.Error()) + return nil, err } - // Read the response body + body, err := ioutil.ReadAll(resp.Body) if err != nil { - panic("Failed to read response body: " + err.Error()) + return nil, err } defer resp.Body.Close() - // Check if the request was successful - if resp.StatusCode != http.StatusOK { - panic("Request failed, Status: " + resp.Status + ", Body: " + string(body)) + // Check if the request was successful when a status code is provided + if fc.StatusCode != 0 && resp.StatusCode != fc.StatusCode { + err := fmt.Errorf("Request failed with status code %d, Body: %s", resp.StatusCode, string(body)) + util.HandleError(cmd, err, util.INVALID_RESP_CODE) + } + return body, nil +} + +func (fc *FunctionConfig) handleFunc(cmd *cobra.Command) string { + jsonData, err := fc.getJSONData() + if err != nil { + util.HandleError(cmd, err, util.FAILED_TO_GET_DATA) + } + + body, err := fc.makeHttpCall(jsonData, cmd) + if err != nil { + util.HandleError(cmd, err, util.FAILED_TO_MAKE_HTTP_CALL) } - // Parse the JSON response responseData, err := util.ParseJSONResponse(body) if err != nil { - panic(err) + util.HandleError(cmd, err, util.FAILED_TO_PARSE_JSON) } - // Extract the desired output from the JSON response - return util.GetOutputField(responseData, fc.Output) + output, err := util.GetOutputField(responseData, fc.Output) + if err != nil { + util.HandleError(cmd, err, util.FAILED_TO_PARSE_OUTPUT_FIELD) + } + + return output } -func (fc *FunctionConfig) getJSONData() []byte { +func (fc *FunctionConfig) getJSONData() ([]byte, error) { jsonData, err := json.Marshal(fc.Data) if err != nil { - panic(err) + return nil, err } jsonData, err = util.ReplaceStdIn(jsonData) if err != nil { - panic(err) + return nil, err } - return jsonData + return jsonData, nil } func (fc *FunctionConfig) replaceEnvVariables(value string) string { @@ -114,5 +141,6 @@ func (fc *FunctionConfig) replaceEnvVariables(value string) string { placeholder := fmt.Sprintf("${%s}", envVar) value = strings.Replace(value, placeholder, envValue, -1) } + return value } diff --git a/util/errors.go b/util/errors.go new file mode 100644 index 0000000..fd44b91 --- /dev/null +++ b/util/errors.go @@ -0,0 +1,63 @@ +package util + +import ( + "fmt" +) + +type TplError struct { + msg string + err error + exitCode int +} + +func (te TplError) Err() error { + return fmt.Errorf(te.msg) +} + +func (te TplError) Msg() string { + return te.msg +} + +func (te TplError) ExitCode() int { + return te.exitCode +} + +var NO_CONFIG_FILE_ERR = TplError{ + msg: "No config file found", + exitCode: 1, +} + +var NO_FUNC_NAME_ERR = TplError{ + msg: "No function name provided", + exitCode: 2, +} +var NO_FUNC_FOUND_ERR = TplError{ + msg: "Function not found in config", + exitCode: 3, +} + +var INVALID_CONFIG_ERR = TplError{ + msg: "Invalid config file", + exitCode: 4, +} + +var INVALID_RESP_CODE = TplError{ + msg: "Invalid response code", + exitCode: 5, +} +var FAILED_TO_GET_DATA = TplError{ + msg: "Failed to get data", + exitCode: 6, +} +var FAILED_TO_MAKE_HTTP_CALL = TplError{ + msg: "Failed to make http call", + exitCode: 7, +} +var FAILED_TO_PARSE_JSON = TplError{ + msg: "Failed to parse JSON", + exitCode: 8, +} +var FAILED_TO_PARSE_OUTPUT_FIELD = TplError{ + msg: "Failed to parse output field", + exitCode: 9, +} diff --git a/util/handleError.go b/util/handleError.go index fd47efd..86ea0ca 100644 --- a/util/handleError.go +++ b/util/handleError.go @@ -3,10 +3,12 @@ package util import ( "fmt" "os" + + "github.com/spf13/cobra" ) -func HandleError(err error, msg string) { - // fmt.Println(err) - fmt.Println(msg) - os.Exit(1) +func HandleError(cmd *cobra.Command, err error, tplError TplError) { + // fmt.Println(err) + fmt.Fprintf(cmd.OutOrStdout(), tplError.msg) + os.Exit(tplError.exitCode) } diff --git a/util/replaceStdIn.go b/util/replaceStdIn.go index c09b55a..0fa731e 100644 --- a/util/replaceStdIn.go +++ b/util/replaceStdIn.go @@ -54,7 +54,7 @@ func ParseJSONResponse(jsonData []byte) (map[string]interface{}, error) { return data, nil } -func GetOutputField(data interface{}, fieldPath string) string { +func GetOutputField(data interface{}, fieldPath string) (string, error) { keys := strings.Split(fieldPath, ".") var result interface{} = data @@ -67,14 +67,14 @@ func GetOutputField(data interface{}, fieldPath string) string { index := key[strings.Index(key, "[")+1 : strings.Index(key, "]")] m, ok := result.(map[string]interface{})[innerKey].([]interface{}) if !ok { - panic("invalid output path") + return "", fmt.Errorf("invalid output path") } intVar, _ := strconv.Atoi(index) result = m[intVar] } else { m, ok := result.(map[string]interface{})[key] if !ok { - panic("invalid output path") + return "", fmt.Errorf("invalid output path") } result = m } @@ -83,12 +83,14 @@ func GetOutputField(data interface{}, fieldPath string) string { if _, ok := result.(map[string]interface{}); ok { jsonResult, err := json.Marshal(result) if err != nil { - panic(err) + return "", err } - return string(jsonResult) + return string(jsonResult), nil } + if _, ok := result.(string); ok { - return result.(string) + return result.(string), nil } - panic("invalid output path") + + return "", fmt.Errorf("invalid output path") }