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

topdown: Add urlquery.decode_object builtin #2670

Merged
merged 2 commits into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
12 changes: 12 additions & 0 deletions ast/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ var DefaultBuiltins = [...]*Builtin{
URLQueryDecode,
URLQueryEncode,
URLQueryEncodeObject,
URLQueryDecodeObject,
YAMLMarshal,
YAMLUnmarshal,

Expand Down Expand Up @@ -1283,6 +1284,17 @@ var URLQueryEncodeObject = &Builtin{
),
}

// URLQueryDecodeObject decodes the given URL query string into an object.
var URLQueryDecodeObject = &Builtin{
Name: "urlquery.decode_object",
Decl: types.NewFunction(
types.Args(types.S),
types.NewObject(nil, types.NewDynamicProperty(
types.S,
types.NewArray(nil, types.S))),
),
}

// YAMLMarshal serializes the input term.
var YAMLMarshal = &Builtin{
Name: "yaml.marshal",
Expand Down
25 changes: 25 additions & 0 deletions capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -3043,6 +3043,31 @@
"type": "function"
}
},
{
"name": "urlquery.decode_object",
"decl": {
"args": [
{
"type": "string"
}
],
"result": {
"dynamic": {
"key": {
"type": "string"
},
"value": {
"dynamic": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
},
"type": "function"
}
},
{
"name": "urlquery.encode",
"decl": {
Expand Down
1 change: 1 addition & 0 deletions docs/content/policy-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ The following table shows examples of how ``glob.match`` works:
| <span class="opa-keep-it-together">``output := urlquery.encode(string)``</span> | ``output`` is ``string`` serialized to a URL query parameter encoded string |
| <span class="opa-keep-it-together">``output := urlquery.encode_object(object)``</span> | ``output`` is ``object`` serialized to a URL query parameter encoded string |
| <span class="opa-keep-it-together">``output := urlquery.decode(string)``</span> | ``output`` is ``string`` deserialized from a URL query parameter encoded string |
| <span class="opa-keep-it-together">``output := urlquery.decode_object(string)``</span> | ``output`` is ``object`` deserialized from a URL query parameter string |
| <span class="opa-keep-it-together">``output := json.marshal(x)``</span> | ``output`` is ``x`` serialized to a JSON string |
| <span class="opa-keep-it-together">``output := json.unmarshal(string)``</span> | ``output`` is ``string`` deserialized to a term from a JSON encoded string |
| <span class="opa-keep-it-together">``output := yaml.marshal(x)``</span> | ``output`` is ``x`` serialized to a YAML string |
Expand Down
34 changes: 34 additions & 0 deletions test/cases/testdata/urlbuiltins/test-urlbuiltins-1076.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
cases:
- modules:
- |
package decode_object

p = x {
x = urlquery.decode_object("a=value_a1&b=value_b&a=value_a2")
}
note: urlbuiltins/decode_object multiple
query: data.decode_object.p = x
want_result:
- x: {"a": ["value_a1", "value_a2"], "b": ["value_b"]}
- modules:
- |
package decode_object

p = x {
x = urlquery.decode_object("a=value_a1&b")
}
note: urlbuiltins/decode_object empty parameter
query: data.decode_object.p = x
want_result:
- x: {"a": ["value_a1"], "b": [""]}
- modules:
- |
package decode_object

p = x {
x = urlquery.decode_object("")
}
note: urlbuiltins/decode_object empty string
query: data.decode_object.p = x
want_result:
- x: {}
24 changes: 24 additions & 0 deletions topdown/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,29 @@ func builtinURLQueryEncodeObject(a ast.Value) (ast.Value, error) {
return ast.String(query.Encode()), nil
}

func builtinURLQueryDecodeObject(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
query, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
}

queryParams, err := url.ParseQuery(string(query))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using ParseQuery here, but if it is better to also include the "?" in the query string we could use https://golang.org/pkg/net/url/#URL.Query instead

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this version is fine.

if err != nil {
return err
}

queryObject := ast.NewObject()
for k, v := range queryParams {
paramsArray := make([]*ast.Term, len(v))
for i, param := range v {
paramsArray[i] = ast.StringTerm(param)
}
queryObject.Insert(ast.StringTerm(k), ast.ArrayTerm(paramsArray...))
}

return iter(ast.NewTerm(queryObject))
}

func builtinYAMLMarshal(a ast.Value) (ast.Value, error) {

asJSON, err := ast.JSON(a)
Expand Down Expand Up @@ -212,6 +235,7 @@ func init() {
RegisterFunctionalBuiltin1(ast.URLQueryDecode.Name, builtinURLQueryDecode)
RegisterFunctionalBuiltin1(ast.URLQueryEncode.Name, builtinURLQueryEncode)
RegisterFunctionalBuiltin1(ast.URLQueryEncodeObject.Name, builtinURLQueryEncodeObject)
RegisterBuiltinFunc(ast.URLQueryDecodeObject.Name, builtinURLQueryDecodeObject)
RegisterFunctionalBuiltin1(ast.YAMLMarshal.Name, builtinYAMLMarshal)
RegisterFunctionalBuiltin1(ast.YAMLUnmarshal.Name, builtinYAMLUnmarshal)
}