Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions cmd/testdata/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ gitdiff:
- role: "user"
content: "Hello"
output: "choices[0].message.content"
statuscode: 200
env:
- "TEST_API_KEY"
- "TEST_SERVER_URL"
Expand Down
88 changes: 58 additions & 30 deletions cmd/tpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand All @@ -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 {
Expand All @@ -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
}
63 changes: 63 additions & 0 deletions util/errors.go
Original file line number Diff line number Diff line change
@@ -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,
}
10 changes: 6 additions & 4 deletions util/handleError.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
16 changes: 9 additions & 7 deletions util/replaceStdIn.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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")
}