Add built-ins to un/marshal JSON

This commit is contained in:
Torin Sandall
2017-04-23 10:47:09 -07:00
parent a898de13c9
commit bb6cffb237
3 changed files with 90 additions and 0 deletions
+21
View File
@@ -47,6 +47,9 @@ var DefaultBuiltins = [...]*Builtin{
// Strings
Concat, FormatInt, IndexOf, Substring, Lower, Upper, Contains, StartsWith, EndsWith,
// JSON
JSONMarshal, JSONUnmarshal,
}
// BuiltinMap provides a convenient mapping of built-in names to
@@ -290,6 +293,24 @@ var Upper = &Builtin{
TargetPos: []int{1},
}
/**
* JSON
*/
// JSONMarshal serializes the input term.
var JSONMarshal = &Builtin{
Name: Var("json_marshal"),
NumArgs: 2,
TargetPos: []int{1},
}
// JSONUnmarshal deserializes the input string.
var JSONUnmarshal = &Builtin{
Name: Var("json_unmarshal"),
NumArgs: 2,
TargetPos: []int{1},
}
/**
* Deprecated built-ins.
*/
+49
View File
@@ -0,0 +1,49 @@
// Copyright 2017 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package topdown
import (
"encoding/json"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/topdown/builtins"
"github.com/open-policy-agent/opa/util"
)
func builtinJSONMarshal(a ast.Value) (ast.Value, error) {
asJSON, err := ast.JSON(a)
if err != nil {
return nil, err
}
bs, err := json.Marshal(asJSON)
if err != nil {
return nil, err
}
return ast.String(string(bs)), nil
}
func builtinJSONUnmarshal(a ast.Value) (ast.Value, error) {
str, err := builtins.StringOperand(a, 1)
if err != nil {
return nil, err
}
var x interface{}
if err := util.UnmarshalJSON([]byte(str), &x); err != nil {
return nil, err
}
return ast.InterfaceToValue(x)
}
func init() {
RegisterFunctionalBuiltin1(ast.JSONMarshal.Name, builtinJSONMarshal)
RegisterFunctionalBuiltin1(ast.JSONUnmarshal.Name, builtinJSONUnmarshal)
}
+20
View File
@@ -1144,6 +1144,26 @@ func TestTopDownStrings(t *testing.T) {
}
}
func TestTopDownJSONBuiltins(t *testing.T) {
tests := []struct {
note string
rules []string
expected interface{}
}{
{"marshal", []string{`p = x { json_marshal([{"foo": {1,2,3}}], x) }`}, `"[{\"foo\":[1,2,3]}]"`},
{"unmarshal", []string{`p = x { json_unmarshal("[{\"foo\":[1,2,3]}]", x) }`}, `[{"foo": [1,2,3]}]"`},
{"unmarshal-non-string", []string{`p = x { json_unmarshal(data.a[0], x) }`}, fmt.Errorf("operand 1 must be string but got number")},
}
data := loadSmallTestData()
for _, tc := range tests {
runTopDownTestCase(t, data, tc.note, tc.rules, tc.expected)
}
}
func TestTopDownEmbeddedVirtualDoc(t *testing.T) {
compiler := compileModules([]string{