Skip to content

Commit

Permalink
Fixing build error and simplifying package structure.
Browse files Browse the repository at this point in the history
  • Loading branch information
danielfireman committed Nov 16, 2017
1 parent fff03ff commit f8a5e63
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 48 deletions.
19 changes: 9 additions & 10 deletions pkg/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,24 @@ import (
"sync"

"github.com/frictionlessdata/datapackage-go/clone"
"github.com/frictionlessdata/datapackage-go/resource"
)

const (
resourcePropName = "resources"
)

type resourceFactory func(map[string]interface{}) (*resource.Resource, error)
type resourceFactory func(map[string]interface{}) (*Resource, error)

// Package represents a https://specs.frictionlessdata.io/data-package/
type Package struct {
resources []*resource.Resource
resources []*Resource

descriptor map[string]interface{}
resFactory resourceFactory
}

// GetResource return the resource which the passed-in name or nil if the resource is not part of the package.
func (p *Package) GetResource(name string) *resource.Resource {
func (p *Package) GetResource(name string) *Resource {
for _, r := range p.resources {
if r.Name == name {
return r
Expand Down Expand Up @@ -62,10 +61,10 @@ func (p *Package) AddResource(d map[string]interface{}) error {
return nil
}

func newResourcesDescriptor(resources []*resource.Resource) []interface{} {
func newResourcesDescriptor(resources []*Resource) []interface{} {
descRes := make([]interface{}, len(resources))
for i := range resources {
descRes[i] = resources[i].Descriptor
descRes[i], _ = resources[i].Descriptor()
}
return descRes
}
Expand All @@ -87,7 +86,7 @@ func (p *Package) RemoveResource(name string) {

// Valid returns true if the passed-in descriptor is valid or false, otherwise.
func Valid(descriptor map[string]interface{}) bool {
return valid(descriptor, resource.New)
return valid(descriptor, NewResource)
}

func valid(descriptor map[string]interface{}, resFactory resourceFactory) bool {
Expand Down Expand Up @@ -146,7 +145,7 @@ func fromDescriptor(descriptor map[string]interface{}, resFactory resourceFactor
if !ok || len(rSlice) == 0 {
return nil, fmt.Errorf("resources property is required, with at least one resource")
}
resources := make([]*resource.Resource, len(rSlice))
resources := make([]*Resource, len(rSlice))
for pos, rInt := range rSlice {
rDesc, ok := rInt.(map[string]interface{})
if !ok {
Expand All @@ -167,7 +166,7 @@ func fromDescriptor(descriptor map[string]interface{}, resFactory resourceFactor

// FromDescriptor creates a data package from a json descriptor.
func FromDescriptor(descriptor map[string]interface{}) (*Package, error) {
return fromDescriptor(descriptor, resource.New)
return fromDescriptor(descriptor, NewResource)
}

func fromReader(r io.Reader, resFactory resourceFactory) (*Package, error) {
Expand All @@ -184,5 +183,5 @@ func fromReader(r io.Reader, resFactory resourceFactory) (*Package, error) {

// FromReader validates and returns a data package from an io.Reader.
func FromReader(r io.Reader) (*Package, error) {
return fromReader(r, resource.New)
return fromReader(r, NewResource)
}
47 changes: 22 additions & 25 deletions pkg/package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,15 @@ import (
"strings"
"testing"

"github.com/frictionlessdata/datapackage-go/resource"
"github.com/matryer/is"
)

func validResource(d map[string]interface{}) (*resource.Resource, error) {
return &resource.Resource{Descriptor: d, Name: d["name"].(string)}, nil
}

var invalidResource = func(map[string]interface{}) (*resource.Resource, error) { return nil, fmt.Errorf("") }
var invalidResource = func(map[string]interface{}) (*Resource, error) { return nil, fmt.Errorf("") }

func TestPackage_GetResource(t *testing.T) {
is := is.New(t)
in := `{"resources":[{"name":"res"}]}`
p, err := fromReader(strings.NewReader(in), validResource)
p, err := fromReader(strings.NewReader(in), NewUncheckedResource)
is.NoErr(err)
is.Equal(p.GetResource("res").Name, "res")
is.True(p.GetResource("foooooo") == nil)
Expand All @@ -31,7 +26,7 @@ func TestPackage_AddResource(t *testing.T) {
r1 := map[string]interface{}{"name": "res1"}
r2 := map[string]interface{}{"name": "res2"}

p, err := fromDescriptor(map[string]interface{}{"resources": []interface{}{r1}}, validResource)
p, err := fromDescriptor(map[string]interface{}{"resources": []interface{}{r1}}, NewUncheckedResource)
is.NoErr(err)
p.AddResource(r2)

Expand All @@ -45,7 +40,7 @@ func TestPackage_AddResource(t *testing.T) {
})
t.Run("CodedPackage", func(t *testing.T) {
is := is.New(t)
p := Package{resFactory: validResource}
p := Package{resFactory: NewUncheckedResource}
r1 := map[string]interface{}{"name": "res1"}
err := p.AddResource(r1)
is.NoErr(err)
Expand Down Expand Up @@ -79,32 +74,34 @@ func TestPackage_RemoveResource(t *testing.T) {
map[string]interface{}{"name": "res1"},
map[string]interface{}{"name": "res2"},
}},
validResource)
NewUncheckedResource)
is.NoErr(err)
p.RemoveResource("res1")
is.Equal(len(p.descriptor), 1)
is.Equal(len(p.resources), 1)
is.Equal(p.descriptor["resources"].([]interface{})[0], p.resources[0].Descriptor)
desc0, err := p.resources[0].Descriptor()
is.NoErr(err)
is.Equal(p.descriptor["resources"].([]interface{})[0], desc0)

// Remove a non-existing resource and checks if everything stays the same.
p.RemoveResource("res1")
is.Equal(len(p.descriptor), 1)
is.Equal(len(p.resources), 1)
is.Equal(p.descriptor["resources"].([]interface{})[0], p.resources[0].Descriptor)
is.Equal(p.descriptor["resources"].([]interface{})[0], desc0)
})
}

func TestPackage_ResourceNames(t *testing.T) {
is := is.New(t)
p := Package{resFactory: validResource}
p := Package{resFactory: NewUncheckedResource}
is.True(p.AddResource(map[string]interface{}{"name": "res1"}) == nil)
is.True(p.AddResource(map[string]interface{}{"name": "res2"}) == nil)
is.Equal(p.ResourceNames(), []string{"res1", "res2"})
}

func TestPackage_Descriptor(t *testing.T) {
is := is.New(t)
p := Package{resFactory: validResource}
p := Package{resFactory: NewUncheckedResource}
is.True(p.AddResource(map[string]interface{}{"name": "res1"}) == nil)
c, err := p.Descriptor()
is.NoErr(err)
Expand Down Expand Up @@ -133,7 +130,7 @@ func TestPackage_UnmarshalJSON(t *testing.T) {

func TestPackage_MarshalJSON(t *testing.T) {
is := is.New(t)
p := Package{resFactory: validResource}
p := Package{resFactory: NewUncheckedResource}
p.AddResource(map[string]interface{}{"name": "res", "path": "foo.csv"})
buf, err := json.Marshal(&p)
is.NoErr(err)
Expand All @@ -146,7 +143,7 @@ func TestPackage_Update(t *testing.T) {
map[string]interface{}{"resources": []interface{}{
map[string]interface{}{"name": "res1"},
}},
validResource)
NewUncheckedResource)
is.NoErr(err)

newDesc := map[string]interface{}{"resources": []interface{}{
Expand All @@ -171,19 +168,19 @@ func TestFromDescriptor(t *testing.T) {
descriptor map[string]interface{}
resFactory resourceFactory
}{
{"EmptyMap", map[string]interface{}{}, validResource},
{"EmptyMap", map[string]interface{}{}, NewUncheckedResource},
{"InvalidResourcePropertyType", map[string]interface{}{
"resources": 10,
}, validResource},
}, NewUncheckedResource},
{"EmptyResourcesSlice", map[string]interface{}{
"resources": []interface{}{},
}, validResource},
}, NewUncheckedResource},
{"InvalidResource", map[string]interface{}{
"resources": []interface{}{map[string]interface{}{}},
}, invalidResource},
{"InvalidResourceType", map[string]interface{}{
"resources": []interface{}{1},
}, validResource},
}, NewUncheckedResource},
}
for _, d := range data {
_, err := fromDescriptor(d.descriptor, d.resFactory)
Expand All @@ -195,7 +192,7 @@ func TestFromDescriptor(t *testing.T) {
r1 := map[string]interface{}{"name": "res"}
p, err := fromDescriptor(
map[string]interface{}{"resources": []interface{}{r1}},
validResource,
NewUncheckedResource,
)
is.NoErr(err)
is.True(p.resources[0] != nil)
Expand All @@ -209,18 +206,18 @@ func TestFromDescriptor(t *testing.T) {
func TestFromReader(t *testing.T) {
t.Run("ValidJSON", func(t *testing.T) {
is := is.New(t)
_, err := fromReader(strings.NewReader(`{"resources":[{"name":"res"}]}`), validResource)
_, err := fromReader(strings.NewReader(`{"resources":[{"name":"res"}]}`), NewUncheckedResource)
is.NoErr(err)
})
t.Run("InvalidJSON", func(t *testing.T) {
is := is.New(t)
_, err := fromReader(strings.NewReader(`{resources}`), validResource)
_, err := fromReader(strings.NewReader(`{resources}`), NewUncheckedResource)
is.True(err != nil)
})
}

func TestValid(t *testing.T) {
is := is.New(t)
is.True(valid(map[string]interface{}{"resources": []interface{}{map[string]interface{}{"name": "res"}}}, validResource))
is.True(!valid(map[string]interface{}{}, validResource))
is.True(valid(map[string]interface{}{"resources": []interface{}{map[string]interface{}{"name": "res"}}}, NewUncheckedResource))
is.True(!valid(map[string]interface{}{}, NewUncheckedResource))
}
29 changes: 24 additions & 5 deletions resource/resource.go → pkg/resource.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Package resource implements the specification: https://specs.frictionlessdata.io/data-resource/
package resource
package pkg

import (
"encoding/json"
Expand Down Expand Up @@ -48,7 +47,7 @@ func (r *Resource) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &descriptor); err != nil {
return err
}
aux, err := New(descriptor)
aux, err := NewResource(descriptor)
if err != nil {
return err
}
Expand All @@ -61,8 +60,14 @@ func (r *Resource) Descriptor() (map[string]interface{}, error) {
return clone.Descriptor(r.descriptor)
}

// New creates a new Resource from the passed-in descriptor.
func New(d map[string]interface{}) (*Resource, error) {
// Valid checks whether the resource is valid.
func (r *Resource) Valid() bool {
_, err := NewResource(r.descriptor)
return err == nil
}

// NewResource creates a new Resource from the passed-in descriptor.
func NewResource(d map[string]interface{}) (*Resource, error) {
if d[pathProp] != nil && d[dataProp] != nil {
return nil, fmt.Errorf("either path or data properties MUST be set (only one of them). Descriptor:%v", d)
}
Expand Down Expand Up @@ -183,3 +188,17 @@ func parsePath(pathI interface{}, d map[string]interface{}) ([]string, error) {
}
return returned, nil
}

// NewUncheckedResource returns an Resource instance based on the descriptor without any verification. The returned Resource might
// not be valid.
func NewUncheckedResource(d map[string]interface{}) (*Resource, error) {
r := &Resource{descriptor: d}
nI, ok := d["name"]
if ok {
nStr, ok := nI.(string)
if ok {
r.Name = nStr
}
}
return r, nil
}
25 changes: 17 additions & 8 deletions resource/resource_test.go → pkg/resource_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package resource
package pkg

import (
"encoding/json"
Expand Down Expand Up @@ -33,7 +33,7 @@ func TestNew_error(t *testing.T) {
for _, d := range data {
t.Run(d.desc, func(t *testing.T) {
is := is.New(t)
_, err := New(d.d)
_, err := NewResource(d.d)
is.True(err != nil)
})
}
Expand All @@ -52,7 +52,7 @@ func TestNew_schema(t *testing.T) {
for _, d := range data {
t.Run(d.testDescription, func(t *testing.T) {
is := is.New(t)
r, err := New(d.descriptor)
r, err := NewResource(d.descriptor)
is.NoErr(err)
is.True(reflect.DeepEqual(d.want, r.descriptor["schema"]))
})
Expand All @@ -71,7 +71,7 @@ func TestNew_name(t *testing.T) {
for _, d := range data {
t.Run(d.testDescription, func(t *testing.T) {
is := is.New(t)
r, err := New(d.descriptor)
r, err := NewResource(d.descriptor)
is.NoErr(err)
is.True(r.Name == d.want)
})
Expand All @@ -93,7 +93,7 @@ func TestNew_path(t *testing.T) {
for _, d := range data {
t.Run(d.testDescription, func(t *testing.T) {
is := is.New(t)
r, err := New(d.descriptor)
r, err := NewResource(d.descriptor)
is.NoErr(err)
is.True(reflect.DeepEqual(d.want, r.Path))
})
Expand Down Expand Up @@ -125,7 +125,7 @@ func TestNew_data(t *testing.T) {
for _, d := range data {
t.Run(d.testDescription, func(t *testing.T) {
is := is.New(t)
r, err := New(d.descriptor)
r, err := NewResource(d.descriptor)
is.NoErr(err)
is.True(reflect.DeepEqual(d.want, r.Data))
})
Expand All @@ -134,7 +134,7 @@ func TestNew_data(t *testing.T) {

func TestResourceDescriptor(t *testing.T) {
is := is.New(t)
r, err := New(validResourceWithURL)
r, err := NewResource(validResourceWithURL)
is.NoErr(err)
cpy, err := r.Descriptor()
is.NoErr(err)
Expand Down Expand Up @@ -171,8 +171,17 @@ func TestResource_UnmarshalJSON(t *testing.T) {

func TestResource_MarshalJSON(t *testing.T) {
is := is.New(t)
r, err := New(validResourceWithURL)
r, err := NewResource(validResourceWithURL)
is.NoErr(err)
buf, err := json.Marshal(&r)
is.Equal(string(buf), `{"name":"foo","path":"http://url.com"}`)
}

func TestResource_Valid(t *testing.T) {
is := is.New(t)
r, err := NewUncheckedResource(map[string]interface{}{})
is.NoErr(err)
if r.Valid() {
t.Fatalf("%+v is not valid.", r.descriptor)
}
}

0 comments on commit f8a5e63

Please sign in to comment.