Skip to content

Commit

Permalink
feat!: switch transformResponse expression language to Expr
Browse files Browse the repository at this point in the history
  • Loading branch information
jahvon committed Dec 27, 2024
1 parent 2c47590 commit 1267582
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 39 deletions.
33 changes: 5 additions & 28 deletions internal/runner/request/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import (
"os"
"path/filepath"

"github.com/itchyny/gojq"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"

"github.com/jahvon/flow/internal/context"
"github.com/jahvon/flow/internal/runner"
"github.com/jahvon/flow/internal/runner/engine"
"github.com/jahvon/flow/internal/services/expr"
"github.com/jahvon/flow/internal/services/rest"
"github.com/jahvon/flow/types/executable"
)
Expand Down Expand Up @@ -63,16 +63,17 @@ func (r *requestRunner) Exec(
return errors.Wrap(err, "request failed")
}

respStr := resp.Body
if requestSpec.TransformResponse != "" {
resp, err = executeJQQuery(requestSpec.TransformResponse, resp)
respStr, err = expr.EvaluateString(requestSpec.TransformResponse, resp)
if err != nil {
return errors.Wrap(err, "unable to transform response")
}
}

logger := ctx.Logger
if requestSpec.LogResponse {
logger.Infox(fmt.Sprintf("Successfully sent request to %s", requestSpec.URL), "response", resp)
logger.Infox(fmt.Sprintf("Successfully sent request to %s", requestSpec.URL), "response", respStr)
} else {
logger.Infof("Successfully sent request to %s", requestSpec.URL)
}
Expand All @@ -92,7 +93,7 @@ func (r *requestRunner) Exec(
}

err = writeResponseToFile(
resp,
respStr,
filepath.Join(targetDir, requestSpec.ResponseFile.Filename),
requestSpec.ResponseFile.SaveAs,
)
Expand All @@ -106,30 +107,6 @@ func (r *requestRunner) Exec(
return nil
}

func executeJQQuery(query, resp string) (string, error) {
var respMap map[string]interface{}
err := json.Unmarshal([]byte(resp), &respMap)
if err != nil {
return "", errors.New("response is not a valid JSON string")
}

jqQuery, err := gojq.Parse(query)
if err != nil {
return "", err
}

iter := jqQuery.Run(respMap)
result, ok := iter.Next()
if !ok {
return "", errors.New("unable to execute jq query")
}
if err, isErr := result.(error); isErr {
return "", err
}

return fmt.Sprintf("%v", result), nil
}

func writeResponseToFile(resp, responseFile string, format executable.RequestResponseFileSaveAs) error {
var formattedResp string
switch format {
Expand Down
17 changes: 16 additions & 1 deletion internal/runner/request/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ var _ = Describe("Request Runner", func() {
},
}

ctx.Logger.EXPECT().Infox(gomock.Any(), gomock.Any(), gomock.Any()).Times(1)
ctx.Logger.EXPECT().Infox(gomock.Any(), gomock.Any(), gomock.Regex("value")).Times(1)
err := requestRnr.Exec(ctx.Ctx, exec, mockEngine, make(map[string]string))
Expect(err).NotTo(HaveOccurred())
})
Expand All @@ -111,5 +111,20 @@ var _ = Describe("Request Runner", func() {
_, err = os.Stat(filepath.Clean(filepath.Join(ctx.Ctx.CurrentWorkspace.Location(), "response.json")))
Expect(err).NotTo(HaveOccurred())
})

It("should transform the response when specified", func() {
exec := &executable.Executable{
Request: &executable.RequestExecutableType{
URL: "https://httpbin.org/get",
Method: executable.RequestExecutableTypeMethodGET,
TransformResponse: `upper(body)`,
LogResponse: true,
},
}

ctx.Logger.EXPECT().Infox(gomock.Any(), gomock.Any(), gomock.Regex("HTTPS://HTTPBIN.ORG")).Times(1)
err := requestRnr.Exec(ctx.Ctx, exec, mockEngine, make(map[string]string))
Expect(err).NotTo(HaveOccurred())
})
})
})
25 changes: 19 additions & 6 deletions internal/services/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ type Request struct {
Timeout time.Duration
}

func SendRequest(reqSpec *Request, validStatusCodes []int) (string, error) {
type Response struct {
Status string `expr:"status"`
Code int `expr:"code"`
Body string `expr:"body"`
Headers http.Header `expr:"headers"`
}

func SendRequest(reqSpec *Request, validStatusCodes []int) (*Response, error) {
setRequestDefaults(reqSpec)
client := http.Client{Timeout: reqSpec.Timeout}
reqURL, err := url.Parse(reqSpec.URL)
if err != nil {
return "", err
return nil, err
}

headers := make(http.Header)
Expand All @@ -44,19 +51,25 @@ func SendRequest(reqSpec *Request, validStatusCodes []int) (string, error) {

httpResp, err := client.Do(&req)
if err != nil {
return "", err
return nil, err
}
defer httpResp.Body.Close()

if !isStatusCodeAccepted(httpResp.StatusCode, validStatusCodes) {
return "", ErrUnexpectedStatusCode
return nil, ErrUnexpectedStatusCode
}

respBody, err := io.ReadAll(httpResp.Body)
if err != nil {
return "", err
return nil, err
}
resp := &Response{
Status: httpResp.Status,
Code: httpResp.StatusCode,
Body: string(respBody),
Headers: httpResp.Header,
}
return string(respBody), nil
return resp, nil
}

func setRequestDefaults(req *Request) {
Expand Down
8 changes: 4 additions & 4 deletions internal/services/rest/rest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ var _ = Describe("Rest", func() {
Method: "GET",
Timeout: 30 * time.Second,
}
body, err := rest.SendRequest(req, []int{http.StatusOK})
resp, err := rest.SendRequest(req, []int{http.StatusOK})
Expect(err).NotTo(HaveOccurred())
Expect(body).To(ContainSubstring("\"url\": \"https://httpbin.org/get\""))
Expect(resp.Body).To(ContainSubstring("\"url\": \"https://httpbin.org/get\""))
})

It("should timeout when the request takes longer than the specified timeout", func() {
Expand All @@ -67,9 +67,9 @@ var _ = Describe("Rest", func() {
Headers: map[string]string{"Test-Header": "Test-Value"},
Timeout: 30 * time.Second,
}
body, err := rest.SendRequest(req, []int{http.StatusOK})
resp, err := rest.SendRequest(req, []int{http.StatusOK})
Expect(err).NotTo(HaveOccurred())
Expect(body).To(ContainSubstring("\"Test-Header\": \"Test-Value\""))
Expect(resp.Body).To(ContainSubstring("\"Test-Header\": \"Test-Value\""))
})
})
})

0 comments on commit 1267582

Please sign in to comment.