List Catalogs for Repo and API Handler (#288)

This commit is contained in:
Todd Knight authored and GitHub committed 2020-08-19 21:49:52 -07:00
1 parent 6bf4a5ce38
commit 7474e956dd
6 files changed
+274 -12

No files matched your search

+62
View File
@@ -1,6 +1,7 @@
package hosts_test
import (
"fmt"
"net/http"
"testing"
@@ -13,6 +14,67 @@ import (
"github.com/stretchr/testify/require"
)
func TestCatalog_List(t *testing.T) {
assert, require := assert.New(t), require.New(t)
amId := "paum_1234567890"
tc := controller.NewTestController(t, &controller.TestControllerOpts{
DisableAuthorizationFailures: true,
DefaultAuthMethodId: amId,
DefaultLoginName: "user",
DefaultPassword: "passpass",
})
defer tc.Shutdown()
client := tc.Client()
_, proj := iam.TestScopes(t, tc.IamRepo())
client.SetScopeId(proj.GetPublicId())
catalogClient := hosts.NewHostCatalogsClient(client)
ul, apiErr, err := catalogClient.List(tc.Context())
assert.NoError(err)
assert.Nil(apiErr)
assert.Empty(ul)
var expected []*hosts.HostCatalog
for i := 0; i < 10; i++ {
expected = append(expected, &hosts.HostCatalog{Name: fmt.Sprint(i)})
}
expected[0], apiErr, err = catalogClient.Create(tc.Context(), "static", hosts.WithName(expected[0].Name))
assert.NoError(err)
assert.Nil(apiErr)
ul, apiErr, err = catalogClient.List(tc.Context())
assert.NoError(err)
assert.Nil(apiErr)
assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(ul))
for i := 1; i < 10; i++ {
expected[i], apiErr, err = catalogClient.Create(tc.Context(), "static", hosts.WithName(expected[i].Name))
assert.NoError(err)
assert.Nil(apiErr)
}
ul, apiErr, err = catalogClient.List(tc.Context())
require.NoError(err)
assert.Nil(apiErr)
assert.ElementsMatch(comparableSlice(expected), comparableSlice(ul))
}
func comparableSlice(in []*hosts.HostCatalog) []hosts.HostCatalog {
var filtered []hosts.HostCatalog
for _, i := range in {
p := hosts.HostCatalog{
Id: i.Id,
Name: i.Name,
Description: i.Description,
CreatedTime: i.CreatedTime,
UpdatedTime: i.UpdatedTime,
}
filtered = append(filtered, p)
}
return filtered
}
func TestCatalogs_Crud(t *testing.T) {
assert, require := assert.New(t), require.New(t)
amId := "paum_1234567890"
@@ -179,6 +179,25 @@ func (r *Repository) LookupCatalog(ctx context.Context, id string, opt ...Option
return c, nil
}
// ListCatalogs returns a slice of HostCatalogs for the scopeId. WithLimit is the only option supported.
func (r *Repository) ListCatalogs(ctx context.Context, scopeId string, opt ...Option) ([]*HostCatalog, error) {
if scopeId == "" {
return nil, fmt.Errorf("list: static host catalog: missing scope id: %w", db.ErrInvalidParameter)
}
opts := getOpts(opt...)
limit := r.defaultLimit
if opts.withLimit != 0 {
// non-zero signals an override of the default limit for the repo.
limit = opts.withLimit
}
var hostCatalogs []*HostCatalog
err := r.reader.SearchWhere(ctx, &hostCatalogs, "scope_id = ?", []interface{}{scopeId}, db.WithLimit(limit))
if err != nil {
return nil, fmt.Errorf("list: static host catalog: %w", err)
}
return hostCatalogs, nil
}
// DeleteCatalog deletes id from the repository returning a count of the
// number of records deleted.
func (r *Repository) DeleteCatalog(ctx context.Context, id string, opt ...Option) (int, error) {
+34
View File
@@ -0,0 +1,34 @@
package static
import (
"context"
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert"
)
// TestCatalogs creates count number of static host catalogs to the provided DB
// with the provided scope id. If any errors are encountered during the creation of
// the host catalog, the test will fail.
func TestCatalogs(t *testing.T, conn *gorm.DB, scopeId string, count int) []*HostCatalog {
t.Helper()
assert := assert.New(t)
var cats []*HostCatalog
for i := 0; i < count; i++ {
cat, err := NewHostCatalog(scopeId)
assert.NoError(err)
assert.NotNil(cat)
id, err := newHostCatalogId()
assert.NoError(err)
assert.NotEmpty(id)
cat.PublicId = id
w := db.New(conn)
err2 := w.Create(context.Background(), cat)
assert.NoError(err2)
cats = append(cats, cat)
}
return cats
}
+26
View File
@@ -0,0 +1,26 @@
package static
import (
"testing"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/iam"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_TestCatalogs(t *testing.T) {
assert, require := assert.New(t), require.New(t)
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
_, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
require.NotNil(proj)
assert.NotEmpty(proj.GetPublicId())
count := 4
cs := TestCatalogs(t, conn, proj.GetPublicId(), count)
assert.Len(cs, count)
for _, c := range cs {
assert.NotEmpty(c.GetPublicId())
}
}
@@ -47,7 +47,21 @@ func NewService(repoFn common.StaticRepoFactory) (Service, error) {
}
func (s Service) ListHostCatalogs(ctx context.Context, req *pbs.ListHostCatalogsRequest) (*pbs.ListHostCatalogsResponse, error) {
return nil, status.Error(codes.Unimplemented, "Requested method is unimplemented for Host Catalogs.")
authResults := auth.Verify(ctx)
if authResults.Error != nil {
return nil, authResults.Error
}
if err := validateListRequest(req); err != nil {
return nil, err
}
ul, err := s.listFromRepo(ctx, authResults.Scope.GetId())
if err != nil {
return nil, err
}
for _, item := range ul {
item.Scope = authResults.Scope
}
return &pbs.ListHostCatalogsResponse{Items: ul}, nil
}
// GetHostCatalog implements the interface pbs.HostCatalogServiceServer.
@@ -135,6 +149,22 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.HostCatalog, e
return toProto(hc), nil
}
func (s Service) listFromRepo(ctx context.Context, scopeId string) ([]*pb.HostCatalog, error) {
repo, err := s.staticRepoFn()
if err != nil {
return nil, err
}
ul, err := repo.ListCatalogs(ctx, scopeId)
if err != nil {
return nil, err
}
var outUl []*pb.HostCatalog
for _, u := range ul {
outUl = append(outUl, toProto(u))
}
return outUl, nil
}
func (s Service) createInRepo(ctx context.Context, projId string, item *pb.HostCatalog) (*pb.HostCatalog, error) {
var opts []static.Option
if item.GetName() != nil {
@@ -240,6 +270,14 @@ func validateGetRequest(req *pbs.GetHostCatalogRequest) error {
return nil
}
func validateListRequest(req *pbs.ListHostCatalogsRequest) error {
badFields := map[string]string{}
if len(badFields) > 0 {
return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields)
}
return nil
}
func validateCreateRequest(req *pbs.CreateHostCatalogRequest) error {
badFields := map[string]string{}
item := req.GetItem()
@@ -12,7 +12,7 @@ import (
"github.com/hashicorp/boundary/internal/auth"
"github.com/hashicorp/boundary/internal/db"
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/hosts"
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
scopepb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/iam"
@@ -62,7 +62,7 @@ func TestGet(t *testing.T) {
pHostCatalog := &pb.HostCatalog{
Id: hc.GetPublicId(),
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Name: &wrappers.StringValue{Value: hc.GetName()},
Description: &wrappers.StringValue{Value: hc.GetDescription()},
CreatedTime: hc.CreateTime.GetTimestamp(),
@@ -121,6 +121,89 @@ func TestGet(t *testing.T) {
}
}
func TestList(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
repoFn := func() (*static.Repository, error) {
return static.NewRepository(rw, rw, kms)
}
iamRepo := iam.TestRepo(t, conn, wrapper)
_, pNoCatalogs := iam.TestScopes(t, iamRepo)
_, pWithCatalogs := iam.TestScopes(t, iamRepo)
_, pWithOtherCatalogs := iam.TestScopes(t, iamRepo)
var wantSomeCatalogs []*pb.HostCatalog
for _, hc := range static.TestCatalogs(t, conn, pWithCatalogs.GetPublicId(), 3) {
wantSomeCatalogs = append(wantSomeCatalogs, &pb.HostCatalog{
Id: hc.GetPublicId(),
CreatedTime: hc.GetCreateTime().GetTimestamp(),
UpdatedTime: hc.GetUpdateTime().GetTimestamp(),
Scope: &scopepb.ScopeInfo{Id: pWithCatalogs.GetPublicId(), Type: scope.Project.String()},
Version: 1,
Type: "static",
})
}
var wantOtherCatalogs []*pb.HostCatalog
for _, hc := range static.TestCatalogs(t, conn, pWithOtherCatalogs.GetPublicId(), 3) {
wantOtherCatalogs = append(wantOtherCatalogs, &pb.HostCatalog{
Id: hc.GetPublicId(),
CreatedTime: hc.GetCreateTime().GetTimestamp(),
UpdatedTime: hc.GetUpdateTime().GetTimestamp(),
Scope: &scopepb.ScopeInfo{Id: pWithOtherCatalogs.GetPublicId(), Type: scope.Project.String()},
Version: 1,
Type: "static",
})
}
cases := []struct {
name string
scopeId string
res *pbs.ListHostCatalogsResponse
errCode codes.Code
}{
{
name: "List Some Catalogs",
scopeId: pWithCatalogs.GetPublicId(),
res: &pbs.ListHostCatalogsResponse{Items: wantSomeCatalogs},
errCode: codes.OK,
},
{
name: "List Other Catalogs",
scopeId: pWithOtherCatalogs.GetPublicId(),
res: &pbs.ListHostCatalogsResponse{Items: wantOtherCatalogs},
errCode: codes.OK,
},
{
name: "List No Catalogs",
scopeId: pNoCatalogs.GetPublicId(),
res: &pbs.ListHostCatalogsResponse{},
errCode: codes.OK,
},
// TODO: When an auth method doesn't exist, we should return a 404 instead of an empty list.
{
name: "Unfound Catalogs",
scopeId: scope.Project.Prefix() + "_DoesntExis",
res: &pbs.ListHostCatalogsResponse{},
errCode: codes.OK,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
s, err := host_catalogs.NewService(repoFn)
require.NoError(err, "Couldn't create new auth_method service.")
got, gErr := s.ListHostCatalogs(auth.DisabledAuthTestContext(auth.WithScopeId(tc.scopeId)), &pbs.ListHostCatalogsRequest{})
assert.Equal(tc.errCode, status.Code(gErr), "ListHostCatalogs() for scope %q got error %v, wanted %v", tc.scopeId, gErr, tc.errCode)
assert.Empty(cmp.Diff(got, tc.res, protocmp.Transform()), "ListHostCatalogs() for scope %q got response %q, wanted %q", tc.scopeId, got, tc.res)
})
}
}
func TestDelete(t *testing.T) {
t.Parallel()
require := require.New(t)
@@ -244,7 +327,7 @@ func TestCreate(t *testing.T) {
res: &pbs.CreateHostCatalogResponse{
Uri: fmt.Sprintf("scopes/%s/host-catalogs/%s_", proj.GetPublicId(), static.HostCatalogPrefix),
Item: &pb.HostCatalog{
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Name: &wrappers.StringValue{Value: "name"},
Description: &wrappers.StringValue{Value: "desc"},
Type: "static",
@@ -373,7 +456,7 @@ func TestUpdate(t *testing.T) {
res: &pbs.UpdateHostCatalogResponse{
Item: &pb.HostCatalog{
Id: hc.GetPublicId(),
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Name: &wrappers.StringValue{Value: "new"},
Description: &wrappers.StringValue{Value: "desc"},
CreatedTime: hc.GetCreateTime().GetTimestamp(),
@@ -396,7 +479,7 @@ func TestUpdate(t *testing.T) {
res: &pbs.UpdateHostCatalogResponse{
Item: &pb.HostCatalog{
Id: hc.GetPublicId(),
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Name: &wrappers.StringValue{Value: "new"},
Description: &wrappers.StringValue{Value: "desc"},
CreatedTime: hc.GetCreateTime().GetTimestamp(),
@@ -450,7 +533,7 @@ func TestUpdate(t *testing.T) {
res: &pbs.UpdateHostCatalogResponse{
Item: &pb.HostCatalog{
Id: hc.GetPublicId(),
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Description: &wrappers.StringValue{Value: "default"},
CreatedTime: hc.GetCreateTime().GetTimestamp(),
Type: "static",
@@ -471,7 +554,7 @@ func TestUpdate(t *testing.T) {
res: &pbs.UpdateHostCatalogResponse{
Item: &pb.HostCatalog{
Id: hc.GetPublicId(),
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Name: &wrappers.StringValue{Value: "default"},
CreatedTime: hc.GetCreateTime().GetTimestamp(),
Type: "static",
@@ -493,7 +576,7 @@ func TestUpdate(t *testing.T) {
res: &pbs.UpdateHostCatalogResponse{
Item: &pb.HostCatalog{
Id: hc.GetPublicId(),
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Name: &wrappers.StringValue{Value: "updated"},
Description: &wrappers.StringValue{Value: "default"},
CreatedTime: hc.GetCreateTime().GetTimestamp(),
@@ -516,7 +599,7 @@ func TestUpdate(t *testing.T) {
res: &pbs.UpdateHostCatalogResponse{
Item: &pb.HostCatalog{
Id: hc.GetPublicId(),
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Name: &wrappers.StringValue{Value: "default"},
Description: &wrappers.StringValue{Value: "notignored"},
CreatedTime: hc.GetCreateTime().GetTimestamp(),
@@ -536,7 +619,7 @@ func TestUpdate(t *testing.T) {
},
Item: &pb.HostCatalog{
Name: &wrappers.StringValue{Value: "new"},
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Description: &wrappers.StringValue{Value: "desc"},
},
},
@@ -551,7 +634,7 @@ func TestUpdate(t *testing.T) {
},
Item: &pb.HostCatalog{
Id: "p_somethinge",
Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Scope: &scopepb.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()},
Name: &wrappers.StringValue{Value: "new"},
Description: &wrappers.StringValue{Value: "new desc"},
}},