diff --git a/Makefile b/Makefile index 847954d06..36695c89b 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ GOVERSION := $(shell go version | cut -d ' ' -f 3 | cut -d '.' -f 2) .PHONY: build check fmt lint test test-race vet test-cover-html help install proto .DEFAULT_GOAL := build -PROTON_COMMIT := "633ae3ee704338ef2620c9387849dfede86e8ddf" +PROTON_COMMIT := "e8a584e192f23f999844b027d17bd738c3981973" install: @echo "Clean up imports..." diff --git a/core/relation/relation.go b/core/relation/relation.go index 58ceaabd7..558c9b3ac 100644 --- a/core/relation/relation.go +++ b/core/relation/relation.go @@ -29,6 +29,7 @@ type AuthzRepository interface { DeleteV2(ctx context.Context, rel RelationV2) error DeleteSubjectRelations(ctx context.Context, resourceType, optionalResourceID string) error AddV2(ctx context.Context, rel RelationV2) error + LookupResources(ctx context.Context, resourceType, permission, subjectType, subjectID string) ([]string, error) } type Relation struct { diff --git a/core/relation/service.go b/core/relation/service.go index 67b39d4a8..f9bb0cbad 100644 --- a/core/relation/service.go +++ b/core/relation/service.go @@ -170,3 +170,7 @@ func (s Service) DeleteSubjectRelations(ctx context.Context, resourceType, optio return nil } + +func (s Service) LookupResources(ctx context.Context, resourceType, permission, subjectType, subjectID string) ([]string, error) { + return s.authzRepository.LookupResources(ctx, resourceType, permission, subjectType, subjectID) +} diff --git a/core/servicedata/mocks/relation_service.go b/core/servicedata/mocks/relation_service.go index 0b42fa352..0fc751e75 100644 --- a/core/servicedata/mocks/relation_service.go +++ b/core/servicedata/mocks/relation_service.go @@ -146,6 +146,68 @@ func (_c *RelationService_Create_Call) RunAndReturn(run func(context.Context, re return _c } +// LookupResources provides a mock function with given fields: ctx, resourceType, permission, subjectType, subjectID +func (_m *RelationService) LookupResources(ctx context.Context, resourceType string, permission string, subjectType string, subjectID string) ([]string, error) { + ret := _m.Called(ctx, resourceType, permission, subjectType, subjectID) + + if len(ret) == 0 { + panic("no return value specified for LookupResources") + } + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) ([]string, error)); ok { + return rf(ctx, resourceType, permission, subjectType, subjectID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) []string); ok { + r0 = rf(ctx, resourceType, permission, subjectType, subjectID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string) error); ok { + r1 = rf(ctx, resourceType, permission, subjectType, subjectID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RelationService_LookupResources_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LookupResources' +type RelationService_LookupResources_Call struct { + *mock.Call +} + +// LookupResources is a helper method to define mock.On call +// - ctx context.Context +// - resourceType string +// - permission string +// - subjectType string +// - subjectID string +func (_e *RelationService_Expecter) LookupResources(ctx interface{}, resourceType interface{}, permission interface{}, subjectType interface{}, subjectID interface{}) *RelationService_LookupResources_Call { + return &RelationService_LookupResources_Call{Call: _e.mock.On("LookupResources", ctx, resourceType, permission, subjectType, subjectID)} +} + +func (_c *RelationService_LookupResources_Call) Run(run func(ctx context.Context, resourceType string, permission string, subjectType string, subjectID string)) *RelationService_LookupResources_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(string)) + }) + return _c +} + +func (_c *RelationService_LookupResources_Call) Return(_a0 []string, _a1 error) *RelationService_LookupResources_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *RelationService_LookupResources_Call) RunAndReturn(run func(context.Context, string, string, string, string) ([]string, error)) *RelationService_LookupResources_Call { + _c.Call.Return(run) + return _c +} + // NewRelationService creates a new instance of RelationService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewRelationService(t interface { diff --git a/core/servicedata/mocks/servicedata_repository.go b/core/servicedata/mocks/servicedata_repository.go index de3fb1787..c3597043f 100644 --- a/core/servicedata/mocks/servicedata_repository.go +++ b/core/servicedata/mocks/servicedata_repository.go @@ -125,6 +125,65 @@ func (_c *Repository_CreateKey_Call) RunAndReturn(run func(context.Context, serv return _c } +// Get provides a mock function with given fields: ctx, filter +func (_m *Repository) Get(ctx context.Context, filter servicedata.Filter) ([]servicedata.ServiceData, error) { + ret := _m.Called(ctx, filter) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 []servicedata.ServiceData + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, servicedata.Filter) ([]servicedata.ServiceData, error)); ok { + return rf(ctx, filter) + } + if rf, ok := ret.Get(0).(func(context.Context, servicedata.Filter) []servicedata.ServiceData); ok { + r0 = rf(ctx, filter) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]servicedata.ServiceData) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, servicedata.Filter) error); ok { + r1 = rf(ctx, filter) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Repository_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type Repository_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - ctx context.Context +// - filter servicedata.Filter +func (_e *Repository_Expecter) Get(ctx interface{}, filter interface{}) *Repository_Get_Call { + return &Repository_Get_Call{Call: _e.mock.On("Get", ctx, filter)} +} + +func (_c *Repository_Get_Call) Run(run func(ctx context.Context, filter servicedata.Filter)) *Repository_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(servicedata.Filter)) + }) + return _c +} + +func (_c *Repository_Get_Call) Return(_a0 []servicedata.ServiceData, _a1 error) *Repository_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Repository_Get_Call) RunAndReturn(run func(context.Context, servicedata.Filter) ([]servicedata.ServiceData, error)) *Repository_Get_Call { + _c.Call.Return(run) + return _c +} + // GetKeyByURN provides a mock function with given fields: ctx, URN func (_m *Repository) GetKeyByURN(ctx context.Context, URN string) (servicedata.Key, error) { ret := _m.Called(ctx, URN) diff --git a/core/servicedata/service.go b/core/servicedata/service.go index 32d492bbc..5d798f8d4 100644 --- a/core/servicedata/service.go +++ b/core/servicedata/service.go @@ -2,6 +2,7 @@ package servicedata import ( "context" + "slices" "github.com/goto/shield/core/action" "github.com/goto/shield/core/namespace" @@ -13,8 +14,12 @@ import ( ) const ( - keyNamespace = schema.ServiceDataKeyNamespace - editActionID = schema.EditPermission + keyNamespace = schema.ServiceDataKeyNamespace + userNamespace = schema.UserPrincipal + groupNamespace = schema.GroupPrincipal + viewActionID = schema.ViewPermission + editActionID = schema.EditPermission + membershipPermission = schema.MembershipPermission ) type ResourceService interface { @@ -25,6 +30,7 @@ type ResourceService interface { type RelationService interface { Create(ctx context.Context, rel relation.RelationV2) (relation.RelationV2, error) CheckPermission(ctx context.Context, usr user.User, resourceNS namespace.Namespace, resourceIdxa string, action action.Action) (bool, error) + LookupResources(ctx context.Context, resourceType, permission, subjectType, subjectID string) ([]string, error) } type ProjectService interface { @@ -169,3 +175,58 @@ func (s Service) Upsert(ctx context.Context, sd ServiceData) (ServiceData, error return returnedServiceData, nil } + +func (s Service) Get(ctx context.Context, filter Filter) ([]ServiceData, error) { + // fetch current user + currentUser, err := s.userService.FetchCurrentUser(ctx) + if err != nil { + return []ServiceData{}, err + } + + // validate project and get project id from project slug + if filter.Project != "" { + prj, err := s.projectService.Get(ctx, filter.Project) + if err != nil { + return []ServiceData{}, err + } + filter.Project = prj.ID + } + + // build entity ID filter + filter.EntityIDs = [][]string{} + if filter.Namespace == groupNamespace { + filter.EntityIDs = append(filter.EntityIDs, []string{groupNamespace, filter.ID}) + } + if filter.Namespace == userNamespace && slices.Contains(filter.Entities, userNamespace) { + filter.EntityIDs = append(filter.EntityIDs, []string{userNamespace, filter.ID}) + } + if filter.Namespace == userNamespace && slices.Contains(filter.Entities, groupNamespace) { + entityGroup, err := s.relationService.LookupResources(ctx, groupNamespace, membershipPermission, userNamespace, filter.ID) + if err != nil { + return []ServiceData{}, err + } + for _, ent := range entityGroup { + filter.EntityIDs = append(filter.EntityIDs, []string{groupNamespace, ent}) + } + } + + // get all service data key resources that visible by current user + viewSD, err := s.relationService.LookupResources(ctx, keyNamespace, viewActionID, userNamespace, currentUser.ID) + if err != nil { + return []ServiceData{}, err + } + + serviceData, err := s.repository.Get(ctx, filter) + if err != nil { + return []ServiceData{}, err + } + + resultSD := []ServiceData{} + for _, sd := range serviceData { + if slices.Contains(viewSD, sd.Key.ResourceID) { + resultSD = append(resultSD, sd) + } + } + + return resultSD, nil +} diff --git a/core/servicedata/service_test.go b/core/servicedata/service_test.go index e16bce14d..946470b53 100644 --- a/core/servicedata/service_test.go +++ b/core/servicedata/service_test.go @@ -61,6 +61,7 @@ var ( }, } testEntityID = "test-entity-id" + testGroupID = "test-group-id" testNamespaceID = "test-namespace-id" testValue = "test-value" testServiceData = servicedata.ServiceData{ @@ -69,6 +70,11 @@ var ( Key: testCreateKey, Value: testValue, } + testServiceDataIDs = []string{"test-sd-key-01", "test-sd-key-02"} + testAuthorizedSD = servicedata.ServiceData{Key: servicedata.Key{ResourceID: testServiceDataIDs[0]}} + testUnauthorizedSD = servicedata.ServiceData{Key: servicedata.Key{ResourceID: "test-sd-key-other"}} + testGetRepositoryResult = []servicedata.ServiceData{testAuthorizedSD, testUnauthorizedSD} + testGetResult = []servicedata.ServiceData{testAuthorizedSD} ) func TestService_CreateKey(t *testing.T) { @@ -489,7 +495,6 @@ func TestService_Upsert(t *testing.T) { wantErr: servicedata.ErrInvalidDetail, }, } - for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { @@ -500,6 +505,231 @@ func TestService_Upsert(t *testing.T) { ctx := user.SetContextWithEmail(context.TODO(), tt.email) got, err := svc.Upsert(ctx, tt.data) + + if tt.wantErr != nil { + assert.Error(t, err) + assert.True(t, errors.Is(err, tt.wantErr)) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, got) + }) + } +} + +func TestService_Get(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + email string + filter servicedata.Filter + setup func(t *testing.T) *servicedata.Service + want []servicedata.ServiceData + wantErr error + }{ + { + name: "Get", + email: "john.doe@gotocompany.com", + filter: servicedata.Filter{ + ID: testEntityID, + Namespace: schema.UserPrincipal, + Entities: []string{schema.UserPrincipal, schema.GroupPrincipal}, + Project: testProjectSlug, + }, + setup: func(t *testing.T) *servicedata.Service { + t.Helper() + repository := &mocks.Repository{} + relationService := &mocks.RelationService{} + projectService := &mocks.ProjectService{} + resourceService := &mocks.ResourceService{} + userService := &mocks.UserService{} + userService.EXPECT().FetchCurrentUser(mock.Anything). + Return(user.User{ + ID: testUserID, + Email: "john.doe@gotocompany.com", + }, nil) + projectService.EXPECT().Get(mock.Anything, testProjectSlug). + Return(project.Project{ + ID: testProjectID, + Slug: testProjectSlug, + }, nil) + relationService.EXPECT().LookupResources(mock.Anything, schema.GroupNamespace, schema.MembershipPermission, schema.UserPrincipal, testEntityID). + Return([]string{testGroupID}, nil) + relationService.EXPECT().LookupResources(mock.Anything, schema.ServiceDataKeyNamespace, schema.ViewPermission, schema.UserPrincipal, testUserID). + Return(testServiceDataIDs, nil) + repository.EXPECT().Get(mock.Anything, servicedata.Filter{ + ID: testEntityID, + Namespace: schema.UserPrincipal, + Entities: []string{schema.UserPrincipal, schema.GroupPrincipal}, + EntityIDs: [][]string{ + {schema.UserPrincipal, testEntityID}, + {schema.GroupPrincipal, testGroupID}, + }, + Project: testProjectID, + }).Return(testGetRepositoryResult, nil) + return servicedata.NewService(repository, resourceService, relationService, projectService, userService) + }, + + want: testGetResult, + }, + { + name: "GetMissingEmail", + filter: servicedata.Filter{}, + setup: func(t *testing.T) *servicedata.Service { + t.Helper() + repository := &mocks.Repository{} + resourceService := &mocks.ResourceService{} + relationService := &mocks.RelationService{} + projectService := &mocks.ProjectService{} + userService := &mocks.UserService{} + userService.EXPECT().FetchCurrentUser(mock.Anything).Return(user.User{}, user.ErrMissingEmail) + return servicedata.NewService(repository, resourceService, relationService, projectService, userService) + }, + wantErr: user.ErrMissingEmail, + want: []servicedata.ServiceData{}, + }, + { + name: "GetInvalidEmail", + filter: servicedata.Filter{}, + email: "jane.doe@gotocompany.com", + setup: func(t *testing.T) *servicedata.Service { + t.Helper() + repository := &mocks.Repository{} + resourceService := &mocks.ResourceService{} + relationService := &mocks.RelationService{} + projectService := &mocks.ProjectService{} + userService := &mocks.UserService{} + userService.EXPECT().FetchCurrentUser(mock.Anything).Return(user.User{}, user.ErrInvalidEmail) + return servicedata.NewService(repository, resourceService, relationService, projectService, userService) + }, + wantErr: user.ErrInvalidEmail, + want: []servicedata.ServiceData{}, + }, + { + name: "GetInvalidProjectID", + filter: servicedata.Filter{ + Project: "invalid-test-project-slug", + }, + email: "jane.doe@gotocompany.com", + setup: func(t *testing.T) *servicedata.Service { + t.Helper() + repository := &mocks.Repository{} + resourceService := &mocks.ResourceService{} + relationService := &mocks.RelationService{} + projectService := &mocks.ProjectService{} + userService := &mocks.UserService{} + userService.EXPECT().FetchCurrentUser(mock.Anything).Return(user.User{Email: "jane.doe@gotocompany.com"}, nil) + projectService.EXPECT().Get(mock.Anything, "invalid-test-project-slug").Return(project.Project{}, project.ErrNotExist) + return servicedata.NewService(repository, resourceService, relationService, projectService, userService) + }, + wantErr: project.ErrNotExist, + want: []servicedata.ServiceData{}, + }, + { + name: "GetErrLookupResourceGroup", + filter: servicedata.Filter{ + Namespace: schema.UserPrincipal, + Entities: []string{schema.UserPrincipal, schema.GroupPrincipal}, + ID: testEntityID, + }, + email: "jane.doe@gotocompany.com", + setup: func(t *testing.T) *servicedata.Service { + t.Helper() + repository := &mocks.Repository{} + resourceService := &mocks.ResourceService{} + relationService := &mocks.RelationService{} + projectService := &mocks.ProjectService{} + userService := &mocks.UserService{} + userService.EXPECT().FetchCurrentUser(mock.Anything).Return(user.User{Email: "jane.doe@gotocompany.com"}, nil) + relationService.EXPECT().LookupResources(mock.Anything, schema.GroupNamespace, schema.MembershipPermission, schema.UserPrincipal, testEntityID). + Return(nil, relation.ErrInvalidDetail) + return servicedata.NewService(repository, resourceService, relationService, projectService, userService) + }, + wantErr: relation.ErrInvalidDetail, + want: []servicedata.ServiceData{}, + }, + { + name: "GetErrLookupResourceKey", + filter: servicedata.Filter{ + Namespace: schema.UserPrincipal, + Entities: []string{schema.UserPrincipal, schema.GroupPrincipal}, + ID: testEntityID, + }, + email: "jane.doe@gotocompany.com", + setup: func(t *testing.T) *servicedata.Service { + t.Helper() + repository := &mocks.Repository{} + resourceService := &mocks.ResourceService{} + relationService := &mocks.RelationService{} + projectService := &mocks.ProjectService{} + userService := &mocks.UserService{} + userService.EXPECT().FetchCurrentUser(mock.Anything).Return(user.User{Email: "jane.doe@gotocompany.com", ID: testUserID}, nil) + relationService.EXPECT().LookupResources(mock.Anything, schema.GroupNamespace, schema.MembershipPermission, schema.UserPrincipal, testEntityID). + Return([]string{testGroupID}, nil) + relationService.EXPECT().LookupResources(mock.Anything, schema.ServiceDataKeyNamespace, schema.ViewPermission, schema.UserPrincipal, testUserID). + Return(nil, relation.ErrInvalidDetail) + return servicedata.NewService(repository, resourceService, relationService, projectService, userService) + }, + wantErr: relation.ErrInvalidDetail, + want: []servicedata.ServiceData{}, + }, + { + name: "GetErrRepository", + email: "john.doe@gotocompany.com", + filter: servicedata.Filter{ + ID: testEntityID, + Namespace: schema.UserPrincipal, + Entities: []string{schema.UserPrincipal, schema.GroupPrincipal}, + Project: testProjectSlug, + }, + setup: func(t *testing.T) *servicedata.Service { + t.Helper() + repository := &mocks.Repository{} + relationService := &mocks.RelationService{} + projectService := &mocks.ProjectService{} + resourceService := &mocks.ResourceService{} + userService := &mocks.UserService{} + userService.EXPECT().FetchCurrentUser(mock.Anything). + Return(user.User{ + ID: testUserID, + Email: "john.doe@gotocompany.com", + }, nil) + projectService.EXPECT().Get(mock.Anything, testProjectSlug). + Return(project.Project{ + ID: testProjectID, + Slug: testProjectSlug, + }, nil) + relationService.EXPECT().LookupResources(mock.Anything, schema.GroupNamespace, schema.MembershipPermission, schema.UserPrincipal, testEntityID). + Return([]string{testGroupID}, nil) + relationService.EXPECT().LookupResources(mock.Anything, schema.ServiceDataKeyNamespace, schema.ViewPermission, schema.UserPrincipal, testUserID). + Return(testServiceDataIDs, nil) + repository.EXPECT().Get(mock.Anything, servicedata.Filter{ + ID: testEntityID, + Namespace: schema.UserPrincipal, + Entities: []string{schema.UserPrincipal, schema.GroupPrincipal}, + EntityIDs: [][]string{ + {schema.UserPrincipal, testEntityID}, + {schema.GroupPrincipal, testGroupID}, + }, + Project: testProjectID, + }).Return(nil, servicedata.ErrInvalidDetail) + return servicedata.NewService(repository, resourceService, relationService, projectService, userService) + }, + wantErr: servicedata.ErrInvalidDetail, + want: []servicedata.ServiceData{}, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + svc := tt.setup(t) + + assert.NotNil(t, svc) + + ctx := user.SetContextWithEmail(context.TODO(), tt.email) + got, err := svc.Get(ctx, tt.filter) if tt.wantErr != nil { assert.Error(t, err) assert.True(t, errors.Is(err, tt.wantErr)) diff --git a/core/servicedata/servicedata.go b/core/servicedata/servicedata.go index 7557592be..5f68bafec 100644 --- a/core/servicedata/servicedata.go +++ b/core/servicedata/servicedata.go @@ -10,6 +10,7 @@ type Repository interface { CreateKey(ctx context.Context, key Key) (Key, error) Upsert(ctx context.Context, servicedata ServiceData) (ServiceData, error) GetKeyByURN(ctx context.Context, URN string) (Key, error) + Get(ctx context.Context, filter Filter) ([]ServiceData, error) } type Transactor interface { @@ -36,6 +37,14 @@ type ServiceData struct { Value string } +type Filter struct { + ID string + Namespace string + Entities []string + EntityIDs [][]string + Project string +} + func (key Key) CreateURN() string { return fmt.Sprintf("%s:servicedata_key:%s", key.ProjectSlug, key.Key) } diff --git a/internal/api/v1beta1/mocks/servicedata_service.go b/internal/api/v1beta1/mocks/servicedata_service.go index 79d4dbf8e..2c8e5622a 100644 --- a/internal/api/v1beta1/mocks/servicedata_service.go +++ b/internal/api/v1beta1/mocks/servicedata_service.go @@ -79,6 +79,65 @@ func (_c *ServiceDataService_CreateKey_Call) RunAndReturn(run func(context.Conte return _c } +// Get provides a mock function with given fields: ctx, filter +func (_m *ServiceDataService) Get(ctx context.Context, filter servicedata.Filter) ([]servicedata.ServiceData, error) { + ret := _m.Called(ctx, filter) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 []servicedata.ServiceData + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, servicedata.Filter) ([]servicedata.ServiceData, error)); ok { + return rf(ctx, filter) + } + if rf, ok := ret.Get(0).(func(context.Context, servicedata.Filter) []servicedata.ServiceData); ok { + r0 = rf(ctx, filter) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]servicedata.ServiceData) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, servicedata.Filter) error); ok { + r1 = rf(ctx, filter) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ServiceDataService_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type ServiceDataService_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - ctx context.Context +// - filter servicedata.Filter +func (_e *ServiceDataService_Expecter) Get(ctx interface{}, filter interface{}) *ServiceDataService_Get_Call { + return &ServiceDataService_Get_Call{Call: _e.mock.On("Get", ctx, filter)} +} + +func (_c *ServiceDataService_Get_Call) Run(run func(ctx context.Context, filter servicedata.Filter)) *ServiceDataService_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(servicedata.Filter)) + }) + return _c +} + +func (_c *ServiceDataService_Get_Call) Return(_a0 []servicedata.ServiceData, _a1 error) *ServiceDataService_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ServiceDataService_Get_Call) RunAndReturn(run func(context.Context, servicedata.Filter) ([]servicedata.ServiceData, error)) *ServiceDataService_Get_Call { + _c.Call.Return(run) + return _c +} + // Upsert provides a mock function with given fields: ctx, serviceData func (_m *ServiceDataService) Upsert(ctx context.Context, serviceData servicedata.ServiceData) (servicedata.ServiceData, error) { ret := _m.Called(ctx, serviceData) diff --git a/internal/api/v1beta1/servicedata.go b/internal/api/v1beta1/servicedata.go index 849b81933..3d3f977aa 100644 --- a/internal/api/v1beta1/servicedata.go +++ b/internal/api/v1beta1/servicedata.go @@ -2,27 +2,37 @@ package v1beta1 import ( "context" + "encoding/json" "errors" + "fmt" "github.com/goto/shield/core/group" - "github.com/goto/shield/core/namespace" "github.com/goto/shield/core/project" "github.com/goto/shield/core/relation" "github.com/goto/shield/core/resource" "github.com/goto/shield/core/servicedata" "github.com/goto/shield/core/user" + "github.com/goto/shield/internal/schema" shieldv1beta1 "github.com/goto/shield/proto/v1beta1" grpczap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" + "golang.org/x/exp/maps" + "google.golang.org/protobuf/types/known/structpb" ) var ( - userNamespaceID = namespace.DefinitionUser.ID - groupNamepaceID = namespace.DefinitionTeam.ID + userNamespaceID = schema.UserPrincipal + groupNamespaceID = schema.GroupPrincipal + projectNamespaceID = schema.ProjectNamespace + entitiesNamespaceMap = map[string]string{ + "user": userNamespaceID, + "group": groupNamespaceID, + } ) type ServiceDataService interface { CreateKey(ctx context.Context, key servicedata.Key) (servicedata.Key, error) Upsert(ctx context.Context, serviceData servicedata.ServiceData) (servicedata.ServiceData, error) + Get(ctx context.Context, filter servicedata.Filter) ([]servicedata.ServiceData, error) } func (h Handler) CreateServiceDataKey(ctx context.Context, request *shieldv1beta1.CreateServiceDataKeyRequest) (*shieldv1beta1.CreateServiceDataKeyResponse, error) { @@ -94,7 +104,6 @@ func (h Handler) UpsertUserServiceData(ctx context.Context, request *shieldv1bet return nil, grpcInternalServerError } } - serviceDataMap := map[string]string{} for k, v := range requestBody.Data { serviceDataResp, err := h.serviceDataService.Upsert(ctx, servicedata.ServiceData{ @@ -156,12 +165,11 @@ func (h Handler) UpsertGroupServiceData(ctx context.Context, request *shieldv1be return nil, grpcInternalServerError } } - serviceDataMap := map[string]string{} for k, v := range requestBody.Data { serviceDataResp, err := h.serviceDataService.Upsert(ctx, servicedata.ServiceData{ EntityID: groupEntity.ID, - NamespaceID: groupNamepaceID, + NamespaceID: groupNamespaceID, Key: servicedata.Key{ Key: k, ProjectID: requestBody.Project, @@ -189,9 +197,159 @@ func (h Handler) UpsertGroupServiceData(ctx context.Context, request *shieldv1be }, nil } +func (h Handler) GetUserServiceData(ctx context.Context, request *shieldv1beta1.GetUserServiceDataRequest) (*shieldv1beta1.GetUserServiceDataResponse, error) { + logger := grpczap.Extract(ctx) + + usr, err := h.userService.Get(ctx, request.GetUserId()) + if err != nil { + logger.Error(err.Error()) + + switch { + case errors.Is(err, user.ErrNotExist), errors.Is(err, user.ErrInvalidEmail), + errors.Is(err, user.ErrInvalidID): + return nil, grpcBadBodyError + default: + return nil, grpcInternalServerError + } + } + + entities := []string{} + if request.GetEntity() != nil { + for _, ent := range request.GetEntity() { + if entNamespace, ok := entitiesNamespaceMap[ent]; ok { + entities = append(entities, entNamespace) + } + } + } + + if len(entities) == 0 { + entities = maps.Values(entitiesNamespaceMap) + } + + filter := servicedata.Filter{ + ID: usr.ID, + Namespace: userNamespaceID, + Entities: entities, + Project: request.GetProject(), + } + + serviceData, err := h.serviceDataService.Get(ctx, filter) + if err != nil { + logger.Error(err.Error()) + switch { + case errors.Is(err, user.ErrInvalidEmail), errors.Is(err, user.ErrMissingEmail): + return nil, grpcUnauthenticated + case errors.Is(err, project.ErrNotExist), errors.Is(err, servicedata.ErrInvalidDetail), + errors.Is(err, relation.ErrInvalidDetail): + return nil, grpcBadBodyError + default: + return nil, grpcInternalServerError + } + } + + serviceDataPB, err := transformServiceDataListToPB(serviceData) + if err != nil { + logger.Error(err.Error()) + return nil, grpcInternalServerError + } + + return &shieldv1beta1.GetUserServiceDataResponse{ + Data: serviceDataPB, + }, nil +} + +func (h Handler) GetGroupServiceData(ctx context.Context, request *shieldv1beta1.GetGroupServiceDataRequest) (*shieldv1beta1.GetGroupServiceDataResponse, error) { + logger := grpczap.Extract(ctx) + + grp, err := h.groupService.Get(ctx, request.GetGroupId()) + if err != nil { + logger.Error(err.Error()) + + switch { + case errors.Is(err, group.ErrNotExist), errors.Is(err, group.ErrInvalidDetail), + errors.Is(err, group.ErrInvalidID): + return nil, grpcBadBodyError + default: + return nil, grpcInternalServerError + } + } + + filter := servicedata.Filter{ + ID: grp.ID, + Namespace: groupNamespaceID, + Project: request.GetProject(), + } + + serviceData, err := h.serviceDataService.Get(ctx, filter) + if err != nil { + logger.Error(err.Error()) + switch { + case errors.Is(err, user.ErrInvalidEmail), errors.Is(err, user.ErrMissingEmail): + return nil, grpcUnauthenticated + case errors.Is(err, project.ErrNotExist), errors.Is(err, servicedata.ErrInvalidDetail), + errors.Is(err, relation.ErrInvalidDetail): + return nil, grpcBadBodyError + default: + return nil, grpcInternalServerError + } + } + + serviceDataPB, err := transformServiceDataListToPB(serviceData) + if err != nil { + logger.Error(err.Error()) + return nil, grpcInternalServerError + } + + return &shieldv1beta1.GetGroupServiceDataResponse{ + Data: serviceDataPB, + }, nil +} + func transformServiceDataKeyToPB(from servicedata.Key) (shieldv1beta1.ServiceDataKey, error) { return shieldv1beta1.ServiceDataKey{ Urn: from.URN, Id: from.ID, }, nil } + +func transformServiceDataListToPB(from []servicedata.ServiceData) (*structpb.Struct, error) { + data := map[string]map[string]map[string]string{} + + for _, sd := range from { + prjKey := fmt.Sprintf("%s:%s", projectNamespaceID, sd.Key.ProjectID) + entKey := fmt.Sprintf("%s:%s", sd.NamespaceID, sd.EntityID) + prj, ok := data[prjKey] + if ok { + ent, ok := prj[entKey] + if ok { + ent[sd.Key.Key] = sd.Value + } else { + prj[entKey] = map[string]string{ + sd.Key.Key: sd.Value, + } + } + } else { + kv := map[string]string{sd.Key.Key: sd.Value} + data[prjKey] = map[string]map[string]string{ + entKey: kv, + } + } + } + + var decodedData map[string]interface{} + encodedData, err := json.Marshal(data) + if err != nil { + return nil, err + } + err = json.Unmarshal(encodedData, &decodedData) + if err != nil { + return nil, err + } + + serviceData, err := structpb.NewStruct(decodedData) + if err != nil { + return nil, err + } + + return serviceData, nil +} diff --git a/internal/api/v1beta1/servicedata_test.go b/internal/api/v1beta1/servicedata_test.go index 84472b6cb..4d0d99195 100644 --- a/internal/api/v1beta1/servicedata_test.go +++ b/internal/api/v1beta1/servicedata_test.go @@ -22,6 +22,7 @@ var ( testKeyResourceID = uuid.NewString() testKeyName = "test-key" testValue = "test-value" + testEntityID = uuid.NewString() testKey = servicedata.Key{ ID: testKeyID, URN: "key-urn", @@ -34,7 +35,6 @@ var ( Id: testKey.ID, Urn: testKey.URN, } - testEntityID = uuid.NewString() testUserServiceDataCreate = servicedata.ServiceData{ EntityID: testEntityID, NamespaceID: userNamespaceID, @@ -46,17 +46,17 @@ var ( } testGroupServiceDataCreate = servicedata.ServiceData{ EntityID: testEntityID, - NamespaceID: groupNamepaceID, + NamespaceID: groupNamespaceID, Key: servicedata.Key{ Key: testKeyName, ProjectID: testKeyProjectID, }, Value: testValue, } + email = "user@gotocompany.com" ) func TestHandler_CreateKey(t *testing.T) { - email := "user@gotocompany.com" tests := []struct { name string setup func(ctx context.Context, ss *mocks.ServiceDataService) context.Context @@ -255,7 +255,7 @@ func TestHandler_UpdateUserServiceData(t *testing.T) { { name: "should return bad body error if user id or email in param does not exist", request: &shieldv1beta1.UpsertUserServiceDataRequest{ - UserId: testEntityID, + UserId: "", Body: &shieldv1beta1.UpsertServiceDataRequestBody{ Data: map[string]string{ testKeyName: testValue, @@ -263,7 +263,7 @@ func TestHandler_UpdateUserServiceData(t *testing.T) { }, }, setup: func(ctx context.Context, ss *mocks.ServiceDataService, us *mocks.UserService) context.Context { - us.EXPECT().Get(mock.AnythingOfType("context.todoCtx"), testEntityID).Return(user.User{}, user.ErrInvalidEmail) + us.EXPECT().Get(mock.AnythingOfType("context.todoCtx"), "").Return(user.User{}, user.ErrInvalidEmail) return ctx }, want: nil, @@ -496,3 +496,133 @@ func TestHandler_UpdateGroupServiceData(t *testing.T) { }) } } + +func TestHandler_GetUserServiceData(t *testing.T) { + tests := []struct { + name string + setup func(ctx context.Context, ss *mocks.ServiceDataService, us *mocks.UserService) context.Context + request *shieldv1beta1.GetUserServiceDataRequest + want *shieldv1beta1.GetUserServiceDataResponse + wantErr error + }{ + { + name: "should return unauthenticated error if auth email in context is empty", + setup: func(ctx context.Context, ss *mocks.ServiceDataService, us *mocks.UserService) context.Context { + us.EXPECT().Get(mock.AnythingOfType("context.todoCtx"), testEntityID).Return(user.User{ID: testEntityID}, nil) + ss.EXPECT().Get(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("servicedata.Filter")).Return([]servicedata.ServiceData{}, user.ErrMissingEmail) + return ctx + }, + request: &shieldv1beta1.GetUserServiceDataRequest{ + UserId: testEntityID, + }, + want: nil, + wantErr: grpcUnauthenticated, + }, + { + name: "should return bad body error if user entity not exist", + setup: func(ctx context.Context, ss *mocks.ServiceDataService, us *mocks.UserService) context.Context { + us.EXPECT().Get(mock.AnythingOfType("*context.valueCtx"), testEntityID).Return(user.User{}, user.ErrNotExist) + return user.SetContextWithEmail(ctx, email) + }, + request: &shieldv1beta1.GetUserServiceDataRequest{ + UserId: testEntityID, + }, + want: nil, + wantErr: grpcBadBodyError, + }, + { + name: "should return bad body error if project not exist", + setup: func(ctx context.Context, ss *mocks.ServiceDataService, us *mocks.UserService) context.Context { + us.EXPECT().Get(mock.AnythingOfType("*context.valueCtx"), testEntityID).Return(user.User{ID: testEntityID}, nil) + ss.EXPECT().Get(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("servicedata.Filter")).Return([]servicedata.ServiceData{}, project.ErrNotExist) + return user.SetContextWithEmail(ctx, email) + }, + request: &shieldv1beta1.GetUserServiceDataRequest{ + UserId: testEntityID, + Project: "invalid-project-id", + }, + want: nil, + wantErr: grpcBadBodyError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockServiceDataService := new(mocks.ServiceDataService) + mockUserService := new(mocks.UserService) + + ctx := context.TODO() + if tt.setup != nil { + ctx = tt.setup(ctx, mockServiceDataService, mockUserService) + } + mockDep := Handler{serviceDataService: mockServiceDataService, userService: mockUserService} + resp, err := mockDep.GetUserServiceData(ctx, tt.request) + assert.EqualValues(t, tt.want, resp) + assert.EqualValues(t, tt.wantErr, err) + }) + } +} + +func TestHandler_GetGroupServiceData(t *testing.T) { + tests := []struct { + name string + setup func(ctx context.Context, ss *mocks.ServiceDataService, gs *mocks.GroupService) context.Context + request *shieldv1beta1.GetGroupServiceDataRequest + want *shieldv1beta1.GetGroupServiceDataResponse + wantErr error + }{ + { + name: "should return unauthenticated error if auth email in context is empty", + setup: func(ctx context.Context, ss *mocks.ServiceDataService, us *mocks.GroupService) context.Context { + us.EXPECT().Get(mock.AnythingOfType("context.todoCtx"), testEntityID).Return(group.Group{ID: testEntityID}, nil) + ss.EXPECT().Get(mock.AnythingOfType("context.todoCtx"), mock.AnythingOfType("servicedata.Filter")).Return([]servicedata.ServiceData{}, user.ErrMissingEmail) + return ctx + }, + request: &shieldv1beta1.GetGroupServiceDataRequest{ + GroupId: testEntityID, + }, + want: nil, + wantErr: grpcUnauthenticated, + }, + { + name: "should return bad body error if user entity not exist", + setup: func(ctx context.Context, ss *mocks.ServiceDataService, gs *mocks.GroupService) context.Context { + gs.EXPECT().Get(mock.AnythingOfType("*context.valueCtx"), testEntityID).Return(group.Group{}, group.ErrNotExist) + return user.SetContextWithEmail(ctx, email) + }, + request: &shieldv1beta1.GetGroupServiceDataRequest{ + GroupId: testEntityID, + }, + want: nil, + wantErr: grpcBadBodyError, + }, + { + name: "should return bad body error if project not exist", + setup: func(ctx context.Context, ss *mocks.ServiceDataService, gs *mocks.GroupService) context.Context { + gs.EXPECT().Get(mock.AnythingOfType("*context.valueCtx"), testEntityID).Return(group.Group{ID: testEntityID}, nil) + ss.EXPECT().Get(mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("servicedata.Filter")).Return([]servicedata.ServiceData{}, project.ErrNotExist) + return user.SetContextWithEmail(ctx, email) + }, + request: &shieldv1beta1.GetGroupServiceDataRequest{ + GroupId: testEntityID, + Project: "invalid-project-id", + }, + want: nil, + wantErr: grpcBadBodyError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockServiceDataService := new(mocks.ServiceDataService) + mockGroupService := new(mocks.GroupService) + + ctx := context.TODO() + if tt.setup != nil { + ctx = tt.setup(ctx, mockServiceDataService, mockGroupService) + } + mockDep := Handler{serviceDataService: mockServiceDataService, groupService: mockGroupService} + resp, err := mockDep.GetGroupServiceData(ctx, tt.request) + assert.EqualValues(t, tt.want, resp) + assert.EqualValues(t, tt.wantErr, err) + }) + } +} diff --git a/internal/store/postgres/postgres_test.go b/internal/store/postgres/postgres_test.go index bf8be2c75..78bbbc136 100644 --- a/internal/store/postgres/postgres_test.go +++ b/internal/store/postgres/postgres_test.go @@ -22,6 +22,7 @@ import ( "github.com/goto/shield/core/role" "github.com/goto/shield/core/servicedata" "github.com/goto/shield/core/user" + "github.com/goto/shield/internal/schema" "github.com/goto/shield/internal/store/postgres" "github.com/goto/shield/internal/store/postgres/migrations" "github.com/goto/shield/pkg/db" @@ -526,3 +527,33 @@ func bootstrapServiceDataKey(client *db.Client, resources []resource.Resource, p return insertedData, nil } + +func bootstrapServiceData(client *db.Client, users []user.User, keys []servicedata.Key) ([]servicedata.ServiceData, error) { + serviceDataRepository := postgres.NewServiceDataRepository(client) + + testFixtureJSON, err := ioutil.ReadFile("./testdata/mock-servicedata.json") + if err != nil { + return nil, err + } + + var data []servicedata.ServiceData + if err = json.Unmarshal(testFixtureJSON, &data); err != nil { + return nil, err + } + + var insertedData []servicedata.ServiceData + for i, d := range data { + d.Key = keys[i] + d.EntityID = users[i].ID + d.NamespaceID = schema.UserPrincipal + + data, err := serviceDataRepository.Upsert(context.Background(), d) + if err != nil { + return nil, err + } + + insertedData = append(insertedData, data) + } + + return insertedData, nil +} diff --git a/internal/store/postgres/servicedata.go b/internal/store/postgres/servicedata.go index 0455d034e..12095c17a 100644 --- a/internal/store/postgres/servicedata.go +++ b/internal/store/postgres/servicedata.go @@ -20,8 +20,27 @@ type Key struct { } type ServiceData struct { - Key string `db:"key"` - Value string `db:"value"` + URN string `db:"urn"` + NamespaceID string `db:"namespace_id"` + EntityID string `db:"entity_id"` + Value string `db:"value"` + Key string `db:"key"` + ProjectID string `db:"project_id"` + ResourceID string `db:"resource_id"` +} + +func (from ServiceData) transformToServiceData() servicedata.ServiceData { + return servicedata.ServiceData{ + NamespaceID: from.NamespaceID, + EntityID: from.EntityID, + Key: servicedata.Key{ + URN: from.URN, + ProjectID: from.ProjectID, + Key: from.Key, + ResourceID: from.ResourceID, + }, + Value: from.Value, + } } func (from Key) transformToServiceDataKey() servicedata.Key { @@ -34,13 +53,3 @@ func (from Key) transformToServiceDataKey() servicedata.Key { ResourceID: from.ResourceID, } } - -func (from ServiceData) transformToServiceData() servicedata.ServiceData { - data := servicedata.ServiceData{ - Key: servicedata.Key{ - Key: from.Key, - }, - Value: from.Value, - } - return data -} diff --git a/internal/store/postgres/servicedata_repository.go b/internal/store/postgres/servicedata_repository.go index b3768d18a..c0fdf952c 100644 --- a/internal/store/postgres/servicedata_repository.go +++ b/internal/store/postgres/servicedata_repository.go @@ -145,6 +145,66 @@ func (r ServiceDataRepository) GetKeyByURN(ctx context.Context, URN string) (ser return keyModel.transformToServiceDataKey(), nil } +func (r ServiceDataRepository) Get(ctx context.Context, filter servicedata.Filter) ([]servicedata.ServiceData, error) { + sqlStatement := dialect.Select( + goqu.I("sk.urn"), + goqu.I("sk.project_id"), + goqu.I("sk.resource_id"), + goqu.I("sd.namespace_id"), + goqu.I("sd.entity_id"), + goqu.I("sk.key"), + goqu.I("sd.value"), + ).From(goqu.T(TABLE_SERVICE_DATA).As("sd")). + Join(goqu.T(TABLE_SERVICE_DATA_KEYS).As("sk"), goqu.On( + goqu.I("sk.id").Eq(goqu.I("sd.key_id")))). + Where(goqu.L( + "(sd.namespace_id, sd.entity_id)", + ).In(filter.EntityIDs)) + + if filter.Project != "" { + sqlStatement = sqlStatement.Where(goqu.Ex{"sk.project_id": filter.Project}) + } + + query, params, err := sqlStatement.ToSQL() + if err != nil { + return []servicedata.ServiceData{}, err + } + + var serviceDataModel []ServiceData + if err = r.dbc.WithTimeout(ctx, func(ctx context.Context) error { + nrCtx := newrelic.FromContext(ctx) + if nrCtx != nil { + nr := newrelic.DatastoreSegment{ + Product: newrelic.DatastorePostgres, + Collection: TABLE_SERVICE_DATA, + Operation: "Get", + StartTime: nrCtx.StartSegmentNow(), + } + defer nr.End() + } + + return r.dbc.SelectContext(ctx, &serviceDataModel, query, params...) + }); err != nil { + err = checkPostgresError(err) + switch { + case errors.Is(err, sql.ErrNoRows): + return []servicedata.ServiceData{}, nil + case errors.Is(err, errInvalidTexRepresentation): + return []servicedata.ServiceData{}, servicedata.ErrInvalidDetail + default: + return []servicedata.ServiceData{}, err + } + } + + var transformedServiceData []servicedata.ServiceData + for _, sdm := range serviceDataModel { + sd := sdm.transformToServiceData() + transformedServiceData = append(transformedServiceData, sd) + } + + return transformedServiceData, nil +} + func (r ServiceDataRepository) WithTransaction(ctx context.Context) context.Context { return r.dbc.WithTransaction(ctx, sql.TxOptions{}) } diff --git a/internal/store/postgres/servicedata_repository_test.go b/internal/store/postgres/servicedata_repository_test.go index d30ed2da8..16038c95b 100644 --- a/internal/store/postgres/servicedata_repository_test.go +++ b/internal/store/postgres/servicedata_repository_test.go @@ -11,6 +11,8 @@ import ( "github.com/goto/shield/core/project" "github.com/goto/shield/core/resource" "github.com/goto/shield/core/servicedata" + "github.com/goto/shield/core/user" + "github.com/goto/shield/internal/schema" "github.com/goto/shield/internal/store/postgres" "github.com/goto/shield/pkg/db" "github.com/goto/shield/pkg/uuid" @@ -28,6 +30,8 @@ type ServiceDataRepositoryTestSuite struct { keys []servicedata.Key projects []project.Project resources []resource.Resource + data []servicedata.ServiceData + users []user.User } func (s *ServiceDataRepositoryTestSuite) SetupSuite() { @@ -56,7 +60,7 @@ func (s *ServiceDataRepositoryTestSuite) SetupTest() { s.T().Fatal(err) } - users, err := bootstrapUser(s.client) + s.users, err = bootstrapUser(s.client) if err != nil { s.T().Fatal(err) } @@ -71,7 +75,7 @@ func (s *ServiceDataRepositoryTestSuite) SetupTest() { s.T().Fatal(err) } - s.resources, err = bootstrapResource(s.client, s.projects, organizations, namespaces, users) + s.resources, err = bootstrapResource(s.client, s.projects, organizations, namespaces, s.users) if err != nil { s.T().Fatal(err) } @@ -80,6 +84,11 @@ func (s *ServiceDataRepositoryTestSuite) SetupTest() { if err != nil { s.T().Fatal(err) } + + s.data, err = bootstrapServiceData(s.client, s.users, s.keys) + if err != nil { + s.T().Fatal(err) + } } func (s *ServiceDataRepositoryTestSuite) TearDownSuite() { @@ -281,6 +290,69 @@ func (s *ServiceDataRepositoryTestSuite) TestGetKeyByURN() { } } +func (s *ServiceDataRepositoryTestSuite) TestGet() { + type testCase struct { + Description string + filter servicedata.Filter + ExpectedData []servicedata.ServiceData + ErrString string + } + + expected := servicedata.ServiceData{ + NamespaceID: schema.UserPrincipal, + EntityID: s.users[0].ID, + Key: servicedata.Key{ + URN: s.keys[0].URN, + ProjectID: s.keys[0].ProjectID, + Key: s.keys[0].Key, + ResourceID: s.keys[0].ResourceID, + }, + Value: s.data[0].Value, + } + + var testCases = []testCase{ + { + Description: "should get a service data", + filter: servicedata.Filter{ + EntityIDs: [][]string{{schema.UserPrincipal, s.users[0].ID}}, + }, + ExpectedData: []servicedata.ServiceData{expected}, + }, + { + Description: "should get none service data", + filter: servicedata.Filter{ + EntityIDs: [][]string{{schema.UserPrincipal, s.users[0].ID}}, + Project: s.projects[1].ID, + }, + }, + { + Description: "should get err invalid detail", + filter: servicedata.Filter{ + EntityIDs: [][]string{{schema.UserPrincipal, "invalid-entity-id"}}, + Project: "invalid-project-uuid", + }, + ErrString: servicedata.ErrInvalidDetail.Error(), + ExpectedData: []servicedata.ServiceData{}, + }, + } + + for _, tc := range testCases { + s.Run(tc.Description, func() { + got, err := s.repository.Get(s.ctx, tc.filter) + if tc.ErrString != "" { + if err.Error() != tc.ErrString { + s.T().Fatalf("got error %s, expected was %s", err.Error(), tc.ErrString) + } + } + if !cmp.Equal(got, tc.ExpectedData, cmpopts.IgnoreFields(servicedata.Key{}, + "ID", + )) { + s.T().Fatalf("got result %+v, expected was %+v", got, tc.ExpectedData) + } + }) + } +} + func TestServiceDataRepository(t *testing.T) { suite.Run(t, new(ServiceDataRepositoryTestSuite)) } diff --git a/internal/store/postgres/testdata/mock-servicedata.json b/internal/store/postgres/testdata/mock-servicedata.json new file mode 100644 index 000000000..116a5135d --- /dev/null +++ b/internal/store/postgres/testdata/mock-servicedata.json @@ -0,0 +1,11 @@ +[ + { + "value": "test-value-01" + }, + { + "value": "test-value-02" + }, + { + "value": "test-value-03" + } +] \ No newline at end of file diff --git a/internal/store/spicedb/relation_repository.go b/internal/store/spicedb/relation_repository.go index bb7138a1e..6d120584c 100644 --- a/internal/store/spicedb/relation_repository.go +++ b/internal/store/spicedb/relation_repository.go @@ -3,6 +3,7 @@ package spicedb import ( "context" "fmt" + "io" "github.com/goto/shield/core/action" "github.com/goto/shield/core/relation" @@ -222,3 +223,48 @@ func (r RelationRepository) DeleteSubjectRelations(ctx context.Context, resource return nil } + +func (r RelationRepository) LookupResources(ctx context.Context, resourceType, permission, subjectType, subjectID string) ([]string, error) { + request := &authzedpb.LookupResourcesRequest{ + ResourceObjectType: resourceType, + Permission: permission, + Subject: &authzedpb.SubjectReference{ + Object: &authzedpb.ObjectReference{ + ObjectType: subjectType, + ObjectId: subjectID, + }, + }, + } + + nrCtx := newrelic.FromContext(ctx) + if nrCtx != nil { + nr := newrelic.DatastoreSegment{ + Product: nrProductName, + Collection: fmt.Sprintf("object:%s::subject:%s", resourceType, subjectType), + Operation: "Lookup_Resources", + StartTime: nrCtx.StartSegmentNow(), + } + defer nr.End() + } + + response, err := r.spiceDB.client.LookupResources(ctx, request) + if err != nil { + return []string{}, err + } + + var res []string + for { + resp, err := response.Recv() + + if err != nil { + if err == io.EOF { + break + } + return []string{}, err + } + + res = append(res, resp.ResourceObjectId) + } + + return res, nil +} diff --git a/proto/shield.swagger.yaml b/proto/shield.swagger.yaml index 5195d1937..ff038afbe 100644 --- a/proto/shield.swagger.yaml +++ b/proto/shield.swagger.yaml @@ -165,6 +165,29 @@ paths: tags: - Group /v1beta1/groups/{group_id}/servicedata: + get: + summary: Get Group Service Data Key + operationId: ServiceDataService_GetGroupServiceData + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/GetGroupServiceDataResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/Status' + parameters: + - name: group_id + in: path + required: true + type: string + - name: project + in: query + required: false + type: string + tags: + - Service Data put: summary: Upsert Group Service Data operationId: ServiceDataService_UpsertGroupServiceData @@ -1026,6 +1049,36 @@ paths: tags: - User /v1beta1/users/{user_id}/servicedata: + get: + summary: Get User Service Data Key + operationId: ServiceDataService_GetUserServiceData + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/GetUserServiceDataResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/Status' + parameters: + - name: user_id + in: path + required: true + type: string + - name: entity + in: query + required: false + type: array + items: + type: string + collectionFormat: multi + - name: project + in: query + required: false + type: string + tags: + - Service Data put: summary: Upsert User Service Data operationId: ServiceDataService_UpsertUserServiceData @@ -1241,6 +1294,11 @@ definitions: properties: group: $ref: '#/definitions/Group' + GetGroupServiceDataResponse: + type: object + properties: + data: + type: object GetNamespaceResponse: type: object properties: @@ -1271,6 +1329,11 @@ definitions: properties: user: $ref: '#/definitions/User' + GetUserServiceDataResponse: + type: object + properties: + data: + type: object Group: type: object properties: diff --git a/proto/v1beta1/servicedata.pb.go b/proto/v1beta1/servicedata.pb.go index 1d6264aea..195150cd6 100644 --- a/proto/v1beta1/servicedata.pb.go +++ b/proto/v1beta1/servicedata.pb.go @@ -12,6 +12,7 @@ import ( _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" reflect "reflect" sync "sync" ) @@ -494,6 +495,218 @@ func (x *UpsertGroupServiceDataResponse) GetData() map[string]string { return nil } +type GetUserServiceDataRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Entity []string `protobuf:"bytes,2,rep,name=entity,proto3" json:"entity,omitempty"` + Project string `protobuf:"bytes,3,opt,name=project,proto3" json:"project,omitempty"` +} + +func (x *GetUserServiceDataRequest) Reset() { + *x = GetUserServiceDataRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUserServiceDataRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserServiceDataRequest) ProtoMessage() {} + +func (x *GetUserServiceDataRequest) ProtoReflect() protoreflect.Message { + mi := &file_gotocompany_shield_v1beta1_servicedata_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 GetUserServiceDataRequest.ProtoReflect.Descriptor instead. +func (*GetUserServiceDataRequest) Descriptor() ([]byte, []int) { + return file_gotocompany_shield_v1beta1_servicedata_proto_rawDescGZIP(), []int{9} +} + +func (x *GetUserServiceDataRequest) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *GetUserServiceDataRequest) GetEntity() []string { + if x != nil { + return x.Entity + } + return nil +} + +func (x *GetUserServiceDataRequest) GetProject() string { + if x != nil { + return x.Project + } + return "" +} + +type GetGroupServiceDataRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GroupId string `protobuf:"bytes,1,opt,name=group_id,json=groupId,proto3" json:"group_id,omitempty"` + Project string `protobuf:"bytes,2,opt,name=project,proto3" json:"project,omitempty"` +} + +func (x *GetGroupServiceDataRequest) Reset() { + *x = GetGroupServiceDataRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGroupServiceDataRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGroupServiceDataRequest) ProtoMessage() {} + +func (x *GetGroupServiceDataRequest) ProtoReflect() protoreflect.Message { + mi := &file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[10] + 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 GetGroupServiceDataRequest.ProtoReflect.Descriptor instead. +func (*GetGroupServiceDataRequest) Descriptor() ([]byte, []int) { + return file_gotocompany_shield_v1beta1_servicedata_proto_rawDescGZIP(), []int{10} +} + +func (x *GetGroupServiceDataRequest) GetGroupId() string { + if x != nil { + return x.GroupId + } + return "" +} + +func (x *GetGroupServiceDataRequest) GetProject() string { + if x != nil { + return x.Project + } + return "" +} + +type GetUserServiceDataResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data *structpb.Struct `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *GetUserServiceDataResponse) Reset() { + *x = GetUserServiceDataResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUserServiceDataResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserServiceDataResponse) ProtoMessage() {} + +func (x *GetUserServiceDataResponse) ProtoReflect() protoreflect.Message { + mi := &file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[11] + 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 GetUserServiceDataResponse.ProtoReflect.Descriptor instead. +func (*GetUserServiceDataResponse) Descriptor() ([]byte, []int) { + return file_gotocompany_shield_v1beta1_servicedata_proto_rawDescGZIP(), []int{11} +} + +func (x *GetUserServiceDataResponse) GetData() *structpb.Struct { + if x != nil { + return x.Data + } + return nil +} + +type GetGroupServiceDataResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Data *structpb.Struct `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *GetGroupServiceDataResponse) Reset() { + *x = GetGroupServiceDataResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetGroupServiceDataResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetGroupServiceDataResponse) ProtoMessage() {} + +func (x *GetGroupServiceDataResponse) ProtoReflect() protoreflect.Message { + mi := &file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[12] + 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 GetGroupServiceDataResponse.ProtoReflect.Descriptor instead. +func (*GetGroupServiceDataResponse) Descriptor() ([]byte, []int) { + return file_gotocompany_shield_v1beta1_servicedata_proto_rawDescGZIP(), []int{12} +} + +func (x *GetGroupServiceDataResponse) GetData() *structpb.Struct { + if x != nil { + return x.Data + } + return nil +} + var File_gotocompany_shield_v1beta1_servicedata_proto protoreflect.FileDescriptor var file_gotocompany_shield_v1beta1_servicedata_proto_rawDesc = []byte{ @@ -508,137 +721,190 @@ var file_gotocompany_shield_v1beta1_servicedata_proto_rawDesc = []byte{ 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, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x69, 0x0a, 0x19, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x18, - 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x32, 0x0a, 0x0e, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x22, 0x68, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x49, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, - 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, - 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x74, 0x0a, 0x1c, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x10, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, - 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, - 0x22, 0xc9, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0x69, 0x0a, 0x19, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x18, 0x0a, 0x07, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x32, 0x0a, 0x0e, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x68, + 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x49, 0x0a, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, + 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x74, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x0e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x22, 0xc9, + 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, + 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x56, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, - 0x79, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x56, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x67, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, - 0x6f, 0x64, 0x79, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x85, 0x01, 0x0a, - 0x1c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, - 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x4c, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, - 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x22, 0x88, 0x01, 0x0a, 0x1d, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x47, + 0x79, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x85, 0x01, 0x0a, 0x1c, 0x55, + 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x4c, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, + 0x64, 0x79, 0x22, 0x88, 0x01, 0x0a, 0x1d, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, + 0x4c, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, + 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xb1, 0x01, + 0x0a, 0x1d, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x57, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, + 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, + 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0xb3, 0x01, 0x0a, 0x1e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x37, + 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x7f, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x2f, 0x0a, + 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, 0x17, 0xfa, + 0x42, 0x14, 0x92, 0x01, 0x11, 0x22, 0x0f, 0x72, 0x0d, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x52, + 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x18, + 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x51, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, - 0x64, 0x12, 0x4c, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, - 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, - 0x65, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, - 0xb1, 0x01, 0x0a, 0x1d, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x57, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x43, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, - 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, - 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x44, 0x61, - 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0xb3, 0x01, 0x0a, 0x1e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x1a, 0x37, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xd0, 0x05, 0x0a, 0x12, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0xd7, 0x01, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x12, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x49, 0x0a, 0x1a, 0x47, + 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4a, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x32, 0x96, 0x09, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xd7, 0x01, 0x0a, 0x14, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, + 0x65, 0x79, 0x12, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4c, 0x92, 0x41, - 0x27, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, - 0x44, 0x61, 0x74, 0x61, 0x20, 0x4b, 0x65, 0x79, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x64, 0x61, 0x74, 0x61, 0x12, 0xeb, 0x01, 0x0a, 0x15, 0x55, - 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, - 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, - 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, - 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5d, 0x92, 0x41, 0x28, 0x0a, 0x0c, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x55, 0x70, - 0x73, 0x65, 0x72, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x3a, 0x04, 0x62, 0x6f, - 0x64, 0x79, 0x1a, 0x24, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x75, 0x73, 0x65, - 0x72, 0x73, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x64, 0x61, 0x74, 0x61, 0x12, 0xf1, 0x01, 0x0a, 0x16, 0x55, 0x70, 0x73, - 0x65, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x39, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4c, 0x92, 0x41, 0x27, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x12, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x4b, 0x65, + 0x79, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x14, 0x2f, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x64, + 0x61, 0x74, 0x61, 0x12, 0xeb, 0x01, 0x0a, 0x15, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x38, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, + 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, + 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x5d, 0x92, 0x41, 0x28, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x20, 0x55, 0x73, + 0x65, 0x72, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x3a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x24, 0x2f, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x75, 0x73, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x64, 0x61, 0x74, + 0x61, 0x12, 0xf1, 0x01, 0x0a, 0x16, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x39, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, + 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x60, 0x92, 0x41, 0x29, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x12, 0x19, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x20, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, + 0x61, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x3a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x26, 0x2f, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x7b, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x64, 0x61, 0x74, 0x61, 0x12, 0xdd, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, + 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x35, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, + 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, + 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, - 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, - 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, - 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x60, 0x92, 0x41, 0x29, 0x0a, - 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x12, 0x19, 0x55, - 0x70, 0x73, 0x65, 0x72, 0x74, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x3a, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x7b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x7d, - 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x64, 0x61, 0x74, 0x61, 0x42, 0x81, 0x01, 0x92, - 0x41, 0x1a, 0x12, 0x15, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, - 0x74, 0x61, 0x32, 0x05, 0x30, 0x2e, 0x31, 0x2e, 0x30, 0x2a, 0x01, 0x01, 0x0a, 0x25, 0x63, 0x6f, - 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x42, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, - 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x74, - 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2f, - 0x76, 0x31, 0x3b, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x58, 0x92, 0x41, 0x29, + 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x12, 0x19, + 0x47, 0x65, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x4b, 0x65, 0x79, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x12, + 0x24, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, + 0x7b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x64, 0x61, 0x74, 0x61, 0x12, 0xe3, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x36, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, + 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5b, + 0x92, 0x41, 0x2a, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x1a, 0x47, 0x65, 0x74, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x4b, 0x65, 0x79, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x73, 0x2f, 0x7b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x7d, 0x2f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x64, 0x61, 0x74, 0x61, 0x42, 0x81, 0x01, 0x92, 0x41, + 0x1a, 0x12, 0x15, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x44, 0x61, 0x74, + 0x61, 0x32, 0x05, 0x30, 0x2e, 0x31, 0x2e, 0x30, 0x2a, 0x01, 0x01, 0x0a, 0x25, 0x63, 0x6f, 0x6d, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x6e, 0x2e, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x42, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x5a, + 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x74, 0x6f, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x2f, 0x76, + 0x31, 0x3b, 0x73, 0x68, 0x69, 0x65, 0x6c, 0x64, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -653,7 +919,7 @@ func file_gotocompany_shield_v1beta1_servicedata_proto_rawDescGZIP() []byte { return file_gotocompany_shield_v1beta1_servicedata_proto_rawDescData } -var file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_gotocompany_shield_v1beta1_servicedata_proto_goTypes = []interface{}{ (*ServiceDataKeyRequestBody)(nil), // 0: gotocompany.shield.v1beta1.ServiceDataKeyRequestBody (*ServiceDataKey)(nil), // 1: gotocompany.shield.v1beta1.ServiceDataKey @@ -664,29 +930,40 @@ var file_gotocompany_shield_v1beta1_servicedata_proto_goTypes = []interface{}{ (*UpsertGroupServiceDataRequest)(nil), // 6: gotocompany.shield.v1beta1.UpsertGroupServiceDataRequest (*UpsertUserServiceDataResponse)(nil), // 7: gotocompany.shield.v1beta1.UpsertUserServiceDataResponse (*UpsertGroupServiceDataResponse)(nil), // 8: gotocompany.shield.v1beta1.UpsertGroupServiceDataResponse - nil, // 9: gotocompany.shield.v1beta1.UpsertServiceDataRequestBody.DataEntry - nil, // 10: gotocompany.shield.v1beta1.UpsertUserServiceDataResponse.DataEntry - nil, // 11: gotocompany.shield.v1beta1.UpsertGroupServiceDataResponse.DataEntry + (*GetUserServiceDataRequest)(nil), // 9: gotocompany.shield.v1beta1.GetUserServiceDataRequest + (*GetGroupServiceDataRequest)(nil), // 10: gotocompany.shield.v1beta1.GetGroupServiceDataRequest + (*GetUserServiceDataResponse)(nil), // 11: gotocompany.shield.v1beta1.GetUserServiceDataResponse + (*GetGroupServiceDataResponse)(nil), // 12: gotocompany.shield.v1beta1.GetGroupServiceDataResponse + nil, // 13: gotocompany.shield.v1beta1.UpsertServiceDataRequestBody.DataEntry + nil, // 14: gotocompany.shield.v1beta1.UpsertUserServiceDataResponse.DataEntry + nil, // 15: gotocompany.shield.v1beta1.UpsertGroupServiceDataResponse.DataEntry + (*structpb.Struct)(nil), // 16: google.protobuf.Struct } var file_gotocompany_shield_v1beta1_servicedata_proto_depIdxs = []int32{ 0, // 0: gotocompany.shield.v1beta1.CreateServiceDataKeyRequest.body:type_name -> gotocompany.shield.v1beta1.ServiceDataKeyRequestBody 1, // 1: gotocompany.shield.v1beta1.CreateServiceDataKeyResponse.service_data_key:type_name -> gotocompany.shield.v1beta1.ServiceDataKey - 9, // 2: gotocompany.shield.v1beta1.UpsertServiceDataRequestBody.data:type_name -> gotocompany.shield.v1beta1.UpsertServiceDataRequestBody.DataEntry + 13, // 2: gotocompany.shield.v1beta1.UpsertServiceDataRequestBody.data:type_name -> gotocompany.shield.v1beta1.UpsertServiceDataRequestBody.DataEntry 4, // 3: gotocompany.shield.v1beta1.UpsertUserServiceDataRequest.body:type_name -> gotocompany.shield.v1beta1.UpsertServiceDataRequestBody 4, // 4: gotocompany.shield.v1beta1.UpsertGroupServiceDataRequest.body:type_name -> gotocompany.shield.v1beta1.UpsertServiceDataRequestBody - 10, // 5: gotocompany.shield.v1beta1.UpsertUserServiceDataResponse.data:type_name -> gotocompany.shield.v1beta1.UpsertUserServiceDataResponse.DataEntry - 11, // 6: gotocompany.shield.v1beta1.UpsertGroupServiceDataResponse.data:type_name -> gotocompany.shield.v1beta1.UpsertGroupServiceDataResponse.DataEntry - 2, // 7: gotocompany.shield.v1beta1.ServiceDataService.CreateServiceDataKey:input_type -> gotocompany.shield.v1beta1.CreateServiceDataKeyRequest - 5, // 8: gotocompany.shield.v1beta1.ServiceDataService.UpsertUserServiceData:input_type -> gotocompany.shield.v1beta1.UpsertUserServiceDataRequest - 6, // 9: gotocompany.shield.v1beta1.ServiceDataService.UpsertGroupServiceData:input_type -> gotocompany.shield.v1beta1.UpsertGroupServiceDataRequest - 3, // 10: gotocompany.shield.v1beta1.ServiceDataService.CreateServiceDataKey:output_type -> gotocompany.shield.v1beta1.CreateServiceDataKeyResponse - 7, // 11: gotocompany.shield.v1beta1.ServiceDataService.UpsertUserServiceData:output_type -> gotocompany.shield.v1beta1.UpsertUserServiceDataResponse - 8, // 12: gotocompany.shield.v1beta1.ServiceDataService.UpsertGroupServiceData:output_type -> gotocompany.shield.v1beta1.UpsertGroupServiceDataResponse - 10, // [10:13] is the sub-list for method output_type - 7, // [7:10] 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 + 14, // 5: gotocompany.shield.v1beta1.UpsertUserServiceDataResponse.data:type_name -> gotocompany.shield.v1beta1.UpsertUserServiceDataResponse.DataEntry + 15, // 6: gotocompany.shield.v1beta1.UpsertGroupServiceDataResponse.data:type_name -> gotocompany.shield.v1beta1.UpsertGroupServiceDataResponse.DataEntry + 16, // 7: gotocompany.shield.v1beta1.GetUserServiceDataResponse.data:type_name -> google.protobuf.Struct + 16, // 8: gotocompany.shield.v1beta1.GetGroupServiceDataResponse.data:type_name -> google.protobuf.Struct + 2, // 9: gotocompany.shield.v1beta1.ServiceDataService.CreateServiceDataKey:input_type -> gotocompany.shield.v1beta1.CreateServiceDataKeyRequest + 5, // 10: gotocompany.shield.v1beta1.ServiceDataService.UpsertUserServiceData:input_type -> gotocompany.shield.v1beta1.UpsertUserServiceDataRequest + 6, // 11: gotocompany.shield.v1beta1.ServiceDataService.UpsertGroupServiceData:input_type -> gotocompany.shield.v1beta1.UpsertGroupServiceDataRequest + 9, // 12: gotocompany.shield.v1beta1.ServiceDataService.GetUserServiceData:input_type -> gotocompany.shield.v1beta1.GetUserServiceDataRequest + 10, // 13: gotocompany.shield.v1beta1.ServiceDataService.GetGroupServiceData:input_type -> gotocompany.shield.v1beta1.GetGroupServiceDataRequest + 3, // 14: gotocompany.shield.v1beta1.ServiceDataService.CreateServiceDataKey:output_type -> gotocompany.shield.v1beta1.CreateServiceDataKeyResponse + 7, // 15: gotocompany.shield.v1beta1.ServiceDataService.UpsertUserServiceData:output_type -> gotocompany.shield.v1beta1.UpsertUserServiceDataResponse + 8, // 16: gotocompany.shield.v1beta1.ServiceDataService.UpsertGroupServiceData:output_type -> gotocompany.shield.v1beta1.UpsertGroupServiceDataResponse + 11, // 17: gotocompany.shield.v1beta1.ServiceDataService.GetUserServiceData:output_type -> gotocompany.shield.v1beta1.GetUserServiceDataResponse + 12, // 18: gotocompany.shield.v1beta1.ServiceDataService.GetGroupServiceData:output_type -> gotocompany.shield.v1beta1.GetGroupServiceDataResponse + 14, // [14:19] is the sub-list for method output_type + 9, // [9:14] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_gotocompany_shield_v1beta1_servicedata_proto_init() } @@ -803,6 +1080,54 @@ func file_gotocompany_shield_v1beta1_servicedata_proto_init() { return nil } } + file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUserServiceDataRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupServiceDataRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUserServiceDataResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gotocompany_shield_v1beta1_servicedata_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetGroupServiceDataResponse); 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{ @@ -810,7 +1135,7 @@ func file_gotocompany_shield_v1beta1_servicedata_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_gotocompany_shield_v1beta1_servicedata_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 16, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/v1beta1/servicedata.pb.gw.go b/proto/v1beta1/servicedata.pb.gw.go index b599b1fb6..0e31d750e 100644 --- a/proto/v1beta1/servicedata.pb.gw.go +++ b/proto/v1beta1/servicedata.pb.gw.go @@ -177,6 +177,146 @@ func local_request_ServiceDataService_UpsertGroupServiceData_0(ctx context.Conte } +var ( + filter_ServiceDataService_GetUserServiceData_0 = &utilities.DoubleArray{Encoding: map[string]int{"user_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_ServiceDataService_GetUserServiceData_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceDataServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetUserServiceDataRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["user_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "user_id") + } + + protoReq.UserId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "user_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_ServiceDataService_GetUserServiceData_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetUserServiceData(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ServiceDataService_GetUserServiceData_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceDataServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetUserServiceDataRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["user_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "user_id") + } + + protoReq.UserId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "user_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_ServiceDataService_GetUserServiceData_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetUserServiceData(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_ServiceDataService_GetGroupServiceData_0 = &utilities.DoubleArray{Encoding: map[string]int{"group_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_ServiceDataService_GetGroupServiceData_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceDataServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetGroupServiceDataRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["group_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "group_id") + } + + protoReq.GroupId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "group_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_ServiceDataService_GetGroupServiceData_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetGroupServiceData(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ServiceDataService_GetGroupServiceData_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceDataServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetGroupServiceDataRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["group_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "group_id") + } + + protoReq.GroupId, err = runtime.String(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "group_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_ServiceDataService_GetGroupServiceData_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetGroupServiceData(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterServiceDataServiceHandlerServer registers the http handlers for service ServiceDataService to "mux". // UnaryRPC :call ServiceDataServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -258,6 +398,56 @@ func RegisterServiceDataServiceHandlerServer(ctx context.Context, mux *runtime.S }) + mux.Handle("GET", pattern_ServiceDataService_GetUserServiceData_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/gotocompany.shield.v1beta1.ServiceDataService/GetUserServiceData", runtime.WithHTTPPathPattern("/v1beta1/users/{user_id}/servicedata")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ServiceDataService_GetUserServiceData_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_ServiceDataService_GetUserServiceData_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ServiceDataService_GetGroupServiceData_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/gotocompany.shield.v1beta1.ServiceDataService/GetGroupServiceData", runtime.WithHTTPPathPattern("/v1beta1/groups/{group_id}/servicedata")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ServiceDataService_GetGroupServiceData_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_ServiceDataService_GetGroupServiceData_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -365,6 +555,50 @@ func RegisterServiceDataServiceHandlerClient(ctx context.Context, mux *runtime.S }) + mux.Handle("GET", pattern_ServiceDataService_GetUserServiceData_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) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/gotocompany.shield.v1beta1.ServiceDataService/GetUserServiceData", runtime.WithHTTPPathPattern("/v1beta1/users/{user_id}/servicedata")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ServiceDataService_GetUserServiceData_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_ServiceDataService_GetUserServiceData_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ServiceDataService_GetGroupServiceData_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) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/gotocompany.shield.v1beta1.ServiceDataService/GetGroupServiceData", runtime.WithHTTPPathPattern("/v1beta1/groups/{group_id}/servicedata")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ServiceDataService_GetGroupServiceData_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_ServiceDataService_GetGroupServiceData_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -374,6 +608,10 @@ var ( pattern_ServiceDataService_UpsertUserServiceData_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "users", "user_id", "servicedata"}, "")) pattern_ServiceDataService_UpsertGroupServiceData_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "groups", "group_id", "servicedata"}, "")) + + pattern_ServiceDataService_GetUserServiceData_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "users", "user_id", "servicedata"}, "")) + + pattern_ServiceDataService_GetGroupServiceData_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "groups", "group_id", "servicedata"}, "")) ) var ( @@ -382,4 +620,8 @@ var ( forward_ServiceDataService_UpsertUserServiceData_0 = runtime.ForwardResponseMessage forward_ServiceDataService_UpsertGroupServiceData_0 = runtime.ForwardResponseMessage + + forward_ServiceDataService_GetUserServiceData_0 = runtime.ForwardResponseMessage + + forward_ServiceDataService_GetGroupServiceData_0 = runtime.ForwardResponseMessage ) diff --git a/proto/v1beta1/servicedata.pb.validate.go b/proto/v1beta1/servicedata.pb.validate.go index f48fed6ff..6ce62a4d8 100644 --- a/proto/v1beta1/servicedata.pb.validate.go +++ b/proto/v1beta1/servicedata.pb.validate.go @@ -1095,3 +1095,499 @@ var _ interface { Cause() error ErrorName() string } = UpsertGroupServiceDataResponseValidationError{} + +// Validate checks the field values on GetUserServiceDataRequest with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *GetUserServiceDataRequest) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on GetUserServiceDataRequest with the +// rules defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// GetUserServiceDataRequestMultiError, or nil if none found. +func (m *GetUserServiceDataRequest) ValidateAll() error { + return m.validate(true) +} + +func (m *GetUserServiceDataRequest) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for UserId + + for idx, item := range m.GetEntity() { + _, _ = idx, item + + if _, ok := _GetUserServiceDataRequest_Entity_InLookup[item]; !ok { + err := GetUserServiceDataRequestValidationError{ + field: fmt.Sprintf("Entity[%v]", idx), + reason: "value must be in list [user group]", + } + if !all { + return err + } + errors = append(errors, err) + } + + } + + // no validation rules for Project + + if len(errors) > 0 { + return GetUserServiceDataRequestMultiError(errors) + } + + return nil +} + +// GetUserServiceDataRequestMultiError is an error wrapping multiple validation +// errors returned by GetUserServiceDataRequest.ValidateAll() if the +// designated constraints aren't met. +type GetUserServiceDataRequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m GetUserServiceDataRequestMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m GetUserServiceDataRequestMultiError) AllErrors() []error { return m } + +// GetUserServiceDataRequestValidationError is the validation error returned by +// GetUserServiceDataRequest.Validate if the designated constraints aren't met. +type GetUserServiceDataRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GetUserServiceDataRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GetUserServiceDataRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GetUserServiceDataRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GetUserServiceDataRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GetUserServiceDataRequestValidationError) ErrorName() string { + return "GetUserServiceDataRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e GetUserServiceDataRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sGetUserServiceDataRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GetUserServiceDataRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GetUserServiceDataRequestValidationError{} + +var _GetUserServiceDataRequest_Entity_InLookup = map[string]struct{}{ + "user": {}, + "group": {}, +} + +// Validate checks the field values on GetGroupServiceDataRequest with the +// rules defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *GetGroupServiceDataRequest) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on GetGroupServiceDataRequest with the +// rules defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// GetGroupServiceDataRequestMultiError, or nil if none found. +func (m *GetGroupServiceDataRequest) ValidateAll() error { + return m.validate(true) +} + +func (m *GetGroupServiceDataRequest) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for GroupId + + // no validation rules for Project + + if len(errors) > 0 { + return GetGroupServiceDataRequestMultiError(errors) + } + + return nil +} + +// GetGroupServiceDataRequestMultiError is an error wrapping multiple +// validation errors returned by GetGroupServiceDataRequest.ValidateAll() if +// the designated constraints aren't met. +type GetGroupServiceDataRequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m GetGroupServiceDataRequestMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m GetGroupServiceDataRequestMultiError) AllErrors() []error { return m } + +// GetGroupServiceDataRequestValidationError is the validation error returned +// by GetGroupServiceDataRequest.Validate if the designated constraints aren't met. +type GetGroupServiceDataRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GetGroupServiceDataRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GetGroupServiceDataRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GetGroupServiceDataRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GetGroupServiceDataRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GetGroupServiceDataRequestValidationError) ErrorName() string { + return "GetGroupServiceDataRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e GetGroupServiceDataRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sGetGroupServiceDataRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GetGroupServiceDataRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GetGroupServiceDataRequestValidationError{} + +// Validate checks the field values on GetUserServiceDataResponse with the +// rules defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *GetUserServiceDataResponse) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on GetUserServiceDataResponse with the +// rules defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// GetUserServiceDataResponseMultiError, or nil if none found. +func (m *GetUserServiceDataResponse) ValidateAll() error { + return m.validate(true) +} + +func (m *GetUserServiceDataResponse) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if all { + switch v := interface{}(m.GetData()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, GetUserServiceDataResponseValidationError{ + field: "Data", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, GetUserServiceDataResponseValidationError{ + field: "Data", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetData()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return GetUserServiceDataResponseValidationError{ + field: "Data", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return GetUserServiceDataResponseMultiError(errors) + } + + return nil +} + +// GetUserServiceDataResponseMultiError is an error wrapping multiple +// validation errors returned by GetUserServiceDataResponse.ValidateAll() if +// the designated constraints aren't met. +type GetUserServiceDataResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m GetUserServiceDataResponseMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m GetUserServiceDataResponseMultiError) AllErrors() []error { return m } + +// GetUserServiceDataResponseValidationError is the validation error returned +// by GetUserServiceDataResponse.Validate if the designated constraints aren't met. +type GetUserServiceDataResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GetUserServiceDataResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GetUserServiceDataResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GetUserServiceDataResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GetUserServiceDataResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GetUserServiceDataResponseValidationError) ErrorName() string { + return "GetUserServiceDataResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e GetUserServiceDataResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sGetUserServiceDataResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GetUserServiceDataResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GetUserServiceDataResponseValidationError{} + +// Validate checks the field values on GetGroupServiceDataResponse with the +// rules defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *GetGroupServiceDataResponse) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on GetGroupServiceDataResponse with the +// rules defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// GetGroupServiceDataResponseMultiError, or nil if none found. +func (m *GetGroupServiceDataResponse) ValidateAll() error { + return m.validate(true) +} + +func (m *GetGroupServiceDataResponse) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if all { + switch v := interface{}(m.GetData()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, GetGroupServiceDataResponseValidationError{ + field: "Data", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, GetGroupServiceDataResponseValidationError{ + field: "Data", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetData()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return GetGroupServiceDataResponseValidationError{ + field: "Data", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return GetGroupServiceDataResponseMultiError(errors) + } + + return nil +} + +// GetGroupServiceDataResponseMultiError is an error wrapping multiple +// validation errors returned by GetGroupServiceDataResponse.ValidateAll() if +// the designated constraints aren't met. +type GetGroupServiceDataResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m GetGroupServiceDataResponseMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m GetGroupServiceDataResponseMultiError) AllErrors() []error { return m } + +// GetGroupServiceDataResponseValidationError is the validation error returned +// by GetGroupServiceDataResponse.Validate if the designated constraints +// aren't met. +type GetGroupServiceDataResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GetGroupServiceDataResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GetGroupServiceDataResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GetGroupServiceDataResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GetGroupServiceDataResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GetGroupServiceDataResponseValidationError) ErrorName() string { + return "GetGroupServiceDataResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e GetGroupServiceDataResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sGetGroupServiceDataResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GetGroupServiceDataResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GetGroupServiceDataResponseValidationError{} diff --git a/proto/v1beta1/servicedata_grpc.pb.go b/proto/v1beta1/servicedata_grpc.pb.go index 1085fc6b7..0af9d593c 100644 --- a/proto/v1beta1/servicedata_grpc.pb.go +++ b/proto/v1beta1/servicedata_grpc.pb.go @@ -22,6 +22,8 @@ const ( ServiceDataService_CreateServiceDataKey_FullMethodName = "/gotocompany.shield.v1beta1.ServiceDataService/CreateServiceDataKey" ServiceDataService_UpsertUserServiceData_FullMethodName = "/gotocompany.shield.v1beta1.ServiceDataService/UpsertUserServiceData" ServiceDataService_UpsertGroupServiceData_FullMethodName = "/gotocompany.shield.v1beta1.ServiceDataService/UpsertGroupServiceData" + ServiceDataService_GetUserServiceData_FullMethodName = "/gotocompany.shield.v1beta1.ServiceDataService/GetUserServiceData" + ServiceDataService_GetGroupServiceData_FullMethodName = "/gotocompany.shield.v1beta1.ServiceDataService/GetGroupServiceData" ) // ServiceDataServiceClient is the client API for ServiceDataService service. @@ -32,6 +34,8 @@ type ServiceDataServiceClient interface { CreateServiceDataKey(ctx context.Context, in *CreateServiceDataKeyRequest, opts ...grpc.CallOption) (*CreateServiceDataKeyResponse, error) UpsertUserServiceData(ctx context.Context, in *UpsertUserServiceDataRequest, opts ...grpc.CallOption) (*UpsertUserServiceDataResponse, error) UpsertGroupServiceData(ctx context.Context, in *UpsertGroupServiceDataRequest, opts ...grpc.CallOption) (*UpsertGroupServiceDataResponse, error) + GetUserServiceData(ctx context.Context, in *GetUserServiceDataRequest, opts ...grpc.CallOption) (*GetUserServiceDataResponse, error) + GetGroupServiceData(ctx context.Context, in *GetGroupServiceDataRequest, opts ...grpc.CallOption) (*GetGroupServiceDataResponse, error) } type serviceDataServiceClient struct { @@ -69,6 +73,24 @@ func (c *serviceDataServiceClient) UpsertGroupServiceData(ctx context.Context, i return out, nil } +func (c *serviceDataServiceClient) GetUserServiceData(ctx context.Context, in *GetUserServiceDataRequest, opts ...grpc.CallOption) (*GetUserServiceDataResponse, error) { + out := new(GetUserServiceDataResponse) + err := c.cc.Invoke(ctx, ServiceDataService_GetUserServiceData_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *serviceDataServiceClient) GetGroupServiceData(ctx context.Context, in *GetGroupServiceDataRequest, opts ...grpc.CallOption) (*GetGroupServiceDataResponse, error) { + out := new(GetGroupServiceDataResponse) + err := c.cc.Invoke(ctx, ServiceDataService_GetGroupServiceData_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ServiceDataServiceServer is the server API for ServiceDataService service. // All implementations must embed UnimplementedServiceDataServiceServer // for forward compatibility @@ -77,6 +99,8 @@ type ServiceDataServiceServer interface { CreateServiceDataKey(context.Context, *CreateServiceDataKeyRequest) (*CreateServiceDataKeyResponse, error) UpsertUserServiceData(context.Context, *UpsertUserServiceDataRequest) (*UpsertUserServiceDataResponse, error) UpsertGroupServiceData(context.Context, *UpsertGroupServiceDataRequest) (*UpsertGroupServiceDataResponse, error) + GetUserServiceData(context.Context, *GetUserServiceDataRequest) (*GetUserServiceDataResponse, error) + GetGroupServiceData(context.Context, *GetGroupServiceDataRequest) (*GetGroupServiceDataResponse, error) mustEmbedUnimplementedServiceDataServiceServer() } @@ -93,6 +117,12 @@ func (UnimplementedServiceDataServiceServer) UpsertUserServiceData(context.Conte func (UnimplementedServiceDataServiceServer) UpsertGroupServiceData(context.Context, *UpsertGroupServiceDataRequest) (*UpsertGroupServiceDataResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpsertGroupServiceData not implemented") } +func (UnimplementedServiceDataServiceServer) GetUserServiceData(context.Context, *GetUserServiceDataRequest) (*GetUserServiceDataResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUserServiceData not implemented") +} +func (UnimplementedServiceDataServiceServer) GetGroupServiceData(context.Context, *GetGroupServiceDataRequest) (*GetGroupServiceDataResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetGroupServiceData not implemented") +} func (UnimplementedServiceDataServiceServer) mustEmbedUnimplementedServiceDataServiceServer() {} // UnsafeServiceDataServiceServer may be embedded to opt out of forward compatibility for this service. @@ -160,6 +190,42 @@ func _ServiceDataService_UpsertGroupServiceData_Handler(srv interface{}, ctx con return interceptor(ctx, in, info, handler) } +func _ServiceDataService_GetUserServiceData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUserServiceDataRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceDataServiceServer).GetUserServiceData(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ServiceDataService_GetUserServiceData_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceDataServiceServer).GetUserServiceData(ctx, req.(*GetUserServiceDataRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ServiceDataService_GetGroupServiceData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetGroupServiceDataRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceDataServiceServer).GetGroupServiceData(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ServiceDataService_GetGroupServiceData_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceDataServiceServer).GetGroupServiceData(ctx, req.(*GetGroupServiceDataRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ServiceDataService_ServiceDesc is the grpc.ServiceDesc for ServiceDataService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -179,6 +245,14 @@ var ServiceDataService_ServiceDesc = grpc.ServiceDesc{ MethodName: "UpsertGroupServiceData", Handler: _ServiceDataService_UpsertGroupServiceData_Handler, }, + { + MethodName: "GetUserServiceData", + Handler: _ServiceDataService_GetUserServiceData_Handler, + }, + { + MethodName: "GetGroupServiceData", + Handler: _ServiceDataService_GetGroupServiceData_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "gotocompany/shield/v1beta1/servicedata.proto", diff --git a/test/e2e_test/testbench/testdata/configs/rules/rule.yaml b/test/e2e_test/testbench/testdata/configs/rules/rule.yaml index 5712ffd39..68141f421 100644 --- a/test/e2e_test/testbench/testdata/configs/rules/rule.yaml +++ b/test/e2e_test/testbench/testdata/configs/rules/rule.yaml @@ -1,7 +1,7 @@ rules: - backends: - name: entropy - target: "http://localhost:52660" + target: "http://localhost:52118" frontends: - name: ping path: "/api/ping"