From 3954efcc8b2cd02c7ee2bb97e3599e991af23d1a Mon Sep 17 00:00:00 2001 From: Gerrit Date: Fri, 10 May 2024 14:36:58 +0200 Subject: [PATCH] Implement membership queries for tenant service (#97) --- api/v1/mocks/TenantServiceClient.go | 111 +++ api/v1/mocks/TenantServiceServer.go | 90 ++ api/v1/tenant.pb.go | 1025 ++++++++++++++++++---- api/v1/tenant_grpc.pb.go | 123 ++- go.mod | 49 +- go.sum | 117 +-- pkg/datastore/postgres_benchmark_test.go | 2 +- pkg/datastore/postgres_test.go | 22 +- pkg/datastore/query_runner.go | 47 + pkg/service/common_test.go | 53 ++ pkg/service/tenant.go | 261 ++++++ pkg/service/tenant_test.go | 648 +++++++++++++- proto/v1/tenant.proto | 43 + 13 files changed, 2318 insertions(+), 273 deletions(-) create mode 100644 pkg/datastore/query_runner.go create mode 100644 pkg/service/common_test.go diff --git a/api/v1/mocks/TenantServiceClient.go b/api/v1/mocks/TenantServiceClient.go index 32a55a5..3889d47 100644 --- a/api/v1/mocks/TenantServiceClient.go +++ b/api/v1/mocks/TenantServiceClient.go @@ -128,6 +128,80 @@ func (_m *TenantServiceClient) Find(ctx context.Context, in *v1.TenantFindReques return r0, r1 } +// FindParticipatingProjects provides a mock function with given fields: ctx, in, opts +func (_m *TenantServiceClient) FindParticipatingProjects(ctx context.Context, in *v1.FindParticipatingProjectsRequest, opts ...grpc.CallOption) (*v1.FindParticipatingProjectsResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for FindParticipatingProjects") + } + + var r0 *v1.FindParticipatingProjectsResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *v1.FindParticipatingProjectsRequest, ...grpc.CallOption) (*v1.FindParticipatingProjectsResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *v1.FindParticipatingProjectsRequest, ...grpc.CallOption) *v1.FindParticipatingProjectsResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1.FindParticipatingProjectsResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *v1.FindParticipatingProjectsRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindParticipatingTenants provides a mock function with given fields: ctx, in, opts +func (_m *TenantServiceClient) FindParticipatingTenants(ctx context.Context, in *v1.FindParticipatingTenantsRequest, opts ...grpc.CallOption) (*v1.FindParticipatingTenantsResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for FindParticipatingTenants") + } + + var r0 *v1.FindParticipatingTenantsResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *v1.FindParticipatingTenantsRequest, ...grpc.CallOption) (*v1.FindParticipatingTenantsResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *v1.FindParticipatingTenantsRequest, ...grpc.CallOption) *v1.FindParticipatingTenantsResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1.FindParticipatingTenantsResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *v1.FindParticipatingTenantsRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Get provides a mock function with given fields: ctx, in, opts func (_m *TenantServiceClient) Get(ctx context.Context, in *v1.TenantGetRequest, opts ...grpc.CallOption) (*v1.TenantResponse, error) { _va := make([]interface{}, len(opts)) @@ -202,6 +276,43 @@ func (_m *TenantServiceClient) GetHistory(ctx context.Context, in *v1.TenantGetH return r0, r1 } +// ListTenantMembers provides a mock function with given fields: ctx, in, opts +func (_m *TenantServiceClient) ListTenantMembers(ctx context.Context, in *v1.ListTenantMembersRequest, opts ...grpc.CallOption) (*v1.ListTenantMembersResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for ListTenantMembers") + } + + var r0 *v1.ListTenantMembersResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *v1.ListTenantMembersRequest, ...grpc.CallOption) (*v1.ListTenantMembersResponse, error)); ok { + return rf(ctx, in, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, *v1.ListTenantMembersRequest, ...grpc.CallOption) *v1.ListTenantMembersResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1.ListTenantMembersResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *v1.ListTenantMembersRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Update provides a mock function with given fields: ctx, in, opts func (_m *TenantServiceClient) Update(ctx context.Context, in *v1.TenantUpdateRequest, opts ...grpc.CallOption) (*v1.TenantResponse, error) { _va := make([]interface{}, len(opts)) diff --git a/api/v1/mocks/TenantServiceServer.go b/api/v1/mocks/TenantServiceServer.go index 4b93da4..d4fe1c9 100644 --- a/api/v1/mocks/TenantServiceServer.go +++ b/api/v1/mocks/TenantServiceServer.go @@ -104,6 +104,66 @@ func (_m *TenantServiceServer) Find(_a0 context.Context, _a1 *v1.TenantFindReque return r0, r1 } +// FindParticipatingProjects provides a mock function with given fields: _a0, _a1 +func (_m *TenantServiceServer) FindParticipatingProjects(_a0 context.Context, _a1 *v1.FindParticipatingProjectsRequest) (*v1.FindParticipatingProjectsResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for FindParticipatingProjects") + } + + var r0 *v1.FindParticipatingProjectsResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *v1.FindParticipatingProjectsRequest) (*v1.FindParticipatingProjectsResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *v1.FindParticipatingProjectsRequest) *v1.FindParticipatingProjectsResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1.FindParticipatingProjectsResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *v1.FindParticipatingProjectsRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindParticipatingTenants provides a mock function with given fields: _a0, _a1 +func (_m *TenantServiceServer) FindParticipatingTenants(_a0 context.Context, _a1 *v1.FindParticipatingTenantsRequest) (*v1.FindParticipatingTenantsResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for FindParticipatingTenants") + } + + var r0 *v1.FindParticipatingTenantsResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *v1.FindParticipatingTenantsRequest) (*v1.FindParticipatingTenantsResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *v1.FindParticipatingTenantsRequest) *v1.FindParticipatingTenantsResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1.FindParticipatingTenantsResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *v1.FindParticipatingTenantsRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Get provides a mock function with given fields: _a0, _a1 func (_m *TenantServiceServer) Get(_a0 context.Context, _a1 *v1.TenantGetRequest) (*v1.TenantResponse, error) { ret := _m.Called(_a0, _a1) @@ -164,6 +224,36 @@ func (_m *TenantServiceServer) GetHistory(_a0 context.Context, _a1 *v1.TenantGet return r0, r1 } +// ListTenantMembers provides a mock function with given fields: _a0, _a1 +func (_m *TenantServiceServer) ListTenantMembers(_a0 context.Context, _a1 *v1.ListTenantMembersRequest) (*v1.ListTenantMembersResponse, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for ListTenantMembers") + } + + var r0 *v1.ListTenantMembersResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *v1.ListTenantMembersRequest) (*v1.ListTenantMembersResponse, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *v1.ListTenantMembersRequest) *v1.ListTenantMembersResponse); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1.ListTenantMembersResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *v1.ListTenantMembersRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Update provides a mock function with given fields: _a0, _a1 func (_m *TenantServiceServer) Update(_a0 context.Context, _a1 *v1.TenantUpdateRequest) (*v1.TenantResponse, error) { ret := _m.Called(_a0, _a1) diff --git a/api/v1/tenant.pb.go b/api/v1/tenant.pb.go index e0ba68c..e5e8d5c 100644 --- a/api/v1/tenant.pb.go +++ b/api/v1/tenant.pb.go @@ -22,6 +22,438 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type FindParticipatingProjectsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"` + IncludeInherited *bool `protobuf:"varint,2,opt,name=include_inherited,json=includeInherited,proto3,oneof" json:"include_inherited,omitempty"` +} + +func (x *FindParticipatingProjectsRequest) Reset() { + *x = FindParticipatingProjectsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_tenant_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FindParticipatingProjectsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FindParticipatingProjectsRequest) ProtoMessage() {} + +func (x *FindParticipatingProjectsRequest) ProtoReflect() protoreflect.Message { + mi := &file_v1_tenant_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FindParticipatingProjectsRequest.ProtoReflect.Descriptor instead. +func (*FindParticipatingProjectsRequest) Descriptor() ([]byte, []int) { + return file_v1_tenant_proto_rawDescGZIP(), []int{0} +} + +func (x *FindParticipatingProjectsRequest) GetTenantId() string { + if x != nil { + return x.TenantId + } + return "" +} + +func (x *FindParticipatingProjectsRequest) GetIncludeInherited() bool { + if x != nil && x.IncludeInherited != nil { + return *x.IncludeInherited + } + return false +} + +type FindParticipatingTenantsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"` + IncludeInherited *bool `protobuf:"varint,2,opt,name=include_inherited,json=includeInherited,proto3,oneof" json:"include_inherited,omitempty"` +} + +func (x *FindParticipatingTenantsRequest) Reset() { + *x = FindParticipatingTenantsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_tenant_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FindParticipatingTenantsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FindParticipatingTenantsRequest) ProtoMessage() {} + +func (x *FindParticipatingTenantsRequest) ProtoReflect() protoreflect.Message { + mi := &file_v1_tenant_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FindParticipatingTenantsRequest.ProtoReflect.Descriptor instead. +func (*FindParticipatingTenantsRequest) Descriptor() ([]byte, []int) { + return file_v1_tenant_proto_rawDescGZIP(), []int{1} +} + +func (x *FindParticipatingTenantsRequest) GetTenantId() string { + if x != nil { + return x.TenantId + } + return "" +} + +func (x *FindParticipatingTenantsRequest) GetIncludeInherited() bool { + if x != nil && x.IncludeInherited != nil { + return *x.IncludeInherited + } + return false +} + +type ListTenantMembersRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"` + IncludeInherited *bool `protobuf:"varint,2,opt,name=include_inherited,json=includeInherited,proto3,oneof" json:"include_inherited,omitempty"` +} + +func (x *ListTenantMembersRequest) Reset() { + *x = ListTenantMembersRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_tenant_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListTenantMembersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListTenantMembersRequest) ProtoMessage() {} + +func (x *ListTenantMembersRequest) ProtoReflect() protoreflect.Message { + mi := &file_v1_tenant_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListTenantMembersRequest.ProtoReflect.Descriptor instead. +func (*ListTenantMembersRequest) Descriptor() ([]byte, []int) { + return file_v1_tenant_proto_rawDescGZIP(), []int{2} +} + +func (x *ListTenantMembersRequest) GetTenantId() string { + if x != nil { + return x.TenantId + } + return "" +} + +func (x *ListTenantMembersRequest) GetIncludeInherited() bool { + if x != nil && x.IncludeInherited != nil { + return *x.IncludeInherited + } + return false +} + +type ListTenantMembersResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Tenants []*TenantWithMembershipAnnotations `protobuf:"bytes,1,rep,name=tenants,proto3" json:"tenants,omitempty"` +} + +func (x *ListTenantMembersResponse) Reset() { + *x = ListTenantMembersResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_tenant_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListTenantMembersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListTenantMembersResponse) ProtoMessage() {} + +func (x *ListTenantMembersResponse) ProtoReflect() protoreflect.Message { + mi := &file_v1_tenant_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListTenantMembersResponse.ProtoReflect.Descriptor instead. +func (*ListTenantMembersResponse) Descriptor() ([]byte, []int) { + return file_v1_tenant_proto_rawDescGZIP(), []int{3} +} + +func (x *ListTenantMembersResponse) GetTenants() []*TenantWithMembershipAnnotations { + if x != nil { + return x.Tenants + } + return nil +} + +type FindParticipatingProjectsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Projects []*ProjectWithMembershipAnnotations `protobuf:"bytes,1,rep,name=projects,proto3" json:"projects,omitempty"` +} + +func (x *FindParticipatingProjectsResponse) Reset() { + *x = FindParticipatingProjectsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_tenant_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FindParticipatingProjectsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FindParticipatingProjectsResponse) ProtoMessage() {} + +func (x *FindParticipatingProjectsResponse) ProtoReflect() protoreflect.Message { + mi := &file_v1_tenant_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FindParticipatingProjectsResponse.ProtoReflect.Descriptor instead. +func (*FindParticipatingProjectsResponse) Descriptor() ([]byte, []int) { + return file_v1_tenant_proto_rawDescGZIP(), []int{4} +} + +func (x *FindParticipatingProjectsResponse) GetProjects() []*ProjectWithMembershipAnnotations { + if x != nil { + return x.Projects + } + return nil +} + +type FindParticipatingTenantsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Tenants []*TenantWithMembershipAnnotations `protobuf:"bytes,1,rep,name=tenants,proto3" json:"tenants,omitempty"` +} + +func (x *FindParticipatingTenantsResponse) Reset() { + *x = FindParticipatingTenantsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_tenant_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FindParticipatingTenantsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FindParticipatingTenantsResponse) ProtoMessage() {} + +func (x *FindParticipatingTenantsResponse) ProtoReflect() protoreflect.Message { + mi := &file_v1_tenant_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FindParticipatingTenantsResponse.ProtoReflect.Descriptor instead. +func (*FindParticipatingTenantsResponse) Descriptor() ([]byte, []int) { + return file_v1_tenant_proto_rawDescGZIP(), []int{5} +} + +func (x *FindParticipatingTenantsResponse) GetTenants() []*TenantWithMembershipAnnotations { + if x != nil { + return x.Tenants + } + return nil +} + +type ProjectWithMembershipAnnotations struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Project *Project `protobuf:"bytes,1,opt,name=project,proto3" json:"project,omitempty"` + ProjectAnnotations map[string]string `protobuf:"bytes,2,rep,name=project_annotations,json=projectAnnotations,proto3" json:"project_annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + TenantAnnotations map[string]string `protobuf:"bytes,3,rep,name=tenant_annotations,json=tenantAnnotations,proto3" json:"tenant_annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ProjectWithMembershipAnnotations) Reset() { + *x = ProjectWithMembershipAnnotations{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_tenant_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectWithMembershipAnnotations) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectWithMembershipAnnotations) ProtoMessage() {} + +func (x *ProjectWithMembershipAnnotations) ProtoReflect() protoreflect.Message { + mi := &file_v1_tenant_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProjectWithMembershipAnnotations.ProtoReflect.Descriptor instead. +func (*ProjectWithMembershipAnnotations) Descriptor() ([]byte, []int) { + return file_v1_tenant_proto_rawDescGZIP(), []int{6} +} + +func (x *ProjectWithMembershipAnnotations) GetProject() *Project { + if x != nil { + return x.Project + } + return nil +} + +func (x *ProjectWithMembershipAnnotations) GetProjectAnnotations() map[string]string { + if x != nil { + return x.ProjectAnnotations + } + return nil +} + +func (x *ProjectWithMembershipAnnotations) GetTenantAnnotations() map[string]string { + if x != nil { + return x.TenantAnnotations + } + return nil +} + +type TenantWithMembershipAnnotations struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Tenant *Tenant `protobuf:"bytes,1,opt,name=tenant,proto3" json:"tenant,omitempty"` + ProjectAnnotations map[string]string `protobuf:"bytes,2,rep,name=project_annotations,json=projectAnnotations,proto3" json:"project_annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + TenantAnnotations map[string]string `protobuf:"bytes,3,rep,name=tenant_annotations,json=tenantAnnotations,proto3" json:"tenant_annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *TenantWithMembershipAnnotations) Reset() { + *x = TenantWithMembershipAnnotations{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_tenant_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TenantWithMembershipAnnotations) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TenantWithMembershipAnnotations) ProtoMessage() {} + +func (x *TenantWithMembershipAnnotations) ProtoReflect() protoreflect.Message { + mi := &file_v1_tenant_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TenantWithMembershipAnnotations.ProtoReflect.Descriptor instead. +func (*TenantWithMembershipAnnotations) Descriptor() ([]byte, []int) { + return file_v1_tenant_proto_rawDescGZIP(), []int{7} +} + +func (x *TenantWithMembershipAnnotations) GetTenant() *Tenant { + if x != nil { + return x.Tenant + } + return nil +} + +func (x *TenantWithMembershipAnnotations) GetProjectAnnotations() map[string]string { + if x != nil { + return x.ProjectAnnotations + } + return nil +} + +func (x *TenantWithMembershipAnnotations) GetTenantAnnotations() map[string]string { + if x != nil { + return x.TenantAnnotations + } + return nil +} + type Tenant struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -38,7 +470,7 @@ type Tenant struct { func (x *Tenant) Reset() { *x = Tenant{} if protoimpl.UnsafeEnabled { - mi := &file_v1_tenant_proto_msgTypes[0] + mi := &file_v1_tenant_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -51,7 +483,7 @@ func (x *Tenant) String() string { func (*Tenant) ProtoMessage() {} func (x *Tenant) ProtoReflect() protoreflect.Message { - mi := &file_v1_tenant_proto_msgTypes[0] + mi := &file_v1_tenant_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -64,7 +496,7 @@ func (x *Tenant) ProtoReflect() protoreflect.Message { // Deprecated: Use Tenant.ProtoReflect.Descriptor instead. func (*Tenant) Descriptor() ([]byte, []int) { - return file_v1_tenant_proto_rawDescGZIP(), []int{0} + return file_v1_tenant_proto_rawDescGZIP(), []int{8} } func (x *Tenant) GetMeta() *Meta { @@ -120,7 +552,7 @@ type TenantCreateRequest struct { func (x *TenantCreateRequest) Reset() { *x = TenantCreateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_v1_tenant_proto_msgTypes[1] + mi := &file_v1_tenant_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -133,7 +565,7 @@ func (x *TenantCreateRequest) String() string { func (*TenantCreateRequest) ProtoMessage() {} func (x *TenantCreateRequest) ProtoReflect() protoreflect.Message { - mi := &file_v1_tenant_proto_msgTypes[1] + mi := &file_v1_tenant_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -146,7 +578,7 @@ func (x *TenantCreateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TenantCreateRequest.ProtoReflect.Descriptor instead. func (*TenantCreateRequest) Descriptor() ([]byte, []int) { - return file_v1_tenant_proto_rawDescGZIP(), []int{1} + return file_v1_tenant_proto_rawDescGZIP(), []int{9} } func (x *TenantCreateRequest) GetTenant() *Tenant { @@ -167,7 +599,7 @@ type TenantUpdateRequest struct { func (x *TenantUpdateRequest) Reset() { *x = TenantUpdateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_v1_tenant_proto_msgTypes[2] + mi := &file_v1_tenant_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -180,7 +612,7 @@ func (x *TenantUpdateRequest) String() string { func (*TenantUpdateRequest) ProtoMessage() {} func (x *TenantUpdateRequest) ProtoReflect() protoreflect.Message { - mi := &file_v1_tenant_proto_msgTypes[2] + mi := &file_v1_tenant_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -193,7 +625,7 @@ func (x *TenantUpdateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TenantUpdateRequest.ProtoReflect.Descriptor instead. func (*TenantUpdateRequest) Descriptor() ([]byte, []int) { - return file_v1_tenant_proto_rawDescGZIP(), []int{2} + return file_v1_tenant_proto_rawDescGZIP(), []int{10} } func (x *TenantUpdateRequest) GetTenant() *Tenant { @@ -214,7 +646,7 @@ type TenantDeleteRequest struct { func (x *TenantDeleteRequest) Reset() { *x = TenantDeleteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_v1_tenant_proto_msgTypes[3] + mi := &file_v1_tenant_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -227,7 +659,7 @@ func (x *TenantDeleteRequest) String() string { func (*TenantDeleteRequest) ProtoMessage() {} func (x *TenantDeleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_v1_tenant_proto_msgTypes[3] + mi := &file_v1_tenant_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -240,7 +672,7 @@ func (x *TenantDeleteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TenantDeleteRequest.ProtoReflect.Descriptor instead. func (*TenantDeleteRequest) Descriptor() ([]byte, []int) { - return file_v1_tenant_proto_rawDescGZIP(), []int{3} + return file_v1_tenant_proto_rawDescGZIP(), []int{11} } func (x *TenantDeleteRequest) GetId() string { @@ -261,7 +693,7 @@ type TenantGetRequest struct { func (x *TenantGetRequest) Reset() { *x = TenantGetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_v1_tenant_proto_msgTypes[4] + mi := &file_v1_tenant_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -274,7 +706,7 @@ func (x *TenantGetRequest) String() string { func (*TenantGetRequest) ProtoMessage() {} func (x *TenantGetRequest) ProtoReflect() protoreflect.Message { - mi := &file_v1_tenant_proto_msgTypes[4] + mi := &file_v1_tenant_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -287,7 +719,7 @@ func (x *TenantGetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TenantGetRequest.ProtoReflect.Descriptor instead. func (*TenantGetRequest) Descriptor() ([]byte, []int) { - return file_v1_tenant_proto_rawDescGZIP(), []int{4} + return file_v1_tenant_proto_rawDescGZIP(), []int{12} } func (x *TenantGetRequest) GetId() string { @@ -309,7 +741,7 @@ type TenantGetHistoryRequest struct { func (x *TenantGetHistoryRequest) Reset() { *x = TenantGetHistoryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_v1_tenant_proto_msgTypes[5] + mi := &file_v1_tenant_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -322,7 +754,7 @@ func (x *TenantGetHistoryRequest) String() string { func (*TenantGetHistoryRequest) ProtoMessage() {} func (x *TenantGetHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_v1_tenant_proto_msgTypes[5] + mi := &file_v1_tenant_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -335,7 +767,7 @@ func (x *TenantGetHistoryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TenantGetHistoryRequest.ProtoReflect.Descriptor instead. func (*TenantGetHistoryRequest) Descriptor() ([]byte, []int) { - return file_v1_tenant_proto_rawDescGZIP(), []int{5} + return file_v1_tenant_proto_rawDescGZIP(), []int{13} } func (x *TenantGetHistoryRequest) GetId() string { @@ -366,7 +798,7 @@ type TenantFindRequest struct { func (x *TenantFindRequest) Reset() { *x = TenantFindRequest{} if protoimpl.UnsafeEnabled { - mi := &file_v1_tenant_proto_msgTypes[6] + mi := &file_v1_tenant_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -379,7 +811,7 @@ func (x *TenantFindRequest) String() string { func (*TenantFindRequest) ProtoMessage() {} func (x *TenantFindRequest) ProtoReflect() protoreflect.Message { - mi := &file_v1_tenant_proto_msgTypes[6] + mi := &file_v1_tenant_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -392,7 +824,7 @@ func (x *TenantFindRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TenantFindRequest.ProtoReflect.Descriptor instead. func (*TenantFindRequest) Descriptor() ([]byte, []int) { - return file_v1_tenant_proto_rawDescGZIP(), []int{6} + return file_v1_tenant_proto_rawDescGZIP(), []int{14} } func (x *TenantFindRequest) GetId() *wrapperspb.StringValue { @@ -434,7 +866,7 @@ type TenantResponse struct { func (x *TenantResponse) Reset() { *x = TenantResponse{} if protoimpl.UnsafeEnabled { - mi := &file_v1_tenant_proto_msgTypes[7] + mi := &file_v1_tenant_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -447,7 +879,7 @@ func (x *TenantResponse) String() string { func (*TenantResponse) ProtoMessage() {} func (x *TenantResponse) ProtoReflect() protoreflect.Message { - mi := &file_v1_tenant_proto_msgTypes[7] + mi := &file_v1_tenant_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -460,7 +892,7 @@ func (x *TenantResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TenantResponse.ProtoReflect.Descriptor instead. func (*TenantResponse) Descriptor() ([]byte, []int) { - return file_v1_tenant_proto_rawDescGZIP(), []int{7} + return file_v1_tenant_proto_rawDescGZIP(), []int{15} } func (x *TenantResponse) GetTenant() *Tenant { @@ -483,7 +915,7 @@ type TenantListResponse struct { func (x *TenantListResponse) Reset() { *x = TenantListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_v1_tenant_proto_msgTypes[8] + mi := &file_v1_tenant_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -496,7 +928,7 @@ func (x *TenantListResponse) String() string { func (*TenantListResponse) ProtoMessage() {} func (x *TenantListResponse) ProtoReflect() protoreflect.Message { - mi := &file_v1_tenant_proto_msgTypes[8] + mi := &file_v1_tenant_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -509,7 +941,7 @@ func (x *TenantListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TenantListResponse.ProtoReflect.Descriptor instead. func (*TenantListResponse) Descriptor() ([]byte, []int) { - return file_v1_tenant_proto_rawDescGZIP(), []int{8} + return file_v1_tenant_proto_rawDescGZIP(), []int{16} } func (x *TenantListResponse) GetTenants() []*Tenant { @@ -537,97 +969,214 @@ var file_v1_tenant_proto_rawDesc = []byte{ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0c, 0x76, 0x31, 0x2f, 0x69, 0x61, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0d, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x76, 0x31, 0x2f, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe5, 0x01, 0x0a, 0x06, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, - 0x1c, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, - 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 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, 0x12, 0x33, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x71, - 0x75, 0x6f, 0x74, 0x61, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x31, - 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x53, 0x65, 0x74, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x12, 0x24, 0x0a, 0x06, 0x71, 0x75, 0x6f, 0x74, - 0x61, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, - 0x6f, 0x74, 0x61, 0x53, 0x65, 0x74, 0x52, 0x06, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x12, 0x2c, - 0x0a, 0x0a, 0x69, 0x61, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x41, 0x4d, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x09, 0x69, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x39, 0x0a, 0x13, - 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, - 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x22, 0x39, 0x0a, 0x13, 0x54, 0x65, 0x6e, 0x61, 0x6e, - 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, - 0x0a, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, - 0x6e, 0x74, 0x22, 0x25, 0x0a, 0x13, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x22, 0x0a, 0x10, 0x54, 0x65, 0x6e, - 0x61, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x55, 0x0a, - 0x17, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x02, 0x61, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x02, 0x61, 0x74, 0x22, 0xa1, 0x02, 0x0a, 0x11, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x46, - 0x69, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x02, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x70, 0x61, - 0x67, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x48, - 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x46, - 0x69, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0x34, 0x0a, 0x0e, 0x54, 0x65, 0x6e, 0x61, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, - 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x22, 0x6a, - 0x0a, 0x12, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x07, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, - 0x74, 0x52, 0x07, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x09, 0x6e, 0x65, - 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, - 0x08, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, - 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x32, 0xdb, 0x02, 0x0a, 0x0d, 0x54, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x35, 0x0a, 0x06, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, - 0x6e, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x10, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x76, 0x31, 0x2f, 0x71, 0x75, 0x6f, 0x74, 0x61, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x87, 0x01, 0x0a, 0x20, 0x46, 0x69, 0x6e, 0x64, 0x50, + 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, + 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, + 0x22, 0x86, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, + 0x70, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, + 0x64, 0x12, 0x30, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x68, + 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x10, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, + 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, + 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, 0x22, 0x7f, 0x0a, 0x18, 0x4c, 0x69, 0x73, + 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, + 0x49, 0x64, 0x12, 0x30, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x69, 0x6e, + 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, + 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, + 0x64, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x5f, 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, 0x22, 0x5a, 0x0a, 0x19, 0x4c, 0x69, + 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x74, 0x65, 0x6e, 0x61, 0x6e, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, + 0x6e, 0x61, 0x6e, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, + 0x69, 0x70, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x74, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x22, 0x65, 0x0a, 0x21, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x61, + 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x08, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x61, 0x0a, + 0x20, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x57, 0x69, + 0x74, 0x68, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x41, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, + 0x22, 0xb1, 0x03, 0x0a, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x6d, 0x0a, 0x13, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x68, 0x69, 0x70, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6a, 0x0a, 0x12, 0x74, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, + 0x69, 0x70, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x65, + 0x6e, 0x61, 0x6e, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x41, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0x1a, 0x44, + 0x0a, 0x16, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 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, 0xab, 0x03, 0x0a, 0x1f, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x57, + 0x69, 0x74, 0x68, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x41, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x6e, 0x61, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, + 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, 0x6c, 0x0a, 0x13, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, + 0x68, 0x69, 0x70, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x50, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x41, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x69, 0x0a, 0x12, 0x74, 0x65, + 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, + 0x6e, 0x74, 0x57, 0x69, 0x74, 0x68, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, + 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x65, 0x6e, 0x61, + 0x6e, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x11, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x45, 0x0a, 0x17, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0x1a, 0x44, 0x0a, 0x16, + 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 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, 0xe5, 0x01, 0x0a, 0x06, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, 0x1c, 0x0a, + 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 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, 0x12, 0x33, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x71, 0x75, 0x6f, + 0x74, 0x61, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x31, 0x2e, 0x51, + 0x75, 0x6f, 0x74, 0x61, 0x53, 0x65, 0x74, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x51, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x12, 0x24, 0x0a, 0x06, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x6f, 0x74, + 0x61, 0x53, 0x65, 0x74, 0x52, 0x06, 0x71, 0x75, 0x6f, 0x74, 0x61, 0x73, 0x12, 0x2c, 0x0a, 0x0a, + 0x69, 0x61, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x41, 0x4d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x09, 0x69, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x39, 0x0a, 0x13, 0x54, 0x65, + 0x6e, 0x61, 0x6e, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x74, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x22, 0x39, 0x0a, 0x13, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x06, + 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, + 0x22, 0x25, 0x0a, 0x13, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x22, 0x0a, 0x10, 0x54, 0x65, 0x6e, 0x61, 0x6e, + 0x74, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x55, 0x0a, 0x17, 0x54, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x02, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x02, + 0x61, 0x74, 0x22, 0xa1, 0x02, 0x0a, 0x11, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x46, 0x69, 0x6e, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x02, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x70, 0x61, 0x67, 0x69, + 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, + 0x67, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x48, 0x0a, 0x0b, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x46, 0x69, 0x6e, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0x34, 0x0a, 0x0e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x06, 0x74, 0x65, 0x6e, 0x61, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, + 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x22, 0x6a, 0x0a, 0x12, + 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x24, 0x0a, 0x07, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, + 0x07, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x09, 0x6e, 0x65, 0x78, 0x74, + 0x5f, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x08, 0x6e, + 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6e, + 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x32, 0xfe, 0x04, 0x0a, 0x0d, 0x54, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2f, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, - 0x6e, 0x61, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x12, 0x1b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x35, 0x0a, 0x04, 0x46, 0x69, 0x6e, 0x64, 0x12, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x16, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x67, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x2e, - 0x76, 0x31, 0x42, 0x0b, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, - 0x01, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x65, - 0x74, 0x61, 0x6c, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, - 0x64, 0x61, 0x74, 0x61, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x56, 0x58, - 0x58, 0xaa, 0x02, 0x02, 0x56, 0x31, 0xca, 0x02, 0x02, 0x56, 0x31, 0xe2, 0x02, 0x0e, 0x56, 0x31, - 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x02, 0x56, - 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x12, 0x35, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2f, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x14, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, + 0x6e, 0x74, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3d, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1b, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x35, 0x0a, 0x04, 0x46, 0x69, 0x6e, 0x64, 0x12, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, + 0x6e, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x1c, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x19, 0x46, 0x69, 0x6e, 0x64, + 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x50, + 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x76, 0x31, + 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x65, 0x0a, 0x18, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, + 0x69, 0x70, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x23, + 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x61, 0x72, + 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x67, 0x0a, 0x06, 0x63, 0x6f, 0x6d, + 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x50, 0x01, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, + 0x65, 0x74, 0x61, 0x6c, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x56, + 0x58, 0x58, 0xaa, 0x02, 0x02, 0x56, 0x31, 0xca, 0x02, 0x02, 0x56, 0x31, 0xe2, 0x02, 0x0e, 0x56, + 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x02, + 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -642,56 +1191,84 @@ func file_v1_tenant_proto_rawDescGZIP() []byte { return file_v1_tenant_proto_rawDescData } -var file_v1_tenant_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_v1_tenant_proto_msgTypes = make([]protoimpl.MessageInfo, 22) var file_v1_tenant_proto_goTypes = []interface{}{ - (*Tenant)(nil), // 0: v1.Tenant - (*TenantCreateRequest)(nil), // 1: v1.TenantCreateRequest - (*TenantUpdateRequest)(nil), // 2: v1.TenantUpdateRequest - (*TenantDeleteRequest)(nil), // 3: v1.TenantDeleteRequest - (*TenantGetRequest)(nil), // 4: v1.TenantGetRequest - (*TenantGetHistoryRequest)(nil), // 5: v1.TenantGetHistoryRequest - (*TenantFindRequest)(nil), // 6: v1.TenantFindRequest - (*TenantResponse)(nil), // 7: v1.TenantResponse - (*TenantListResponse)(nil), // 8: v1.TenantListResponse - nil, // 9: v1.TenantFindRequest.AnnotationsEntry - (*Meta)(nil), // 10: v1.Meta - (*QuotaSet)(nil), // 11: v1.QuotaSet - (*IAMConfig)(nil), // 12: v1.IAMConfig - (*timestamppb.Timestamp)(nil), // 13: google.protobuf.Timestamp - (*wrapperspb.StringValue)(nil), // 14: google.protobuf.StringValue - (*Paging)(nil), // 15: v1.Paging + (*FindParticipatingProjectsRequest)(nil), // 0: v1.FindParticipatingProjectsRequest + (*FindParticipatingTenantsRequest)(nil), // 1: v1.FindParticipatingTenantsRequest + (*ListTenantMembersRequest)(nil), // 2: v1.ListTenantMembersRequest + (*ListTenantMembersResponse)(nil), // 3: v1.ListTenantMembersResponse + (*FindParticipatingProjectsResponse)(nil), // 4: v1.FindParticipatingProjectsResponse + (*FindParticipatingTenantsResponse)(nil), // 5: v1.FindParticipatingTenantsResponse + (*ProjectWithMembershipAnnotations)(nil), // 6: v1.ProjectWithMembershipAnnotations + (*TenantWithMembershipAnnotations)(nil), // 7: v1.TenantWithMembershipAnnotations + (*Tenant)(nil), // 8: v1.Tenant + (*TenantCreateRequest)(nil), // 9: v1.TenantCreateRequest + (*TenantUpdateRequest)(nil), // 10: v1.TenantUpdateRequest + (*TenantDeleteRequest)(nil), // 11: v1.TenantDeleteRequest + (*TenantGetRequest)(nil), // 12: v1.TenantGetRequest + (*TenantGetHistoryRequest)(nil), // 13: v1.TenantGetHistoryRequest + (*TenantFindRequest)(nil), // 14: v1.TenantFindRequest + (*TenantResponse)(nil), // 15: v1.TenantResponse + (*TenantListResponse)(nil), // 16: v1.TenantListResponse + nil, // 17: v1.ProjectWithMembershipAnnotations.ProjectAnnotationsEntry + nil, // 18: v1.ProjectWithMembershipAnnotations.TenantAnnotationsEntry + nil, // 19: v1.TenantWithMembershipAnnotations.ProjectAnnotationsEntry + nil, // 20: v1.TenantWithMembershipAnnotations.TenantAnnotationsEntry + nil, // 21: v1.TenantFindRequest.AnnotationsEntry + (*Project)(nil), // 22: v1.Project + (*Meta)(nil), // 23: v1.Meta + (*QuotaSet)(nil), // 24: v1.QuotaSet + (*IAMConfig)(nil), // 25: v1.IAMConfig + (*timestamppb.Timestamp)(nil), // 26: google.protobuf.Timestamp + (*wrapperspb.StringValue)(nil), // 27: google.protobuf.StringValue + (*Paging)(nil), // 28: v1.Paging } var file_v1_tenant_proto_depIdxs = []int32{ - 10, // 0: v1.Tenant.meta:type_name -> v1.Meta - 11, // 1: v1.Tenant.default_quotas:type_name -> v1.QuotaSet - 11, // 2: v1.Tenant.quotas:type_name -> v1.QuotaSet - 12, // 3: v1.Tenant.iam_config:type_name -> v1.IAMConfig - 0, // 4: v1.TenantCreateRequest.tenant:type_name -> v1.Tenant - 0, // 5: v1.TenantUpdateRequest.tenant:type_name -> v1.Tenant - 13, // 6: v1.TenantGetHistoryRequest.at:type_name -> google.protobuf.Timestamp - 14, // 7: v1.TenantFindRequest.id:type_name -> google.protobuf.StringValue - 14, // 8: v1.TenantFindRequest.name:type_name -> google.protobuf.StringValue - 15, // 9: v1.TenantFindRequest.paging:type_name -> v1.Paging - 9, // 10: v1.TenantFindRequest.annotations:type_name -> v1.TenantFindRequest.AnnotationsEntry - 0, // 11: v1.TenantResponse.tenant:type_name -> v1.Tenant - 0, // 12: v1.TenantListResponse.tenants:type_name -> v1.Tenant - 1, // 13: v1.TenantService.Create:input_type -> v1.TenantCreateRequest - 2, // 14: v1.TenantService.Update:input_type -> v1.TenantUpdateRequest - 3, // 15: v1.TenantService.Delete:input_type -> v1.TenantDeleteRequest - 4, // 16: v1.TenantService.Get:input_type -> v1.TenantGetRequest - 5, // 17: v1.TenantService.GetHistory:input_type -> v1.TenantGetHistoryRequest - 6, // 18: v1.TenantService.Find:input_type -> v1.TenantFindRequest - 7, // 19: v1.TenantService.Create:output_type -> v1.TenantResponse - 7, // 20: v1.TenantService.Update:output_type -> v1.TenantResponse - 7, // 21: v1.TenantService.Delete:output_type -> v1.TenantResponse - 7, // 22: v1.TenantService.Get:output_type -> v1.TenantResponse - 7, // 23: v1.TenantService.GetHistory:output_type -> v1.TenantResponse - 8, // 24: v1.TenantService.Find:output_type -> v1.TenantListResponse - 19, // [19:25] is the sub-list for method output_type - 13, // [13:19] is the sub-list for method input_type - 13, // [13:13] is the sub-list for extension type_name - 13, // [13:13] is the sub-list for extension extendee - 0, // [0:13] is the sub-list for field type_name + 7, // 0: v1.ListTenantMembersResponse.tenants:type_name -> v1.TenantWithMembershipAnnotations + 6, // 1: v1.FindParticipatingProjectsResponse.projects:type_name -> v1.ProjectWithMembershipAnnotations + 7, // 2: v1.FindParticipatingTenantsResponse.tenants:type_name -> v1.TenantWithMembershipAnnotations + 22, // 3: v1.ProjectWithMembershipAnnotations.project:type_name -> v1.Project + 17, // 4: v1.ProjectWithMembershipAnnotations.project_annotations:type_name -> v1.ProjectWithMembershipAnnotations.ProjectAnnotationsEntry + 18, // 5: v1.ProjectWithMembershipAnnotations.tenant_annotations:type_name -> v1.ProjectWithMembershipAnnotations.TenantAnnotationsEntry + 8, // 6: v1.TenantWithMembershipAnnotations.tenant:type_name -> v1.Tenant + 19, // 7: v1.TenantWithMembershipAnnotations.project_annotations:type_name -> v1.TenantWithMembershipAnnotations.ProjectAnnotationsEntry + 20, // 8: v1.TenantWithMembershipAnnotations.tenant_annotations:type_name -> v1.TenantWithMembershipAnnotations.TenantAnnotationsEntry + 23, // 9: v1.Tenant.meta:type_name -> v1.Meta + 24, // 10: v1.Tenant.default_quotas:type_name -> v1.QuotaSet + 24, // 11: v1.Tenant.quotas:type_name -> v1.QuotaSet + 25, // 12: v1.Tenant.iam_config:type_name -> v1.IAMConfig + 8, // 13: v1.TenantCreateRequest.tenant:type_name -> v1.Tenant + 8, // 14: v1.TenantUpdateRequest.tenant:type_name -> v1.Tenant + 26, // 15: v1.TenantGetHistoryRequest.at:type_name -> google.protobuf.Timestamp + 27, // 16: v1.TenantFindRequest.id:type_name -> google.protobuf.StringValue + 27, // 17: v1.TenantFindRequest.name:type_name -> google.protobuf.StringValue + 28, // 18: v1.TenantFindRequest.paging:type_name -> v1.Paging + 21, // 19: v1.TenantFindRequest.annotations:type_name -> v1.TenantFindRequest.AnnotationsEntry + 8, // 20: v1.TenantResponse.tenant:type_name -> v1.Tenant + 8, // 21: v1.TenantListResponse.tenants:type_name -> v1.Tenant + 9, // 22: v1.TenantService.Create:input_type -> v1.TenantCreateRequest + 10, // 23: v1.TenantService.Update:input_type -> v1.TenantUpdateRequest + 11, // 24: v1.TenantService.Delete:input_type -> v1.TenantDeleteRequest + 12, // 25: v1.TenantService.Get:input_type -> v1.TenantGetRequest + 13, // 26: v1.TenantService.GetHistory:input_type -> v1.TenantGetHistoryRequest + 14, // 27: v1.TenantService.Find:input_type -> v1.TenantFindRequest + 2, // 28: v1.TenantService.ListTenantMembers:input_type -> v1.ListTenantMembersRequest + 0, // 29: v1.TenantService.FindParticipatingProjects:input_type -> v1.FindParticipatingProjectsRequest + 1, // 30: v1.TenantService.FindParticipatingTenants:input_type -> v1.FindParticipatingTenantsRequest + 15, // 31: v1.TenantService.Create:output_type -> v1.TenantResponse + 15, // 32: v1.TenantService.Update:output_type -> v1.TenantResponse + 15, // 33: v1.TenantService.Delete:output_type -> v1.TenantResponse + 15, // 34: v1.TenantService.Get:output_type -> v1.TenantResponse + 15, // 35: v1.TenantService.GetHistory:output_type -> v1.TenantResponse + 16, // 36: v1.TenantService.Find:output_type -> v1.TenantListResponse + 3, // 37: v1.TenantService.ListTenantMembers:output_type -> v1.ListTenantMembersResponse + 4, // 38: v1.TenantService.FindParticipatingProjects:output_type -> v1.FindParticipatingProjectsResponse + 5, // 39: v1.TenantService.FindParticipatingTenants:output_type -> v1.FindParticipatingTenantsResponse + 31, // [31:40] is the sub-list for method output_type + 22, // [22:31] is the sub-list for method input_type + 22, // [22:22] is the sub-list for extension type_name + 22, // [22:22] is the sub-list for extension extendee + 0, // [0:22] is the sub-list for field type_name } func init() { file_v1_tenant_proto_init() } @@ -702,10 +1279,11 @@ func file_v1_tenant_proto_init() { file_v1_common_proto_init() file_v1_iam_proto_init() file_v1_meta_proto_init() + file_v1_project_proto_init() file_v1_quota_proto_init() if !protoimpl.UnsafeEnabled { file_v1_tenant_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Tenant); i { + switch v := v.(*FindParticipatingProjectsRequest); i { case 0: return &v.state case 1: @@ -717,7 +1295,7 @@ func file_v1_tenant_proto_init() { } } file_v1_tenant_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TenantCreateRequest); i { + switch v := v.(*FindParticipatingTenantsRequest); i { case 0: return &v.state case 1: @@ -729,7 +1307,7 @@ func file_v1_tenant_proto_init() { } } file_v1_tenant_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TenantUpdateRequest); i { + switch v := v.(*ListTenantMembersRequest); i { case 0: return &v.state case 1: @@ -741,7 +1319,7 @@ func file_v1_tenant_proto_init() { } } file_v1_tenant_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TenantDeleteRequest); i { + switch v := v.(*ListTenantMembersResponse); i { case 0: return &v.state case 1: @@ -753,7 +1331,7 @@ func file_v1_tenant_proto_init() { } } file_v1_tenant_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TenantGetRequest); i { + switch v := v.(*FindParticipatingProjectsResponse); i { case 0: return &v.state case 1: @@ -765,7 +1343,7 @@ func file_v1_tenant_proto_init() { } } file_v1_tenant_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TenantGetHistoryRequest); i { + switch v := v.(*FindParticipatingTenantsResponse); i { case 0: return &v.state case 1: @@ -777,7 +1355,7 @@ func file_v1_tenant_proto_init() { } } file_v1_tenant_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TenantFindRequest); i { + switch v := v.(*ProjectWithMembershipAnnotations); i { case 0: return &v.state case 1: @@ -789,7 +1367,7 @@ func file_v1_tenant_proto_init() { } } file_v1_tenant_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TenantResponse); i { + switch v := v.(*TenantWithMembershipAnnotations); i { case 0: return &v.state case 1: @@ -801,6 +1379,102 @@ func file_v1_tenant_proto_init() { } } file_v1_tenant_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Tenant); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_tenant_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TenantCreateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_tenant_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TenantUpdateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_tenant_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TenantDeleteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_tenant_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TenantGetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_tenant_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TenantGetHistoryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_tenant_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TenantFindRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_tenant_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TenantResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_tenant_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TenantListResponse); i { case 0: return &v.state @@ -813,14 +1487,17 @@ func file_v1_tenant_proto_init() { } } } - file_v1_tenant_proto_msgTypes[8].OneofWrappers = []interface{}{} + file_v1_tenant_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_v1_tenant_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_v1_tenant_proto_msgTypes[2].OneofWrappers = []interface{}{} + file_v1_tenant_proto_msgTypes[16].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_v1_tenant_proto_rawDesc, NumEnums: 0, - NumMessages: 10, + NumMessages: 22, NumExtensions: 0, NumServices: 1, }, diff --git a/api/v1/tenant_grpc.pb.go b/api/v1/tenant_grpc.pb.go index 92f034a..1421ce8 100644 --- a/api/v1/tenant_grpc.pb.go +++ b/api/v1/tenant_grpc.pb.go @@ -19,12 +19,15 @@ import ( const _ = grpc.SupportPackageIsVersion7 const ( - TenantService_Create_FullMethodName = "/v1.TenantService/Create" - TenantService_Update_FullMethodName = "/v1.TenantService/Update" - TenantService_Delete_FullMethodName = "/v1.TenantService/Delete" - TenantService_Get_FullMethodName = "/v1.TenantService/Get" - TenantService_GetHistory_FullMethodName = "/v1.TenantService/GetHistory" - TenantService_Find_FullMethodName = "/v1.TenantService/Find" + TenantService_Create_FullMethodName = "/v1.TenantService/Create" + TenantService_Update_FullMethodName = "/v1.TenantService/Update" + TenantService_Delete_FullMethodName = "/v1.TenantService/Delete" + TenantService_Get_FullMethodName = "/v1.TenantService/Get" + TenantService_GetHistory_FullMethodName = "/v1.TenantService/GetHistory" + TenantService_Find_FullMethodName = "/v1.TenantService/Find" + TenantService_ListTenantMembers_FullMethodName = "/v1.TenantService/ListTenantMembers" + TenantService_FindParticipatingProjects_FullMethodName = "/v1.TenantService/FindParticipatingProjects" + TenantService_FindParticipatingTenants_FullMethodName = "/v1.TenantService/FindParticipatingTenants" ) // TenantServiceClient is the client API for TenantService service. @@ -37,6 +40,9 @@ type TenantServiceClient interface { Get(ctx context.Context, in *TenantGetRequest, opts ...grpc.CallOption) (*TenantResponse, error) GetHistory(ctx context.Context, in *TenantGetHistoryRequest, opts ...grpc.CallOption) (*TenantResponse, error) Find(ctx context.Context, in *TenantFindRequest, opts ...grpc.CallOption) (*TenantListResponse, error) + ListTenantMembers(ctx context.Context, in *ListTenantMembersRequest, opts ...grpc.CallOption) (*ListTenantMembersResponse, error) + FindParticipatingProjects(ctx context.Context, in *FindParticipatingProjectsRequest, opts ...grpc.CallOption) (*FindParticipatingProjectsResponse, error) + FindParticipatingTenants(ctx context.Context, in *FindParticipatingTenantsRequest, opts ...grpc.CallOption) (*FindParticipatingTenantsResponse, error) } type tenantServiceClient struct { @@ -101,6 +107,33 @@ func (c *tenantServiceClient) Find(ctx context.Context, in *TenantFindRequest, o return out, nil } +func (c *tenantServiceClient) ListTenantMembers(ctx context.Context, in *ListTenantMembersRequest, opts ...grpc.CallOption) (*ListTenantMembersResponse, error) { + out := new(ListTenantMembersResponse) + err := c.cc.Invoke(ctx, TenantService_ListTenantMembers_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *tenantServiceClient) FindParticipatingProjects(ctx context.Context, in *FindParticipatingProjectsRequest, opts ...grpc.CallOption) (*FindParticipatingProjectsResponse, error) { + out := new(FindParticipatingProjectsResponse) + err := c.cc.Invoke(ctx, TenantService_FindParticipatingProjects_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *tenantServiceClient) FindParticipatingTenants(ctx context.Context, in *FindParticipatingTenantsRequest, opts ...grpc.CallOption) (*FindParticipatingTenantsResponse, error) { + out := new(FindParticipatingTenantsResponse) + err := c.cc.Invoke(ctx, TenantService_FindParticipatingTenants_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // TenantServiceServer is the server API for TenantService service. // All implementations should embed UnimplementedTenantServiceServer // for forward compatibility @@ -111,6 +144,9 @@ type TenantServiceServer interface { Get(context.Context, *TenantGetRequest) (*TenantResponse, error) GetHistory(context.Context, *TenantGetHistoryRequest) (*TenantResponse, error) Find(context.Context, *TenantFindRequest) (*TenantListResponse, error) + ListTenantMembers(context.Context, *ListTenantMembersRequest) (*ListTenantMembersResponse, error) + FindParticipatingProjects(context.Context, *FindParticipatingProjectsRequest) (*FindParticipatingProjectsResponse, error) + FindParticipatingTenants(context.Context, *FindParticipatingTenantsRequest) (*FindParticipatingTenantsResponse, error) } // UnimplementedTenantServiceServer should be embedded to have forward compatible implementations. @@ -135,6 +171,15 @@ func (UnimplementedTenantServiceServer) GetHistory(context.Context, *TenantGetHi func (UnimplementedTenantServiceServer) Find(context.Context, *TenantFindRequest) (*TenantListResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Find not implemented") } +func (UnimplementedTenantServiceServer) ListTenantMembers(context.Context, *ListTenantMembersRequest) (*ListTenantMembersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListTenantMembers not implemented") +} +func (UnimplementedTenantServiceServer) FindParticipatingProjects(context.Context, *FindParticipatingProjectsRequest) (*FindParticipatingProjectsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FindParticipatingProjects not implemented") +} +func (UnimplementedTenantServiceServer) FindParticipatingTenants(context.Context, *FindParticipatingTenantsRequest) (*FindParticipatingTenantsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FindParticipatingTenants not implemented") +} // UnsafeTenantServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TenantServiceServer will @@ -255,6 +300,60 @@ func _TenantService_Find_Handler(srv interface{}, ctx context.Context, dec func( return interceptor(ctx, in, info, handler) } +func _TenantService_ListTenantMembers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListTenantMembersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TenantServiceServer).ListTenantMembers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TenantService_ListTenantMembers_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TenantServiceServer).ListTenantMembers(ctx, req.(*ListTenantMembersRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TenantService_FindParticipatingProjects_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FindParticipatingProjectsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TenantServiceServer).FindParticipatingProjects(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TenantService_FindParticipatingProjects_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TenantServiceServer).FindParticipatingProjects(ctx, req.(*FindParticipatingProjectsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TenantService_FindParticipatingTenants_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FindParticipatingTenantsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TenantServiceServer).FindParticipatingTenants(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TenantService_FindParticipatingTenants_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TenantServiceServer).FindParticipatingTenants(ctx, req.(*FindParticipatingTenantsRequest)) + } + return interceptor(ctx, in, info, handler) +} + // TenantService_ServiceDesc is the grpc.ServiceDesc for TenantService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -286,6 +385,18 @@ var TenantService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Find", Handler: _TenantService_Find_Handler, }, + { + MethodName: "ListTenantMembers", + Handler: _TenantService_ListTenantMembers_Handler, + }, + { + MethodName: "FindParticipatingProjects", + Handler: _TenantService_FindParticipatingProjects_Handler, + }, + { + MethodName: "FindParticipatingTenants", + Handler: _TenantService_FindParticipatingTenants_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "v1/tenant.proto", diff --git a/go.mod b/go.mod index b5cfeeb..7fa2afa 100644 --- a/go.mod +++ b/go.mod @@ -6,29 +6,28 @@ require ( github.com/Masterminds/squirrel v1.5.4 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 - github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0 + github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 - github.com/jmoiron/sqlx v1.3.5 + github.com/jmoiron/sqlx v1.4.0 github.com/json-iterator/go v1.1.12 github.com/lib/pq v1.10.9 github.com/lopezator/migrator v0.3.1 github.com/metal-stack/metal-lib v0.16.2 github.com/metal-stack/security v0.8.0 github.com/metal-stack/v v1.0.3 - github.com/prometheus/client_golang v1.19.0 + github.com/prometheus/client_golang v1.19.1 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.9.0 - github.com/testcontainers/testcontainers-go v0.30.0 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0 - go.opentelemetry.io/otel/trace v1.25.0 + github.com/testcontainers/testcontainers-go v0.31.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 + go.opentelemetry.io/otel/trace v1.26.0 google.golang.org/grpc v1.63.2 - google.golang.org/protobuf v1.33.0 + google.golang.org/protobuf v1.34.1 sigs.k8s.io/yaml v1.4.0 ) require ( - cloud.google.com/go/compute v1.25.1 // indirect dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect @@ -37,14 +36,14 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/containerd/containerd v1.7.15 // indirect + github.com/containerd/containerd v1.7.16 // indirect github.com/containerd/log v0.1.0 // indirect github.com/coreos/go-oidc/v3 v3.10.0 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/docker v26.0.2+incompatible // indirect + github.com/docker/docker v26.1.2+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -89,7 +88,7 @@ require ( github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect @@ -99,7 +98,7 @@ require ( github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/shirou/gopsutil/v3 v3.24.3 // indirect + github.com/shirou/gopsutil/v3 v3.24.4 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect @@ -108,22 +107,24 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tklauser/go-sysconf v0.3.13 // indirect - github.com/tklauser/numcpus v0.7.0 // indirect + github.com/tklauser/go-sysconf v0.3.14 // indirect + github.com/tklauser/numcpus v0.8.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.mongodb.org/mongo-driver v1.15.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect - go.opentelemetry.io/otel v1.25.0 // indirect - go.opentelemetry.io/otel/metric v1.25.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect + go.opentelemetry.io/otel v1.26.0 // indirect + go.opentelemetry.io/otel/metric v1.26.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/oauth2 v0.19.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index bc1c01a..ae47977 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,11 @@ -cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= -cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= -cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -23,8 +24,8 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes= -github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY= +github.com/containerd/containerd v1.7.16 h1:7Zsfe8Fkj4Wi2My6DXGQ87hiqIrmOXolm72ZEkFU5Mg= +github.com/containerd/containerd v1.7.16/go.mod h1:NL49g7A/Fui7ccmxV6zkBWwqMgmMxFWzujYCc+JLt7k= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU= @@ -42,8 +43,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnN github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v26.0.2+incompatible h1:yGVmKUFGgcxA6PXWAokO0sQL22BrQ67cgVjko8tGdXE= -github.com/docker/docker v26.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v26.1.2+incompatible h1:UVX5ZOrrfTGZZYEP+ZDq3Xn9PdHNXaSYMFPDumMqG2k= +github.com/docker/docker v26.1.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -84,8 +85,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -103,8 +104,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0 h1:f4tggROQKKcnh4eItay6z/HbHLqghBxS8g7pyMhmDio= -github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0/go.mod h1:hKAkSgNkL0FII46ZkJcpVEAai4KV+swlIWCKfekd1pA= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= @@ -131,8 +132,8 @@ github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38= github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w= github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -161,7 +162,6 @@ github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55F github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lopezator/migrator v0.3.1 h1:ZFPT6aC7+nGWkqhleynABZ6ftycSf6hmHHLOaryq1Og= @@ -173,8 +173,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/metal-stack/metal-lib v0.16.2 h1:RJls/Spai4h5xr3BEmQt9UdWNN4RB9+SOINoZcjYaA8= github.com/metal-stack/metal-lib v0.16.2/go.mod h1:nyNGI4DZFOcWbSoq2Y6V3SHpFxuXBIqYBZHTb6cy//s= github.com/metal-stack/security v0.8.0 h1:tVaSDB9m5clwYrnLyaXfPy7mQlJTnmeoHscG+RUy/xo= @@ -206,8 +206,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg= -github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -216,8 +216,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= @@ -233,8 +233,8 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE= -github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg= +github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= +github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -269,36 +269,36 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/testcontainers/testcontainers-go v0.30.0 h1:jmn/XS22q4YRrcMwWg0pAwlClzs/abopbsBzrepyc4E= -github.com/testcontainers/testcontainers-go v0.30.0/go.mod h1:K+kHNGiM5zjklKjgTtcrEetF3uhWbMUyqAQoyoh8Pf0= +github.com/testcontainers/testcontainers-go v0.31.0 h1:W0VwIhcEVhRflwL9as3dhY6jXjVCA27AkmbnZ+UTh3U= +github.com/testcontainers/testcontainers-go v0.31.0/go.mod h1:D2lAoA0zUFiSY+eAflqK5mcUx/A5hrrORaEQrd0SefI= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= -github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= +github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= -github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= +github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= +github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0 h1:zvpPXY7RfYAGSdYQLjp6zxdJNSYD/+FFoCTQN9IPxBs= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0/go.mod h1:BMn8NB1vsxTljvuorms2hyOs8IBuuBEq0pl7ltOfy30= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= -go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= -go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 h1:A3SayB3rNyt+1S6qpI9mHPkeHTZbD7XILEqWnYZb2l0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0/go.mod h1:27iA5uvhuRNmalO+iEUdVn5ZMj2qy10Mm+XRIpRmyuU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= +go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= +go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= -go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= +go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= +go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= -go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= -go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -306,20 +306,20 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= -golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -335,13 +335,15 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -352,18 +354,19 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ= google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434 h1:umK/Ey0QEzurTNlsV3R+MfxHAb78HCEX/IkuR+zH4WQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/datastore/postgres_benchmark_test.go b/pkg/datastore/postgres_benchmark_test.go index 0141db4..e99d539 100644 --- a/pkg/datastore/postgres_benchmark_test.go +++ b/pkg/datastore/postgres_benchmark_test.go @@ -17,7 +17,7 @@ var ( ) func init() { - db, _ = createPostgresConnection() + _, db, _ = createPostgresConnection() ds, _ = New(slog.Default(), db, &v1.Tenant{}) } diff --git a/pkg/datastore/postgres_test.go b/pkg/datastore/postgres_test.go index 25dd205..fbf1b71 100644 --- a/pkg/datastore/postgres_test.go +++ b/pkg/datastore/postgres_test.go @@ -36,11 +36,19 @@ func TestMain(m *testing.M) { } }() - var err error - db, err = createPostgresConnection() + var ( + err error + c testcontainers.Container + ) + c, db, err = createPostgresConnection() if err != nil { panic(err) } + defer func() { + err = c.Stop(context.Background(), pointer.Pointer(3*time.Second)) + panic(err) + }() + code = m.Run() } @@ -783,7 +791,7 @@ func resetNow() { Now = time.Now } -func createPostgresConnection() (*sqlx.DB, error) { +func createPostgresConnection() (testcontainers.Container, *sqlx.DB, error) { ctx := context.Background() req := testcontainers.ContainerRequest{ @@ -800,16 +808,16 @@ func createPostgresConnection() (*sqlx.DB, error) { Started: true, }) if err != nil { - return nil, err + return nil, nil, err } ip, err := postgres.Host(ctx) if err != nil { - return nil, err + return nil, nil, err } port, err := postgres.MappedPort(ctx, "5432/tcp") if err != nil { - return nil, err + return nil, nil, err } log := slog.Default() @@ -830,5 +838,5 @@ func createPostgresConnection() (*sqlx.DB, error) { break } } - return db, nil + return postgres, db, nil } diff --git a/pkg/datastore/query_runner.go b/pkg/datastore/query_runner.go new file mode 100644 index 0000000..5cfe9f6 --- /dev/null +++ b/pkg/datastore/query_runner.go @@ -0,0 +1,47 @@ +package datastore + +import ( + "context" + "log/slog" + + "github.com/Masterminds/squirrel" + "github.com/jmoiron/sqlx" +) + +func RunQuery[E any](ctx context.Context, log *slog.Logger, db *sqlx.DB, builder squirrel.SelectBuilder, input map[string]any, resultFn func(e E) error) error { + query, vals, err := builder.ToSql() + if err != nil { + return err + } + + if log.Enabled(ctx, slog.LevelDebug) { + log.Debug("query", "sql", query, "values", vals) + } + + rows, err := db.NamedQueryContext(ctx, query, input) + if err != nil { + return err + } + defer func() { + err = rows.Close() + if err != nil { + log.Error("error closing result rows", "error", err) + } + }() + + for rows.Next() { + var e E + + err := rows.StructScan(&e) + if err != nil { + return err + } + + err = resultFn(e) + if err != nil { + return err + } + } + + return nil +} diff --git a/pkg/service/common_test.go b/pkg/service/common_test.go new file mode 100644 index 0000000..d32eb83 --- /dev/null +++ b/pkg/service/common_test.go @@ -0,0 +1,53 @@ +package service + +import ( + "context" + "fmt" + + "github.com/jmoiron/sqlx" + "github.com/metal-stack/masterdata-api/pkg/datastore" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" +) + +var ( + pgContainer testcontainers.Container +) + +func StartPostgres(ctx context.Context, ves ...datastore.Entity) (testcontainers.Container, *sqlx.DB, error) { + var err error + pgContainer, err = testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: "postgres:16-alpine", + ExposedPorts: []string{"5432/tcp"}, + Env: map[string]string{"POSTGRES_PASSWORD": "password"}, + WaitingFor: wait.ForAll( + wait.ForLog("database system is ready to accept connections"), + wait.ForListeningPort("5432/tcp"), + ), + Cmd: []string{"postgres", "-c", "max_connections=200"}, + }, + Started: true, + }) + if err != nil { + panic(err.Error()) + } + + ip, err := pgContainer.Host(ctx) + if err != nil { + return nil, nil, err + } + port, err := pgContainer.MappedPort(ctx, "5432") + if err != nil { + return nil, nil, err + } + + fmt.Println(port.Port()) + + db, err := datastore.NewPostgresDB(log, ip, port.Port(), "postgres", "password", "postgres", "disable", ves...) + if err != nil { + return nil, nil, err + } + + return pgContainer, db, err +} diff --git a/pkg/service/tenant.go b/pkg/service/tenant.go index 2afe0b9..24c3157 100644 --- a/pkg/service/tenant.go +++ b/pkg/service/tenant.go @@ -2,20 +2,30 @@ package service import ( "context" + "encoding/json" "fmt" "log/slog" + sq "github.com/Masterminds/squirrel" "github.com/jmoiron/sqlx" v1 "github.com/metal-stack/masterdata-api/api/v1" "github.com/metal-stack/masterdata-api/pkg/datastore" ) type tenantService struct { + db *sqlx.DB tenantStore datastore.Storage[*v1.Tenant] tenantMemberStore datastore.Storage[*v1.TenantMember] log *slog.Logger } +var ( + projectMembers = datastore.Entity(&v1.ProjectMember{}) + tenantMembers = datastore.Entity(&v1.TenantMember{}) + projects = datastore.Entity(&v1.Project{}) + tenants = datastore.Entity(&v1.Tenant{}) +) + func NewTenantService(db *sqlx.DB, l *slog.Logger) (*tenantService, error) { ts, err := datastore.New(l, db, &v1.Tenant{}) if err != nil { @@ -26,6 +36,7 @@ func NewTenantService(db *sqlx.DB, l *slog.Logger) (*tenantService, error) { return nil, err } return &tenantService{ + db: db, tenantStore: NewStorageStatusWrapper(ts), tenantMemberStore: NewStorageStatusWrapper(tms), log: l, @@ -129,3 +140,253 @@ func (s *tenantService) Find(ctx context.Context, req *v1.TenantFindRequest) (*v resp.NextPage = nextPage return resp, nil } + +var ( + queryDirectProjectParticipations = sq.Select( + projects.JSONField(), + projectMembers.JSONField()+"->'meta'->>'annotations' AS project_membership_annotations", + ). + From(projectMembers.TableName()). + Join(projects.TableName() + " ON " + projects.TableName() + ".id = " + projectMembers.JSONField() + "->>'project_id'"). + Where(projectMembers.JSONField() + "->>'tenant_id' = :tenantId") + + queryInheritedProjectParticipations = sq.Select( + projects.JSONField(), + tenantMembers.JSONField()+"->'meta'->>'annotations' AS tenant_membership_annotations", + ). + From(tenantMembers.TableName()). + Join(projects.TableName() + " ON " + projects.JSONField() + "->>'tenant_id' = " + tenantMembers.JSONField() + "->>'tenant_id'"). + Where(tenantMembers.JSONField() + "->>'member_id' = :tenantId") +) + +// FindParticipatingProjects returns all projects in which a member participates. +// This includes projects in which the member is explicitly participating through a project membership but may also +// include memberships, which are inherited by the tenant membership. +func (s *tenantService) FindParticipatingProjects(ctx context.Context, req *v1.FindParticipatingProjectsRequest) (*v1.FindParticipatingProjectsResponse, error) { + type result struct { + Project *v1.Project + TenantMembershipAnnotations []byte `db:"tenant_membership_annotations"` + ProjectMembershipAnnotations []byte `db:"project_membership_annotations"` + } + + var ( + res []*v1.ProjectWithMembershipAnnotations + resultMap = map[string]*v1.ProjectWithMembershipAnnotations{} + + input = map[string]any{"tenantId": req.TenantId} + + resultFn = func(e result) error { + p, ok := resultMap[e.Project.Meta.Id] + if !ok { + p = &v1.ProjectWithMembershipAnnotations{ + Project: e.Project, + } + } + + if e.TenantMembershipAnnotations != nil { + err := json.Unmarshal(e.TenantMembershipAnnotations, &p.TenantAnnotations) + if err != nil { + return err + } + } + + if e.ProjectMembershipAnnotations != nil { + err := json.Unmarshal(e.ProjectMembershipAnnotations, &p.ProjectAnnotations) + if err != nil { + return err + } + } + + resultMap[e.Project.Meta.Id] = p + + return nil + } + ) + + err := datastore.RunQuery(ctx, s.log, s.db, queryDirectProjectParticipations, input, resultFn) + if err != nil { + return nil, err + } + + includeInherited := true + if req.IncludeInherited != nil { + includeInherited = *req.IncludeInherited + } + + if includeInherited { + err := datastore.RunQuery(ctx, s.log, s.db, queryInheritedProjectParticipations, input, resultFn) + if err != nil { + return nil, err + } + } + + for _, p := range resultMap { + res = append(res, p) + } + + return &v1.FindParticipatingProjectsResponse{Projects: res}, nil +} + +var ( + queryDirectTenantParticipations = sq.Select( + tenants.JSONField(), + tenantMembers.JSONField()+"->'meta'->>'annotations' AS tenant_membership_annotations", + ). + From(tenantMembers.TableName()). + Join(tenants.TableName() + " ON " + tenants.TableName() + ".id = " + tenantMembers.JSONField() + "->>'tenant_id'"). + Where(tenantMembers.JSONField() + "->>'member_id' = :tenantId") + + queryInheritedTenantParticipations = sq.Select( + tenants.JSONField(), + projectMembers.JSONField()+"->'meta'->>'annotations' AS project_membership_annotations", + ). + From(projectMembers.TableName()). + Join(projects.TableName() + " ON " + projects.TableName() + ".id = " + projectMembers.JSONField() + "->>'project_id'"). + Join(tenants.TableName() + " ON " + tenants.TableName() + ".id = " + projects.JSONField() + "->>'tenant_id'"). + Where(projectMembers.JSONField() + "->>'tenant_id' = :tenantId") +) + +// FindParticipatingTenants returns all tenants in which a member participates. +// This includes tenants in which the member is explicitly participating through a tenant membership but may also +// include memberships, which are inherited by the project memberships (e.g. through project invites). +func (s *tenantService) FindParticipatingTenants(ctx context.Context, req *v1.FindParticipatingTenantsRequest) (*v1.FindParticipatingTenantsResponse, error) { + type result struct { + Tenant *v1.Tenant + TenantMembershipAnnotations []byte `db:"tenant_membership_annotations"` + ProjectMembershipAnnotations []byte `db:"project_membership_annotations"` + } + + var ( + input = map[string]any{"tenantId": req.TenantId} + + res []*v1.TenantWithMembershipAnnotations + resultMap = map[string]*v1.TenantWithMembershipAnnotations{} + + resultFn = func(e result) error { + t, ok := resultMap[e.Tenant.Meta.Id] + if !ok { + t = &v1.TenantWithMembershipAnnotations{ + Tenant: e.Tenant, + } + } + + if e.TenantMembershipAnnotations != nil { + err := json.Unmarshal(e.TenantMembershipAnnotations, &t.TenantAnnotations) + if err != nil { + return err + } + } + + if e.ProjectMembershipAnnotations != nil { + err := json.Unmarshal(e.ProjectMembershipAnnotations, &t.ProjectAnnotations) + if err != nil { + return err + } + } + + resultMap[e.Tenant.Meta.Id] = t + + return nil + } + ) + + err := datastore.RunQuery(ctx, s.log, s.db, queryDirectTenantParticipations, input, resultFn) + if err != nil { + return nil, err + } + + includeInherited := true + if req.IncludeInherited != nil { + includeInherited = *req.IncludeInherited + } + + if includeInherited { + err = datastore.RunQuery(ctx, s.log, s.db, queryInheritedTenantParticipations, input, resultFn) + if err != nil { + return nil, err + } + } + + for _, t := range resultMap { + res = append(res, t) + } + + return &v1.FindParticipatingTenantsResponse{Tenants: res}, nil +} + +var ( + queryDirectTenantsMembers = sq.Select( + tenants.JSONField(), + tenantMembers.JSONField()+"->'meta'->>'annotations' AS tenant_membership_annotations", + ). + From(tenantMembers.TableName()). + Join(tenants.TableName() + " ON " + tenants.TableName() + ".id = " + tenantMembers.JSONField() + "->>'member_id'"). + Where(tenantMembers.JSONField() + "->>'tenant_id' = :tenantId") + + queryInheritedTenantMembers = sq.Select( + tenants.JSONField(), + ). + From(projectMembers.TableName()). + Join(projects.TableName() + " ON " + projects.TableName() + ".id = " + projectMembers.JSONField() + "->>'project_id'"). + Join(tenants.TableName() + " ON " + tenants.TableName() + ".id = " + projectMembers.JSONField() + "->>'tenant_id'"). + Where(projects.JSONField() + "->>'tenant_id' = :tenantId") +) + +// ListTenantMembers returns all members of a tenant. +// This includes members which are explicitly participating through a tenant membership but may also +// include memberships, which are inherited by the project memberships (e.g. through project invites). +func (s *tenantService) ListTenantMembers(ctx context.Context, req *v1.ListTenantMembersRequest) (*v1.ListTenantMembersResponse, error) { + type result struct { + Tenant *v1.Tenant + TenantMembershipAnnotations []byte `db:"tenant_membership_annotations"` + } + + var ( + res []*v1.TenantWithMembershipAnnotations + resultMap = map[string]*v1.TenantWithMembershipAnnotations{} + + input = map[string]any{"tenantId": req.TenantId} + + resultFn = func(e result) error { + t, ok := resultMap[e.Tenant.Meta.Id] + if !ok { + t = &v1.TenantWithMembershipAnnotations{ + Tenant: e.Tenant, + } + } + + if e.TenantMembershipAnnotations != nil { + err := json.Unmarshal(e.TenantMembershipAnnotations, &t.TenantAnnotations) + if err != nil { + return err + } + } + + resultMap[e.Tenant.Meta.Id] = t + + return nil + } + ) + + err := datastore.RunQuery(ctx, s.log, s.db, queryDirectTenantsMembers, input, resultFn) + if err != nil { + return nil, err + } + + includeInherited := true + if req.IncludeInherited != nil { + includeInherited = *req.IncludeInherited + } + + if includeInherited { + err = datastore.RunQuery(ctx, s.log, s.db, queryInheritedTenantMembers, input, resultFn) + if err != nil { + return nil, err + } + } + for _, t := range resultMap { + res = append(res, t) + } + + return &v1.ListTenantMembersResponse{Tenants: res}, nil +} diff --git a/pkg/service/tenant_test.go b/pkg/service/tenant_test.go index db6c590..672f1ed 100644 --- a/pkg/service/tenant_test.go +++ b/pkg/service/tenant_test.go @@ -4,16 +4,21 @@ import ( "context" "log/slog" "os" + "slices" + "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" v1 "github.com/metal-stack/masterdata-api/api/v1" + "github.com/metal-stack/masterdata-api/pkg/datastore" + "github.com/metal-stack/masterdata-api/pkg/datastore/mocks" + "github.com/metal-stack/metal-lib/pkg/pointer" + "github.com/metal-stack/metal-lib/pkg/testcommon" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/runtime/protoimpl" "google.golang.org/protobuf/types/known/wrapperspb" - - "testing" - - "github.com/metal-stack/masterdata-api/pkg/datastore/mocks" ) var log *slog.Logger @@ -180,3 +185,638 @@ func TestFindTenantByName(t *testing.T) { require.NoError(t, err) assert.NotNil(t, resp) } + +func Test_tenantService_FindParticipatingProjects(t *testing.T) { + ctx := context.Background() + ves := []datastore.Entity{ + &v1.Project{}, + &v1.ProjectMember{}, + &v1.Tenant{}, + &v1.TenantMember{}, + } + + container, db, err := StartPostgres(ctx, ves...) + require.NoError(t, err) + defer func() { + require.NoError(t, db.Close()) + require.NoError(t, container.Terminate(ctx)) + }() + + s := &tenantService{ + db: db, + log: slog.Default(), + } + + var ( + projectStore, _ = datastore.New(log, db, &v1.Project{}) + tenantMemberStore, _ = datastore.New(log, db, &v1.TenantMember{}) + projectMemberStore, _ = datastore.New(log, db, &v1.ProjectMember{}) + ) + + tests := []struct { + name string + prepare func() + req *v1.FindParticipatingProjectsRequest + want *v1.FindParticipatingProjectsResponse + wantErr error + }{ + { + name: "no memberships", + req: &v1.FindParticipatingProjectsRequest{ + TenantId: "a", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + }, + want: &v1.FindParticipatingProjectsResponse{}, + wantErr: nil, + }, + { + name: "ignores foreign memberships", + req: &v1.FindParticipatingProjectsRequest{ + TenantId: "a", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err := projectStore.Create(ctx, &v1.Project{Meta: &v1.Meta{Id: "1"}}) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, ProjectId: "1", TenantId: "someone else"}) + require.NoError(t, err) + }, + want: &v1.FindParticipatingProjectsResponse{}, + wantErr: nil, + }, + { + name: "direct membership including 0 inherited", + req: &v1.FindParticipatingProjectsRequest{ + TenantId: "a", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err := projectStore.Create(ctx, &v1.Project{Meta: &v1.Meta{Id: "1"}}) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, ProjectId: "1", TenantId: "a"}) + require.NoError(t, err) + }, + want: &v1.FindParticipatingProjectsResponse{ + Projects: []*v1.ProjectWithMembershipAnnotations{{ + Project: &v1.Project{ + Meta: &v1.Meta{ + Kind: "Project", + Apiversion: "v1", + Id: "1", + }, + }, + ProjectAnnotations: map[string]string{"role": "admin"}, + TenantAnnotations: nil, + }}, + }, + wantErr: nil, + }, + { + name: "direct membership excluding inherited", + req: &v1.FindParticipatingProjectsRequest{ + TenantId: "a", + IncludeInherited: pointer.Pointer(false), + }, + prepare: func() { + err := projectStore.Create(ctx, &v1.Project{Meta: &v1.Meta{Id: "1"}}) + require.NoError(t, err) + err = projectStore.Create(ctx, &v1.Project{Meta: &v1.Meta{Id: "2"}, TenantId: "b"}) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, ProjectId: "1", TenantId: "a"}) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, ProjectId: "2", TenantId: "b"}) + require.NoError(t, err) + err = tenantMemberStore.Create(ctx, &v1.TenantMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "editor"}}, MemberId: "a", TenantId: "b"}) + require.NoError(t, err) + }, + want: &v1.FindParticipatingProjectsResponse{ + Projects: []*v1.ProjectWithMembershipAnnotations{{ + Project: &v1.Project{ + Meta: &v1.Meta{ + Kind: "Project", + Apiversion: "v1", + Id: "1", + }, + }, + ProjectAnnotations: map[string]string{"role": "admin"}, + TenantAnnotations: nil, + }}, + }, + wantErr: nil, + }, + { + name: "inherited membership", + req: &v1.FindParticipatingProjectsRequest{ + TenantId: "a", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err := projectStore.Create(ctx, &v1.Project{Meta: &v1.Meta{Id: "1"}, TenantId: "b"}) + require.NoError(t, err) + err = tenantMemberStore.Create(ctx, &v1.TenantMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "viewer"}}, TenantId: "b", MemberId: "a"}) + require.NoError(t, err) + }, + want: &v1.FindParticipatingProjectsResponse{ + Projects: []*v1.ProjectWithMembershipAnnotations{{ + Project: &v1.Project{ + Meta: &v1.Meta{ + Kind: "Project", + Apiversion: "v1", + Id: "1", + }, + TenantId: "b", + }, + ProjectAnnotations: nil, + TenantAnnotations: map[string]string{"role": "viewer"}, + }}, + }, + wantErr: nil, + }, + { + name: "direct and indirect memberships including inherited", + req: &v1.FindParticipatingProjectsRequest{ + TenantId: "req-tenant", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err := projectStore.Create(ctx, &v1.Project{ + Meta: &v1.Meta{Id: "direct-1"}, + TenantId: "req-tenant", + }) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{ + Meta: &v1.Meta{Annotations: map[string]string{"role": "owner"}}, + ProjectId: "direct-1", + TenantId: "req-tenant", + }) + require.NoError(t, err) + err = tenantMemberStore.Create(ctx, &v1.TenantMember{ + Meta: &v1.Meta{Annotations: map[string]string{"role": "editor"}}, + MemberId: "req-tenant", + TenantId: "parent", + }) + require.NoError(t, err) + err = projectStore.Create(ctx, &v1.Project{ + Meta: &v1.Meta{Id: "indirect-2"}, + TenantId: "parent", + }) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{ + Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, + ProjectId: "indirect-2", + TenantId: "parent", + }) + require.NoError(t, err) + }, + want: &v1.FindParticipatingProjectsResponse{ + Projects: []*v1.ProjectWithMembershipAnnotations{ + { + Project: &v1.Project{ + Meta: &v1.Meta{ + Kind: "Project", + Apiversion: "v1", + Id: "direct-1", + }, + TenantId: "req-tenant", + }, + ProjectAnnotations: map[string]string{"role": "owner"}, + TenantAnnotations: nil, + }, + { + Project: &v1.Project{ + Meta: &v1.Meta{ + Kind: "Project", + Apiversion: "v1", + Id: "indirect-2", + }, + TenantId: "parent", + }, + ProjectAnnotations: nil, + TenantAnnotations: map[string]string{"role": "editor"}, + }, + }, + }, + wantErr: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for _, ve := range ves { + _, err := db.ExecContext(ctx, "TRUNCATE TABLE "+ve.TableName()) + require.NoError(t, err) + } + + if tt.prepare != nil { + tt.prepare() + } + + got, err := s.FindParticipatingProjects(ctx, tt.req) + if diff := cmp.Diff(err, tt.wantErr); diff != "" { + t.Errorf("(-want +got):\n%s", diff) + return + } + slices.SortFunc(got.Projects, func(i, j *v1.ProjectWithMembershipAnnotations) int { + if i.Project.Meta.Id < j.Project.Meta.Id { + return -1 + } else { + return 1 + } + }) + if diff := cmp.Diff(tt.want, got, cmpopts.IgnoreTypes(protoimpl.MessageState{}), cmpopts.IgnoreFields(v1.Meta{}, "CreatedTime"), testcommon.IgnoreUnexported()); diff != "" { + t.Errorf("(-want +got):\n%s", diff) + } + }) + } +} + +func Test_tenantService_FindParticipatingTenants(t *testing.T) { + ctx := context.Background() + ves := []datastore.Entity{ + &v1.Project{}, + &v1.ProjectMember{}, + &v1.Tenant{}, + &v1.TenantMember{}, + } + + container, db, err := StartPostgres(ctx, ves...) + require.NoError(t, err) + defer func() { + require.NoError(t, db.Close()) + require.NoError(t, container.Terminate(ctx)) + }() + + s := &tenantService{ + db: db, + log: slog.Default(), + } + + var ( + projectStore, _ = datastore.New(log, db, &v1.Project{}) + tenantMemberStore, _ = datastore.New(log, db, &v1.TenantMember{}) + projectMemberStore, _ = datastore.New(log, db, &v1.ProjectMember{}) + tenantStore, _ = datastore.New(log, db, &v1.Tenant{}) + ) + + tests := []struct { + name string + req *v1.FindParticipatingTenantsRequest + prepare func() + want *v1.FindParticipatingTenantsResponse + wantErr error + }{ + { + name: "no memberships", + req: &v1.FindParticipatingTenantsRequest{ + TenantId: "a", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() {}, + want: &v1.FindParticipatingTenantsResponse{}, + wantErr: nil, + }, + { + name: "ignore foreign memberships", + req: &v1.FindParticipatingTenantsRequest{ + TenantId: "a", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err := tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "a"}}) + require.NoError(t, err) + err = tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "b"}}) + require.NoError(t, err) + err = tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "c"}}) + require.NoError(t, err) + err = tenantMemberStore.Create(ctx, &v1.TenantMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, MemberId: "c", TenantId: "b"}) + require.NoError(t, err) + }, + want: &v1.FindParticipatingTenantsResponse{}, + wantErr: err, + }, + { + name: "direct membership", + req: &v1.FindParticipatingTenantsRequest{ + TenantId: "a", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err := tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "b"}}) + require.NoError(t, err) + err = tenantMemberStore.Create(ctx, &v1.TenantMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, MemberId: "a", TenantId: "b"}) + require.NoError(t, err) + }, + want: &v1.FindParticipatingTenantsResponse{ + Tenants: []*v1.TenantWithMembershipAnnotations{ + { + Tenant: &v1.Tenant{ + Meta: &v1.Meta{ + Kind: "Tenant", + Apiversion: "v1", + Id: "b", + }, + }, + TenantAnnotations: map[string]string{"role": "admin"}, + }, + }, + }, + wantErr: nil, + }, + { + name: "indirect membership", + req: &v1.FindParticipatingTenantsRequest{ + TenantId: "a", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err := projectStore.Create(ctx, &v1.Project{Meta: &v1.Meta{Id: "1"}, TenantId: "b"}) + require.NoError(t, err) + err = tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "b"}}) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, ProjectId: "1", TenantId: "a"}) + require.NoError(t, err) + }, + want: &v1.FindParticipatingTenantsResponse{ + Tenants: []*v1.TenantWithMembershipAnnotations{ + { + Tenant: &v1.Tenant{ + Meta: &v1.Meta{ + Kind: "Tenant", + Apiversion: "v1", + Id: "b", + }, + }, + ProjectAnnotations: map[string]string{"role": "admin"}, + }, + }, + }, + wantErr: nil, + }, + { + name: "exclude inherited", + req: &v1.FindParticipatingTenantsRequest{ + TenantId: "a", + IncludeInherited: pointer.Pointer(false), + }, + prepare: func() { + err := projectStore.Create(ctx, &v1.Project{Meta: &v1.Meta{Id: "1"}, TenantId: "b"}) + require.NoError(t, err) + err = tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "b"}}) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, ProjectId: "1", TenantId: "a"}) + require.NoError(t, err) + }, + want: &v1.FindParticipatingTenantsResponse{}, + wantErr: nil, + }, + { + name: "direct and indirect memberships", + req: &v1.FindParticipatingTenantsRequest{ + TenantId: "req-tnt", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err = tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "indirect-tnt"}}) + require.NoError(t, err) + err := projectStore.Create(ctx, &v1.Project{ + Meta: &v1.Meta{Id: "indirect"}, + TenantId: "indirect-tnt", + }) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{ + Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, + ProjectId: "indirect", + TenantId: "req-tnt", + }) + require.NoError(t, err) + + err = tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "direct-tnt"}}) + require.NoError(t, err) + err = tenantMemberStore.Create(ctx, &v1.TenantMember{ + Meta: &v1.Meta{Annotations: map[string]string{"relation": "direct"}}, + TenantId: "direct-tnt", + MemberId: "req-tnt", + }) + require.NoError(t, err) + }, + want: &v1.FindParticipatingTenantsResponse{ + Tenants: []*v1.TenantWithMembershipAnnotations{ + { + Tenant: &v1.Tenant{ + Meta: &v1.Meta{ + Kind: "Tenant", + Apiversion: "v1", + Id: "direct-tnt", + }, + }, + TenantAnnotations: map[string]string{"relation": "direct"}, + }, + { + Tenant: &v1.Tenant{ + Meta: &v1.Meta{ + Kind: "Tenant", + Apiversion: "v1", + Id: "indirect-tnt", + }, + }, + ProjectAnnotations: map[string]string{"role": "admin"}, + }, + }, + }, + wantErr: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for _, ve := range ves { + _, err := db.ExecContext(ctx, "TRUNCATE TABLE "+ve.TableName()) + require.NoError(t, err) + } + + if tt.prepare != nil { + tt.prepare() + } + + got, err := s.FindParticipatingTenants(ctx, tt.req) + if diff := cmp.Diff(err, tt.wantErr); diff != "" { + t.Errorf("(-want +got):\n%s", diff) + return + } + + slices.SortFunc(got.Tenants, func(i, j *v1.TenantWithMembershipAnnotations) int { + if i.Tenant.Meta.Id < j.Tenant.Meta.Id { + return -1 + } else { + return 1 + } + }) + if diff := cmp.Diff(tt.want, got, cmpopts.IgnoreTypes(protoimpl.MessageState{}), cmpopts.IgnoreFields(v1.Meta{}, "CreatedTime"), testcommon.IgnoreUnexported()); diff != "" { + t.Errorf("(-want +got):\n%s", diff) + } + }) + } +} + +func Test_tenantService_ListTenantMembers(t *testing.T) { + ctx := context.Background() + ves := []datastore.Entity{ + &v1.Project{}, + &v1.ProjectMember{}, + &v1.Tenant{}, + &v1.TenantMember{}, + } + + container, db, err := StartPostgres(ctx, ves...) + require.NoError(t, err) + defer func() { + require.NoError(t, db.Close()) + require.NoError(t, container.Terminate(ctx)) + }() + + s := &tenantService{ + db: db, + log: slog.Default(), + } + + var ( + projectStore, _ = datastore.New(log, db, &v1.Project{}) + tenantMemberStore, _ = datastore.New(log, db, &v1.TenantMember{}) + projectMemberStore, _ = datastore.New(log, db, &v1.ProjectMember{}) + tenantStore, _ = datastore.New(log, db, &v1.Tenant{}) + ) + + tests := []struct { + name string + req *v1.ListTenantMembersRequest + prepare func() + want *v1.ListTenantMembersResponse + wantErr error + }{ + { + name: "no members", + req: &v1.ListTenantMembersRequest{ + TenantId: "acme", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + }, + want: &v1.ListTenantMembersResponse{}, + wantErr: err, + }, + { + name: "ignore foreign members", + req: &v1.ListTenantMembersRequest{ + TenantId: "acme", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err := tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "acme"}}) + require.NoError(t, err) + err = tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "azure"}}) + require.NoError(t, err) + err = tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "google"}}) + require.NoError(t, err) + err = tenantMemberStore.Create(ctx, &v1.TenantMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, MemberId: "azure", TenantId: "google"}) + require.NoError(t, err) + }, + want: &v1.ListTenantMembersResponse{}, + wantErr: err, + }, + { + name: "direct membership", + req: &v1.ListTenantMembersRequest{ + TenantId: "acme", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err := tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "azure"}}) + require.NoError(t, err) + err = tenantMemberStore.Create(ctx, &v1.TenantMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "admin"}}, MemberId: "azure", TenantId: "acme"}) + require.NoError(t, err) + }, + want: &v1.ListTenantMembersResponse{ + Tenants: []*v1.TenantWithMembershipAnnotations{ + { + Tenant: &v1.Tenant{ + Meta: &v1.Meta{ + Kind: "Tenant", + Apiversion: "v1", + Id: "azure", + }, + }, + TenantAnnotations: map[string]string{"role": "admin"}, + }, + }, + }, + wantErr: nil, + }, + { + name: "indirect membership", + req: &v1.ListTenantMembersRequest{ + TenantId: "acme", + IncludeInherited: pointer.Pointer(true), + }, + prepare: func() { + err := projectStore.Create(ctx, &v1.Project{Meta: &v1.Meta{Id: "1"}, TenantId: "acme"}) + require.NoError(t, err) + err = tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "google"}}) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "editor"}}, ProjectId: "1", TenantId: "google"}) + require.NoError(t, err) + }, + want: &v1.ListTenantMembersResponse{ + Tenants: []*v1.TenantWithMembershipAnnotations{ + { + Tenant: &v1.Tenant{ + Meta: &v1.Meta{ + Kind: "Tenant", + Apiversion: "v1", + Id: "google", + }, + }, + }, + }, + }, + wantErr: nil, + }, + { + name: "exclude inherited", + req: &v1.ListTenantMembersRequest{ + TenantId: "acme", + IncludeInherited: pointer.Pointer(false), + }, + prepare: func() { + err := projectStore.Create(ctx, &v1.Project{Meta: &v1.Meta{Id: "1"}, TenantId: "acme"}) + require.NoError(t, err) + err = tenantStore.Create(ctx, &v1.Tenant{Meta: &v1.Meta{Id: "google"}}) + require.NoError(t, err) + err = projectMemberStore.Create(ctx, &v1.ProjectMember{Meta: &v1.Meta{Annotations: map[string]string{"role": "editor"}}, ProjectId: "1", TenantId: "google"}) + require.NoError(t, err) + }, + want: &v1.ListTenantMembersResponse{}, + wantErr: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for _, ve := range ves { + _, err := db.ExecContext(ctx, "TRUNCATE TABLE "+ve.TableName()) + require.NoError(t, err) + } + + if tt.prepare != nil { + tt.prepare() + } + + got, err := s.ListTenantMembers(ctx, tt.req) + if diff := cmp.Diff(err, tt.wantErr); diff != "" { + t.Errorf("(-want +got):\n%s", diff) + return + } + if diff := cmp.Diff(tt.want, got, cmpopts.IgnoreTypes(protoimpl.MessageState{}), cmpopts.IgnoreFields(v1.Meta{}, "CreatedTime"), testcommon.IgnoreUnexported()); diff != "" { + t.Errorf("(-want +got):\n%s", diff) + } + }) + } +} diff --git a/proto/v1/tenant.proto b/proto/v1/tenant.proto index 028f697..380957f 100644 --- a/proto/v1/tenant.proto +++ b/proto/v1/tenant.proto @@ -7,6 +7,7 @@ import "google/protobuf/wrappers.proto"; import "v1/common.proto"; import "v1/iam.proto"; import "v1/meta.proto"; +import "v1/project.proto"; import "v1/quota.proto"; service TenantService { @@ -16,6 +17,48 @@ service TenantService { rpc Get(TenantGetRequest) returns (TenantResponse); rpc GetHistory(TenantGetHistoryRequest) returns (TenantResponse); rpc Find(TenantFindRequest) returns (TenantListResponse); + rpc ListTenantMembers(ListTenantMembersRequest) returns (ListTenantMembersResponse); + rpc FindParticipatingProjects(FindParticipatingProjectsRequest) returns (FindParticipatingProjectsResponse); + rpc FindParticipatingTenants(FindParticipatingTenantsRequest) returns (FindParticipatingTenantsResponse); +} + +message FindParticipatingProjectsRequest { + string tenant_id = 1; + optional bool include_inherited = 2; +} + +message FindParticipatingTenantsRequest { + string tenant_id = 1; + optional bool include_inherited = 2; +} + +message ListTenantMembersRequest { + string tenant_id = 1; + optional bool include_inherited = 2; +} + +message ListTenantMembersResponse { + repeated TenantWithMembershipAnnotations tenants = 1; +} + +message FindParticipatingProjectsResponse { + repeated ProjectWithMembershipAnnotations projects = 1; +} + +message FindParticipatingTenantsResponse { + repeated TenantWithMembershipAnnotations tenants = 1; +} + +message ProjectWithMembershipAnnotations { + Project project = 1; + map project_annotations = 2; + map tenant_annotations = 3; +} + +message TenantWithMembershipAnnotations { + Tenant tenant = 1; + map project_annotations = 2; + map tenant_annotations = 3; } message Tenant {