Skip to content

Commit

Permalink
schema: split schema package into multiple files
Browse files Browse the repository at this point in the history
  • Loading branch information
Denys Smirnov authored and dennwc committed Oct 20, 2018
1 parent 14279cc commit 330365b
Show file tree
Hide file tree
Showing 8 changed files with 1,341 additions and 1,269 deletions.
487 changes: 487 additions & 0 deletions schema/loader.go

Large diffs are not rendered by default.

289 changes: 289 additions & 0 deletions schema/loader_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
package schema_test

import (
"reflect"
"testing"

"github.com/cayleygraph/cayley/graph"
"github.com/cayleygraph/cayley/graph/iterator"
"github.com/cayleygraph/cayley/graph/memstore"
"github.com/cayleygraph/cayley/quad"
"github.com/cayleygraph/cayley/schema"
)

func TestLoadIteratorTo(t *testing.T) {
sch := schema.NewConfig()
for i, c := range testFillValueCases {
t.Run(c.name, func(t *testing.T) {
qs := memstore.New(c.quads...)
out := reflect.New(reflect.TypeOf(c.expect))
var it graph.Iterator
if c.from != nil {
fixed := iterator.NewFixed()
for _, id := range c.from {
fixed.Add(qs.ValueOf(id))
}
it = fixed
}
depth := c.depth
if depth == 0 {
depth = -1
}
if err := sch.LoadIteratorToDepth(nil, qs, out, depth, it); err != nil {
t.Errorf("case %d failed: %v", i+1, err)
return
}
got := out.Elem().Interface()
if s, ok := got.(interface {
Sort()
}); ok {
s.Sort()
}
if s, ok := c.expect.(interface {
Sort()
}); ok {
s.Sort()
}
if !reflect.DeepEqual(got, c.expect) {
t.Errorf("case %d failed: objects are different\n%#v\n%#v",
i+1, out.Elem().Interface(), c.expect,
)
}
})
}
}

