func parseRequest(s []string) (ast.Value, bool, error) { pairs := make([][2]*ast.Term, len(s)) nonGround := false for i := range s { var k *ast.Term var v *ast.Term var err error if s[i][0] == ':' { k = ast.NewTerm(ast.EmptyRef()) v, err = ast.ParseTerm(s[i][1:]) } else { v, err = ast.ParseTerm(s[i]) if err == nil { k = ast.NewTerm(ast.EmptyRef()) } else { vs := strings.SplitN(s[i], ":", 2) if len(vs) != 2 { return nil, false, errRequestPathFormat } k, err = ast.ParseTerm(ast.RequestRootDocument.String() + "." + vs[0]) if err != nil { return nil, false, errRequestPathFormat } v, err = ast.ParseTerm(vs[1]) } } if err != nil { return nil, false, err } pairs[i] = [...]*ast.Term{k, v} if !nonGround { ast.WalkVars(v, func(x ast.Var) bool { if x.Equal(ast.DefaultRootDocument.Value) { return false } nonGround = true return true }) } } request, err := topdown.MakeRequest(pairs) if err != nil { return nil, false, err } return request, nonGround, nil }
// Build initializes the references' index by walking the store for the reference and // creating the index that maps values to bindings. func (ind *indices) Build(ctx context.Context, store Store, txn Transaction, ref ast.Ref) error { index := newBindingIndex() ind.registerTriggers(store) err := iterStorage(ctx, store, txn, ref, ast.EmptyRef(), ast.NewValueMap(), func(bindings *ast.ValueMap, val interface{}) { index.Add(val, bindings) }) if err != nil { return err } hashCode := ref.Hash() head := ind.table[hashCode] entry := &indicesNode{ key: ref, val: index, next: head, } ind.table[hashCode] = entry return nil }
func TestMakeRequest(t *testing.T) { tests := []struct { note string request [][2]string expected interface{} }{ {"var", [][2]string{{`hello`, `"world"`}}, `{"hello": "world"}`}, {"multiple vars", [][2]string{{`a`, `"a"`}, {`b`, `"b"`}}, `{"a": "a", "b": "b"}`}, {"multiple overlapping vars", [][2]string{{`a.b.c`, `"c"`}, {`a.b.d`, `"d"`}, {`x.y`, `[]`}}, `{"a": {"b": {"c": "c", "d": "d"}}, "x": {"y": []}}`}, {"ref value", [][2]string{{"foo.bar", "data.com.example.widgets[i]"}}, `{"foo": {"bar": data.com.example.widgets[i]}}`}, {"non-object", [][2]string{{"", "[1,2,3]"}}, "[1,2,3]"}, {"non-object conflict", [][2]string{{"", "[1,2,3]"}, {"a.b", "true"}}, fmt.Errorf("conflicting request values: check request parameters")}, {"conflicting vars", [][2]string{{`a.b`, `"c"`}, {`a.b.d`, `"d"`}}, fmt.Errorf("conflicting request value request.a.b.d: check request parameters")}, {"conflicting vars-2", [][2]string{{`a.b`, `{"c":[]}`}, {`a.b.c`, `["d"]`}}, fmt.Errorf("conflicting request value request.a.b.c: check request parameters")}, {"conflicting vars-3", [][2]string{{"a", "100"}, {`a.b`, `"c"`}}, fmt.Errorf("conflicting request value request.a.b: check request parameters")}, {"conflicting vars-4", [][2]string{{`a.b`, `"c"`}, {`a`, `100`}}, fmt.Errorf("conflicting request value request.a: check request parameters")}, {"bad path", [][2]string{{`a[1]`, `1`}}, fmt.Errorf("invalid request path: invalid path request.a[1]: path elements must be strings"), }, } for i, tc := range tests { pairs := make([][2]*ast.Term, len(tc.request)) for j := range tc.request { var k *ast.Term if len(tc.request[j][0]) == 0 { k = ast.NewTerm(ast.EmptyRef()) } else { k = ast.MustParseTerm("request." + tc.request[j][0]) } v := ast.MustParseTerm(tc.request[j][1]) pairs[j] = [...]*ast.Term{k, v} } request, err := MakeRequest(pairs) switch e := tc.expected.(type) { case error: if err == nil { t.Errorf("%v (#%d): Expected error %v but got: %v", tc.note, i+1, e, request) continue } if err.Error() != e.Error() { t.Errorf("%v (#%d): Expected error %v but got: %v", tc.note, i+1, e, err) } case string: if err != nil { t.Errorf("%v (#%d): Unexpected error: %v", tc.note, i+1, err) continue } expected := ast.MustParseTerm(e) if !expected.Value.Equal(request) { t.Errorf("%v (#%d): Expected request to equal %v but got: %v", tc.note, i+1, expected, request) } } } }