Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(backport release-1.1): fix(controller): expressions: fix quote() of a json object #3116

Merged
merged 1 commit into from
Dec 10, 2024
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
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 @@ -1299,7 +1299,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 @@ -1439,6 +1439,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 @@ -1451,8 +1454,8 @@ steps:
- name: Content-Type
value: application/json
body: |
{
"channel": ${{ vars.slackChannel }},
${{ quote({
"channel": vars.slackChannel,
"blocks": [
{
"type": "section",
Expand All @@ -1462,7 +1465,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 @@
}
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)
}

Check warning on line 112 in internal/expressions/json_templates.go

View check run for this annotation

Codecov / codecov/patch

internal/expressions/json_templates.go#L111-L112

Added lines #L111 - L112 were not covered by tests
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
Loading