Skip to content

Commit

Permalink
fix(controller): expressions: fix quote() of a json object (akuity#3111)
Browse files Browse the repository at this point in the history
Signed-off-by: Kent Rancourt <[email protected]>
  • Loading branch information
krancour authored and fykaa committed Dec 20, 2024
1 parent 38f9496 commit 33d352f
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 8 deletions.
11 changes: 7 additions & 4 deletions docs/docs/35-references/10-promotion-steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -1300,7 +1300,7 @@ with a wide variety of external services.
| `queryParams` | `[]object` | N | A list of query parameters to include in the request. |
| `queryParams[].name` | `string` | Y | The name of the query parameter. |
| `queryParams[].value` | `string` | Y | The value of the query parameter. The provided value will automatically be URL-encoded if necessary. |
| `body` | `string` | N | The body of the request. |
| `body` | `string` | N | The body of the request. __Note:__ As this field is a `string`, take care to utilize [`quote()`](./20-expression-language.md#quote) if the body is a valid JSON `object`. Refer to the example below of posting a message to a Slack channel. |
| `insecureSkipTLSVerify` | `boolean` | N | Indicates whether to bypass TLS certificate verification when making the request. Setting this to `true` is highly discouraged. |
| `timeout` | `string` | N | A string representation of the maximum time interval to wait for a request to complete. _This is the timeout for an individual HTTP request. If a request is retried, each attempt is independently subject to this timeout._ See Go's [`time` package docs](https://pkg.go.dev/time#ParseDuration) for a description of the accepted format. |
| `successExpression` | `string` | N | An [expr-lang] expression that can evaluate the response to determine success. If this is left undefined and `failureExpression` _is_ defined, the default success criteria will be the inverse of the specified failure criteria. If both are left undefined, success is `true` when the HTTP status code is `2xx`. If `successExpression` and `failureExpression` are both defined and both evaluate to `true`, the failure takes precedence. Note that this expression should _not_ be offset by `${{` and `}}`. See examples for more details. |
Expand Down Expand Up @@ -1440,6 +1440,9 @@ This examples is adapted from
[Slack's own documentation](https://api.slack.com/tutorials/tracks/posting-messages-with-curl):

```yaml
vars:
- name: slackChannel
value: C123456
steps:
# ...
- uses: http
Expand All @@ -1452,8 +1455,8 @@ steps:
- name: Content-Type
value: application/json
body: |
{
"channel": ${{ vars.slackChannel }},
${{ quote({
"channel": vars.slackChannel,
"blocks": [
{
"type": "section",
Expand All @@ -1463,7 +1466,7 @@ steps:
}
}
]
}
}) }}
```

</TabItem>
Expand Down
8 changes: 7 additions & 1 deletion internal/expressions/json_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,13 @@ func EvaluateTemplate(template string, env map[string]any, exprOpts ...expr.Opti
}
exprOpts = append(exprOpts, expr.Function(
"quote",
func(a ...any) (any, error) { return fmt.Sprintf(`"%v"`, a[0]), nil },
func(a ...any) (any, error) {
jsonBytes, err := json.Marshal(a[0])
if err != nil {
return nil, fmt.Errorf("error applying quote() function: %w", err)
}
return fmt.Sprintf(`"%s"`, jsonBytes), nil
},
new(func(any) string),
))
t, err := fasttemplate.NewTemplate(template, "${{", "}}")
Expand Down
36 changes: 33 additions & 3 deletions internal/expressions/json_templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,43 @@ func TestEvaluateJSONTemplate(t *testing.T) {
},
},
{
name: "quote function forces string result",
jsonTemplate: `{ "AString": "${{ quote(anInt) }}" }`,
name: "quote function forces null to string",
jsonTemplate: `{ "AString": "${{ quote(null) }}" }`,
assertions: func(t *testing.T, jsonOutput []byte, err error) {
require.NoError(t, err)
parsed := testStruct{}
require.NoError(t, json.Unmarshal(jsonOutput, &parsed))
require.Equal(t, fmt.Sprintf("%d", testEnv["anInt"]), parsed.AString)
require.Equal(t, "null", parsed.AString)
},
},
{
name: "quote function forces bool to string",
jsonTemplate: `{ "AString": "${{ quote(true) }}" }`,
assertions: func(t *testing.T, jsonOutput []byte, err error) {
require.NoError(t, err)
parsed := testStruct{}
require.NoError(t, json.Unmarshal(jsonOutput, &parsed))
require.Equal(t, "true", parsed.AString)
},
},
{
name: "quote function forces number to string",
jsonTemplate: `{ "AString": "${{ quote(42) }}" }`,
assertions: func(t *testing.T, jsonOutput []byte, err error) {
require.NoError(t, err)
parsed := testStruct{}
require.NoError(t, json.Unmarshal(jsonOutput, &parsed))
require.Equal(t, "42", parsed.AString)
},
},
{
name: "quote function forces object to string",
jsonTemplate: `{ "AString": "${{ quote({ 'foo': 'bar' }) }}" }`,
assertions: func(t *testing.T, jsonOutput []byte, err error) {
require.NoError(t, err)
parsed := testStruct{}
require.NoError(t, json.Unmarshal(jsonOutput, &parsed))
require.Equal(t, `{"foo":"bar"}`, parsed.AString)
},
},
}
Expand Down

0 comments on commit 33d352f

Please sign in to comment.