var testFillValueCases = []struct {
name string
expect interface{}
quads []quad.Quad
depth int
from []quad.Value
}{
{
name: "complex object",
expect: struct {
rdfType struct{} `quad:"rdf:type > some:Type"`
ID quad.IRI `quad:"@id"`
Name string `quad:"name"`
Values []string `quad:"values"`
Items []item `quad:"items"`
Sub *item `quad:"sub"`
Val int `quad:"val"`
}{
ID: "1234",
Name: "some item",
Values: []string{"val1", "val2"},
Items: []item{
{ID: "sub1", Name: "Sub 1"},
{ID: "sub2", Name: "Sub 2"},
},
Sub: &item{ID: "sub3", Name: "Sub 3"},
Val: 123,
},
quads: []quad.Quad{
{iri("1234"), typeIRI, iri("some:Type"), nil},
{iri("1234"), iri("name"), quad.String("some item"), nil},
{iri("1234"), iri("values"), quad.String("val1"), nil},
{iri("1234"), iri("values"), quad.String("val2"), nil},
{iri("sub1"), typeIRI, iri("some:item"), nil},
{iri("sub1"), iri("name"), quad.String("Sub 1"), nil},
{iri("1234"), iri("items"), iri("sub1"), nil},
{iri("sub2"), typeIRI, iri("some:item"), nil},
{iri("sub2"), iri("name"), quad.String("Sub 2"), nil},
{iri("1234"), iri("items"), iri("sub2"), nil},
{iri("sub3"), typeIRI, iri("some:item"), nil},
{iri("sub3"), iri("name"), quad.String("Sub 3"), nil},
{iri("1234"), iri("sub"), iri("sub3"), nil},
{iri("1234"), iri("val"), quad.Int(123), nil},
},
},
{
name: "complex object (id value)",
expect: struct {
rdfType struct{} `quad:"rdf:type > some:Type"`
ID quad.Value `quad:"@id"`
Name string `quad:"name"`
Values []string `quad:"values"`
Items []item `quad:"items"`
}{
ID: quad.BNode("1234"),
Name: "some item",
Values: []string{"val1", "val2"},
Items: []item{
{ID: "sub1", Name: "Sub 1"},
{ID: "sub2", Name: "Sub 2"},
},
},
quads: []quad.Quad{
{quad.BNode("1234"), typeIRI, iri("some:Type"), nil},
{quad.BNode("1234"), iri("name"), quad.String("some item"), nil},
{quad.BNode("1234"), iri("values"), quad.String("val1"), nil},
{quad.BNode("1234"), iri("values"), quad.String("val2"), nil},
{iri("sub1"), typeIRI, iri("some:item"), nil},
{iri("sub1"), iri("name"), quad.String("Sub 1"), nil},
{quad.BNode("1234"), iri("items"), iri("sub1"), nil},
{iri("sub2"), typeIRI, iri("some:item"), nil},
{iri("sub2"), iri("name"), quad.String("Sub 2"), nil},
{quad.BNode("1234"), iri("items"), iri("sub2"), nil},
},
},
{
name: "embedded object",
expect: struct {
rdfType struct{} `quad:"rdf:type > some:Type"`
item2
ID quad.IRI `quad:"@id"`
Values []string `quad:"values"`
}{
item2: item2{Name: "Sub 1", Spec: "special"},
ID: "1234",
Values: []string{"val1", "val2"},
},
quads: []quad.Quad{
{iri("1234"), typeIRI, iri("some:Type"), nil},
{iri("1234"), iri("name"), quad.String("Sub 1"), nil},
{iri("1234"), iri("spec"), quad.String("special"), nil},
{iri("1234"), iri("values"), quad.String("val1"), nil},
{iri("1234"), iri("values"), quad.String("val2"), nil},
},
},
{
name: "type shorthand",
expect: struct {
rdfType struct{} `quad:"@type > some:Type"`
item2
ID quad.IRI `quad:"@id"`
Values []string `quad:"values"`
}{
item2: item2{Name: "Sub 1", Spec: "special"},
ID: "1234",
Values: []string{"val1", "val2"},
},
quads: []quad.Quad{
{iri("1234"), typeIRI, iri("some:Type"), nil},
{iri("1234"), iri("name"), quad.String("Sub 1"), nil},
{iri("1234"), iri("spec"), quad.String("special"), nil},
{iri("1234"), iri("values"), quad.String("val1"), nil},
{iri("1234"), iri("values"), quad.String("val2"), nil},
},
},
{
name: "tree",
expect: treeItem{
ID: iri("n1"),
Name: "Node 1",
Children: []treeItem{
{
ID: iri("n2"),
Name: "Node 2",
},
{
ID: iri("n3"),
Name: "Node 3",
Children: []treeItem{
{
ID: iri("n4"),
Name: "Node 4",
},
},
},
},
},
quads: treeQuads,
from: []quad.Value{iri("n1")},
},
{
name: "tree with depth limit 1",
expect: treeItem{
ID: iri("n1"),
Name: "Node 1",
Children: []treeItem{
{
ID: iri("n2"),
Name: "Node 2",
},
{
ID: iri("n3"),
Name: "Node 3",
Children: []treeItem{
{
ID: iri("n4"),
},
},
},
},
},
depth: 1,
quads: treeQuads,
from: []quad.Value{iri("n1")},
},
{
name: "tree with depth limit 2",
expect: treeItemOpt{
ID: iri("n1"),
Name: "Node 1",
Children: []treeItemOpt{
{
ID: iri("n2"),
Name: "Node 2",
},
{
ID: iri("n3"),
Name: "Node 3",
Children: []treeItemOpt{
{
ID: iri("n4"),
Name: "Node 4",
},
},
},
},
},
depth: 2,
quads: treeQuads,
from: []quad.Value{iri("n1")},
},
{
name: "tree with required children",
expect: treeItemReq{
ID: iri("n1"),
Name: "Node 1",
Children: []treeItemReq{
{
ID: iri("n3"),
Name: "Node 3",
// TODO(dennwc): a strange behavior: this field is required, but it's empty for current object,
// because all it's children are missing the same field. Leaving this as-is for now because
// it's weird to set Children field as required in a tree.
Children: nil,
},
},
},
quads: treeQuads,
from: []quad.Value{iri("n1")},
},
{
name: "simple object",
expect: subObject{
genObject: genObject{
ID: "1234",
Name: "Obj",
},
Num: 3,
},
quads: []quad.Quad{
{iri("1234"), iri("name"), quad.String("Obj"), nil},
{iri("1234"), iri("num"), quad.Int(3), nil},
},
},
{
name: "coords",
expect: Coords{Lat: 12.3, Lng: 34.5},
quads: []quad.Quad{
{iri("c1"), typeIRI, iri("ex:Coords"), nil},
{iri("c1"), iri("ex:lat"), quad.Float(12.3), nil},
{iri("c1"), iri("ex:lng"), quad.Float(34.5), nil},
},
},
}
57 changes: 57 additions & 0 deletions schema/namespaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package schema

import (
"context"
"fmt"
"reflect"

"github.com/cayleygraph/cayley/graph"
"github.com/cayleygraph/cayley/quad"
"github.com/cayleygraph/cayley/voc"
)

type namespace struct {
_ struct{} `quad:"@type > cayley:namespace"`
Full quad.IRI `quad:"@id"`
Prefix quad.IRI `quad:"cayley:prefix"`
}

// WriteNamespaces will writes namespaces list into graph.
func (c *Config) WriteNamespaces(w quad.Writer, n *voc.Namespaces) error {
rules, err := c.rulesFor(reflect.TypeOf(namespace{}))
if err != nil {
return fmt.Errorf("can't load rules: %v", err)
}
wr := c.newWriter(w)
for _, ns := range n.List() {
obj := namespace{
Full: quad.IRI(ns.Full),
Prefix: quad.IRI(ns.Prefix),
}
rv := reflect.ValueOf(obj)
if err = wr.writeValueAs(obj.Full, rv, "", rules); err != nil {
return err
}
}
return nil
}

// LoadNamespaces will load namespaces stored in graph to a specified list.
// If destination list is empty, global namespace registry will be used.
func (c *Config) LoadNamespaces(ctx context.Context, qs graph.QuadStore, dest *voc.Namespaces) error {
var list []namespace
if err := c.LoadTo(ctx, qs, &list); err != nil {
return err
}
register := dest.Register
if dest == nil {
register = voc.Register
}
for _, ns := range list {
register(voc.Namespace{
Prefix: string(ns.Prefix),
Full: string(ns.Full),
})
}
return nil
}
Loading

0 comments on commit 330365b

Please sign in to comment.