Skip to content

Commit

Permalink
Move api.findEntry to plugin.FindEntry
Browse files Browse the repository at this point in the history
Makes FindEntry a generally available utility for searching for a path
in a plugin hierarchy. This is useful to both the FUSE and API modules.

Signed-off-by: Michael Smith <[email protected]>
  • Loading branch information
MikaelSmith committed Jun 27, 2019
1 parent ce774c4 commit 35773ea
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 111 deletions.
39 changes: 6 additions & 33 deletions api/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,41 +30,14 @@ func toAPIEntry(e plugin.Entry) apitypes.Entry {

func findEntry(ctx context.Context, root plugin.Entry, segments []string) (plugin.Entry, *errorResponse) {
path := strings.Join(segments, "/")

curEntry := root
visitedSegments := make([]string, 0, cap(segments))
for _, segment := range segments {
switch curParent := curEntry.(type) {
case plugin.Parent:
// Get the entries via. List()
entries, err := plugin.CachedList(ctx, curParent)
if err != nil {
if cnameErr, ok := err.(plugin.DuplicateCNameErr); ok {
return nil, duplicateCNameResponse(cnameErr)
}

return nil, entryNotFoundResponse(path, err.Error())
}

// Search for the specific entry
entry, ok := entries[segment]
if !ok {
reason := fmt.Sprintf("The %v entry does not exist", segment)
if len(visitedSegments) != 0 {
reason += fmt.Sprintf(" in the %v parent", strings.Join(visitedSegments, "/"))
}

return nil, entryNotFoundResponse(path, reason)
}

curEntry = entry
visitedSegments = append(visitedSegments, segment)
default:
reason := fmt.Sprintf("The entry %v is not a parent", strings.Join(visitedSegments, "/"))
return nil, entryNotFoundResponse(path, reason)
curEntry, err := plugin.FindEntry(ctx, root, segments)
if err != nil {
if cnameErr, ok := err.(plugin.DuplicateCNameErr); ok {
return nil, duplicateCNameResponse(cnameErr)
}
}

return nil, entryNotFoundResponse(path, err.Error())
}
return curEntry, nil
}

Expand Down
78 changes: 0 additions & 78 deletions api/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,6 @@ import (
"github.com/stretchr/testify/suite"
)

type mockParent struct {
plugin.EntryBase
entries []plugin.Entry
}

func (g *mockParent) List(context.Context) ([]plugin.Entry, error) {
return g.entries, nil
}

func (g *mockParent) ChildSchemas() []*plugin.EntrySchema {
return nil
}

func (g *mockParent) Schema() *plugin.EntrySchema {
return nil
}

type mockEntry struct {
plugin.EntryBase
}
Expand All @@ -54,67 +37,6 @@ func (suite *HelpersTestSuite) TearDownSuite() {
plugin.UnsetTestCache()
}

func (suite *HelpersTestSuite) TestFindEntry() {
type testcase struct {
segments []string
expectedEntry string
expectedErr error
}
runTestCase := func(parent plugin.Parent, c testcase) {
got, err := findEntry(context.Background(), parent, c.segments)
if c.expectedEntry != "" && suite.NotNil(got) {
suite.Equal(c.expectedEntry, plugin.CName(got))
} else {
suite.Nil(got)
}
if c.expectedErr == nil {
suite.Nil(err)
} else {
suite.Equal(c.expectedErr, err)
}
}

foo := newMockEntry("foo/bar")
parent := &mockParent{plugin.NewEntry("root"), []plugin.Entry{foo}}
parent.SetTestID("/root")
parent.DisableDefaultCaching()
for _, c := range []testcase{
{[]string{"not found"}, "", entryNotFoundResponse("not found", "The not found entry does not exist")},
{[]string{"foo#bar"}, "foo#bar", nil},
{[]string{"foo#bar", "bar"}, "", entryNotFoundResponse("foo#bar/bar", "The entry foo#bar is not a parent")},
} {
runTestCase(parent, c)
}

baz := newMockEntry("baz")
nestedParent := &mockParent{plugin.NewEntry("bar"), []plugin.Entry{baz}}
nestedParent.DisableDefaultCaching()
parent.entries = append(parent.entries, nestedParent)
for _, c := range []testcase{
{[]string{"bar"}, "bar", nil},
{[]string{"bar", "foo"}, "", entryNotFoundResponse("bar/foo", "The foo entry does not exist in the bar parent")},
{[]string{"bar", "baz"}, "baz", nil},
} {
runTestCase(parent, c)
}

// Finally, test the duplicate cname error response
duplicateFoo := newMockEntry("foo#bar")
parent.entries = append(parent.entries, duplicateFoo)
expectedErr := plugin.DuplicateCNameErr{
ParentID: plugin.ID(parent),
FirstChildName: foo.Name(),
FirstChildSlashReplacer: '#',
SecondChildName: duplicateFoo.Name(),
SecondChildSlashReplacer: '#',
CName: "foo#bar",
}
runTestCase(
parent,
testcase{[]string{"foo#bar"}, "", duplicateCNameResponse(expectedErr)},
)
}

type mockRoot struct {
plugin.EntryBase
mock.Mock
Expand Down
39 changes: 39 additions & 0 deletions plugin/findEntry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package plugin

import (
"context"
"fmt"
"strings"
)

// FindEntry returns the child of start found by following the segments, or an error if it cannot be found.
func FindEntry(ctx context.Context, start Entry, segments []string) (Entry, error) {
visitedSegments := make([]string, 0, cap(segments))
for _, segment := range segments {
switch curParent := start.(type) {
case Parent:
// Get the entries via. List()
entries, err := CachedList(ctx, curParent)
if err != nil {
return nil, err
}

// Search for the specific entry
entry, ok := entries[segment]
if !ok {
reason := fmt.Sprintf("The %v entry does not exist", segment)
if len(visitedSegments) != 0 {
reason += fmt.Sprintf(" in the %v parent", strings.Join(visitedSegments, "/"))
}
return nil, fmt.Errorf(reason)
}

start = entry
visitedSegments = append(visitedSegments, segment)
default:
return nil, fmt.Errorf("The entry %v is not a parent", strings.Join(visitedSegments, "/"))
}
}

return start, nil
}
105 changes: 105 additions & 0 deletions plugin/findEntry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package plugin

import (
"context"
"fmt"
"testing"

"github.com/puppetlabs/wash/datastore"
"github.com/stretchr/testify/assert"
)

type mockParent struct {
EntryBase
entries []Entry
}

func (g *mockParent) List(context.Context) ([]Entry, error) {
return g.entries, nil
}

func (g *mockParent) ChildSchemas() []*EntrySchema {
return nil
}

func (g *mockParent) Schema() *EntrySchema {
return nil
}

type mockEntry struct {
EntryBase
}

func newMockEntry(name string) *mockEntry {
return &mockEntry{
EntryBase: NewEntry(name),
}
}

func (e *mockEntry) Schema() *EntrySchema {
return nil
}

func TestFindEntry(t *testing.T) {
SetTestCache(datastore.NewMemCache())
defer UnsetTestCache()

type testcase struct {
segments []string
expectedEntry string
expectedErr error
}
runTestCase := func(parent Parent, c testcase) {
got, err := FindEntry(context.Background(), parent, c.segments)
if c.expectedEntry != "" && assert.NotNil(t, got) {
assert.Equal(t, c.expectedEntry, CName(got))
} else {
assert.Nil(t, got)
}
if c.expectedErr == nil {
assert.Nil(t, err)
} else {
assert.Equal(t, c.expectedErr, err)
}
}

foo := newMockEntry("foo/bar")
parent := &mockParent{NewEntry("root"), []Entry{foo}}
parent.SetTestID("/root")
parent.DisableDefaultCaching()
for _, c := range []testcase{
{[]string{"not found"}, "", fmt.Errorf("The not found entry does not exist")},
{[]string{"foo#bar"}, "foo#bar", nil},
{[]string{"foo#bar", "bar"}, "", fmt.Errorf("The entry foo#bar is not a parent")},
} {
runTestCase(parent, c)
}

baz := newMockEntry("baz")
nestedParent := &mockParent{NewEntry("bar"), []Entry{baz}}
nestedParent.DisableDefaultCaching()
parent.entries = append(parent.entries, nestedParent)
for _, c := range []testcase{
{[]string{"bar"}, "bar", nil},
{[]string{"bar", "foo"}, "", fmt.Errorf("The foo entry does not exist in the bar parent")},
{[]string{"bar", "baz"}, "baz", nil},
} {
runTestCase(parent, c)
}

// Finally, test the duplicate cname error response
duplicateFoo := newMockEntry("foo#bar")
parent.entries = append(parent.entries, duplicateFoo)
expectedErr := DuplicateCNameErr{
ParentID: ID(parent),
FirstChildName: foo.Name(),
FirstChildSlashReplacer: '#',
SecondChildName: duplicateFoo.Name(),
SecondChildSlashReplacer: '#',
CName: "foo#bar",
}
runTestCase(
parent,
testcase{[]string{"foo#bar"}, "", expectedErr},
)
}

0 comments on commit 35773ea

Please sign in to comment.