Feat: urldecode function #1234 (#1283)

Signed-off-by: pooriaghaedi <pooria.ghaedi@gmail.com>
This commit is contained in:
Pooria Ghaedi 2024-03-01 18:39:45 +03:30 committed by GitHub
parent 835dcb8121
commit b5889b10eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 211 additions and 1 deletions

View File

@ -522,6 +522,10 @@ var DescriptionList = map[string]descriptionEntry{
Description: "`urlencode` applies URL encoding to a given string.",
ParamDescription: []string{""},
},
"urldecode": {
Description: "`urldecode` applies URL decoding to a given encoded string.",
ParamDescription: []string{""},
},
"uuid": {
Description: "`uuid` generates a unique identifier string.",
ParamDescription: []string{},

View File

@ -227,6 +227,26 @@ var URLEncodeFunc = function.New(&function.Spec{
},
})
// URLDecodeFunc constructs a function that applies URL decoding to a given encoded string.
var URLDecodeFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
RefineResult: refineNotNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
query, err := url.QueryUnescape(args[0].AsString())
if err != nil {
return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode URL '%s': %v", query, err)
}
return cty.StringVal(query), nil
},
})
// Base64Decode decodes a string containing a base64 sequence.
//
// OpenTofu uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
@ -281,6 +301,16 @@ func URLEncode(str cty.Value) (cty.Value, error) {
return URLEncodeFunc.Call([]cty.Value{str})
}
// URLDecode decodes a URL encoded string.
//
// This function decodes the given string that has been encoded.
//
// If the given string contains non-ASCII characters, these are first encoded as
// UTF-8 and then percent decoding is applied separately to each UTF-8 byte.
func URLDecode(str cty.Value) (cty.Value, error) {
return URLDecodeFunc.Call([]cty.Value{str})
}
// TextEncodeBase64 applies Base64 encoding to a string that was encoded before with a target encoding.
//
// OpenTofu uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.

View File

@ -243,6 +243,141 @@ func TestURLEncode(t *testing.T) {
}
}
func TestURLDecode(t *testing.T) {
tests := []struct {
String cty.Value
Want cty.Value
Err bool
}{
{
cty.StringVal("abc123-_"),
cty.StringVal("abc123-_"),
false,
},
{
cty.StringVal("foo%3Abar%40localhost%3Ffoo%3Dbar%26bar%3Dbaz"),
cty.StringVal("foo:bar@localhost?foo=bar&bar=baz"),
false,
},
{
cty.StringVal("mailto%3Aemail%3Fsubject%3Dthis%2Bis%2Bmy%2Bsubject"),
cty.StringVal("mailto:email?subject=this+is+my+subject"),
false,
},
{
cty.StringVal("foo%2Fbar"),
cty.StringVal("foo/bar"),
false,
},
{
cty.StringVal("foo% bar"),
cty.UnknownVal(cty.String),
true,
},
{
cty.StringVal("foo%2 bar"),
cty.UnknownVal(cty.String),
true,
},
{
cty.StringVal("%GGfoo%2bar"),
cty.UnknownVal(cty.String),
true,
},
{
cty.StringVal("foo%00, bar!"),
cty.StringVal("foo\x00, bar!"),
false,
},
{
cty.StringVal("hello%20%E4%B8%96%E7%95%8C"), //Unicode character support
cty.StringVal("hello 世界"),
false,
},
{
cty.StringVal("hello%20%D8%AF%D9%86%DB%8C%D8%A7"), //Unicode character support
cty.StringVal("hello دنیا"),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("urldecode(%#v)", test.String), func(t *testing.T) {
got, err := URLDecode(test.String)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestURLEncodeDecode(t *testing.T) {
tests := []struct {
String cty.Value
Want cty.Value
Err bool
}{
{
cty.StringVal("abc123-_"),
cty.StringVal("abc123-_"),
false,
},
{
cty.StringVal("foo:bar@localhost?foo=bar&bar=baz"),
cty.StringVal("foo:bar@localhost?foo=bar&bar=baz"),
false,
},
{
cty.StringVal("mailto:email?subject=this+is+my+subject"),
cty.StringVal("mailto:email?subject=this+is+my+subject"),
false,
},
{
cty.StringVal("foo/bar"),
cty.StringVal("foo/bar"),
false,
},
{
cty.StringVal("foo%00, bar!"),
cty.StringVal("foo%00, bar!"),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("url encode decode(%#v)", test.String), func(t *testing.T) {
encoded, err := URLEncode(test.String)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
got, err := URLDecode(encoded)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
return
} else if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if !got.RawEquals(test.Want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
}
})
}
}
func TestBase64TextEncode(t *testing.T) {
tests := []struct {
String cty.Value

View File

@ -148,6 +148,7 @@ func (s *Scope) Functions() map[string]function.Function {
"try": tryfunc.TryFunc,
"upper": stdlib.UpperFunc,
"urlencode": funcs.URLEncodeFunc,
"urldecode": funcs.URLDecodeFunc,
"uuid": funcs.UUIDFunc,
"uuidv5": funcs.UUIDV5Func,
"values": stdlib.ValuesFunc,

View File

@ -1132,6 +1132,12 @@ func TestFunctions(t *testing.T) {
cty.StringVal("foo%3Abar%40localhost%3Ffoo%3Dbar%26bar%3Dbaz"),
},
},
"urldecode": {
{
`urldecode("foo%3Abar%40localhost%3Ffoo%3Dbar%26bar%3Dbaz")`,
cty.StringVal("foo:bar@localhost?foo=bar&bar=baz"),
},
},
"uuidv5": {
{

View File

@ -541,6 +541,10 @@
"title": "<code>urlencode</code>",
"path": "language/functions/urlencode"
},
{
"title": "<code>urldecode</code>",
"path": "language/functions/urldecode"
},
{
"title": "<code>yamldecode</code>",
"path": "language/functions/yamldecode"
@ -1180,6 +1184,11 @@
"title": "urlencode",
"path": "language/functions/urlencode",
"hidden": true
},
{
"title": "urldecode",
"path": "language/functions/urldecode",
"hidden": true
},
{ "title": "uuid", "path": "language/functions/uuid", "hidden": true },
{
@ -1358,4 +1367,4 @@
"title": "v1.x Compatibility Promises",
"path": "language/v1-compatibility-promises"
}
]
]

View File

@ -0,0 +1,25 @@
---
sidebar_label: urldecode
description: The urldecode function applies URL decoding to a given string.
---
# `urldecode` Function
`urldecode` targets encoded characters within a string.
The function is capable of decoding a comprehensive range of characters,
including those outside the ASCII range. Non-ASCII characters are first treated as UTF-8 bytes,
followed by the application of percent decoding to each byte,
facilitating the accurate decoding of multibyte characters.
## Examples
```
> urldecode("Hello+World%21")
Hello World!
> urldecode("%E2%98%83")
> urldecode("foo%3Abar%40localhost%3Ffoo%3Dbar%26bar%3Dbaz")
foo:bar@localhost?foo=bar&bar=baz
```