From 6ea448a7f3dcf73ed602447362c0fe7b78793870 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Mon, 15 Jun 2020 15:22:41 -0700 Subject: [PATCH] Adding Groups API endpoint and SDK (#122) * feat: Add group grpc handler. * feat: Add group sdk hooks. * fix: Adding forgotten generated group sdk file. * fix: Fix a copy/paste error in test msg. * fix: Correct assert error msg. * fix: Remove unused proto imports. --- api/groups/group.gen.go | 53 + api/groups/group_test.go | 98 ++ api/hosts/host_catalog_test.go | 2 +- api/internal/genapi/create.go | 5 + api/internal/genapi/delete.go | 5 + api/internal/genapi/input.go | 11 + api/internal/genapi/read.go | 5 + api/internal/genapi/update.go | 5 + api/scopes/create.gen.go | 39 + api/scopes/delete.gen.go | 45 + api/scopes/read.gen.go | 42 + api/scopes/update.gen.go | 42 + api/users/user_test.go | 2 +- go.sum | 4 + internal/gen/controller.swagger.json | 248 ++++ .../api/resources/groups/group.pb.go | 237 ++++ .../api/services/group_service.pb.go | 1189 +++++++++++++++++ .../api/services/group_service.pb.gw.go | 770 +++++++++++ .../api/resources/groups/v1/group.proto | 32 + .../api/services/v1/group_service.proto | 138 ++ internal/servers/controller/handler.go | 8 + .../handlers/groups/group_service.go | 323 +++++ .../handlers/groups/group_service_test.go | 533 ++++++++ 23 files changed, 3834 insertions(+), 2 deletions(-) create mode 100644 api/groups/group.gen.go create mode 100644 api/groups/group_test.go create mode 100644 internal/gen/controller/api/resources/groups/group.pb.go create mode 100644 internal/gen/controller/api/services/group_service.pb.go create mode 100644 internal/gen/controller/api/services/group_service.pb.gw.go create mode 100644 internal/proto/local/controller/api/resources/groups/v1/group.proto create mode 100644 internal/proto/local/controller/api/services/v1/group_service.proto create mode 100644 internal/servers/controller/handlers/groups/group_service.go create mode 100644 internal/servers/controller/handlers/groups/group_service_test.go diff --git a/api/groups/group.gen.go b/api/groups/group.gen.go new file mode 100644 index 000000000..7da80595b --- /dev/null +++ b/api/groups/group.gen.go @@ -0,0 +1,53 @@ +// Code generated by "make api"; DO NOT EDIT. +package groups + +import ( + "encoding/json" + "time" + + "github.com/fatih/structs" + + "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/internal/strutil" +) + +type Group struct { + Client *api.Client `json:"-"` + + defaultFields []string + + // The ID of the Project + // Output only. + Id string `json:"id,omitempty"` + // Optional name for identification purposes + Name *string `json:"name,omitempty"` + // Optional user-set descripton for identification purposes + Description *string `json:"description,omitempty"` + // The time this resource was created + // Output only. + CreatedTime time.Time `json:"created_time,omitempty"` + // The time this resource was last updated. + // Output only. + UpdatedTime time.Time `json:"updated_time,omitempty"` + // Whether the resource is disabled + Disabled *bool `json:"disabled,omitempty"` +} + +func (s *Group) SetDefault(key string) { + s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key) +} + +func (s *Group) UnsetDefault(key string) { + s.defaultFields = strutil.StrListDelete(s.defaultFields, key) +} + +func (s Group) MarshalJSON() ([]byte, error) { + m := structs.Map(s) + if m == nil { + m = make(map[string]interface{}) + } + for _, k := range s.defaultFields { + m[k] = nil + } + return json.Marshal(m) +} diff --git a/api/groups/group_test.go b/api/groups/group_test.go new file mode 100644 index 000000000..09fed5399 --- /dev/null +++ b/api/groups/group_test.go @@ -0,0 +1,98 @@ +package groups_test + +import ( + "net/http" + "testing" + + "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/groups" + "github.com/hashicorp/watchtower/api/scopes" + "github.com/hashicorp/watchtower/internal/iam" + "github.com/hashicorp/watchtower/internal/servers/controller" + "github.com/stretchr/testify/assert" +) + +func TestGroup_Crud(t *testing.T) { + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + + client := tc.Client() + org := &scopes.Organization{ + Client: client, + } + + checkGroup := func(step string, g *groups.Group, apiErr *api.Error, err error, wantedName string) { + assert := assert.New(t) + assert.NoError(err, step) + if !assert.Nil(apiErr, step) && apiErr.Message != nil { + t.Errorf("ApiError message: %q", *apiErr.Message) + } + assert.NotNil(g, "returned no resource", step) + gotName := "" + if g.Name != nil { + gotName = *g.Name + } + assert.Equal(wantedName, gotName, step) + } + + g, apiErr, err := org.CreateGroup(tc.Context(), &groups.Group{Name: api.String("foo")}) + checkGroup("create", g, apiErr, err, "foo") + + g, apiErr, err = org.ReadGroup(tc.Context(), &groups.Group{Id: g.Id}) + checkGroup("read", g, apiErr, err, "foo") + + g = &groups.Group{Id: g.Id} + g.Name = api.String("bar") + g, apiErr, err = org.UpdateGroup(tc.Context(), g) + checkGroup("update", g, apiErr, err, "bar") + + g = &groups.Group{Id: g.Id} + g.SetDefault("name") + g, apiErr, err = org.UpdateGroup(tc.Context(), g) + checkGroup("update", g, apiErr, err, "") + + existed, apiErr, err := org.DeleteGroup(tc.Context(), g) + assert.NoError(t, err) + assert.True(t, existed, "Expected existing user when deleted, but it wasn't.") + + existed, apiErr, err = org.DeleteGroup(tc.Context(), g) + assert.NoError(t, err) + assert.False(t, existed, "Expected user to not exist when deleted, but it did.") +} + +func TestGroup_Errors(t *testing.T) { + assert := assert.New(t) + tc := controller.NewTestController(t, nil) + defer tc.Shutdown() + ctx := tc.Context() + + client := tc.Client() + org := &scopes.Organization{ + Client: client, + } + + u, apiErr, err := org.CreateGroup(ctx, &groups.Group{Name: api.String("first")}) + assert.NoError(err) + assert.Nil(apiErr) + assert.NotNil(u) + + // Create another resource with the same name. + _, apiErr, err = org.CreateGroup(ctx, &groups.Group{Name: api.String("first")}) + assert.NoError(err) + assert.NotNil(apiErr) + + _, apiErr, err = org.ReadGroup(ctx, &groups.Group{Id: iam.GroupPrefix + "_doesntexis"}) + assert.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(*apiErr.Status, http.StatusNotFound) + + _, apiErr, err = org.ReadGroup(ctx, &groups.Group{Id: "invalid id"}) + assert.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(*apiErr.Status, http.StatusBadRequest) + + _, apiErr, err = org.UpdateGroup(ctx, &groups.Group{Id: u.Id}) + assert.NoError(err) + assert.NotNil(apiErr) + assert.EqualValues(*apiErr.Status, http.StatusBadRequest) +} diff --git a/api/hosts/host_catalog_test.go b/api/hosts/host_catalog_test.go index a51b8aada..e58dbe339 100644 --- a/api/hosts/host_catalog_test.go +++ b/api/hosts/host_catalog_test.go @@ -32,7 +32,7 @@ func TestCatalogs_Crud(t *testing.T) { if !assert.Nil(apiErr, step) && apiErr.Message != nil { t.Errorf("ApiError message: %q", *apiErr.Message) } - assert.NotNil(hc, "returned project", step) + assert.NotNil(hc, "returned no resource", step) gotName := "" if hc.Name != nil { gotName = *hc.Name diff --git a/api/internal/genapi/create.go b/api/internal/genapi/create.go index 5f605adc6..479aa7cd1 100644 --- a/api/internal/genapi/create.go +++ b/api/internal/genapi/create.go @@ -34,6 +34,11 @@ var createFuncs = map[string][]*createInfo{ targetType: "Project", path: `"projects"`, }, + { + baseType: "Organization", + targetType: "groups.Group", + path: `"groups"`, + }, { baseType: "Organization", targetType: "users.User", diff --git a/api/internal/genapi/delete.go b/api/internal/genapi/delete.go index 14c8d12d8..11b2fe6b2 100644 --- a/api/internal/genapi/delete.go +++ b/api/internal/genapi/delete.go @@ -22,6 +22,11 @@ var deleteFuncs = map[string][]*deleteInfo{ "Project", "projects", }, + { + "Organization", + "groups.Group", + "groups", + }, { "Organization", "users.User", diff --git a/api/internal/genapi/input.go b/api/internal/genapi/input.go index 436d37d67..8d8c3f1a9 100644 --- a/api/internal/genapi/input.go +++ b/api/internal/genapi/input.go @@ -114,6 +114,17 @@ var inputStructs = []*structInfo{ "", templateTypeResource, }, + { + os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/groups/group.pb.go", + "Group", + os.Getenv("GEN_BASEPATH") + "/api/groups/group.gen.go", + "Group", + "groups", + "", + "", + "", + templateTypeResource, + }, { os.Getenv("GEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go", "StaticHostCatalogDetails", diff --git a/api/internal/genapi/read.go b/api/internal/genapi/read.go index f1383d4e9..b2842614b 100644 --- a/api/internal/genapi/read.go +++ b/api/internal/genapi/read.go @@ -22,6 +22,11 @@ var readFuncs = map[string][]*readInfo{ targetType: "Project", path: "projects", }, + { + baseType: "Organization", + targetType: "groups.Group", + path: "groups", + }, { baseType: "Organization", targetType: "users.User", diff --git a/api/internal/genapi/update.go b/api/internal/genapi/update.go index 4073915da..136ca187c 100644 --- a/api/internal/genapi/update.go +++ b/api/internal/genapi/update.go @@ -22,6 +22,11 @@ var updateFuncs = map[string][]*updateInfo{ targetType: "Project", path: "projects", }, + { + baseType: "Organization", + targetType: "groups.Group", + path: "groups", + }, { baseType: "Organization", targetType: "users.User", diff --git a/api/scopes/create.gen.go b/api/scopes/create.gen.go index 5c4606a97..de8a7176f 100644 --- a/api/scopes/create.gen.go +++ b/api/scopes/create.gen.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/groups" "github.com/hashicorp/watchtower/api/hosts" "github.com/hashicorp/watchtower/api/users" ) @@ -49,6 +50,44 @@ func (s Organization) CreateProject(ctx context.Context, r *Project) (*Project, return target, apiErr, nil } +func (s Organization) CreateGroup(ctx context.Context, r *groups.Group) (*groups.Group, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in Creategroups.Group 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, "POST", "groups", r) + if err != nil { + return nil, nil, fmt.Errorf("error creating Creategroups.Group request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during Creategroups.Group call: %w", err) + } + + target := new(groups.Group) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding Creategroups.Group repsonse: %w", err) + } + + target.Client = s.Client + + return target, apiErr, nil +} + func (s Organization) CreateUser(ctx context.Context, r *users.User) (*users.User, *api.Error, error) { if s.Client == nil { return nil, nil, fmt.Errorf("nil client in Createusers.User request") diff --git a/api/scopes/delete.gen.go b/api/scopes/delete.gen.go index adb930f25..766cdb333 100644 --- a/api/scopes/delete.gen.go +++ b/api/scopes/delete.gen.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/groups" "github.com/hashicorp/watchtower/api/hosts" "github.com/hashicorp/watchtower/api/users" ) @@ -54,6 +55,50 @@ func (s Organization) DeleteProject(ctx context.Context, r *Project) (bool, *api return target.Existed, apiErr, nil } +// DeleteGroup returns true iff the groups.Group existed when the delete attempt was made. +func (s Organization) DeleteGroup(ctx context.Context, r *groups.Group) (bool, *api.Error, error) { + if s.Client == nil { + return false, nil, fmt.Errorf("nil client in DeleteGroup 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) + + } + if r.Id == "" { + return false, nil, fmt.Errorf("empty groups.Group ID field in DeleteGroup request") + } + + req, err := s.Client.NewRequest(ctx, "DELETE", fmt.Sprintf("%s/%s", "groups", r.Id), nil) + if err != nil { + return false, nil, fmt.Errorf("error creating DeleteGroup request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return false, nil, fmt.Errorf("error performing client request during DeleteGroup call: %w", err) + } + + type deleteResponse struct { + Existed bool + } + target := &deleteResponse{} + + apiErr, err := resp.Decode(target) + if err != nil { + return false, nil, fmt.Errorf("error decoding DeleteGroup repsonse: %w", err) + } + + return target.Existed, apiErr, nil +} + // DeleteUser returns true iff the users.User existed when the delete attempt was made. func (s Organization) DeleteUser(ctx context.Context, r *users.User) (bool, *api.Error, error) { if s.Client == nil { diff --git a/api/scopes/read.gen.go b/api/scopes/read.gen.go index 07ec60e47..537b5f194 100644 --- a/api/scopes/read.gen.go +++ b/api/scopes/read.gen.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/groups" "github.com/hashicorp/watchtower/api/hosts" "github.com/hashicorp/watchtower/api/users" ) @@ -52,6 +53,47 @@ func (s Organization) ReadProject(ctx context.Context, r *Project) (*Project, *a return target, apiErr, nil } +func (s Organization) ReadGroup(ctx context.Context, r *groups.Group) (*groups.Group, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in ReadGroup 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) + + } + if r.Id == "" { + return nil, nil, fmt.Errorf("empty groups.Group ID field in ReadGroup request") + } + + req, err := s.Client.NewRequest(ctx, "GET", fmt.Sprintf("%s/%s", "groups", r.Id), r) + if err != nil { + return nil, nil, fmt.Errorf("error creating ReadGroup request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during ReadGroup call: %w", err) + } + + target := new(groups.Group) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding ReadGroup repsonse: %w", err) + } + + target.Client = s.Client + + return target, apiErr, nil +} + func (s Organization) ReadUser(ctx context.Context, r *users.User) (*users.User, *api.Error, error) { if s.Client == nil { return nil, nil, fmt.Errorf("nil client in ReadUser request") diff --git a/api/scopes/update.gen.go b/api/scopes/update.gen.go index d18bbc612..b9e996bb9 100644 --- a/api/scopes/update.gen.go +++ b/api/scopes/update.gen.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/hashicorp/watchtower/api" + "github.com/hashicorp/watchtower/api/groups" "github.com/hashicorp/watchtower/api/hosts" "github.com/hashicorp/watchtower/api/users" ) @@ -52,6 +53,47 @@ func (s Organization) UpdateProject(ctx context.Context, r *Project) (*Project, return target, apiErr, nil } +func (s Organization) UpdateGroup(ctx context.Context, r *groups.Group) (*groups.Group, *api.Error, error) { + if s.Client == nil { + return nil, nil, fmt.Errorf("nil client in CreateGroup 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) + + } + + id := r.Id + r.Id = "" + + req, err := s.Client.NewRequest(ctx, "PATCH", fmt.Sprintf("%s/%s", "groups", id), r) + if err != nil { + return nil, nil, fmt.Errorf("error creating CreateGroup request: %w", err) + } + + resp, err := s.Client.Do(req) + if err != nil { + return nil, nil, fmt.Errorf("error performing client request during UpdateGroup call: %w", err) + } + + target := new(groups.Group) + apiErr, err := resp.Decode(target) + if err != nil { + return nil, nil, fmt.Errorf("error decoding UpdateGroup repsonse: %w", err) + } + + target.Client = s.Client + + return target, apiErr, nil +} + func (s Organization) UpdateUser(ctx context.Context, r *users.User) (*users.User, *api.Error, error) { if s.Client == nil { return nil, nil, fmt.Errorf("nil client in CreateUser request") diff --git a/api/users/user_test.go b/api/users/user_test.go index fdeed59a1..6b8646f3f 100644 --- a/api/users/user_test.go +++ b/api/users/user_test.go @@ -27,7 +27,7 @@ func TestUser_Crud(t *testing.T) { if !assert.Nil(apiErr, step) && apiErr.Message != nil { t.Errorf("ApiError message: %q", *apiErr.Message) } - assert.NotNil(u, "returned project", step) + assert.NotNil(u, "returned no resource", step) gotName := "" if u.Name != nil { gotName = *u.Name diff --git a/go.sum b/go.sum index 748e8bcc3..99bb248b2 100644 --- a/go.sum +++ b/go.sum @@ -828,6 +828,7 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8= github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4= +github.com/jhump/protoreflect v1.6.2-0.20200602154950-9a9113f80b57 h1:DDbkVWVTpzxHbD80e2zpbczB+gkTWI1w2gSeL+viRhc= github.com/jhump/protoreflect v1.6.2-0.20200602154950-9a9113f80b57/go.mod h1:RZkzh7Hi9J7qT/sPlWnJ/UwZqCJvciFxKDA0UCeltSM= github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= @@ -872,8 +873,10 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg= github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A= github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= @@ -1075,6 +1078,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.4.0 h1:uCmaf4vVbWAOZz36k1hrQD7ijGRzLwaME8Am/7a4jZI= github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= +github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug= github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 95637dfab..bf5cf4e34 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -16,6 +16,171 @@ "application/json" ], "paths": { + "/v1/orgs/{org_id}/groups": { + "get": { + "summary": "Lists all Groups", + "operationId": "GroupService_ListGroups", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/controller.api.services.v1.ListGroupsResponse" + } + } + }, + "parameters": [ + { + "name": "org_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "view", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.GroupService" + ] + }, + "post": { + "summary": "Creates a single Group", + "operationId": "GroupService_CreateGroup", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.groups.v1.Group" + } + } + }, + "parameters": [ + { + "name": "org_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.resources.groups.v1.Group" + } + } + ], + "tags": [ + "controller.api.services.v1.GroupService" + ] + } + }, + "/v1/orgs/{org_id}/groups/{id}": { + "get": { + "summary": "Gets a single Group", + "operationId": "GroupService_GetGroup", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.groups.v1.Group" + } + } + }, + "parameters": [ + { + "name": "org_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "view", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.GroupService" + ] + }, + "delete": { + "summary": "Deletes a Group", + "operationId": "GroupService_DeleteGroup", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/controller.api.services.v1.DeleteGroupResponse" + } + } + }, + "parameters": [ + { + "name": "org_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "controller.api.services.v1.GroupService" + ] + }, + "patch": { + "summary": "Updates a Group", + "operationId": "GroupService_UpdateGroup", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/controller.api.resources.groups.v1.Group" + } + } + }, + "parameters": [ + { + "name": "org_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/controller.api.resources.groups.v1.Group" + } + } + ], + "tags": [ + "controller.api.services.v1.GroupService" + ] + } + }, "/v1/orgs/{org_id}/host-catalogs": { "get": { "summary": "List all Hosts for the specified catalog", @@ -1737,6 +1902,42 @@ } }, "definitions": { + "controller.api.resources.groups.v1.Group": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the Project\nOutput only.", + "readOnly": true + }, + "name": { + "type": "string", + "title": "Optional name for identification purposes" + }, + "description": { + "type": "string", + "title": "Optional user-set descripton for identification purposes" + }, + "created_time": { + "type": "string", + "format": "date-time", + "description": "The time this resource was created\nOutput only.", + "readOnly": true + }, + "updated_time": { + "type": "string", + "format": "date-time", + "description": "The time this resource was last updated.\nOutput only.", + "readOnly": true + }, + "disabled": { + "type": "boolean", + "format": "boolean", + "title": "Whether the resource is disabled" + } + }, + "title": "Group contains all fields related to a Group resource" + }, "controller.api.resources.hosts.v1.Host": { "type": "object", "properties": { @@ -1954,6 +2155,17 @@ "controller.api.services.v1.AddToHostSetResponse": { "type": "object" }, + "controller.api.services.v1.CreateGroupResponse": { + "type": "object", + "properties": { + "uri": { + "type": "string" + }, + "item": { + "$ref": "#/definitions/controller.api.resources.groups.v1.Group" + } + } + }, "controller.api.services.v1.CreateHostCatalogResponse": { "type": "object", "properties": { @@ -2009,6 +2221,15 @@ } } }, + "controller.api.services.v1.DeleteGroupResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean", + "format": "boolean" + } + } + }, "controller.api.services.v1.DeleteHostCatalogResponse": { "type": "object", "properties": { @@ -2054,6 +2275,14 @@ } } }, + "controller.api.services.v1.GetGroupResponse": { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/controller.api.resources.groups.v1.Group" + } + } + }, "controller.api.services.v1.GetHostCatalogResponse": { "type": "object", "properties": { @@ -2094,6 +2323,17 @@ } } }, + "controller.api.services.v1.ListGroupsResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/controller.api.resources.groups.v1.Group" + } + } + } + }, "controller.api.services.v1.ListHostCatalogsResponse": { "type": "object", "properties": { @@ -2152,6 +2392,14 @@ "controller.api.services.v1.RemoveFromHostSetResponse": { "type": "object" }, + "controller.api.services.v1.UpdateGroupResponse": { + "type": "object", + "properties": { + "item": { + "$ref": "#/definitions/controller.api.resources.groups.v1.Group" + } + } + }, "controller.api.services.v1.UpdateHostCatalogResponse": { "type": "object", "properties": { diff --git a/internal/gen/controller/api/resources/groups/group.pb.go b/internal/gen/controller/api/resources/groups/group.pb.go new file mode 100644 index 000000000..6b0a4efa1 --- /dev/null +++ b/internal/gen/controller/api/resources/groups/group.pb.go @@ -0,0 +1,237 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.24.0 +// protoc v3.12.3 +// source: controller/api/resources/groups/v1/group.proto + +package groups + +import ( + proto "github.com/golang/protobuf/proto" + timestamp "github.com/golang/protobuf/ptypes/timestamp" + wrappers "github.com/golang/protobuf/ptypes/wrappers" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +// Group contains all fields related to a Group resource +type Group struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The ID of the Project + // Output only. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Optional name for identification purposes + Name *wrappers.StringValue `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // Optional user-set descripton for identification purposes + Description *wrappers.StringValue `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + // The time this resource was created + // Output only. + CreatedTime *timestamp.Timestamp `protobuf:"bytes,4,opt,name=created_time,proto3" json:"created_time,omitempty"` + // The time this resource was last updated. + // Output only. + UpdatedTime *timestamp.Timestamp `protobuf:"bytes,5,opt,name=updated_time,proto3" json:"updated_time,omitempty"` + // Whether the resource is disabled + Disabled *wrappers.BoolValue `protobuf:"bytes,6,opt,name=disabled,proto3" json:"disabled,omitempty"` +} + +func (x *Group) Reset() { + *x = Group{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_resources_groups_v1_group_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Group) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Group) ProtoMessage() {} + +func (x *Group) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_resources_groups_v1_group_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Group.ProtoReflect.Descriptor instead. +func (*Group) Descriptor() ([]byte, []int) { + return file_controller_api_resources_groups_v1_group_proto_rawDescGZIP(), []int{0} +} + +func (x *Group) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Group) GetName() *wrappers.StringValue { + if x != nil { + return x.Name + } + return nil +} + +func (x *Group) GetDescription() *wrappers.StringValue { + if x != nil { + return x.Description + } + return nil +} + +func (x *Group) GetCreatedTime() *timestamp.Timestamp { + if x != nil { + return x.CreatedTime + } + return nil +} + +func (x *Group) GetUpdatedTime() *timestamp.Timestamp { + if x != nil { + return x.UpdatedTime + } + return nil +} + +func (x *Group) GetDisabled() *wrappers.BoolValue { + if x != nil { + return x.Disabled + } + return nil +} + +var File_controller_api_resources_groups_v1_group_proto protoreflect.FileDescriptor + +var file_controller_api_resources_groups_v1_group_proto_rawDesc = []byte{ + 0x0a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc1, 0x02, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x30, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x3e, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x12, 0x36, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x55, 0x5a, 0x53, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x3b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_api_resources_groups_v1_group_proto_rawDescOnce sync.Once + file_controller_api_resources_groups_v1_group_proto_rawDescData = file_controller_api_resources_groups_v1_group_proto_rawDesc +) + +func file_controller_api_resources_groups_v1_group_proto_rawDescGZIP() []byte { + file_controller_api_resources_groups_v1_group_proto_rawDescOnce.Do(func() { + file_controller_api_resources_groups_v1_group_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_resources_groups_v1_group_proto_rawDescData) + }) + return file_controller_api_resources_groups_v1_group_proto_rawDescData +} + +var file_controller_api_resources_groups_v1_group_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_api_resources_groups_v1_group_proto_goTypes = []interface{}{ + (*Group)(nil), // 0: controller.api.resources.groups.v1.Group + (*wrappers.StringValue)(nil), // 1: google.protobuf.StringValue + (*timestamp.Timestamp)(nil), // 2: google.protobuf.Timestamp + (*wrappers.BoolValue)(nil), // 3: google.protobuf.BoolValue +} +var file_controller_api_resources_groups_v1_group_proto_depIdxs = []int32{ + 1, // 0: controller.api.resources.groups.v1.Group.name:type_name -> google.protobuf.StringValue + 1, // 1: controller.api.resources.groups.v1.Group.description:type_name -> google.protobuf.StringValue + 2, // 2: controller.api.resources.groups.v1.Group.created_time:type_name -> google.protobuf.Timestamp + 2, // 3: controller.api.resources.groups.v1.Group.updated_time:type_name -> google.protobuf.Timestamp + 3, // 4: controller.api.resources.groups.v1.Group.disabled:type_name -> google.protobuf.BoolValue + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_controller_api_resources_groups_v1_group_proto_init() } +func file_controller_api_resources_groups_v1_group_proto_init() { + if File_controller_api_resources_groups_v1_group_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_api_resources_groups_v1_group_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Group); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_api_resources_groups_v1_group_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_api_resources_groups_v1_group_proto_goTypes, + DependencyIndexes: file_controller_api_resources_groups_v1_group_proto_depIdxs, + MessageInfos: file_controller_api_resources_groups_v1_group_proto_msgTypes, + }.Build() + File_controller_api_resources_groups_v1_group_proto = out.File + file_controller_api_resources_groups_v1_group_proto_rawDesc = nil + file_controller_api_resources_groups_v1_group_proto_goTypes = nil + file_controller_api_resources_groups_v1_group_proto_depIdxs = nil +} diff --git a/internal/gen/controller/api/services/group_service.pb.go b/internal/gen/controller/api/services/group_service.pb.go new file mode 100644 index 000000000..1d9e27572 --- /dev/null +++ b/internal/gen/controller/api/services/group_service.pb.go @@ -0,0 +1,1189 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.24.0 +// protoc v3.12.3 +// source: controller/api/services/v1/group_service.proto + +package services + +import ( + context "context" + proto "github.com/golang/protobuf/proto" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" + groups "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/groups" + _ "google.golang.org/genproto/googleapis/api/annotations" + field_mask "google.golang.org/genproto/protobuf/field_mask" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type GetGroupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OrgId string `protobuf:"bytes,1,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + View string `protobuf:"bytes,3,opt,name=view,proto3" json:"view,omitempty"` +} + +func (x *GetGroupRequest) Reset() { + *x = GetGroupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGroupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGroupRequest) ProtoMessage() {} + +func (x *GetGroupRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetGroupRequest.ProtoReflect.Descriptor instead. +func (*GetGroupRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_group_service_proto_rawDescGZIP(), []int{0} +} + +func (x *GetGroupRequest) GetOrgId() string { + if x != nil { + return x.OrgId + } + return "" +} + +func (x *GetGroupRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *GetGroupRequest) GetView() string { + if x != nil { + return x.View + } + return "" +} + +type GetGroupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *groups.Group `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *GetGroupResponse) Reset() { + *x = GetGroupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGroupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGroupResponse) ProtoMessage() {} + +func (x *GetGroupResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetGroupResponse.ProtoReflect.Descriptor instead. +func (*GetGroupResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_group_service_proto_rawDescGZIP(), []int{1} +} + +func (x *GetGroupResponse) GetItem() *groups.Group { + if x != nil { + return x.Item + } + return nil +} + +type ListGroupsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OrgId string `protobuf:"bytes,1,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"` + View string `protobuf:"bytes,2,opt,name=view,proto3" json:"view,omitempty"` +} + +func (x *ListGroupsRequest) Reset() { + *x = ListGroupsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListGroupsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListGroupsRequest) ProtoMessage() {} + +func (x *ListGroupsRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListGroupsRequest.ProtoReflect.Descriptor instead. +func (*ListGroupsRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_group_service_proto_rawDescGZIP(), []int{2} +} + +func (x *ListGroupsRequest) GetOrgId() string { + if x != nil { + return x.OrgId + } + return "" +} + +func (x *ListGroupsRequest) GetView() string { + if x != nil { + return x.View + } + return "" +} + +type ListGroupsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Items []*groups.Group `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` +} + +func (x *ListGroupsResponse) Reset() { + *x = ListGroupsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListGroupsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListGroupsResponse) ProtoMessage() {} + +func (x *ListGroupsResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListGroupsResponse.ProtoReflect.Descriptor instead. +func (*ListGroupsResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_group_service_proto_rawDescGZIP(), []int{3} +} + +func (x *ListGroupsResponse) GetItems() []*groups.Group { + if x != nil { + return x.Items + } + return nil +} + +type CreateGroupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OrgId string `protobuf:"bytes,1,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"` + Item *groups.Group `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *CreateGroupRequest) Reset() { + *x = CreateGroupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateGroupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateGroupRequest) ProtoMessage() {} + +func (x *CreateGroupRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateGroupRequest.ProtoReflect.Descriptor instead. +func (*CreateGroupRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_group_service_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateGroupRequest) GetOrgId() string { + if x != nil { + return x.OrgId + } + return "" +} + +func (x *CreateGroupRequest) GetItem() *groups.Group { + if x != nil { + return x.Item + } + return nil +} + +type CreateGroupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uri string `protobuf:"bytes,1,opt,name=uri,proto3" json:"uri,omitempty"` + Item *groups.Group `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *CreateGroupResponse) Reset() { + *x = CreateGroupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateGroupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateGroupResponse) ProtoMessage() {} + +func (x *CreateGroupResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateGroupResponse.ProtoReflect.Descriptor instead. +func (*CreateGroupResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_group_service_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateGroupResponse) GetUri() string { + if x != nil { + return x.Uri + } + return "" +} + +func (x *CreateGroupResponse) GetItem() *groups.Group { + if x != nil { + return x.Item + } + return nil +} + +type UpdateGroupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OrgId string `protobuf:"bytes,1,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Item *groups.Group `protobuf:"bytes,3,opt,name=item,proto3" json:"item,omitempty"` + UpdateMask *field_mask.FieldMask `protobuf:"bytes,4,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` +} + +func (x *UpdateGroupRequest) Reset() { + *x = UpdateGroupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateGroupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateGroupRequest) ProtoMessage() {} + +func (x *UpdateGroupRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateGroupRequest.ProtoReflect.Descriptor instead. +func (*UpdateGroupRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_group_service_proto_rawDescGZIP(), []int{6} +} + +func (x *UpdateGroupRequest) GetOrgId() string { + if x != nil { + return x.OrgId + } + return "" +} + +func (x *UpdateGroupRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UpdateGroupRequest) GetItem() *groups.Group { + if x != nil { + return x.Item + } + return nil +} + +func (x *UpdateGroupRequest) GetUpdateMask() *field_mask.FieldMask { + if x != nil { + return x.UpdateMask + } + return nil +} + +type UpdateGroupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Item *groups.Group `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` +} + +func (x *UpdateGroupResponse) Reset() { + *x = UpdateGroupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateGroupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateGroupResponse) ProtoMessage() {} + +func (x *UpdateGroupResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateGroupResponse.ProtoReflect.Descriptor instead. +func (*UpdateGroupResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_group_service_proto_rawDescGZIP(), []int{7} +} + +func (x *UpdateGroupResponse) GetItem() *groups.Group { + if x != nil { + return x.Item + } + return nil +} + +type DeleteGroupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OrgId string `protobuf:"bytes,1,opt,name=org_id,json=orgId,proto3" json:"org_id,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteGroupRequest) Reset() { + *x = DeleteGroupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteGroupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteGroupRequest) ProtoMessage() {} + +func (x *DeleteGroupRequest) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteGroupRequest.ProtoReflect.Descriptor instead. +func (*DeleteGroupRequest) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_group_service_proto_rawDescGZIP(), []int{8} +} + +func (x *DeleteGroupRequest) GetOrgId() string { + if x != nil { + return x.OrgId + } + return "" +} + +func (x *DeleteGroupRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DeleteGroupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Existed bool `protobuf:"varint,1,opt,name=existed,proto3" json:"existed,omitempty"` +} + +func (x *DeleteGroupResponse) Reset() { + *x = DeleteGroupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteGroupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteGroupResponse) ProtoMessage() {} + +func (x *DeleteGroupResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_services_v1_group_service_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteGroupResponse.ProtoReflect.Descriptor instead. +func (*DeleteGroupResponse) Descriptor() ([]byte, []int) { + return file_controller_api_services_v1_group_service_proto_rawDescGZIP(), []int{9} +} + +func (x *DeleteGroupResponse) GetExisted() bool { + if x != nil { + return x.Existed + } + return false +} + +var File_controller_api_services_v1_group_service_proto protoreflect.FileDescriptor + +var file_controller_api_services_v1_group_service_proto_rawDesc = []byte{ + 0x0a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x1a, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x1a, 0x2c, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, + 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, + 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4c, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, + 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, + 0x72, 0x67, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x22, 0x51, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x04, + 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x3e, 0x0a, 0x11, 0x4c, + 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x22, 0x55, 0x0a, 0x12, 0x4c, + 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3f, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x69, 0x74, 0x65, + 0x6d, 0x73, 0x22, 0x6a, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, + 0x3d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x66, + 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x3d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xb7, 0x01, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, + 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, + 0x72, 0x67, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x3d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x04, 0x69, + 0x74, 0x65, 0x6d, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, + 0x73, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, + 0x22, 0x54, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, + 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x3b, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, + 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, + 0x67, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x22, 0x2f, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, + 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x69, + 0x73, 0x74, 0x65, 0x64, 0x32, 0xfe, 0x06, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xaa, 0x01, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, + 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, + 0x7b, 0x69, 0x64, 0x7d, 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x15, 0x12, 0x13, 0x47, + 0x65, 0x74, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x12, 0xa2, 0x01, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, + 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x73, 0x92, 0x41, 0x12, 0x12, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6c, 0x6c, + 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0xb7, 0x01, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x47, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, + 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, + 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, + 0x62, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x18, 0x12, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x12, 0xb5, 0x01, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x12, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x45, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x32, 0x1d, 0x2f, 0x76, 0x31, 0x2f, + 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x3a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x62, + 0x04, 0x69, 0x74, 0x65, 0x6d, 0x92, 0x41, 0x11, 0x12, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x20, 0x61, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0xa9, 0x01, 0x0a, 0x0b, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1f, 0x2a, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x72, 0x67, 0x73, 0x2f, 0x7b, 0x6f, 0x72, + 0x67, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x92, 0x41, 0x11, 0x12, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, + 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_api_services_v1_group_service_proto_rawDescOnce sync.Once + file_controller_api_services_v1_group_service_proto_rawDescData = file_controller_api_services_v1_group_service_proto_rawDesc +) + +func file_controller_api_services_v1_group_service_proto_rawDescGZIP() []byte { + file_controller_api_services_v1_group_service_proto_rawDescOnce.Do(func() { + file_controller_api_services_v1_group_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_services_v1_group_service_proto_rawDescData) + }) + return file_controller_api_services_v1_group_service_proto_rawDescData +} + +var file_controller_api_services_v1_group_service_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_controller_api_services_v1_group_service_proto_goTypes = []interface{}{ + (*GetGroupRequest)(nil), // 0: controller.api.services.v1.GetGroupRequest + (*GetGroupResponse)(nil), // 1: controller.api.services.v1.GetGroupResponse + (*ListGroupsRequest)(nil), // 2: controller.api.services.v1.ListGroupsRequest + (*ListGroupsResponse)(nil), // 3: controller.api.services.v1.ListGroupsResponse + (*CreateGroupRequest)(nil), // 4: controller.api.services.v1.CreateGroupRequest + (*CreateGroupResponse)(nil), // 5: controller.api.services.v1.CreateGroupResponse + (*UpdateGroupRequest)(nil), // 6: controller.api.services.v1.UpdateGroupRequest + (*UpdateGroupResponse)(nil), // 7: controller.api.services.v1.UpdateGroupResponse + (*DeleteGroupRequest)(nil), // 8: controller.api.services.v1.DeleteGroupRequest + (*DeleteGroupResponse)(nil), // 9: controller.api.services.v1.DeleteGroupResponse + (*groups.Group)(nil), // 10: controller.api.resources.groups.v1.Group + (*field_mask.FieldMask)(nil), // 11: google.protobuf.FieldMask +} +var file_controller_api_services_v1_group_service_proto_depIdxs = []int32{ + 10, // 0: controller.api.services.v1.GetGroupResponse.item:type_name -> controller.api.resources.groups.v1.Group + 10, // 1: controller.api.services.v1.ListGroupsResponse.items:type_name -> controller.api.resources.groups.v1.Group + 10, // 2: controller.api.services.v1.CreateGroupRequest.item:type_name -> controller.api.resources.groups.v1.Group + 10, // 3: controller.api.services.v1.CreateGroupResponse.item:type_name -> controller.api.resources.groups.v1.Group + 10, // 4: controller.api.services.v1.UpdateGroupRequest.item:type_name -> controller.api.resources.groups.v1.Group + 11, // 5: controller.api.services.v1.UpdateGroupRequest.update_mask:type_name -> google.protobuf.FieldMask + 10, // 6: controller.api.services.v1.UpdateGroupResponse.item:type_name -> controller.api.resources.groups.v1.Group + 0, // 7: controller.api.services.v1.GroupService.GetGroup:input_type -> controller.api.services.v1.GetGroupRequest + 2, // 8: controller.api.services.v1.GroupService.ListGroups:input_type -> controller.api.services.v1.ListGroupsRequest + 4, // 9: controller.api.services.v1.GroupService.CreateGroup:input_type -> controller.api.services.v1.CreateGroupRequest + 6, // 10: controller.api.services.v1.GroupService.UpdateGroup:input_type -> controller.api.services.v1.UpdateGroupRequest + 8, // 11: controller.api.services.v1.GroupService.DeleteGroup:input_type -> controller.api.services.v1.DeleteGroupRequest + 1, // 12: controller.api.services.v1.GroupService.GetGroup:output_type -> controller.api.services.v1.GetGroupResponse + 3, // 13: controller.api.services.v1.GroupService.ListGroups:output_type -> controller.api.services.v1.ListGroupsResponse + 5, // 14: controller.api.services.v1.GroupService.CreateGroup:output_type -> controller.api.services.v1.CreateGroupResponse + 7, // 15: controller.api.services.v1.GroupService.UpdateGroup:output_type -> controller.api.services.v1.UpdateGroupResponse + 9, // 16: controller.api.services.v1.GroupService.DeleteGroup:output_type -> controller.api.services.v1.DeleteGroupResponse + 12, // [12:17] is the sub-list for method output_type + 7, // [7:12] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_controller_api_services_v1_group_service_proto_init() } +func file_controller_api_services_v1_group_service_proto_init() { + if File_controller_api_services_v1_group_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_api_services_v1_group_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_group_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_group_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListGroupsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_group_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListGroupsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_group_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateGroupRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_group_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateGroupResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_group_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateGroupRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_group_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateGroupResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_group_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteGroupRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controller_api_services_v1_group_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteGroupResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_api_services_v1_group_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_controller_api_services_v1_group_service_proto_goTypes, + DependencyIndexes: file_controller_api_services_v1_group_service_proto_depIdxs, + MessageInfos: file_controller_api_services_v1_group_service_proto_msgTypes, + }.Build() + File_controller_api_services_v1_group_service_proto = out.File + file_controller_api_services_v1_group_service_proto_rawDesc = nil + file_controller_api_services_v1_group_service_proto_goTypes = nil + file_controller_api_services_v1_group_service_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// GroupServiceClient is the client API for GroupService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type GroupServiceClient interface { + // GetGroup returns a stored Group if present. The provided request + // must include the org and group id for the group being retrieved. If + // any of those ids are missing, malformed or reference a non existing + // resource an error is returned. + GetGroup(ctx context.Context, in *GetGroupRequest, opts ...grpc.CallOption) (*GetGroupResponse, error) + // ListGroups returns a list of stored groups which exist inside the org + // referenced inside the request. The request must include the org id for + // the groups being retrieved. If the org id is missing, malformed, or + // reference a non existing organization, an error is returned. + ListGroups(ctx context.Context, in *ListGroupsRequest, opts ...grpc.CallOption) (*ListGroupsResponse, error) + // CreateGroup creates and stores a group in watchtower. The provided + // request must include the org id in which the group will be created. + // If the org id is missing, malformed or references a non existing + // organization, an error is returned. If a name is provided that is in + // use in another group in the same organization, an error is returned. + CreateGroup(ctx context.Context, in *CreateGroupRequest, opts ...grpc.CallOption) (*CreateGroupResponse, error) + // UpdateGroup updates an existing group in watchtower. The provided + // group must not have any read only fields set. The update mask must be + // included in the request and contain at least 1 mutable field. To unset + // a field's value, include the field in the update mask and don't set it + // in the provided group. An error is returned if either the organization + // or group ids are missing or reference a non existing resource. An error + // is also returned if the request attempts to update the name to one that is + // already in use in this organization. + UpdateGroup(ctx context.Context, in *UpdateGroupRequest, opts ...grpc.CallOption) (*UpdateGroupResponse, error) + // DeleteGroup removes a group from Watchtower. If the provided org or group ids + // are malformed or not provided an error is returned. No error is returned + // if either ids reference resources that do not exist as the response itself + // specifies if the resource existed before the DeleteGroup request was + // received. + DeleteGroup(ctx context.Context, in *DeleteGroupRequest, opts ...grpc.CallOption) (*DeleteGroupResponse, error) +} + +type groupServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewGroupServiceClient(cc grpc.ClientConnInterface) GroupServiceClient { + return &groupServiceClient{cc} +} + +func (c *groupServiceClient) GetGroup(ctx context.Context, in *GetGroupRequest, opts ...grpc.CallOption) (*GetGroupResponse, error) { + out := new(GetGroupResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.GroupService/GetGroup", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) ListGroups(ctx context.Context, in *ListGroupsRequest, opts ...grpc.CallOption) (*ListGroupsResponse, error) { + out := new(ListGroupsResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.GroupService/ListGroups", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) CreateGroup(ctx context.Context, in *CreateGroupRequest, opts ...grpc.CallOption) (*CreateGroupResponse, error) { + out := new(CreateGroupResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.GroupService/CreateGroup", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) UpdateGroup(ctx context.Context, in *UpdateGroupRequest, opts ...grpc.CallOption) (*UpdateGroupResponse, error) { + out := new(UpdateGroupResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.GroupService/UpdateGroup", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) DeleteGroup(ctx context.Context, in *DeleteGroupRequest, opts ...grpc.CallOption) (*DeleteGroupResponse, error) { + out := new(DeleteGroupResponse) + err := c.cc.Invoke(ctx, "/controller.api.services.v1.GroupService/DeleteGroup", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GroupServiceServer is the server API for GroupService service. +type GroupServiceServer interface { + // GetGroup returns a stored Group if present. The provided request + // must include the org and group id for the group being retrieved. If + // any of those ids are missing, malformed or reference a non existing + // resource an error is returned. + GetGroup(context.Context, *GetGroupRequest) (*GetGroupResponse, error) + // ListGroups returns a list of stored groups which exist inside the org + // referenced inside the request. The request must include the org id for + // the groups being retrieved. If the org id is missing, malformed, or + // reference a non existing organization, an error is returned. + ListGroups(context.Context, *ListGroupsRequest) (*ListGroupsResponse, error) + // CreateGroup creates and stores a group in watchtower. The provided + // request must include the org id in which the group will be created. + // If the org id is missing, malformed or references a non existing + // organization, an error is returned. If a name is provided that is in + // use in another group in the same organization, an error is returned. + CreateGroup(context.Context, *CreateGroupRequest) (*CreateGroupResponse, error) + // UpdateGroup updates an existing group in watchtower. The provided + // group must not have any read only fields set. The update mask must be + // included in the request and contain at least 1 mutable field. To unset + // a field's value, include the field in the update mask and don't set it + // in the provided group. An error is returned if either the organization + // or group ids are missing or reference a non existing resource. An error + // is also returned if the request attempts to update the name to one that is + // already in use in this organization. + UpdateGroup(context.Context, *UpdateGroupRequest) (*UpdateGroupResponse, error) + // DeleteGroup removes a group from Watchtower. If the provided org or group ids + // are malformed or not provided an error is returned. No error is returned + // if either ids reference resources that do not exist as the response itself + // specifies if the resource existed before the DeleteGroup request was + // received. + DeleteGroup(context.Context, *DeleteGroupRequest) (*DeleteGroupResponse, error) +} + +// UnimplementedGroupServiceServer can be embedded to have forward compatible implementations. +type UnimplementedGroupServiceServer struct { +} + +func (*UnimplementedGroupServiceServer) GetGroup(context.Context, *GetGroupRequest) (*GetGroupResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetGroup not implemented") +} +func (*UnimplementedGroupServiceServer) ListGroups(context.Context, *ListGroupsRequest) (*ListGroupsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListGroups not implemented") +} +func (*UnimplementedGroupServiceServer) CreateGroup(context.Context, *CreateGroupRequest) (*CreateGroupResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateGroup not implemented") +} +func (*UnimplementedGroupServiceServer) UpdateGroup(context.Context, *UpdateGroupRequest) (*UpdateGroupResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateGroup not implemented") +} +func (*UnimplementedGroupServiceServer) DeleteGroup(context.Context, *DeleteGroupRequest) (*DeleteGroupResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteGroup not implemented") +} + +func RegisterGroupServiceServer(s *grpc.Server, srv GroupServiceServer) { + s.RegisterService(&_GroupService_serviceDesc, srv) +} + +func _GroupService_GetGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetGroupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).GetGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.GroupService/GetGroup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).GetGroup(ctx, req.(*GetGroupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_ListGroups_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListGroupsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).ListGroups(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.GroupService/ListGroups", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).ListGroups(ctx, req.(*ListGroupsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_CreateGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateGroupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).CreateGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.GroupService/CreateGroup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).CreateGroup(ctx, req.(*CreateGroupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_UpdateGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateGroupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).UpdateGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.GroupService/UpdateGroup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).UpdateGroup(ctx, req.(*UpdateGroupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_DeleteGroup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteGroupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).DeleteGroup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/controller.api.services.v1.GroupService/DeleteGroup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).DeleteGroup(ctx, req.(*DeleteGroupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _GroupService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "controller.api.services.v1.GroupService", + HandlerType: (*GroupServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetGroup", + Handler: _GroupService_GetGroup_Handler, + }, + { + MethodName: "ListGroups", + Handler: _GroupService_ListGroups_Handler, + }, + { + MethodName: "CreateGroup", + Handler: _GroupService_CreateGroup_Handler, + }, + { + MethodName: "UpdateGroup", + Handler: _GroupService_UpdateGroup_Handler, + }, + { + MethodName: "DeleteGroup", + Handler: _GroupService_DeleteGroup_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "controller/api/services/v1/group_service.proto", +} diff --git a/internal/gen/controller/api/services/group_service.pb.gw.go b/internal/gen/controller/api/services/group_service.pb.gw.go new file mode 100644 index 000000000..161503ab0 --- /dev/null +++ b/internal/gen/controller/api/services/group_service.pb.gw.go @@ -0,0 +1,770 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: controller/api/services/v1/group_service.proto + +/* +Package services is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package services + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage + +var ( + filter_GroupService_GetGroup_0 = &utilities.DoubleArray{Encoding: map[string]int{"org_id": 0, "id": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + +func request_GroupService_GetGroup_0(ctx context.Context, marshaler runtime.Marshaler, client GroupServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetGroupRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GroupService_GetGroup_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GroupService_GetGroup_0(ctx context.Context, marshaler runtime.Marshaler, server GroupServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetGroupRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GroupService_GetGroup_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetGroup(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GroupService_ListGroups_0 = &utilities.DoubleArray{Encoding: map[string]int{"org_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_GroupService_ListGroups_0(ctx context.Context, marshaler runtime.Marshaler, client GroupServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListGroupsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GroupService_ListGroups_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListGroups(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GroupService_ListGroups_0(ctx context.Context, marshaler runtime.Marshaler, server GroupServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListGroupsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GroupService_ListGroups_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListGroups(ctx, &protoReq) + return msg, metadata, err + +} + +func request_GroupService_CreateGroup_0(ctx context.Context, marshaler runtime.Marshaler, client GroupServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateGroupRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + msg, err := client.CreateGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GroupService_CreateGroup_0(ctx context.Context, marshaler runtime.Marshaler, server GroupServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateGroupRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + msg, err := server.CreateGroup(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GroupService_UpdateGroup_0 = &utilities.DoubleArray{Encoding: map[string]int{"item": 0, "org_id": 1, "id": 2}, Base: []int{1, 1, 2, 3, 0, 0, 0}, Check: []int{0, 1, 1, 1, 2, 3, 4}} +) + +func request_GroupService_UpdateGroup_0(ctx context.Context, marshaler runtime.Marshaler, client GroupServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateGroupRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 { + _, md := descriptor.ForMessage(protoReq.Item) + if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), md); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } else { + protoReq.UpdateMask = fieldMask + } + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GroupService_UpdateGroup_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.UpdateGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GroupService_UpdateGroup_0(ctx context.Context, marshaler runtime.Marshaler, server GroupServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateGroupRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Item); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 { + _, md := descriptor.ForMessage(protoReq.Item) + if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), md); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } else { + protoReq.UpdateMask = fieldMask + } + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GroupService_UpdateGroup_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.UpdateGroup(ctx, &protoReq) + return msg, metadata, err + +} + +func request_GroupService_DeleteGroup_0(ctx context.Context, marshaler runtime.Marshaler, client GroupServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteGroupRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := client.DeleteGroup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GroupService_DeleteGroup_0(ctx context.Context, marshaler runtime.Marshaler, server GroupServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteGroupRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["org_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "org_id") + } + + protoReq.OrgId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "org_id", err) + } + + val, ok = pathParams["id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id") + } + + protoReq.Id, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err) + } + + msg, err := server.DeleteGroup(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterGroupServiceHandlerServer registers the http handlers for service GroupService to "mux". +// UnaryRPC :call GroupServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +func RegisterGroupServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server GroupServiceServer) error { + + mux.Handle("GET", pattern_GroupService_GetGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GroupService_GetGroup_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GroupService_GetGroup_0(ctx, mux, outboundMarshaler, w, req, response_GroupService_GetGroup_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GroupService_ListGroups_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GroupService_ListGroups_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GroupService_ListGroups_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_GroupService_CreateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GroupService_CreateGroup_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GroupService_CreateGroup_0(ctx, mux, outboundMarshaler, w, req, response_GroupService_CreateGroup_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PATCH", pattern_GroupService_UpdateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GroupService_UpdateGroup_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GroupService_UpdateGroup_0(ctx, mux, outboundMarshaler, w, req, response_GroupService_UpdateGroup_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_GroupService_DeleteGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GroupService_DeleteGroup_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GroupService_DeleteGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterGroupServiceHandlerFromEndpoint is same as RegisterGroupServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterGroupServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterGroupServiceHandler(ctx, mux, conn) +} + +// RegisterGroupServiceHandler registers the http handlers for service GroupService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterGroupServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterGroupServiceHandlerClient(ctx, mux, NewGroupServiceClient(conn)) +} + +// RegisterGroupServiceHandlerClient registers the http handlers for service GroupService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "GroupServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "GroupServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "GroupServiceClient" to call the correct interceptors. +func RegisterGroupServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client GroupServiceClient) error { + + mux.Handle("GET", pattern_GroupService_GetGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GroupService_GetGroup_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GroupService_GetGroup_0(ctx, mux, outboundMarshaler, w, req, response_GroupService_GetGroup_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GroupService_ListGroups_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GroupService_ListGroups_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GroupService_ListGroups_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_GroupService_CreateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GroupService_CreateGroup_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GroupService_CreateGroup_0(ctx, mux, outboundMarshaler, w, req, response_GroupService_CreateGroup_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PATCH", pattern_GroupService_UpdateGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GroupService_UpdateGroup_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GroupService_UpdateGroup_0(ctx, mux, outboundMarshaler, w, req, response_GroupService_UpdateGroup_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_GroupService_DeleteGroup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GroupService_DeleteGroup_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GroupService_DeleteGroup_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +type response_GroupService_GetGroup_0 struct { + proto.Message +} + +func (m response_GroupService_GetGroup_0) XXX_ResponseBody() interface{} { + response := m.Message.(*GetGroupResponse) + return response.Item +} + +type response_GroupService_CreateGroup_0 struct { + proto.Message +} + +func (m response_GroupService_CreateGroup_0) XXX_ResponseBody() interface{} { + response := m.Message.(*CreateGroupResponse) + return response.Item +} + +type response_GroupService_UpdateGroup_0 struct { + proto.Message +} + +func (m response_GroupService_UpdateGroup_0) XXX_ResponseBody() interface{} { + response := m.Message.(*UpdateGroupResponse) + return response.Item +} + +var ( + pattern_GroupService_GetGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "orgs", "org_id", "groups", "id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GroupService_ListGroups_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1", "orgs", "org_id", "groups"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GroupService_CreateGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1", "orgs", "org_id", "groups"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GroupService_UpdateGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "orgs", "org_id", "groups", "id"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GroupService_DeleteGroup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "orgs", "org_id", "groups", "id"}, "", runtime.AssumeColonVerbOpt(true))) +) + +var ( + forward_GroupService_GetGroup_0 = runtime.ForwardResponseMessage + + forward_GroupService_ListGroups_0 = runtime.ForwardResponseMessage + + forward_GroupService_CreateGroup_0 = runtime.ForwardResponseMessage + + forward_GroupService_UpdateGroup_0 = runtime.ForwardResponseMessage + + forward_GroupService_DeleteGroup_0 = runtime.ForwardResponseMessage +) diff --git a/internal/proto/local/controller/api/resources/groups/v1/group.proto b/internal/proto/local/controller/api/resources/groups/v1/group.proto new file mode 100644 index 000000000..4bc79791b --- /dev/null +++ b/internal/proto/local/controller/api/resources/groups/v1/group.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package controller.api.resources.groups.v1; + +option go_package = "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/groups;groups"; + +import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; + +// Group contains all fields related to a Group resource +message Group { + // The ID of the Project + // Output only. + string id = 1; + + // Optional name for identification purposes + google.protobuf.StringValue name = 2; + + // Optional user-set descripton for identification purposes + google.protobuf.StringValue description = 3; + + // The time this resource was created + // Output only. + google.protobuf.Timestamp created_time = 4 [json_name="created_time"]; + + // The time this resource was last updated. + // Output only. + google.protobuf.Timestamp updated_time = 5 [json_name="updated_time"]; + + // Whether the resource is disabled + google.protobuf.BoolValue disabled = 6; +} diff --git a/internal/proto/local/controller/api/services/v1/group_service.proto b/internal/proto/local/controller/api/services/v1/group_service.proto new file mode 100644 index 000000000..190cbc74e --- /dev/null +++ b/internal/proto/local/controller/api/services/v1/group_service.proto @@ -0,0 +1,138 @@ +syntax = "proto3"; + +package controller.api.services.v1; + +option go_package = "github.com/hashicorp/watchtower/internal/gen/controller/api/services;services"; + +import "protoc-gen-swagger/options/annotations.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/field_mask.proto"; +import "controller/api/resources/groups/v1/group.proto"; + +service GroupService { + + // GetGroup returns a stored Group if present. The provided request + // must include the org and group id for the group being retrieved. If + // any of those ids are missing, malformed or reference a non existing + // resource an error is returned. + rpc GetGroup(GetGroupRequest) returns (GetGroupResponse) { + option (google.api.http) = { + get: "/v1/orgs/{org_id}/groups/{id}" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { + summary: "Gets a single Group" + }; + } + + // ListGroups returns a list of stored groups which exist inside the org + // referenced inside the request. The request must include the org id for + // the groups being retrieved. If the org id is missing, malformed, or + // reference a non existing organization, an error is returned. + rpc ListGroups(ListGroupsRequest) returns (ListGroupsResponse) { + option (google.api.http) = { + get: "/v1/orgs/{org_id}/groups" + }; + option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { + summary: "Lists all Groups" + }; + } + + // CreateGroup creates and stores a group in watchtower. The provided + // request must include the org id in which the group will be created. + // If the org id is missing, malformed or references a non existing + // organization, an error is returned. If a name is provided that is in + // use in another group in the same organization, an error is returned. + rpc CreateGroup(CreateGroupRequest) returns (CreateGroupResponse) { + option (google.api.http) = { + post: "/v1/orgs/{org_id}/groups" + body: "item" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { + summary: "Creates a single Group" + }; + } + + // UpdateGroup updates an existing group in watchtower. The provided + // group must not have any read only fields set. The update mask must be + // included in the request and contain at least 1 mutable field. To unset + // a field's value, include the field in the update mask and don't set it + // in the provided group. An error is returned if either the organization + // or group ids are missing or reference a non existing resource. An error + // is also returned if the request attempts to update the name to one that is + // already in use in this organization. + rpc UpdateGroup(UpdateGroupRequest) returns (UpdateGroupResponse) { + option (google.api.http) = { + patch: "/v1/orgs/{org_id}/groups/{id}" + body: "item" + response_body: "item" + }; + option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { + summary: "Updates a Group" + }; + } + + // DeleteGroup removes a group from Watchtower. If the provided org or group ids + // are malformed or not provided an error is returned. No error is returned + // if either ids reference resources that do not exist as the response itself + // specifies if the resource existed before the DeleteGroup request was + // received. + rpc DeleteGroup(DeleteGroupRequest) returns (DeleteGroupResponse) { + option (google.api.http) = { + delete: "/v1/orgs/{org_id}/groups/{id}" + }; + option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { + summary: "Deletes a Group" + }; + } +} + +message GetGroupRequest { + string org_id = 1; + string id = 2; + string view = 3; +} + +message GetGroupResponse { + resources.groups.v1.Group item = 1; +} + +message ListGroupsRequest { + string org_id = 1; + string view = 2; +} + +message ListGroupsResponse { + repeated resources.groups.v1.Group items = 1; +} + +message CreateGroupRequest { + string org_id = 1; + resources.groups.v1.Group item = 2; +} + +message CreateGroupResponse { + string uri = 1; + resources.groups.v1.Group item = 2; +} + +message UpdateGroupRequest { + string org_id = 1; + string id = 2; + resources.groups.v1.Group item = 3; + google.protobuf.FieldMask update_mask = 4; +} + +message UpdateGroupResponse { + resources.groups.v1.Group item = 1; +} + +message DeleteGroupRequest { + string org_id = 1; + string id = 2; +} + +message DeleteGroupResponse { + bool existed = 1; +} diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index e9e36fc17..2e91a248c 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/watchtower/globals" "github.com/hashicorp/watchtower/internal/gen/controller/api/services" "github.com/hashicorp/watchtower/internal/servers/controller/handlers" + "github.com/hashicorp/watchtower/internal/servers/controller/handlers/groups" "github.com/hashicorp/watchtower/internal/servers/controller/handlers/host_catalogs" "github.com/hashicorp/watchtower/internal/servers/controller/handlers/host_sets" "github.com/hashicorp/watchtower/internal/servers/controller/handlers/hosts" @@ -92,6 +93,13 @@ func handleGrpcGateway(c *Controller) (http.Handler, error) { if err := services.RegisterUserServiceHandlerServer(ctx, mux, us); err != nil { return nil, fmt.Errorf("failed to register user service handler: %w", err) } + gs, err := groups.NewService(c.IamRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create group handler service: %w", err) + } + if err := services.RegisterGroupServiceHandlerServer(ctx, mux, gs); err != nil { + return nil, fmt.Errorf("failed to register group service handler: %w", err) + } return mux, nil } diff --git a/internal/servers/controller/handlers/groups/group_service.go b/internal/servers/controller/handlers/groups/group_service.go new file mode 100644 index 000000000..9d900bdad --- /dev/null +++ b/internal/servers/controller/handlers/groups/group_service.go @@ -0,0 +1,323 @@ +package groups + +import ( + "context" + "errors" + "fmt" + "regexp" + "strings" + + "github.com/hashicorp/watchtower/internal/db" + pb "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/groups" + pbs "github.com/hashicorp/watchtower/internal/gen/controller/api/services" + "github.com/hashicorp/watchtower/internal/iam" + "github.com/hashicorp/watchtower/internal/servers/controller/handlers" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +const orgIdFieldName = "org_id" + +var ( + reInvalidID = regexp.MustCompile("[^A-Za-z0-9]") + // TODO(ICU-28): Find a way to auto update these names and enforce the mappings between wire and storage. + wireToStorageMask = map[string]string{ + "name": "Name", + "description": "Description", + } +) + +// Service handles request as described by the pbs.GroupServiceServer interface. +type Service struct { + repoFn func() (*iam.Repository, error) +} + +// NewService returns a group service which handles group related requests to watchtower. +func NewService(repo func() (*iam.Repository, error)) (Service, error) { + if repo == nil { + return Service{}, fmt.Errorf("nil iam repostiroy provided") + } + return Service{repoFn: repo}, nil +} + +var _ pbs.GroupServiceServer = Service{} + +// CreateGroup is not yet implemented but will implement the interface pbs.GroupServiceServer. +func (s Service) ListGroups(context.Context, *pbs.ListGroupsRequest) (*pbs.ListGroupsResponse, error) { + return nil, status.Error(codes.Unimplemented, "List not enabled for this resource.") +} + +// GetGroups implements the interface pbs.GroupServiceServer. +func (s Service) GetGroup(ctx context.Context, req *pbs.GetGroupRequest) (*pbs.GetGroupResponse, error) { + if err := validateGetRequest(req); err != nil { + return nil, err + } + u, err := s.getFromRepo(ctx, req.GetId()) + if err != nil { + return nil, err + } + return &pbs.GetGroupResponse{Item: u}, nil +} + +// CreateGroup implements the interface pbs.GroupServiceServer. +func (s Service) CreateGroup(ctx context.Context, req *pbs.CreateGroupRequest) (*pbs.CreateGroupResponse, error) { + if err := validateCreateRequest(req); err != nil { + return nil, err + } + u, err := s.createInRepo(ctx, req.GetOrgId(), req.GetItem()) + if err != nil { + return nil, err + } + return &pbs.CreateGroupResponse{Item: u, Uri: fmt.Sprintf("orgs/%s/groups/%s", req.GetOrgId(), u.GetId())}, nil +} + +// UpdateGroup implements the interface pbs.GroupServiceServer. +func (s Service) UpdateGroup(ctx context.Context, req *pbs.UpdateGroupRequest) (*pbs.UpdateGroupResponse, error) { + if err := validateUpdateRequest(req); err != nil { + return nil, err + } + u, err := s.updateInRepo(ctx, req.GetOrgId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem()) + if err != nil { + return nil, err + } + return &pbs.UpdateGroupResponse{Item: u}, nil +} + +// DeleteGroup implements the interface pbs.GroupServiceServer. +func (s Service) DeleteGroup(ctx context.Context, req *pbs.DeleteGroupRequest) (*pbs.DeleteGroupResponse, error) { + if err := validateDeleteRequest(req); err != nil { + return nil, err + } + existed, err := s.deleteFromRepo(ctx, req.GetId()) + if err != nil { + return nil, err + } + return &pbs.DeleteGroupResponse{Existed: existed}, nil +} + +func (s Service) getFromRepo(ctx context.Context, id string) (*pb.Group, error) { + repo, err := s.repoFn() + if err != nil { + return nil, err + } + u, err := repo.LookupGroup(ctx, id) + if err != nil { + if errors.Is(err, db.ErrRecordNotFound) { + return nil, handlers.NotFoundErrorf("Group %q doesn't exist.", id) + } + return nil, err + } + if u == nil { + return nil, handlers.NotFoundErrorf("Group %q doesn't exist.", id) + } + return toProto(u), nil +} + +func (s Service) createInRepo(ctx context.Context, orgId string, item *pb.Group) (*pb.Group, error) { + var opts []iam.Option + if item.GetName() != nil { + opts = append(opts, iam.WithName(item.GetName().GetValue())) + } + if item.GetDescription() != nil { + opts = append(opts, iam.WithDescription(item.GetDescription().GetValue())) + } + u, err := iam.NewGroup(orgId, opts...) + if err != nil { + return nil, status.Errorf(codes.Internal, "Unable to build group for creation: %v.", err) + } + repo, err := s.repoFn() + if err != nil { + return nil, err + } + out, err := repo.CreateGroup(ctx, u) + if err != nil { + return nil, status.Errorf(codes.Internal, "Unable to create group: %v.", err) + } + if out == nil { + return nil, status.Error(codes.Internal, "Unable to create group but no error returned from repository.") + } + return toProto(out), nil +} + +func (s Service) updateInRepo(ctx context.Context, orgId, id string, mask []string, item *pb.Group) (*pb.Group, error) { + var opts []iam.Option + if desc := item.GetDescription(); desc != nil { + opts = append(opts, iam.WithDescription(desc.GetValue())) + } + if name := item.GetName(); name != nil { + opts = append(opts, iam.WithName(name.GetValue())) + } + u, err := iam.NewGroup(orgId, opts...) + if err != nil { + return nil, status.Errorf(codes.Internal, "Unable to build group for update: %v.", err) + } + u.PublicId = id + dbMask, err := toDbUpdateMask(mask) + if err != nil { + return nil, err + } + if len(dbMask) == 0 { + return nil, handlers.InvalidArgumentErrorf("No valid fields included in the update mask.", map[string]string{"update_mask": "No valid paths provided in the update mask."}) + } + repo, err := s.repoFn() + if err != nil { + return nil, err + } + out, rowsUpdated, err := repo.UpdateGroup(ctx, u, dbMask) + if err != nil { + return nil, status.Errorf(codes.Internal, "Unable to update group: %v.", err) + } + if rowsUpdated == 0 { + return nil, handlers.NotFoundErrorf("Group %q doesn't exist.", id) + } + return toProto(out), nil +} + +func (s Service) deleteFromRepo(ctx context.Context, id string) (bool, error) { + repo, err := s.repoFn() + if err != nil { + return false, err + } + rows, err := repo.DeleteGroup(ctx, id) + if err != nil { + if errors.Is(err, db.ErrRecordNotFound) { + return false, nil + } + return false, status.Errorf(codes.Internal, "Unable to delete group: %v.", err) + } + return rows > 0, nil +} + +// toDbUpdateMask converts the wire format's FieldMask into a list of strings containing FieldMask paths used +func toDbUpdateMask(paths []string) ([]string, error) { + var dbPaths []string + var invalid []string + for _, p := range paths { + for _, f := range strings.Split(p, ",") { + if dbField, ok := wireToStorageMask[strings.TrimSpace(f)]; ok { + dbPaths = append(dbPaths, dbField) + } else { + invalid = append(invalid, f) + } + } + } + if len(invalid) > 0 { + return nil, handlers.InvalidArgumentErrorf(fmt.Sprintf("Invalid fields passed in update_update mask: %v.", invalid), map[string]string{"update_mask": fmt.Sprintf("Unknown paths provided in update mask: %q", strings.Join(invalid, ","))}) + } + return dbPaths, nil +} + +func toProto(in *iam.Group) *pb.Group { + out := pb.Group{ + Id: in.GetPublicId(), + CreatedTime: in.GetCreateTime().GetTimestamp(), + UpdatedTime: in.GetUpdateTime().GetTimestamp(), + } + if in.GetDescription() != "" { + out.Description = &wrapperspb.StringValue{Value: in.GetDescription()} + } + if in.GetName() != "" { + out.Name = &wrapperspb.StringValue{Value: in.GetName()} + } + return &out +} + +// A validateX method should exist for each method above. These methods do not make calls to any backing service but enforce +// requirements on the structure of the request. They verify that: +// * The path passed in is correctly formatted +// * All required parameters are set +// * There are no conflicting parameters provided +func validateGetRequest(req *pbs.GetGroupRequest) error { + badFields := validateAncestors(req) + if !validId(req.GetId(), iam.GroupPrefix+"_") { + badFields["id"] = "Invalid formatted group id." + } + if len(badFields) > 0 { + return handlers.InvalidArgumentErrorf("Improperly formatted identifier.", badFields) + } + return nil +} + +func validateCreateRequest(req *pbs.CreateGroupRequest) error { + badFields := validateAncestors(req) + item := req.GetItem() + if item.GetId() != "" { + badFields["id"] = "This is a read only field." + } + if item.GetCreatedTime() != nil { + badFields["created_time"] = "This is a read only field." + } + if item.GetUpdatedTime() != nil { + badFields["updated_time"] = "This is a read only field." + } + if len(badFields) > 0 { + return handlers.InvalidArgumentErrorf("Argument errors found in the request.", badFields) + } + return nil +} + +func validateUpdateRequest(req *pbs.UpdateGroupRequest) error { + badFields := validateAncestors(req) + if !validId(req.GetId(), iam.GroupPrefix+"_") { + badFields["group_id"] = "Improperly formatted path identifier." + } + if req.GetUpdateMask() == nil { + badFields["update_mask"] = "UpdateMask not provided but is required to update a group." + } + + item := req.GetItem() + if item == nil { + // It is legitimate for no item to be specified in an update request as it indicates all fields provided in + // the mask will be marked as unset. + return nil + } + if item.GetId() != "" { + badFields["id"] = "This is a read only field and cannot be specified in an update request." + } + if item.GetCreatedTime() != nil { + badFields["created_time"] = "This is a read only field and cannot be specified in an update request." + } + if item.GetUpdatedTime() != nil { + badFields["updated_time"] = "This is a read only field and cannot be specified in an update request." + } + if len(badFields) > 0 { + return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) + } + + return nil +} + +func validateDeleteRequest(req *pbs.DeleteGroupRequest) error { + badFields := validateAncestors(req) + if !validId(req.GetId(), iam.GroupPrefix+"_") { + badFields["id"] = "Incorrectly formatted identifier." + } + if len(badFields) > 0 { + return handlers.InvalidArgumentErrorf("Errors in provided fields.", badFields) + } + return nil +} + +func validId(id, prefix string) bool { + if !strings.HasPrefix(id, prefix) { + return false + } + id = strings.TrimPrefix(id, prefix) + return !reInvalidID.Match([]byte(id)) +} + +type ancestorProvider interface { + GetOrgId() string +} + +// validateAncestors verifies that the ancestors of this call are properly set and provided. +func validateAncestors(r ancestorProvider) map[string]string { + if r.GetOrgId() == "" { + return map[string]string{orgIdFieldName: "Missing organization id."} + } + if !validId(r.GetOrgId(), iam.OrganizationScope.Prefix()+"_") { + return map[string]string{orgIdFieldName: "Improperly formatted identifier."} + } + return map[string]string{} +} diff --git a/internal/servers/controller/handlers/groups/group_service_test.go b/internal/servers/controller/handlers/groups/group_service_test.go new file mode 100644 index 000000000..accc5852c --- /dev/null +++ b/internal/servers/controller/handlers/groups/group_service_test.go @@ -0,0 +1,533 @@ +package groups_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/golang/protobuf/ptypes" + "github.com/hashicorp/watchtower/internal/db" + pb "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/groups" + pbs "github.com/hashicorp/watchtower/internal/gen/controller/api/services" + "github.com/hashicorp/watchtower/internal/iam" + "github.com/hashicorp/watchtower/internal/servers/controller/handlers/groups" + "google.golang.org/genproto/protobuf/field_mask" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/wrapperspb" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func createDefaultGroupAndRepo(t *testing.T) (*iam.Group, func() (*iam.Repository, error)) { + t.Helper() + cleanup, conn, _ := db.TestSetup(t, "postgres") + t.Cleanup(func() { + if err := conn.Close(); err != nil { + t.Errorf("Error when closing gorm DB: %v", err) + } + if err := cleanup(); err != nil { + t.Errorf("Error when cleaning up TestSetup: %v", err) + } + }) + rw := db.New(conn) + wrap := db.TestWrapper(t) + repoFn := func() (*iam.Repository, error) { + return iam.NewRepository(rw, rw, wrap) + } + + o, _ := iam.TestScopes(t, conn) + g := iam.TestGroup(t, conn, o.GetPublicId(), iam.WithDescription("default"), iam.WithName("default")) + return g, repoFn +} + +func TestGet(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + g, repo := createDefaultGroupAndRepo(t) + toMerge := &pbs.GetGroupRequest{ + OrgId: g.GetScopeId(), + Id: g.GetPublicId(), + } + + wantU := &pb.Group{ + Id: g.GetPublicId(), + Name: &wrapperspb.StringValue{Value: g.GetName()}, + Description: &wrapperspb.StringValue{Value: g.GetDescription()}, + CreatedTime: g.CreateTime.GetTimestamp(), + UpdatedTime: g.UpdateTime.GetTimestamp(), + } + + cases := []struct { + name string + req *pbs.GetGroupRequest + res *pbs.GetGroupResponse + errCode codes.Code + }{ + { + name: "Get an Existing Group", + req: &pbs.GetGroupRequest{Id: g.GetPublicId()}, + res: &pbs.GetGroupResponse{Item: wantU}, + errCode: codes.OK, + }, + { + name: "Get a non existant Group", + req: &pbs.GetGroupRequest{Id: iam.GroupPrefix + "_DoesntExis"}, + res: nil, + errCode: codes.NotFound, + }, + { + name: "Wrong id prefix", + req: &pbs.GetGroupRequest{Id: "j_1234567890"}, + res: nil, + errCode: codes.InvalidArgument, + }, + { + name: "space in id", + req: &pbs.GetGroupRequest{Id: iam.GroupPrefix + "_1 23456789"}, + res: nil, + errCode: codes.InvalidArgument, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + req := proto.Clone(toMerge).(*pbs.GetGroupRequest) + proto.Merge(req, tc.req) + + s, err := groups.NewService(repo) + require.NoError(err, "Couldn't create new group service.") + + got, gErr := s.GetGroup(context.Background(), req) + assert.Equal(tc.errCode, status.Code(gErr), "GetGroup(%+v) got error %v, wanted %v", req, gErr, tc.errCode) + assert.True(proto.Equal(got, tc.res), "GetGroup(%q) got response %q, wanted %q", req, got, tc.res) + }) + } +} + +func TestDelete(t *testing.T) { + require := require.New(t) + g, repo := createDefaultGroupAndRepo(t) + + s, err := groups.NewService(repo) + require.NoError(err, "Error when getting new group service.") + + cases := []struct { + name string + req *pbs.DeleteGroupRequest + res *pbs.DeleteGroupResponse + errCode codes.Code + }{ + { + name: "Delete an Existing Group", + req: &pbs.DeleteGroupRequest{ + OrgId: g.GetScopeId(), + Id: g.GetPublicId(), + }, + res: &pbs.DeleteGroupResponse{ + Existed: true, + }, + errCode: codes.OK, + }, + { + name: "Delete bad group id", + req: &pbs.DeleteGroupRequest{ + OrgId: g.GetScopeId(), + Id: iam.GroupPrefix + "_doesntexis", + }, + res: &pbs.DeleteGroupResponse{ + Existed: false, + }, + errCode: codes.OK, + }, + { + name: "Delete bad org id", + req: &pbs.DeleteGroupRequest{ + OrgId: "o_doesntexis", + Id: g.GetPublicId(), + }, + res: &pbs.DeleteGroupResponse{ + Existed: false, + }, + errCode: codes.OK, + }, + { + name: "Bad org formatting", + req: &pbs.DeleteGroupRequest{ + OrgId: "bad_format", + Id: g.GetPublicId(), + }, + res: nil, + errCode: codes.InvalidArgument, + }, + { + name: "Bad Group Id formatting", + req: &pbs.DeleteGroupRequest{ + OrgId: g.GetScopeId(), + Id: "bad_format", + }, + res: nil, + errCode: codes.InvalidArgument, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert := assert.New(t) + got, gErr := s.DeleteGroup(context.Background(), tc.req) + assert.Equal(tc.errCode, status.Code(gErr), "DeleteGroup(%+v) got error %v, wanted %v", tc.req, gErr, tc.errCode) + assert.EqualValuesf(tc.res, got, "DeleteGroup(%q) got response %q, wanted %q", tc.req, got, tc.res) + }) + } +} + +func TestDelete_twice(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + g, repo := createDefaultGroupAndRepo(t) + + s, err := groups.NewService(repo) + require.NoError(err, "Error when getting new group service") + req := &pbs.DeleteGroupRequest{ + OrgId: g.GetScopeId(), + Id: g.GetPublicId(), + } + got, gErr := s.DeleteGroup(context.Background(), req) + assert.NoError(gErr, "First attempt") + assert.True(got.GetExisted(), "Expected existed to be true for the first delete.") + got, gErr = s.DeleteGroup(context.Background(), req) + assert.NoError(gErr, "Second attempt") + assert.False(got.GetExisted(), "Expected existed to be false for the second delete.") +} + +func TestCreate(t *testing.T) { + require := require.New(t) + defaultGroup, repo := createDefaultGroupAndRepo(t) + defaultCreated, err := ptypes.Timestamp(defaultGroup.GetCreateTime().GetTimestamp()) + require.NoError(err, "Error converting proto to timestamp.") + toMerge := &pbs.CreateGroupRequest{ + OrgId: defaultGroup.GetScopeId(), + } + + cases := []struct { + name string + req *pbs.CreateGroupRequest + res *pbs.CreateGroupResponse + errCode codes.Code + }{ + { + name: "Create a valid Group", + req: &pbs.CreateGroupRequest{Item: &pb.Group{ + Name: &wrapperspb.StringValue{Value: "name"}, + Description: &wrapperspb.StringValue{Value: "desc"}, + }}, + res: &pbs.CreateGroupResponse{ + Uri: fmt.Sprintf("orgs/%s/groups/%s_", defaultGroup.GetScopeId(), iam.GroupPrefix), + Item: &pb.Group{ + Name: &wrapperspb.StringValue{Value: "name"}, + Description: &wrapperspb.StringValue{Value: "desc"}, + }, + }, + errCode: codes.OK, + }, + { + name: "Can't specify Id", + req: &pbs.CreateGroupRequest{Item: &pb.Group{ + Id: iam.GroupPrefix + "_notallowed", + }}, + res: nil, + errCode: codes.InvalidArgument, + }, + { + name: "Can't specify Created Time", + req: &pbs.CreateGroupRequest{Item: &pb.Group{ + CreatedTime: ptypes.TimestampNow(), + }}, + res: nil, + errCode: codes.InvalidArgument, + }, + { + name: "Can't specify Update Time", + req: &pbs.CreateGroupRequest{Item: &pb.Group{ + UpdatedTime: ptypes.TimestampNow(), + }}, + res: nil, + errCode: codes.InvalidArgument, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert := assert.New(t) + req := proto.Clone(toMerge).(*pbs.CreateGroupRequest) + proto.Merge(req, tc.req) + + s, err := groups.NewService(repo) + require.NoError(err, "Error when getting new group service.") + + got, gErr := s.CreateGroup(context.Background(), req) + assert.Equal(tc.errCode, status.Code(gErr), "CreateGroup(%+v) got error %v, wanted %v", req, gErr, tc.errCode) + if got != nil { + assert.True(strings.HasPrefix(got.GetUri(), tc.res.Uri)) + assert.True(strings.HasPrefix(got.GetItem().GetId(), iam.GroupPrefix+"_")) + gotCreateTime, err := ptypes.Timestamp(got.GetItem().GetCreatedTime()) + require.NoError(err, "Error converting proto to timestamp.") + gotUpdateTime, err := ptypes.Timestamp(got.GetItem().GetUpdatedTime()) + require.NoError(err, "Error converting proto to timestamp.") + // Verify it is a group created after the test setup's default group + assert.True(gotCreateTime.After(defaultCreated), "New group should have been created after default group. Was created %v, which is after %v", gotCreateTime, defaultCreated) + assert.True(gotUpdateTime.After(defaultCreated), "New group should have been updated after default group. Was updated %v, which is after %v", gotUpdateTime, defaultCreated) + + // Clear all values which are hard to compare against. + got.Uri, tc.res.Uri = "", "" + got.Item.Id, tc.res.Item.Id = "", "" + got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil + } + assert.True(proto.Equal(got, tc.res), "CreateGroup(%q) got response %q, wanted %q", req, got, tc.res) + }) + } +} + +func TestUpdate(t *testing.T) { + require := require.New(t) + g, repoFn := createDefaultGroupAndRepo(t) + tested, err := groups.NewService(repoFn) + require.NoError(err, "Error when getting new group service.") + + resetGroup := func() { + repo, err := repoFn() + require.NoError(err, "Couldn't get a new repo") + g, _, err = repo.UpdateGroup(context.Background(), g, []string{"Name", "Description"}) + require.NoError(err, "Failed to reset the group") + } + + created, err := ptypes.Timestamp(g.GetCreateTime().GetTimestamp()) + require.NoError(err, "Error converting proto to timestamp") + toMerge := &pbs.UpdateGroupRequest{ + OrgId: g.GetScopeId(), + Id: g.GetPublicId(), + } + + cases := []struct { + name string + req *pbs.UpdateGroupRequest + res *pbs.UpdateGroupResponse + errCode codes.Code + }{ + { + name: "Update an Existing Group", + req: &pbs.UpdateGroupRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name", "description"}, + }, + Item: &pb.Group{ + Name: &wrapperspb.StringValue{Value: "new"}, + Description: &wrapperspb.StringValue{Value: "desc"}, + }, + }, + res: &pbs.UpdateGroupResponse{ + Item: &pb.Group{ + Id: g.GetPublicId(), + Name: &wrapperspb.StringValue{Value: "new"}, + Description: &wrapperspb.StringValue{Value: "desc"}, + CreatedTime: g.GetCreateTime().GetTimestamp(), + }, + }, + errCode: codes.OK, + }, + { + name: "Multiple Paths in single string", + req: &pbs.UpdateGroupRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name,description"}, + }, + Item: &pb.Group{ + Name: &wrapperspb.StringValue{Value: "new"}, + Description: &wrapperspb.StringValue{Value: "desc"}, + }, + }, + res: &pbs.UpdateGroupResponse{ + Item: &pb.Group{ + Id: g.GetPublicId(), + Name: &wrapperspb.StringValue{Value: "new"}, + Description: &wrapperspb.StringValue{Value: "desc"}, + CreatedTime: g.GetCreateTime().GetTimestamp(), + }, + }, + errCode: codes.OK, + }, + { + name: "No Update Mask", + req: &pbs.UpdateGroupRequest{ + Item: &pb.Group{ + Name: &wrapperspb.StringValue{Value: "updated name"}, + Description: &wrapperspb.StringValue{Value: "updated desc"}, + }, + }, + errCode: codes.InvalidArgument, + }, + { + name: "No Paths in Mask", + req: &pbs.UpdateGroupRequest{ + UpdateMask: &field_mask.FieldMask{Paths: []string{}}, + Item: &pb.Group{ + Name: &wrapperspb.StringValue{Value: "updated name"}, + Description: &wrapperspb.StringValue{Value: "updated desc"}, + }, + }, + errCode: codes.InvalidArgument, + }, + { + name: "Only non-existant paths in Mask", + req: &pbs.UpdateGroupRequest{ + UpdateMask: &field_mask.FieldMask{Paths: []string{"nonexistant_field"}}, + Item: &pb.Group{ + Name: &wrapperspb.StringValue{Value: "updated name"}, + Description: &wrapperspb.StringValue{Value: "updated desc"}, + }, + }, + errCode: codes.InvalidArgument, + }, + { + name: "Unset Name", + req: &pbs.UpdateGroupRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name"}, + }, + Item: &pb.Group{ + Description: &wrapperspb.StringValue{Value: "ignored"}, + }, + }, + res: &pbs.UpdateGroupResponse{ + Item: &pb.Group{ + Id: g.GetPublicId(), + Description: &wrapperspb.StringValue{Value: "default"}, + CreatedTime: g.GetCreateTime().GetTimestamp(), + }, + }, + errCode: codes.OK, + }, + { + name: "Update Only Name", + req: &pbs.UpdateGroupRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"name"}, + }, + Item: &pb.Group{ + Name: &wrapperspb.StringValue{Value: "updated"}, + Description: &wrapperspb.StringValue{Value: "ignored"}, + }, + }, + res: &pbs.UpdateGroupResponse{ + Item: &pb.Group{ + Id: g.GetPublicId(), + Name: &wrapperspb.StringValue{Value: "updated"}, + Description: &wrapperspb.StringValue{Value: "default"}, + CreatedTime: g.GetCreateTime().GetTimestamp(), + }, + }, + errCode: codes.OK, + }, + { + name: "Update Only Description", + req: &pbs.UpdateGroupRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"description"}, + }, + Item: &pb.Group{ + Name: &wrapperspb.StringValue{Value: "ignored"}, + Description: &wrapperspb.StringValue{Value: "notignored"}, + }, + }, + res: &pbs.UpdateGroupResponse{ + Item: &pb.Group{ + Id: g.GetPublicId(), + Name: &wrapperspb.StringValue{Value: "default"}, + Description: &wrapperspb.StringValue{Value: "notignored"}, + CreatedTime: g.GetCreateTime().GetTimestamp(), + }, + }, + errCode: codes.OK, + }, + // TODO: Updating a non existant group should result in a NotFound exception but currently results in + // the repoFn returning an internal error. + { + name: "Update a Non Existing Group", + req: &pbs.UpdateGroupRequest{ + Id: iam.GroupPrefix + "_DoesntExis", + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"description"}, + }, + Item: &pb.Group{ + Name: &wrapperspb.StringValue{Value: "new"}, + Description: &wrapperspb.StringValue{Value: "desc"}, + }, + }, + errCode: codes.Internal, + }, + { + name: "Cant change Id", + req: &pbs.UpdateGroupRequest{ + Id: g.GetPublicId(), + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"id"}, + }, + Item: &pb.Group{ + Id: iam.GroupPrefix + "_somethinge", + Name: &wrapperspb.StringValue{Value: "new"}, + Description: &wrapperspb.StringValue{Value: "new desc"}, + }}, + res: nil, + errCode: codes.InvalidArgument, + }, + { + name: "Cant specify Created Time", + req: &pbs.UpdateGroupRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"created_time"}, + }, + Item: &pb.Group{ + CreatedTime: ptypes.TimestampNow(), + }, + }, + res: nil, + errCode: codes.InvalidArgument, + }, + { + name: "Cant specify Updated Time", + req: &pbs.UpdateGroupRequest{ + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"updated_time"}, + }, + Item: &pb.Group{ + UpdatedTime: ptypes.TimestampNow(), + }, + }, + res: nil, + errCode: codes.InvalidArgument, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + defer resetGroup() + assert := assert.New(t) + req := proto.Clone(toMerge).(*pbs.UpdateGroupRequest) + proto.Merge(req, tc.req) + + got, gErr := tested.UpdateGroup(context.Background(), req) + assert.Equal(tc.errCode, status.Code(gErr), "UpdateGroup(%+v) got error %v, wanted %v", req, gErr, tc.errCode) + + if got != nil { + assert.NotNilf(tc.res, "Expected UpdateGroup response to be nil, but was %v", got) + gotUpdateTime, err := ptypes.Timestamp(got.GetItem().GetUpdatedTime()) + require.NoError(err, "Error converting proto to timestamp") + // Verify it is a group updated after it was created + assert.True(gotUpdateTime.After(created), "Updated group should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, created) + + // Clear all values which are hard to compare against. + got.Item.UpdatedTime, tc.res.Item.UpdatedTime = nil, nil + } + assert.True(proto.Equal(got, tc.res), "UpdateGroup(%q) got response %q, wanted %q", req, got, tc.res) + }) + } +}