From 2727917b2a2c708be240df8c4dccfff680df823e Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Thu, 2 Jul 2020 10:26:31 -0700 Subject: [PATCH] Adding List to go SDK (#164) --- api/groups/group_test.go | 79 ++++++++++ api/internal/genapi/list.go | 140 +++++++++++++++++ api/internal/genapi/main.go | 1 + api/roles/role_test.go | 79 ++++++++++ api/scopes/list.gen.go | 289 ++++++++++++++++++++++++++++++++++++ api/scopes/project_test.go | 57 +++++++ api/users/user_test.go | 57 +++++++ 7 files changed, 702 insertions(+) create mode 100644 api/internal/genapi/list.go create mode 100644 api/scopes/list.gen.go diff --git a/api/groups/group_test.go b/api/groups/group_test.go index ead77a566c..c5ec1ca4dc 100644 --- a/api/groups/group_test.go +++ b/api/groups/group_test.go @@ -2,6 +2,7 @@ package groups_test import ( "context" + "fmt" "net/http" "testing" @@ -19,6 +20,84 @@ type groupCrud interface { ReadGroup(context.Context, *groups.Group) (*groups.Group, *api.Error, error) UpdateGroup(context.Context, *groups.Group) (*groups.Group, *api.Error, error) DeleteGroup(context.Context, *groups.Group) (bool, *api.Error, error) + ListGroups(ctx context.Context) ([]*groups.Group, *api.Error, error) +} + +func TestGroup_List(t *testing.T) { + assert := assert.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + org := &scopes.Organization{ + Client: client, + } + proj, apiErr, err := org.CreateProject(context.Background(), &scopes.Project{}) + require.NoError(t, err) + require.Nil(t, apiErr) + + cases := []struct { + name string + scope groupCrud + }{ + { + name: "org", + scope: org, + }, + { + name: "proj", + scope: proj, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + pl, apiErr, err := tc.scope.ListGroups(ctx) + assert.NoError(err) + assert.Nil(apiErr) + assert.Empty(pl) + + var expected []*groups.Group + for i := 0; i < 10; i++ { + expected = append(expected, &groups.Group{Name: api.String(fmt.Sprint(i))}) + } + + expected[0], apiErr, err = tc.scope.CreateGroup(ctx, expected[0]) + assert.NoError(err) + assert.Nil(apiErr) + + pl, apiErr, err = tc.scope.ListGroups(ctx) + assert.NoError(err) + assert.Nil(apiErr) + assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(pl)) + + for i := 1; i < 10; i++ { + expected[i], apiErr, err = tc.scope.CreateGroup(ctx, expected[i]) + assert.NoError(err) + assert.Nil(apiErr) + } + pl, apiErr, err = tc.scope.ListGroups(ctx) + assert.ElementsMatch(comparableSlice(expected), comparableSlice(pl)) + }) + } +} + +func comparableSlice(in []*groups.Group) []groups.Group { + var filtered []groups.Group + for _, i := range in { + p := groups.Group{ + Id: i.Id, + Name: i.Name, + Description: i.Description, + CreatedTime: i.CreatedTime, + UpdatedTime: i.UpdatedTime, + Disabled: i.Disabled, + } + filtered = append(filtered, p) + } + return filtered } func TestGroup_Crud(t *testing.T) { diff --git a/api/internal/genapi/list.go b/api/internal/genapi/list.go new file mode 100644 index 0000000000..70e25c22e2 --- /dev/null +++ b/api/internal/genapi/list.go @@ -0,0 +1,140 @@ +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "strings" + "text/template" +) + +type listInfo struct { + baseType string + targetType string + path string +} + +var listFuncs = map[string][]*listInfo{ + "scopes": { + { + baseType: "Organization", + targetType: "Project", + path: "projects", + }, + { + baseType: "Organization", + targetType: "groups.Group", + path: "groups", + }, + { + baseType: "Organization", + targetType: "roles.Role", + path: "roles", + }, + { + baseType: "Organization", + targetType: "users.User", + path: "users", + }, + { + baseType: "Project", + targetType: "groups.Group", + path: "groups", + }, + { + baseType: "Project", + targetType: "roles.Role", + path: "roles", + }, + }, +} + +func writeListFuncs() { + for outPkg, funcs := range listFuncs { + outFile := os.Getenv("GEN_BASEPATH") + fmt.Sprintf("/api/%s/list.gen.go", outPkg) + outBuf := bytes.NewBuffer([]byte(fmt.Sprintf( + `// Code generated by "make api"; DO NOT EDIT. +package %s +`, outPkg))) + for _, listInfo := range funcs { + listFuncTemplate.Execute(outBuf, struct { + BaseType string + TargetType string + TargetName string + Path string + }{ + BaseType: listInfo.baseType, + TargetType: listInfo.targetType, + TargetName: strings.Split(listInfo.targetType, ".")[strings.Count(listInfo.targetType, ".")], + Path: listInfo.path, + }) + } + if err := ioutil.WriteFile(outFile, outBuf.Bytes(), 0644); err != nil { + fmt.Printf("error writing file %q: %v\n", outFile, err) + os.Exit(1) + } + } +} + +var listFuncTemplate = template.Must(template.New("").Parse( + ` +func (s {{ .BaseType }}) List{{ .TargetName }}s(ctx context.Context) ([]*{{ .TargetType }}, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in List{{ .TargetName }} request") + } + if s.Id == "" { + {{ if (eq .BaseType "Organization") }} + // Assume the client has been configured with organization already and + // move on + {{ else if (eq .BaseType "Project") }} + // Assume the client has been configured with project already and move + // on + {{ else }} + return nil, nil, fmt.Errorf("missing {{ .BaseType }} ID in List{{ .TargetType }}s request") + {{ end }} + } else { + // If it's explicitly set here, override anything that might be in the + // client + {{ if (eq .BaseType "Organization") }} + ctx = context.WithValue(ctx, "org", s.Id) + {{ else if (eq .BaseType "Project") }} + ctx = context.WithValue(ctx, "project", s.Id) + {{ end }} + } + + req, err := s.Client.NewRequest(ctx, "GET", "{{ .Path }}", nil) + if err != nil { + return nil, nil, fmt.Errorf("error creating List{{ .TargetName }}s request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during List{{ .TargetName }}s call: %w", err) + } + + type listResponse struct { + Items []*{{ .TargetType }} + } + target := &listResponse{} + + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding List{{ .TargetName }}s response: %w", err) + } + + for _, t := range target.Items { + {{ if (eq .TargetType "Organization") }} + t.Client = s.Client.Clone() + t.Client.SetOrgnization(t.Id) + {{ else if (eq .TargetType "Project") }} + t.Client = s.Client.Clone() + t.Client.SetProject(t.Id) + {{ else }} + t.Client = s.Client + {{ end }} + } + + return target.Items, apiErr, nil +} +`)) diff --git a/api/internal/genapi/main.go b/api/internal/genapi/main.go index 40715db699..0d246fe4a8 100644 --- a/api/internal/genapi/main.go +++ b/api/internal/genapi/main.go @@ -6,5 +6,6 @@ func main() { writeCreateFuncs() writeReadFuncs() writeUpdateFuncs() + writeListFuncs() writeDeleteFuncs() } diff --git a/api/roles/role_test.go b/api/roles/role_test.go index a510804a24..3082982692 100644 --- a/api/roles/role_test.go +++ b/api/roles/role_test.go @@ -2,6 +2,7 @@ package roles_test import ( "context" + "fmt" "net/http" "testing" @@ -19,6 +20,84 @@ type roleCrud interface { ReadRole(context.Context, *roles.Role) (*roles.Role, *api.Error, error) UpdateRole(context.Context, *roles.Role) (*roles.Role, *api.Error, error) DeleteRole(context.Context, *roles.Role) (bool, *api.Error, error) + ListRoles(ctx context.Context) ([]*roles.Role, *api.Error, error) +} + +func TestRole_List(t *testing.T) { + assert := assert.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + org := &scopes.Organization{ + Client: client, + } + proj, apiErr, err := org.CreateProject(context.Background(), &scopes.Project{}) + require.NoError(t, err) + require.Nil(t, apiErr) + + cases := []struct { + name string + scope roleCrud + }{ + { + name: "org", + scope: org, + }, + { + name: "proj", + scope: proj, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + pl, apiErr, err := tc.scope.ListRoles(ctx) + assert.NoError(err) + assert.Nil(apiErr) + assert.Empty(pl) + + var expected []*roles.Role + for i := 0; i < 10; i++ { + expected = append(expected, &roles.Role{Name: api.String(fmt.Sprint(i))}) + } + + expected[0], apiErr, err = tc.scope.CreateRole(ctx, expected[0]) + assert.NoError(err) + assert.Nil(apiErr) + + pl, apiErr, err = tc.scope.ListRoles(ctx) + assert.NoError(err) + assert.Nil(apiErr) + assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(pl)) + + for i := 1; i < 10; i++ { + expected[i], apiErr, err = tc.scope.CreateRole(ctx, expected[i]) + assert.NoError(err) + assert.Nil(apiErr) + } + pl, apiErr, err = tc.scope.ListRoles(ctx) + assert.ElementsMatch(comparableSlice(expected), comparableSlice(pl)) + }) + } +} + +func comparableSlice(in []*roles.Role) []roles.Role { + var filtered []roles.Role + for _, i := range in { + p := roles.Role{ + Id: i.Id, + Name: i.Name, + Description: i.Description, + CreatedTime: i.CreatedTime, + UpdatedTime: i.UpdatedTime, + Disabled: i.Disabled, + } + filtered = append(filtered, p) + } + return filtered } func TestRole_Crud(t *testing.T) { diff --git a/api/scopes/list.gen.go b/api/scopes/list.gen.go new file mode 100644 index 0000000000..63485d37af --- /dev/null +++ b/api/scopes/list.gen.go @@ -0,0 +1,289 @@ +// Code generated by "make api"; DO NOT EDIT. +package scopes + +import ( + "context" + "fmt" + + "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/groups" + "github.com/hashicorp/watchtower/api/roles" + "github.com/hashicorp/watchtower/api/users" +) + +func (s Organization) ListProjects(ctx context.Context) ([]*Project, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in ListProject request") + } + if s.Id == "" { + + // Assume the client has been configured with organization already and + // move on + + } else { + // If it's explicitly set here, override anything that might be in the + // client + + ctx = context.WithValue(ctx, "org", s.Id) + + } + + req, err := s.Client.NewRequest(ctx, "GET", "projects", nil) + if err != nil { + return nil, nil, fmt.Errorf("error creating ListProjects request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during ListProjects call: %w", err) + } + + type listResponse struct { + Items []*Project + } + target := &listResponse{} + + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding ListProjects response: %w", err) + } + + for _, t := range target.Items { + + t.Client = s.Client.Clone() + t.Client.SetProject(t.Id) + + } + + return target.Items, apiErr, nil +} + +func (s Organization) ListGroups(ctx context.Context) ([]*groups.Group, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in ListGroup request") + } + if s.Id == "" { + + // Assume the client has been configured with organization already and + // move on + + } else { + // If it's explicitly set here, override anything that might be in the + // client + + ctx = context.WithValue(ctx, "org", s.Id) + + } + + req, err := s.Client.NewRequest(ctx, "GET", "groups", nil) + if err != nil { + return nil, nil, fmt.Errorf("error creating ListGroups request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during ListGroups call: %w", err) + } + + type listResponse struct { + Items []*groups.Group + } + target := &listResponse{} + + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding ListGroups response: %w", err) + } + + for _, t := range target.Items { + + t.Client = s.Client + + } + + return target.Items, apiErr, nil +} + +func (s Organization) ListRoles(ctx context.Context) ([]*roles.Role, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in ListRole request") + } + if s.Id == "" { + + // Assume the client has been configured with organization already and + // move on + + } else { + // If it's explicitly set here, override anything that might be in the + // client + + ctx = context.WithValue(ctx, "org", s.Id) + + } + + req, err := s.Client.NewRequest(ctx, "GET", "roles", nil) + if err != nil { + return nil, nil, fmt.Errorf("error creating ListRoles request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during ListRoles call: %w", err) + } + + type listResponse struct { + Items []*roles.Role + } + target := &listResponse{} + + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding ListRoles response: %w", err) + } + + for _, t := range target.Items { + + t.Client = s.Client + + } + + return target.Items, apiErr, nil +} + +func (s Organization) ListUsers(ctx context.Context) ([]*users.User, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in ListUser request") + } + if s.Id == "" { + + // Assume the client has been configured with organization already and + // move on + + } else { + // If it's explicitly set here, override anything that might be in the + // client + + ctx = context.WithValue(ctx, "org", s.Id) + + } + + req, err := s.Client.NewRequest(ctx, "GET", "users", nil) + if err != nil { + return nil, nil, fmt.Errorf("error creating ListUsers request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during ListUsers call: %w", err) + } + + type listResponse struct { + Items []*users.User + } + target := &listResponse{} + + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding ListUsers response: %w", err) + } + + for _, t := range target.Items { + + t.Client = s.Client + + } + + return target.Items, apiErr, nil +} + +func (s Project) ListGroups(ctx context.Context) ([]*groups.Group, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in ListGroup request") + } + if s.Id == "" { + + // Assume the client has been configured with project already and move + // on + + } else { + // If it's explicitly set here, override anything that might be in the + // client + + ctx = context.WithValue(ctx, "project", s.Id) + + } + + req, err := s.Client.NewRequest(ctx, "GET", "groups", nil) + if err != nil { + return nil, nil, fmt.Errorf("error creating ListGroups request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during ListGroups call: %w", err) + } + + type listResponse struct { + Items []*groups.Group + } + target := &listResponse{} + + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding ListGroups response: %w", err) + } + + for _, t := range target.Items { + + t.Client = s.Client + + } + + return target.Items, apiErr, nil +} + +func (s Project) ListRoles(ctx context.Context) ([]*roles.Role, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in ListRole request") + } + if s.Id == "" { + + // Assume the client has been configured with project already and move + // on + + } else { + // If it's explicitly set here, override anything that might be in the + // client + + ctx = context.WithValue(ctx, "project", s.Id) + + } + + req, err := s.Client.NewRequest(ctx, "GET", "roles", nil) + if err != nil { + return nil, nil, fmt.Errorf("error creating ListRoles request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during ListRoles call: %w", err) + } + + type listResponse struct { + Items []*roles.Role + } + target := &listResponse{} + + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding ListRoles response: %w", err) + } + + for _, t := range target.Items { + + t.Client = s.Client + + } + + return target.Items, apiErr, nil +} diff --git a/api/scopes/project_test.go b/api/scopes/project_test.go index ecc02dc2e5..2bb59f0915 100644 --- a/api/scopes/project_test.go +++ b/api/scopes/project_test.go @@ -1,6 +1,8 @@ package scopes_test import ( + "context" + "fmt" "net/http" "testing" @@ -10,6 +12,61 @@ import ( "github.com/stretchr/testify/assert" ) +func TestProjects_List(t *testing.T) { + assert := assert.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + org := &scopes.Organization{ + Client: client, + } + ctx := context.Background() + + pl, apiErr, err := org.ListProjects(ctx) + assert.NoError(err) + assert.Nil(apiErr) + assert.Empty(pl) + + var expected []*scopes.Project + for i := 0; i < 10; i++ { + expected = append(expected, &scopes.Project{Name: api.String(fmt.Sprint(i))}) + } + + expected[0], apiErr, err = org.CreateProject(ctx, expected[0]) + assert.NoError(err) + assert.Nil(apiErr) + + pl, apiErr, err = org.ListProjects(ctx) + assert.NoError(err) + assert.Nil(apiErr) + assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(pl)) + + for i := 1; i < 10; i++ { + expected[i], apiErr, err = org.CreateProject(ctx, expected[i]) + assert.NoError(err) + assert.Nil(apiErr) + } + pl, apiErr, err = org.ListProjects(ctx) + assert.ElementsMatch(comparableSlice(expected), comparableSlice(pl)) +} + +func comparableSlice(in []*scopes.Project) []scopes.Project { + var filtered []scopes.Project + for _, i := range in { + p := scopes.Project{ + Id: i.Id, + Name: i.Name, + Description: i.Description, + CreatedTime: i.CreatedTime, + UpdatedTime: i.UpdatedTime, + Disabled: i.Disabled, + } + filtered = append(filtered, p) + } + return filtered +} + func TestProjects_Crud(t *testing.T) { tc := controller.NewTestController(t, nil) defer tc.Shutdown() diff --git a/api/users/user_test.go b/api/users/user_test.go index 6b8646f3f2..8e70cb2681 100644 --- a/api/users/user_test.go +++ b/api/users/user_test.go @@ -1,6 +1,8 @@ package users_test import ( + "context" + "fmt" "net/http" "testing" @@ -12,6 +14,61 @@ import ( "github.com/stretchr/testify/assert" ) +func TestUsers_List(t *testing.T) { + assert := assert.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + org := &scopes.Organization{ + Client: client, + } + ctx := context.Background() + + ul, apiErr, err := org.ListUsers(ctx) + assert.NoError(err) + assert.Nil(apiErr) + assert.Empty(ul) + + var expected []*users.User + for i := 0; i < 10; i++ { + expected = append(expected, &users.User{Name: api.String(fmt.Sprint(i))}) + } + + expected[0], apiErr, err = org.CreateUser(ctx, expected[0]) + assert.NoError(err) + assert.Nil(apiErr) + + ul, apiErr, err = org.ListUsers(ctx) + assert.NoError(err) + assert.Nil(apiErr) + assert.ElementsMatch(comparableSlice(expected[:1]), comparableSlice(ul)) + + for i := 1; i < 10; i++ { + expected[i], apiErr, err = org.CreateUser(ctx, expected[i]) + assert.NoError(err) + assert.Nil(apiErr) + } + ul, apiErr, err = org.ListUsers(ctx) + assert.ElementsMatch(comparableSlice(expected), comparableSlice(ul)) +} + +func comparableSlice(in []*users.User) []users.User { + var filtered []users.User + for _, i := range in { + p := users.User{ + Id: i.Id, + Name: i.Name, + Description: i.Description, + CreatedTime: i.CreatedTime, + UpdatedTime: i.UpdatedTime, + Disabled: i.Disabled, + } + filtered = append(filtered, p) + } + return filtered +} + func TestUser_Crud(t *testing.T) { tc := controller.NewTestController(t, nil) defer tc.Shutdown()