Skip to content

Commit

Permalink
Fix: Interpolate variables into MultiHTTP request bodies (#713)
Browse files Browse the repository at this point in the history
Update the MultiHTTP script template to interpolate variables in the body using JS.

Uses string.replace() to swap the variable placeholders with the variable value.
  • Loading branch information
The-9880 committed May 30, 2024
1 parent 67934ee commit bac84cb
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 8 deletions.
51 changes: 45 additions & 6 deletions internal/prober/multihttp/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,44 @@ func buildBody(body *sm.HttpRequestBody) string {
}
}

func interpolateBodyVariables(bodyVarName string, body *sm.HttpRequestBody) []string {
switch {
case body == nil || len(body.Payload) == 0:
return nil

default:
var buf strings.Builder

matches := userVariables.FindAllString(string(body.Payload), -1)
parsedMatches := make(map[string]struct{})
out := make([]string, 0, len(matches))

// For every instance of ${variable} in the body,
// this block returns {bodyVarName}.replace('${variable}', vars['variable'])
for _, m := range matches {
if _, found := parsedMatches[m]; found {
continue
}

buf.Reset()
buf.WriteString(bodyVarName)
buf.WriteString(".replace('")
buf.WriteString(m)
buf.WriteString("', vars['")
// writing the variable name from between ${ and }
for i := 2; i < len(m)-1; i++ {
buf.WriteByte(m[i])
}
buf.WriteString("'])")
out = append(out, buf.String())

parsedMatches[m] = struct{}{}
}

return out
}
}

func buildHeaders(headers []*sm.HttpHeader, body *sm.HttpRequestBody) string {
var buf strings.Builder

Expand Down Expand Up @@ -387,12 +425,13 @@ func settingsToScript(settings *sm.MultiHttpSettings) ([]byte, error) {
tmpl, err := template.
New("").
Funcs(template.FuncMap{
"buildBody": buildBody,
"buildChecks": buildChecks,
"buildHeaders": buildHeaders,
"buildUrl": performVariableExpansion,
"buildQueryParams": buildQueryParams,
"buildVars": buildVars,
"buildBody": buildBody,
"buildChecks": buildChecks,
"buildHeaders": buildHeaders,
"buildUrl": performVariableExpansion,
"buildQueryParams": buildQueryParams,
"buildVars": buildVars,
"interpolateBodyVars": interpolateBodyVariables,
}).
ParseFS(templateFS, "*.tmpl")
if err != nil {
Expand Down
10 changes: 8 additions & 2 deletions internal/prober/multihttp/script.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function assertHeader(headers, name, matcher) {

export default function() {
let response;
let body;
let url;
let currentCheck;
let match;
Expand All @@ -87,9 +88,14 @@ export default function() {
{{ range $queries }}{{ . }};
{{ end -}}
{{- $method := .Request.Method.String }}
{{- $body := buildBody .Request.Body }}

body = {{ buildBody .Request.Body }};
{{- $bodyVars := interpolateBodyVars "body" .Request.Body }}
{{ range $bodyVars }}{{ . }};
{{ end -}}

{{- $headers := buildHeaders .Request.Headers .Request.Body }}
response = http.request('{{ $method }}', url.toString(), {{ $body }}, {
response = http.request('{{ $method }}', url.toString(), body, {
// TODO(mem): build params out of options for the check
tags: {
name: '{{ $idx }}', // TODO(mem): give the user some control over this?
Expand Down
39 changes: 39 additions & 0 deletions internal/prober/multihttp/script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,45 @@ func TestBuildBody(t *testing.T) {
}
}

func TestInterpolateBodyVariables(t *testing.T) {
t.Parallel()

type input struct {
body *sm.HttpRequestBody
}

testcases := map[string]struct {
input input
expected []string
}{
"no variables": {
input: input{body: &sm.HttpRequestBody{Payload: []byte("test")}},
expected: []string{},
},
"basic": {
input: input{body: &sm.HttpRequestBody{Payload: []byte("test ${variable1}")}},
expected: []string{
"body.replace('${variable1}', vars['variable1'])",
},
},
"several variables with repeats": {
input: input{body: &sm.HttpRequestBody{Payload: []byte("${variable1} is ${variable1} fun ${variable2} ok ${variable3}")}},
expected: []string{
"body.replace('${variable1}', vars['variable1'])",
"body.replace('${variable2}', vars['variable2'])",
"body.replace('${variable3}', vars['variable3'])",
},
},
}
for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
t.Parallel()
actual := interpolateBodyVariables("body", tc.input.body)
require.Equal(t, tc.expected, actual)
})
}
}

func TestAssertionConditionName(t *testing.T) {
testcases := map[string]struct {
condition sm.MultiHttpEntryAssertionConditionVariant
Expand Down

0 comments on commit bac84cb

Please sign in to comment.