diff --git a/app/controlplane/api/controlplane/v1/group.pb.go b/app/controlplane/api/controlplane/v1/group.pb.go index f1bf62e1a..9bf4efc7f 100644 --- a/app/controlplane/api/controlplane/v1/group.pb.go +++ b/app/controlplane/api/controlplane/v1/group.pb.go @@ -1392,6 +1392,216 @@ func (*GroupServiceUpdateMemberMaintainerStatusResponse) Descriptor() ([]byte, [ return file_controlplane_v1_group_proto_rawDescGZIP(), []int{22} } +// GroupServiceListProjectsRequest contains parameters for filtering and paginating project results for a group +type GroupServiceListProjectsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // IdentityReference is used to specify the group by either its ID or name + GroupReference *IdentityReference `protobuf:"bytes,1,opt,name=group_reference,json=groupReference,proto3" json:"group_reference,omitempty"` + // Pagination parameters to limit and offset results + Pagination *OffsetPaginationRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (x *GroupServiceListProjectsRequest) Reset() { + *x = GroupServiceListProjectsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceListProjectsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceListProjectsRequest) ProtoMessage() {} + +func (x *GroupServiceListProjectsRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[23] + 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 GroupServiceListProjectsRequest.ProtoReflect.Descriptor instead. +func (*GroupServiceListProjectsRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{23} +} + +func (x *GroupServiceListProjectsRequest) GetGroupReference() *IdentityReference { + if x != nil { + return x.GroupReference + } + return nil +} + +func (x *GroupServiceListProjectsRequest) GetPagination() *OffsetPaginationRequest { + if x != nil { + return x.Pagination + } + return nil +} + +// GroupServiceListProjectsResponse contains a paginated list of projects for a group +type GroupServiceListProjectsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // List of projects memberships matching the request criteria + Projects []*ProjectInfo `protobuf:"bytes,1,rep,name=projects,proto3" json:"projects,omitempty"` + // Pagination information for the response + Pagination *OffsetPaginationResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (x *GroupServiceListProjectsResponse) Reset() { + *x = GroupServiceListProjectsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceListProjectsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceListProjectsResponse) ProtoMessage() {} + +func (x *GroupServiceListProjectsResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[24] + 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 GroupServiceListProjectsResponse.ProtoReflect.Descriptor instead. +func (*GroupServiceListProjectsResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{24} +} + +func (x *GroupServiceListProjectsResponse) GetProjects() []*ProjectInfo { + if x != nil { + return x.Projects + } + return nil +} + +func (x *GroupServiceListProjectsResponse) GetPagination() *OffsetPaginationResponse { + if x != nil { + return x.Pagination + } + return nil +} + +// ProjectInfo represents detailed information about a project that a group is a member of +type ProjectInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Unique identifier of the project + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Name of the project + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // Description of the project + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + // Role of the group in the project (admin or viewer) + Role ProjectMemberRole `protobuf:"varint,4,opt,name=role,proto3,enum=controlplane.v1.ProjectMemberRole" json:"role,omitempty"` + // The latest version ID of the project, if available + LatestVersionId *string `protobuf:"bytes,5,opt,name=latest_version_id,json=latestVersionId,proto3,oneof" json:"latest_version_id,omitempty"` + // Timestamp when the project was created + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` +} + +func (x *ProjectInfo) Reset() { + *x = ProjectInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProjectInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProjectInfo) ProtoMessage() {} + +func (x *ProjectInfo) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[25] + 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 ProjectInfo.ProtoReflect.Descriptor instead. +func (*ProjectInfo) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{25} +} + +func (x *ProjectInfo) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *ProjectInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ProjectInfo) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *ProjectInfo) GetRole() ProjectMemberRole { + if x != nil { + return x.Role + } + return ProjectMemberRole_PROJECT_MEMBER_ROLE_UNSPECIFIED +} + +func (x *ProjectInfo) GetLatestVersionId() string { + if x != nil && x.LatestVersionId != nil { + return *x.LatestVersionId + } + return "" +} + +func (x *ProjectInfo) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + var File_controlplane_v1_group_proto protoreflect.FileDescriptor var file_controlplane_v1_group_proto_rawDesc = []byte{ @@ -1628,86 +1838,133 @@ var file_controlplane_v1_group_proto_rawDesc = []byte{ 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x22, 0x32, 0x0a, 0x30, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x8f, 0x09, 0x0a, - 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x63, 0x0a, - 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc0, 0x01, 0x0a, + 0x1f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x53, 0x0a, 0x0f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, + 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0xa7, 0x01, 0x0a, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x49, + 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, + 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x8d, 0x02, 0x0a, 0x0b, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 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, + 0x36, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, + 0x65, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x2f, 0x0a, 0x11, 0x6c, 0x61, 0x74, 0x65, 0x73, + 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x0f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x32, 0x86, 0x0a, 0x0a, 0x0c, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x63, 0x0a, 0x06, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x5a, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x04, 0x4c, + 0x69, 0x73, 0x74, 0x12, 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x06, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x63, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, - 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, - 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x72, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x73, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x63, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2a, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x72, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, 0x0c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x09, 0x41, - 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, 0x0c, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x6f, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0xa5, 0x01, + 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, + 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x40, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x41, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, 0x69, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x93, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x3a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x75, 0x0a, 0x0c, 0x4c, + 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0xa5, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x4d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x40, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, 0x69, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, - 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x93, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x3a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, - 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x3b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, - 0x73, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4c, - 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1722,7 +1979,7 @@ func file_controlplane_v1_group_proto_rawDescGZIP() []byte { return file_controlplane_v1_group_proto_rawDescData } -var file_controlplane_v1_group_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_controlplane_v1_group_proto_msgTypes = make([]protoimpl.MessageInfo, 26) var file_controlplane_v1_group_proto_goTypes = []interface{}{ (*GroupServiceCreateRequest)(nil), // 0: controlplane.v1.GroupServiceCreateRequest (*GroupServiceCreateResponse)(nil), // 1: controlplane.v1.GroupServiceCreateResponse @@ -1747,65 +2004,77 @@ var file_controlplane_v1_group_proto_goTypes = []interface{}{ (*GroupMember)(nil), // 20: controlplane.v1.GroupMember (*GroupServiceUpdateMemberMaintainerStatusRequest)(nil), // 21: controlplane.v1.GroupServiceUpdateMemberMaintainerStatusRequest (*GroupServiceUpdateMemberMaintainerStatusResponse)(nil), // 22: controlplane.v1.GroupServiceUpdateMemberMaintainerStatusResponse - (*IdentityReference)(nil), // 23: controlplane.v1.IdentityReference - (*OffsetPaginationRequest)(nil), // 24: controlplane.v1.OffsetPaginationRequest - (*OffsetPaginationResponse)(nil), // 25: controlplane.v1.OffsetPaginationResponse - (*User)(nil), // 26: controlplane.v1.User - (*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp + (*GroupServiceListProjectsRequest)(nil), // 23: controlplane.v1.GroupServiceListProjectsRequest + (*GroupServiceListProjectsResponse)(nil), // 24: controlplane.v1.GroupServiceListProjectsResponse + (*ProjectInfo)(nil), // 25: controlplane.v1.ProjectInfo + (*IdentityReference)(nil), // 26: controlplane.v1.IdentityReference + (*OffsetPaginationRequest)(nil), // 27: controlplane.v1.OffsetPaginationRequest + (*OffsetPaginationResponse)(nil), // 28: controlplane.v1.OffsetPaginationResponse + (*User)(nil), // 29: controlplane.v1.User + (*timestamppb.Timestamp)(nil), // 30: google.protobuf.Timestamp + (ProjectMemberRole)(0), // 31: controlplane.v1.ProjectMemberRole } var file_controlplane_v1_group_proto_depIdxs = []int32{ 19, // 0: controlplane.v1.GroupServiceCreateResponse.group:type_name -> controlplane.v1.Group - 23, // 1: controlplane.v1.GroupServiceGetRequest.group_reference:type_name -> controlplane.v1.IdentityReference + 26, // 1: controlplane.v1.GroupServiceGetRequest.group_reference:type_name -> controlplane.v1.IdentityReference 19, // 2: controlplane.v1.GroupServiceGetResponse.group:type_name -> controlplane.v1.Group - 24, // 3: controlplane.v1.GroupServiceListRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest + 27, // 3: controlplane.v1.GroupServiceListRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest 19, // 4: controlplane.v1.GroupServiceListResponse.groups:type_name -> controlplane.v1.Group - 25, // 5: controlplane.v1.GroupServiceListResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse - 23, // 6: controlplane.v1.GroupServiceUpdateRequest.group_reference:type_name -> controlplane.v1.IdentityReference + 28, // 5: controlplane.v1.GroupServiceListResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse + 26, // 6: controlplane.v1.GroupServiceUpdateRequest.group_reference:type_name -> controlplane.v1.IdentityReference 19, // 7: controlplane.v1.GroupServiceUpdateResponse.group:type_name -> controlplane.v1.Group - 23, // 8: controlplane.v1.GroupServiceDeleteRequest.group_reference:type_name -> controlplane.v1.IdentityReference + 26, // 8: controlplane.v1.GroupServiceDeleteRequest.group_reference:type_name -> controlplane.v1.IdentityReference 20, // 9: controlplane.v1.GroupServiceListMembersResponse.members:type_name -> controlplane.v1.GroupMember - 25, // 10: controlplane.v1.GroupServiceListMembersResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse - 23, // 11: controlplane.v1.GroupServiceListMembersRequest.group_reference:type_name -> controlplane.v1.IdentityReference - 24, // 12: controlplane.v1.GroupServiceListMembersRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest - 23, // 13: controlplane.v1.GroupServiceAddMemberRequest.group_reference:type_name -> controlplane.v1.IdentityReference - 23, // 14: controlplane.v1.GroupServiceRemoveMemberRequest.group_reference:type_name -> controlplane.v1.IdentityReference - 23, // 15: controlplane.v1.GroupServiceListPendingInvitationsRequest.group_reference:type_name -> controlplane.v1.IdentityReference - 24, // 16: controlplane.v1.GroupServiceListPendingInvitationsRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest + 28, // 10: controlplane.v1.GroupServiceListMembersResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse + 26, // 11: controlplane.v1.GroupServiceListMembersRequest.group_reference:type_name -> controlplane.v1.IdentityReference + 27, // 12: controlplane.v1.GroupServiceListMembersRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest + 26, // 13: controlplane.v1.GroupServiceAddMemberRequest.group_reference:type_name -> controlplane.v1.IdentityReference + 26, // 14: controlplane.v1.GroupServiceRemoveMemberRequest.group_reference:type_name -> controlplane.v1.IdentityReference + 26, // 15: controlplane.v1.GroupServiceListPendingInvitationsRequest.group_reference:type_name -> controlplane.v1.IdentityReference + 27, // 16: controlplane.v1.GroupServiceListPendingInvitationsRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest 18, // 17: controlplane.v1.GroupServiceListPendingInvitationsResponse.invitations:type_name -> controlplane.v1.PendingGroupInvitation - 25, // 18: controlplane.v1.GroupServiceListPendingInvitationsResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse - 26, // 19: controlplane.v1.PendingGroupInvitation.invited_by:type_name -> controlplane.v1.User - 27, // 20: controlplane.v1.PendingGroupInvitation.created_at:type_name -> google.protobuf.Timestamp - 27, // 21: controlplane.v1.Group.created_at:type_name -> google.protobuf.Timestamp - 27, // 22: controlplane.v1.Group.updated_at:type_name -> google.protobuf.Timestamp - 26, // 23: controlplane.v1.GroupMember.user:type_name -> controlplane.v1.User - 27, // 24: controlplane.v1.GroupMember.created_at:type_name -> google.protobuf.Timestamp - 27, // 25: controlplane.v1.GroupMember.updated_at:type_name -> google.protobuf.Timestamp - 23, // 26: controlplane.v1.GroupServiceUpdateMemberMaintainerStatusRequest.group_reference:type_name -> controlplane.v1.IdentityReference - 0, // 27: controlplane.v1.GroupService.Create:input_type -> controlplane.v1.GroupServiceCreateRequest - 2, // 28: controlplane.v1.GroupService.Get:input_type -> controlplane.v1.GroupServiceGetRequest - 4, // 29: controlplane.v1.GroupService.List:input_type -> controlplane.v1.GroupServiceListRequest - 6, // 30: controlplane.v1.GroupService.Update:input_type -> controlplane.v1.GroupServiceUpdateRequest - 8, // 31: controlplane.v1.GroupService.Delete:input_type -> controlplane.v1.GroupServiceDeleteRequest - 11, // 32: controlplane.v1.GroupService.ListMembers:input_type -> controlplane.v1.GroupServiceListMembersRequest - 12, // 33: controlplane.v1.GroupService.AddMember:input_type -> controlplane.v1.GroupServiceAddMemberRequest - 14, // 34: controlplane.v1.GroupService.RemoveMember:input_type -> controlplane.v1.GroupServiceRemoveMemberRequest - 21, // 35: controlplane.v1.GroupService.UpdateMemberMaintainerStatus:input_type -> controlplane.v1.GroupServiceUpdateMemberMaintainerStatusRequest - 16, // 36: controlplane.v1.GroupService.ListPendingInvitations:input_type -> controlplane.v1.GroupServiceListPendingInvitationsRequest - 1, // 37: controlplane.v1.GroupService.Create:output_type -> controlplane.v1.GroupServiceCreateResponse - 3, // 38: controlplane.v1.GroupService.Get:output_type -> controlplane.v1.GroupServiceGetResponse - 5, // 39: controlplane.v1.GroupService.List:output_type -> controlplane.v1.GroupServiceListResponse - 7, // 40: controlplane.v1.GroupService.Update:output_type -> controlplane.v1.GroupServiceUpdateResponse - 9, // 41: controlplane.v1.GroupService.Delete:output_type -> controlplane.v1.GroupServiceDeleteResponse - 10, // 42: controlplane.v1.GroupService.ListMembers:output_type -> controlplane.v1.GroupServiceListMembersResponse - 13, // 43: controlplane.v1.GroupService.AddMember:output_type -> controlplane.v1.GroupServiceAddMemberResponse - 15, // 44: controlplane.v1.GroupService.RemoveMember:output_type -> controlplane.v1.GroupServiceRemoveMemberResponse - 22, // 45: controlplane.v1.GroupService.UpdateMemberMaintainerStatus:output_type -> controlplane.v1.GroupServiceUpdateMemberMaintainerStatusResponse - 17, // 46: controlplane.v1.GroupService.ListPendingInvitations:output_type -> controlplane.v1.GroupServiceListPendingInvitationsResponse - 37, // [37:47] is the sub-list for method output_type - 27, // [27:37] is the sub-list for method input_type - 27, // [27:27] is the sub-list for extension type_name - 27, // [27:27] is the sub-list for extension extendee - 0, // [0:27] is the sub-list for field type_name + 28, // 18: controlplane.v1.GroupServiceListPendingInvitationsResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse + 29, // 19: controlplane.v1.PendingGroupInvitation.invited_by:type_name -> controlplane.v1.User + 30, // 20: controlplane.v1.PendingGroupInvitation.created_at:type_name -> google.protobuf.Timestamp + 30, // 21: controlplane.v1.Group.created_at:type_name -> google.protobuf.Timestamp + 30, // 22: controlplane.v1.Group.updated_at:type_name -> google.protobuf.Timestamp + 29, // 23: controlplane.v1.GroupMember.user:type_name -> controlplane.v1.User + 30, // 24: controlplane.v1.GroupMember.created_at:type_name -> google.protobuf.Timestamp + 30, // 25: controlplane.v1.GroupMember.updated_at:type_name -> google.protobuf.Timestamp + 26, // 26: controlplane.v1.GroupServiceUpdateMemberMaintainerStatusRequest.group_reference:type_name -> controlplane.v1.IdentityReference + 26, // 27: controlplane.v1.GroupServiceListProjectsRequest.group_reference:type_name -> controlplane.v1.IdentityReference + 27, // 28: controlplane.v1.GroupServiceListProjectsRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest + 25, // 29: controlplane.v1.GroupServiceListProjectsResponse.projects:type_name -> controlplane.v1.ProjectInfo + 28, // 30: controlplane.v1.GroupServiceListProjectsResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse + 31, // 31: controlplane.v1.ProjectInfo.role:type_name -> controlplane.v1.ProjectMemberRole + 30, // 32: controlplane.v1.ProjectInfo.created_at:type_name -> google.protobuf.Timestamp + 0, // 33: controlplane.v1.GroupService.Create:input_type -> controlplane.v1.GroupServiceCreateRequest + 2, // 34: controlplane.v1.GroupService.Get:input_type -> controlplane.v1.GroupServiceGetRequest + 4, // 35: controlplane.v1.GroupService.List:input_type -> controlplane.v1.GroupServiceListRequest + 6, // 36: controlplane.v1.GroupService.Update:input_type -> controlplane.v1.GroupServiceUpdateRequest + 8, // 37: controlplane.v1.GroupService.Delete:input_type -> controlplane.v1.GroupServiceDeleteRequest + 11, // 38: controlplane.v1.GroupService.ListMembers:input_type -> controlplane.v1.GroupServiceListMembersRequest + 12, // 39: controlplane.v1.GroupService.AddMember:input_type -> controlplane.v1.GroupServiceAddMemberRequest + 14, // 40: controlplane.v1.GroupService.RemoveMember:input_type -> controlplane.v1.GroupServiceRemoveMemberRequest + 21, // 41: controlplane.v1.GroupService.UpdateMemberMaintainerStatus:input_type -> controlplane.v1.GroupServiceUpdateMemberMaintainerStatusRequest + 16, // 42: controlplane.v1.GroupService.ListPendingInvitations:input_type -> controlplane.v1.GroupServiceListPendingInvitationsRequest + 23, // 43: controlplane.v1.GroupService.ListProjects:input_type -> controlplane.v1.GroupServiceListProjectsRequest + 1, // 44: controlplane.v1.GroupService.Create:output_type -> controlplane.v1.GroupServiceCreateResponse + 3, // 45: controlplane.v1.GroupService.Get:output_type -> controlplane.v1.GroupServiceGetResponse + 5, // 46: controlplane.v1.GroupService.List:output_type -> controlplane.v1.GroupServiceListResponse + 7, // 47: controlplane.v1.GroupService.Update:output_type -> controlplane.v1.GroupServiceUpdateResponse + 9, // 48: controlplane.v1.GroupService.Delete:output_type -> controlplane.v1.GroupServiceDeleteResponse + 10, // 49: controlplane.v1.GroupService.ListMembers:output_type -> controlplane.v1.GroupServiceListMembersResponse + 13, // 50: controlplane.v1.GroupService.AddMember:output_type -> controlplane.v1.GroupServiceAddMemberResponse + 15, // 51: controlplane.v1.GroupService.RemoveMember:output_type -> controlplane.v1.GroupServiceRemoveMemberResponse + 22, // 52: controlplane.v1.GroupService.UpdateMemberMaintainerStatus:output_type -> controlplane.v1.GroupServiceUpdateMemberMaintainerStatusResponse + 17, // 53: controlplane.v1.GroupService.ListPendingInvitations:output_type -> controlplane.v1.GroupServiceListPendingInvitationsResponse + 24, // 54: controlplane.v1.GroupService.ListProjects:output_type -> controlplane.v1.GroupServiceListProjectsResponse + 44, // [44:55] is the sub-list for method output_type + 33, // [33:44] is the sub-list for method input_type + 33, // [33:33] is the sub-list for extension type_name + 33, // [33:33] is the sub-list for extension extendee + 0, // [0:33] is the sub-list for field type_name } func init() { file_controlplane_v1_group_proto_init() } @@ -2093,18 +2362,55 @@ func file_controlplane_v1_group_proto_init() { return nil } } + file_controlplane_v1_group_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceListProjectsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_group_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceListProjectsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_group_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProjectInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_controlplane_v1_group_proto_msgTypes[4].OneofWrappers = []interface{}{} file_controlplane_v1_group_proto_msgTypes[6].OneofWrappers = []interface{}{} file_controlplane_v1_group_proto_msgTypes[11].OneofWrappers = []interface{}{} file_controlplane_v1_group_proto_msgTypes[18].OneofWrappers = []interface{}{} + file_controlplane_v1_group_proto_msgTypes[25].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controlplane_v1_group_proto_rawDesc, NumEnums: 0, - NumMessages: 23, + NumMessages: 26, NumExtensions: 0, NumServices: 1, }, diff --git a/app/controlplane/api/controlplane/v1/group.proto b/app/controlplane/api/controlplane/v1/group.proto index fd5488859..a0e22b7f6 100644 --- a/app/controlplane/api/controlplane/v1/group.proto +++ b/app/controlplane/api/controlplane/v1/group.proto @@ -47,6 +47,8 @@ service GroupService { rpc UpdateMemberMaintainerStatus(GroupServiceUpdateMemberMaintainerStatusRequest) returns (GroupServiceUpdateMemberMaintainerStatusResponse) {} // ListPendingInvitations retrieves pending invitations for a group rpc ListPendingInvitations(GroupServiceListPendingInvitationsRequest) returns (GroupServiceListPendingInvitationsResponse) {} + // ListProjects retrieves a paginated list of projects the group is a member of + rpc ListProjects(GroupServiceListProjectsRequest) returns (GroupServiceListProjectsResponse) {} } // GroupServiceCreateRequest contains the information needed to create a new group @@ -236,3 +238,35 @@ message GroupServiceUpdateMemberMaintainerStatusRequest { // GroupServiceUpdateMemberMaintainerStatusResponse is returned upon successful update of a member's maintainer status message GroupServiceUpdateMemberMaintainerStatusResponse {} + +// GroupServiceListProjectsRequest contains parameters for filtering and paginating project results for a group +message GroupServiceListProjectsRequest { + // IdentityReference is used to specify the group by either its ID or name + IdentityReference group_reference = 1 [(buf.validate.field).required = true]; + // Pagination parameters to limit and offset results + OffsetPaginationRequest pagination = 2; +} + +// GroupServiceListProjectsResponse contains a paginated list of projects for a group +message GroupServiceListProjectsResponse { + // List of projects memberships matching the request criteria + repeated ProjectInfo projects = 1; + // Pagination information for the response + OffsetPaginationResponse pagination = 2; +} + +// ProjectInfo represents detailed information about a project that a group is a member of +message ProjectInfo { + // Unique identifier of the project + string id = 1; + // Name of the project + string name = 2; + // Description of the project + string description = 3; + // Role of the group in the project (admin or viewer) + ProjectMemberRole role = 4; + // The latest version ID of the project, if available + optional string latest_version_id = 5; + // Timestamp when the project was created + google.protobuf.Timestamp created_at = 6; +} diff --git a/app/controlplane/api/controlplane/v1/group_grpc.pb.go b/app/controlplane/api/controlplane/v1/group_grpc.pb.go index eb3102cf2..4a57fbd1f 100644 --- a/app/controlplane/api/controlplane/v1/group_grpc.pb.go +++ b/app/controlplane/api/controlplane/v1/group_grpc.pb.go @@ -44,6 +44,7 @@ const ( GroupService_RemoveMember_FullMethodName = "/controlplane.v1.GroupService/RemoveMember" GroupService_UpdateMemberMaintainerStatus_FullMethodName = "/controlplane.v1.GroupService/UpdateMemberMaintainerStatus" GroupService_ListPendingInvitations_FullMethodName = "/controlplane.v1.GroupService/ListPendingInvitations" + GroupService_ListProjects_FullMethodName = "/controlplane.v1.GroupService/ListProjects" ) // GroupServiceClient is the client API for GroupService service. @@ -70,6 +71,8 @@ type GroupServiceClient interface { UpdateMemberMaintainerStatus(ctx context.Context, in *GroupServiceUpdateMemberMaintainerStatusRequest, opts ...grpc.CallOption) (*GroupServiceUpdateMemberMaintainerStatusResponse, error) // ListPendingInvitations retrieves pending invitations for a group ListPendingInvitations(ctx context.Context, in *GroupServiceListPendingInvitationsRequest, opts ...grpc.CallOption) (*GroupServiceListPendingInvitationsResponse, error) + // ListProjects retrieves a paginated list of projects the group is a member of + ListProjects(ctx context.Context, in *GroupServiceListProjectsRequest, opts ...grpc.CallOption) (*GroupServiceListProjectsResponse, error) } type groupServiceClient struct { @@ -170,6 +173,15 @@ func (c *groupServiceClient) ListPendingInvitations(ctx context.Context, in *Gro return out, nil } +func (c *groupServiceClient) ListProjects(ctx context.Context, in *GroupServiceListProjectsRequest, opts ...grpc.CallOption) (*GroupServiceListProjectsResponse, error) { + out := new(GroupServiceListProjectsResponse) + err := c.cc.Invoke(ctx, GroupService_ListProjects_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // GroupServiceServer is the server API for GroupService service. // All implementations must embed UnimplementedGroupServiceServer // for forward compatibility @@ -194,6 +206,8 @@ type GroupServiceServer interface { UpdateMemberMaintainerStatus(context.Context, *GroupServiceUpdateMemberMaintainerStatusRequest) (*GroupServiceUpdateMemberMaintainerStatusResponse, error) // ListPendingInvitations retrieves pending invitations for a group ListPendingInvitations(context.Context, *GroupServiceListPendingInvitationsRequest) (*GroupServiceListPendingInvitationsResponse, error) + // ListProjects retrieves a paginated list of projects the group is a member of + ListProjects(context.Context, *GroupServiceListProjectsRequest) (*GroupServiceListProjectsResponse, error) mustEmbedUnimplementedGroupServiceServer() } @@ -231,6 +245,9 @@ func (UnimplementedGroupServiceServer) UpdateMemberMaintainerStatus(context.Cont func (UnimplementedGroupServiceServer) ListPendingInvitations(context.Context, *GroupServiceListPendingInvitationsRequest) (*GroupServiceListPendingInvitationsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListPendingInvitations not implemented") } +func (UnimplementedGroupServiceServer) ListProjects(context.Context, *GroupServiceListProjectsRequest) (*GroupServiceListProjectsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListProjects not implemented") +} func (UnimplementedGroupServiceServer) mustEmbedUnimplementedGroupServiceServer() {} // UnsafeGroupServiceServer may be embedded to opt out of forward compatibility for this service. @@ -424,6 +441,24 @@ func _GroupService_ListPendingInvitations_Handler(srv interface{}, ctx context.C return interceptor(ctx, in, info, handler) } +func _GroupService_ListProjects_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupServiceListProjectsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).ListProjects(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_ListProjects_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).ListProjects(ctx, req.(*GroupServiceListProjectsRequest)) + } + return interceptor(ctx, in, info, handler) +} + // GroupService_ServiceDesc is the grpc.ServiceDesc for GroupService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -471,6 +506,10 @@ var GroupService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListPendingInvitations", Handler: _GroupService_ListPendingInvitations_Handler, }, + { + MethodName: "ListProjects", + Handler: _GroupService_ListProjects_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "controlplane/v1/group.proto", diff --git a/app/controlplane/api/controlplane/v1/project.pb.go b/app/controlplane/api/controlplane/v1/project.pb.go index 618dfc62c..ca961d15e 100644 --- a/app/controlplane/api/controlplane/v1/project.pb.go +++ b/app/controlplane/api/controlplane/v1/project.pb.go @@ -37,59 +37,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// ProjectMemberRole defines the roles a member can have in a project -type ProjectMemberRole int32 - -const ( - // Default role for a project member - ProjectMemberRole_PROJECT_MEMBER_ROLE_UNSPECIFIED ProjectMemberRole = 0 - // Admin role for a project member - ProjectMemberRole_PROJECT_MEMBER_ROLE_ADMIN ProjectMemberRole = 1 - // Viewer role for a project member - ProjectMemberRole_PROJECT_MEMBER_ROLE_VIEWER ProjectMemberRole = 2 -) - -// Enum value maps for ProjectMemberRole. -var ( - ProjectMemberRole_name = map[int32]string{ - 0: "PROJECT_MEMBER_ROLE_UNSPECIFIED", - 1: "PROJECT_MEMBER_ROLE_ADMIN", - 2: "PROJECT_MEMBER_ROLE_VIEWER", - } - ProjectMemberRole_value = map[string]int32{ - "PROJECT_MEMBER_ROLE_UNSPECIFIED": 0, - "PROJECT_MEMBER_ROLE_ADMIN": 1, - "PROJECT_MEMBER_ROLE_VIEWER": 2, - } -) - -func (x ProjectMemberRole) Enum() *ProjectMemberRole { - p := new(ProjectMemberRole) - *p = x - return p -} - -func (x ProjectMemberRole) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ProjectMemberRole) Descriptor() protoreflect.EnumDescriptor { - return file_controlplane_v1_project_proto_enumTypes[0].Descriptor() -} - -func (ProjectMemberRole) Type() protoreflect.EnumType { - return &file_controlplane_v1_project_proto_enumTypes[0] -} - -func (x ProjectMemberRole) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ProjectMemberRole.Descriptor instead. -func (ProjectMemberRole) EnumDescriptor() ([]byte, []int) { - return file_controlplane_v1_project_proto_rawDescGZIP(), []int{0} -} - // ProjectServiceListMembersRequest contains the information needed to list members of a project type ProjectServiceListMembersRequest struct { state protoimpl.MessageState @@ -225,6 +172,8 @@ type ProjectMember struct { CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` // Timestamp when the project membership was last modified UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + // The ID of latest project version this member is associated with + LatestProjectVersionId string `protobuf:"bytes,6,opt,name=latest_project_version_id,json=latestProjectVersionId,proto3" json:"latest_project_version_id,omitempty"` } func (x *ProjectMember) Reset() { @@ -301,6 +250,13 @@ func (x *ProjectMember) GetUpdatedAt() *timestamppb.Timestamp { return nil } +func (x *ProjectMember) GetLatestProjectVersionId() string { + if x != nil { + return x.LatestProjectVersionId + } + return "" +} + type isProjectMember_Subject interface { isProjectMember_Subject() } @@ -947,7 +903,7 @@ var file_controlplane_v1_project_proto_rawDesc = []byte{ 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0xa5, 0x02, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0xe0, 0x02, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x2e, @@ -964,172 +920,168 @@ var file_controlplane_v1_project_proto_rawDesc = []byte{ 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x42, 0x09, 0x0a, 0x07, - 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0xa1, 0x02, 0x0a, 0x1e, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, - 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, - 0x01, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, - 0x6e, 0x63, 0x65, 0x12, 0x5e, 0x0a, 0x10, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x72, 0x65, - 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, - 0x70, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, - 0x01, 0x01, 0x52, 0x0f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, - 0x6e, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x42, 0x0e, 0xba, 0x48, 0x0b, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x05, - 0x10, 0x01, 0x22, 0x01, 0x00, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x22, 0x21, 0x0a, 0x1f, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdc, - 0x01, 0x0a, 0x21, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, - 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, - 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x10, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x5e, 0x0a, - 0x10, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0f, 0x6d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x24, 0x0a, - 0x22, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0xad, 0x01, 0x0a, 0x1a, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x12, 0x28, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x48, - 0x00, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x4d, 0x0a, 0x0f, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, - 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x16, 0x0a, 0x14, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, - 0x6e, 0x63, 0x65, 0x22, 0xa7, 0x02, 0x0a, 0x25, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, 0x0a, - 0x11, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, - 0x03, 0xc8, 0x01, 0x01, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x5e, 0x0a, 0x10, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, - 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x72, 0x6f, - 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x19, + 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x16, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x22, 0xa1, 0x02, 0x0a, 0x1e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x10, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x5e, + 0x0a, 0x10, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x42, 0x06, 0xba, 0x48, - 0x03, 0xc8, 0x01, 0x01, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x52, 0x6f, 0x6c, 0x65, 0x22, 0x28, 0x0a, - 0x26, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x2b, 0x50, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x10, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x12, 0x48, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, - 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x50, 0x61, 0x67, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, - 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc6, 0x01, 0x0a, 0x2c, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x69, - 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x69, 0x6e, 0x76, - 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x49, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, - 0x66, 0x66, 0x73, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0xec, 0x01, 0x0a, 0x18, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x26, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x52, 0x09, 0x75, - 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x39, 0x0a, 0x0a, 0x69, 0x6e, 0x76, 0x69, - 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, - 0x73, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x64, 0x42, 0x79, - 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x18, 0x03, 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, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x23, - 0x0a, 0x0d, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x64, 0x5f, - 0x62, 0x79, 0x2a, 0x77, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x52, 0x4f, 0x4a, 0x45, - 0x43, 0x54, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, - 0x50, 0x52, 0x4f, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x52, - 0x4f, 0x4c, 0x45, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x50, - 0x52, 0x4f, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x52, 0x4f, - 0x4c, 0x45, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x45, 0x52, 0x10, 0x02, 0x32, 0x8f, 0x05, 0x0a, 0x0e, - 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x74, - 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x31, 0x2e, + 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0f, 0x6d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x46, + 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, + 0x42, 0x0e, 0xba, 0x48, 0x0b, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x05, 0x10, 0x01, 0x22, 0x01, 0x00, + 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x22, 0x21, 0x0a, 0x1f, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdc, 0x01, 0x0a, 0x21, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x57, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, + 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x5e, 0x0a, 0x10, 0x6d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, + 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x24, 0x0a, 0x22, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xad, + 0x01, 0x0a, 0x1a, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x28, 0x0a, + 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x48, 0x00, 0x52, 0x09, 0x75, 0x73, + 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x4d, 0x0a, 0x0f, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x65, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x16, 0x0a, 0x14, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x68, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x22, 0xa7, + 0x02, 0x0a, 0x25, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, + 0x10, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x12, 0x5e, 0x0a, 0x10, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, + 0x52, 0x0f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x12, 0x45, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, + 0x07, 0x6e, 0x65, 0x77, 0x52, 0x6f, 0x6c, 0x65, 0x22, 0x28, 0x0a, 0x26, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x2b, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, - 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x0a, 0x70, + 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc6, 0x01, 0x0a, 0x2c, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x76, 0x69, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x49, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xec, + 0x01, 0x0a, 0x18, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0a, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x60, 0x01, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x45, 0x6d, + 0x61, 0x69, 0x6c, 0x12, 0x39, 0x0a, 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x64, 0x5f, 0x62, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x09, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x64, 0x42, 0x79, 0x88, 0x01, 0x01, 0x12, 0x39, + 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 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, 0x09, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x76, + 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x42, 0x0d, + 0x0a, 0x0b, 0x5f, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x32, 0x8f, 0x05, + 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x74, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, + 0x31, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x0c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x12, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x12, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x83, 0x01, - 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, - 0x6c, 0x65, 0x12, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, - 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x63, 0x6f, 0x6e, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x0c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x32, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x97, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, + 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x83, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x97, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x3c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4c, 0x5a, - 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, - 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, - 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1144,65 +1096,64 @@ func file_controlplane_v1_project_proto_rawDescGZIP() []byte { return file_controlplane_v1_project_proto_rawDescData } -var file_controlplane_v1_project_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_controlplane_v1_project_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_controlplane_v1_project_proto_goTypes = []interface{}{ - (ProjectMemberRole)(0), // 0: controlplane.v1.ProjectMemberRole - (*ProjectServiceListMembersRequest)(nil), // 1: controlplane.v1.ProjectServiceListMembersRequest - (*ProjectServiceListMembersResponse)(nil), // 2: controlplane.v1.ProjectServiceListMembersResponse - (*ProjectMember)(nil), // 3: controlplane.v1.ProjectMember - (*ProjectServiceAddMemberRequest)(nil), // 4: controlplane.v1.ProjectServiceAddMemberRequest - (*ProjectServiceAddMemberResponse)(nil), // 5: controlplane.v1.ProjectServiceAddMemberResponse - (*ProjectServiceRemoveMemberRequest)(nil), // 6: controlplane.v1.ProjectServiceRemoveMemberRequest - (*ProjectServiceRemoveMemberResponse)(nil), // 7: controlplane.v1.ProjectServiceRemoveMemberResponse - (*ProjectMembershipReference)(nil), // 8: controlplane.v1.ProjectMembershipReference - (*ProjectServiceUpdateMemberRoleRequest)(nil), // 9: controlplane.v1.ProjectServiceUpdateMemberRoleRequest - (*ProjectServiceUpdateMemberRoleResponse)(nil), // 10: controlplane.v1.ProjectServiceUpdateMemberRoleResponse - (*ProjectServiceListPendingInvitationsRequest)(nil), // 11: controlplane.v1.ProjectServiceListPendingInvitationsRequest - (*ProjectServiceListPendingInvitationsResponse)(nil), // 12: controlplane.v1.ProjectServiceListPendingInvitationsResponse - (*PendingProjectInvitation)(nil), // 13: controlplane.v1.PendingProjectInvitation - (*IdentityReference)(nil), // 14: controlplane.v1.IdentityReference - (*OffsetPaginationRequest)(nil), // 15: controlplane.v1.OffsetPaginationRequest - (*OffsetPaginationResponse)(nil), // 16: controlplane.v1.OffsetPaginationResponse - (*User)(nil), // 17: controlplane.v1.User - (*Group)(nil), // 18: controlplane.v1.Group + (*ProjectServiceListMembersRequest)(nil), // 0: controlplane.v1.ProjectServiceListMembersRequest + (*ProjectServiceListMembersResponse)(nil), // 1: controlplane.v1.ProjectServiceListMembersResponse + (*ProjectMember)(nil), // 2: controlplane.v1.ProjectMember + (*ProjectServiceAddMemberRequest)(nil), // 3: controlplane.v1.ProjectServiceAddMemberRequest + (*ProjectServiceAddMemberResponse)(nil), // 4: controlplane.v1.ProjectServiceAddMemberResponse + (*ProjectServiceRemoveMemberRequest)(nil), // 5: controlplane.v1.ProjectServiceRemoveMemberRequest + (*ProjectServiceRemoveMemberResponse)(nil), // 6: controlplane.v1.ProjectServiceRemoveMemberResponse + (*ProjectMembershipReference)(nil), // 7: controlplane.v1.ProjectMembershipReference + (*ProjectServiceUpdateMemberRoleRequest)(nil), // 8: controlplane.v1.ProjectServiceUpdateMemberRoleRequest + (*ProjectServiceUpdateMemberRoleResponse)(nil), // 9: controlplane.v1.ProjectServiceUpdateMemberRoleResponse + (*ProjectServiceListPendingInvitationsRequest)(nil), // 10: controlplane.v1.ProjectServiceListPendingInvitationsRequest + (*ProjectServiceListPendingInvitationsResponse)(nil), // 11: controlplane.v1.ProjectServiceListPendingInvitationsResponse + (*PendingProjectInvitation)(nil), // 12: controlplane.v1.PendingProjectInvitation + (*IdentityReference)(nil), // 13: controlplane.v1.IdentityReference + (*OffsetPaginationRequest)(nil), // 14: controlplane.v1.OffsetPaginationRequest + (*OffsetPaginationResponse)(nil), // 15: controlplane.v1.OffsetPaginationResponse + (*User)(nil), // 16: controlplane.v1.User + (*Group)(nil), // 17: controlplane.v1.Group + (ProjectMemberRole)(0), // 18: controlplane.v1.ProjectMemberRole (*timestamppb.Timestamp)(nil), // 19: google.protobuf.Timestamp } var file_controlplane_v1_project_proto_depIdxs = []int32{ - 14, // 0: controlplane.v1.ProjectServiceListMembersRequest.project_reference:type_name -> controlplane.v1.IdentityReference - 15, // 1: controlplane.v1.ProjectServiceListMembersRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest - 3, // 2: controlplane.v1.ProjectServiceListMembersResponse.members:type_name -> controlplane.v1.ProjectMember - 16, // 3: controlplane.v1.ProjectServiceListMembersResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse - 17, // 4: controlplane.v1.ProjectMember.user:type_name -> controlplane.v1.User - 18, // 5: controlplane.v1.ProjectMember.group:type_name -> controlplane.v1.Group - 0, // 6: controlplane.v1.ProjectMember.role:type_name -> controlplane.v1.ProjectMemberRole + 13, // 0: controlplane.v1.ProjectServiceListMembersRequest.project_reference:type_name -> controlplane.v1.IdentityReference + 14, // 1: controlplane.v1.ProjectServiceListMembersRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest + 2, // 2: controlplane.v1.ProjectServiceListMembersResponse.members:type_name -> controlplane.v1.ProjectMember + 15, // 3: controlplane.v1.ProjectServiceListMembersResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse + 16, // 4: controlplane.v1.ProjectMember.user:type_name -> controlplane.v1.User + 17, // 5: controlplane.v1.ProjectMember.group:type_name -> controlplane.v1.Group + 18, // 6: controlplane.v1.ProjectMember.role:type_name -> controlplane.v1.ProjectMemberRole 19, // 7: controlplane.v1.ProjectMember.created_at:type_name -> google.protobuf.Timestamp 19, // 8: controlplane.v1.ProjectMember.updated_at:type_name -> google.protobuf.Timestamp - 14, // 9: controlplane.v1.ProjectServiceAddMemberRequest.project_reference:type_name -> controlplane.v1.IdentityReference - 8, // 10: controlplane.v1.ProjectServiceAddMemberRequest.member_reference:type_name -> controlplane.v1.ProjectMembershipReference - 0, // 11: controlplane.v1.ProjectServiceAddMemberRequest.role:type_name -> controlplane.v1.ProjectMemberRole - 14, // 12: controlplane.v1.ProjectServiceRemoveMemberRequest.project_reference:type_name -> controlplane.v1.IdentityReference - 8, // 13: controlplane.v1.ProjectServiceRemoveMemberRequest.member_reference:type_name -> controlplane.v1.ProjectMembershipReference - 14, // 14: controlplane.v1.ProjectMembershipReference.group_reference:type_name -> controlplane.v1.IdentityReference - 14, // 15: controlplane.v1.ProjectServiceUpdateMemberRoleRequest.project_reference:type_name -> controlplane.v1.IdentityReference - 8, // 16: controlplane.v1.ProjectServiceUpdateMemberRoleRequest.member_reference:type_name -> controlplane.v1.ProjectMembershipReference - 0, // 17: controlplane.v1.ProjectServiceUpdateMemberRoleRequest.new_role:type_name -> controlplane.v1.ProjectMemberRole - 14, // 18: controlplane.v1.ProjectServiceListPendingInvitationsRequest.project_reference:type_name -> controlplane.v1.IdentityReference - 15, // 19: controlplane.v1.ProjectServiceListPendingInvitationsRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest - 13, // 20: controlplane.v1.ProjectServiceListPendingInvitationsResponse.invitations:type_name -> controlplane.v1.PendingProjectInvitation - 16, // 21: controlplane.v1.ProjectServiceListPendingInvitationsResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse - 17, // 22: controlplane.v1.PendingProjectInvitation.invited_by:type_name -> controlplane.v1.User + 13, // 9: controlplane.v1.ProjectServiceAddMemberRequest.project_reference:type_name -> controlplane.v1.IdentityReference + 7, // 10: controlplane.v1.ProjectServiceAddMemberRequest.member_reference:type_name -> controlplane.v1.ProjectMembershipReference + 18, // 11: controlplane.v1.ProjectServiceAddMemberRequest.role:type_name -> controlplane.v1.ProjectMemberRole + 13, // 12: controlplane.v1.ProjectServiceRemoveMemberRequest.project_reference:type_name -> controlplane.v1.IdentityReference + 7, // 13: controlplane.v1.ProjectServiceRemoveMemberRequest.member_reference:type_name -> controlplane.v1.ProjectMembershipReference + 13, // 14: controlplane.v1.ProjectMembershipReference.group_reference:type_name -> controlplane.v1.IdentityReference + 13, // 15: controlplane.v1.ProjectServiceUpdateMemberRoleRequest.project_reference:type_name -> controlplane.v1.IdentityReference + 7, // 16: controlplane.v1.ProjectServiceUpdateMemberRoleRequest.member_reference:type_name -> controlplane.v1.ProjectMembershipReference + 18, // 17: controlplane.v1.ProjectServiceUpdateMemberRoleRequest.new_role:type_name -> controlplane.v1.ProjectMemberRole + 13, // 18: controlplane.v1.ProjectServiceListPendingInvitationsRequest.project_reference:type_name -> controlplane.v1.IdentityReference + 14, // 19: controlplane.v1.ProjectServiceListPendingInvitationsRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest + 12, // 20: controlplane.v1.ProjectServiceListPendingInvitationsResponse.invitations:type_name -> controlplane.v1.PendingProjectInvitation + 15, // 21: controlplane.v1.ProjectServiceListPendingInvitationsResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse + 16, // 22: controlplane.v1.PendingProjectInvitation.invited_by:type_name -> controlplane.v1.User 19, // 23: controlplane.v1.PendingProjectInvitation.created_at:type_name -> google.protobuf.Timestamp - 1, // 24: controlplane.v1.ProjectService.ListMembers:input_type -> controlplane.v1.ProjectServiceListMembersRequest - 4, // 25: controlplane.v1.ProjectService.AddMember:input_type -> controlplane.v1.ProjectServiceAddMemberRequest - 6, // 26: controlplane.v1.ProjectService.RemoveMember:input_type -> controlplane.v1.ProjectServiceRemoveMemberRequest - 9, // 27: controlplane.v1.ProjectService.UpdateMemberRole:input_type -> controlplane.v1.ProjectServiceUpdateMemberRoleRequest - 11, // 28: controlplane.v1.ProjectService.ListPendingInvitations:input_type -> controlplane.v1.ProjectServiceListPendingInvitationsRequest - 2, // 29: controlplane.v1.ProjectService.ListMembers:output_type -> controlplane.v1.ProjectServiceListMembersResponse - 5, // 30: controlplane.v1.ProjectService.AddMember:output_type -> controlplane.v1.ProjectServiceAddMemberResponse - 7, // 31: controlplane.v1.ProjectService.RemoveMember:output_type -> controlplane.v1.ProjectServiceRemoveMemberResponse - 10, // 32: controlplane.v1.ProjectService.UpdateMemberRole:output_type -> controlplane.v1.ProjectServiceUpdateMemberRoleResponse - 12, // 33: controlplane.v1.ProjectService.ListPendingInvitations:output_type -> controlplane.v1.ProjectServiceListPendingInvitationsResponse + 0, // 24: controlplane.v1.ProjectService.ListMembers:input_type -> controlplane.v1.ProjectServiceListMembersRequest + 3, // 25: controlplane.v1.ProjectService.AddMember:input_type -> controlplane.v1.ProjectServiceAddMemberRequest + 5, // 26: controlplane.v1.ProjectService.RemoveMember:input_type -> controlplane.v1.ProjectServiceRemoveMemberRequest + 8, // 27: controlplane.v1.ProjectService.UpdateMemberRole:input_type -> controlplane.v1.ProjectServiceUpdateMemberRoleRequest + 10, // 28: controlplane.v1.ProjectService.ListPendingInvitations:input_type -> controlplane.v1.ProjectServiceListPendingInvitationsRequest + 1, // 29: controlplane.v1.ProjectService.ListMembers:output_type -> controlplane.v1.ProjectServiceListMembersResponse + 4, // 30: controlplane.v1.ProjectService.AddMember:output_type -> controlplane.v1.ProjectServiceAddMemberResponse + 6, // 31: controlplane.v1.ProjectService.RemoveMember:output_type -> controlplane.v1.ProjectServiceRemoveMemberResponse + 9, // 32: controlplane.v1.ProjectService.UpdateMemberRole:output_type -> controlplane.v1.ProjectServiceUpdateMemberRoleResponse + 11, // 33: controlplane.v1.ProjectService.ListPendingInvitations:output_type -> controlplane.v1.ProjectServiceListPendingInvitationsResponse 29, // [29:34] is the sub-list for method output_type 24, // [24:29] is the sub-list for method input_type 24, // [24:24] is the sub-list for extension type_name @@ -1391,14 +1342,13 @@ func file_controlplane_v1_project_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controlplane_v1_project_proto_rawDesc, - NumEnums: 1, + NumEnums: 0, NumMessages: 13, NumExtensions: 0, NumServices: 1, }, GoTypes: file_controlplane_v1_project_proto_goTypes, DependencyIndexes: file_controlplane_v1_project_proto_depIdxs, - EnumInfos: file_controlplane_v1_project_proto_enumTypes, MessageInfos: file_controlplane_v1_project_proto_msgTypes, }.Build() File_controlplane_v1_project_proto = out.File diff --git a/app/controlplane/api/controlplane/v1/project.proto b/app/controlplane/api/controlplane/v1/project.proto index b4248d2d9..756880981 100644 --- a/app/controlplane/api/controlplane/v1/project.proto +++ b/app/controlplane/api/controlplane/v1/project.proto @@ -66,6 +66,8 @@ message ProjectMember { google.protobuf.Timestamp created_at = 4; // Timestamp when the project membership was last modified google.protobuf.Timestamp updated_at = 5; + // The ID of latest project version this member is associated with + string latest_project_version_id = 6; } // ProjectServiceAddMemberRequest contains the information needed to add a user to a project @@ -108,16 +110,6 @@ message ProjectMembershipReference { } } -// ProjectMemberRole defines the roles a member can have in a project -enum ProjectMemberRole { - // Default role for a project member - PROJECT_MEMBER_ROLE_UNSPECIFIED = 0; - // Admin role for a project member - PROJECT_MEMBER_ROLE_ADMIN = 1; - // Viewer role for a project member - PROJECT_MEMBER_ROLE_VIEWER = 2; -} - // ProjectServiceUpdateMemberRoleRequest contains the information needed to update a member's role in a project message ProjectServiceUpdateMemberRoleRequest { // IdentityReference is used to specify the project by either its ID or name diff --git a/app/controlplane/api/controlplane/v1/shared_message.pb.go b/app/controlplane/api/controlplane/v1/shared_message.pb.go index 8f5cedb7c..95b4e2afc 100644 --- a/app/controlplane/api/controlplane/v1/shared_message.pb.go +++ b/app/controlplane/api/controlplane/v1/shared_message.pb.go @@ -36,6 +36,59 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// ProjectMemberRole defines the roles a member can have in a project +type ProjectMemberRole int32 + +const ( + // Default role for a project member + ProjectMemberRole_PROJECT_MEMBER_ROLE_UNSPECIFIED ProjectMemberRole = 0 + // Admin role for a project member + ProjectMemberRole_PROJECT_MEMBER_ROLE_ADMIN ProjectMemberRole = 1 + // Viewer role for a project member + ProjectMemberRole_PROJECT_MEMBER_ROLE_VIEWER ProjectMemberRole = 2 +) + +// Enum value maps for ProjectMemberRole. +var ( + ProjectMemberRole_name = map[int32]string{ + 0: "PROJECT_MEMBER_ROLE_UNSPECIFIED", + 1: "PROJECT_MEMBER_ROLE_ADMIN", + 2: "PROJECT_MEMBER_ROLE_VIEWER", + } + ProjectMemberRole_value = map[string]int32{ + "PROJECT_MEMBER_ROLE_UNSPECIFIED": 0, + "PROJECT_MEMBER_ROLE_ADMIN": 1, + "PROJECT_MEMBER_ROLE_VIEWER": 2, + } +) + +func (x ProjectMemberRole) Enum() *ProjectMemberRole { + p := new(ProjectMemberRole) + *p = x + return p +} + +func (x ProjectMemberRole) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ProjectMemberRole) Descriptor() protoreflect.EnumDescriptor { + return file_controlplane_v1_shared_message_proto_enumTypes[0].Descriptor() +} + +func (ProjectMemberRole) Type() protoreflect.EnumType { + return &file_controlplane_v1_shared_message_proto_enumTypes[0] +} + +func (x ProjectMemberRole) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ProjectMemberRole.Descriptor instead. +func (ProjectMemberRole) EnumDescriptor() ([]byte, []int) { + return file_controlplane_v1_shared_message_proto_rawDescGZIP(), []int{0} +} + // IdentityReference represents a reference to an identity in the system. type IdentityReference struct { state protoimpl.MessageState @@ -118,13 +171,20 @@ var file_controlplane_v1_shared_message_proto_rawDesc = []byte{ 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x26, 0x26, 0x20, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x69, 0x64, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x42, - 0x05, 0x0a, 0x03, 0x5f, 0x69, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, - 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x05, 0x0a, 0x03, 0x5f, 0x69, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x2a, + 0x77, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x52, 0x4f, 0x4a, 0x45, 0x43, 0x54, 0x5f, + 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x52, 0x4f, + 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4c, 0x45, + 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x50, 0x52, 0x4f, 0x4a, + 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, + 0x56, 0x49, 0x45, 0x57, 0x45, 0x52, 0x10, 0x02, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, + 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, + 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -139,9 +199,11 @@ func file_controlplane_v1_shared_message_proto_rawDescGZIP() []byte { return file_controlplane_v1_shared_message_proto_rawDescData } +var file_controlplane_v1_shared_message_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_controlplane_v1_shared_message_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_controlplane_v1_shared_message_proto_goTypes = []interface{}{ - (*IdentityReference)(nil), // 0: controlplane.v1.IdentityReference + (ProjectMemberRole)(0), // 0: controlplane.v1.ProjectMemberRole + (*IdentityReference)(nil), // 1: controlplane.v1.IdentityReference } var file_controlplane_v1_shared_message_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type @@ -176,13 +238,14 @@ func file_controlplane_v1_shared_message_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controlplane_v1_shared_message_proto_rawDesc, - NumEnums: 0, + NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_controlplane_v1_shared_message_proto_goTypes, DependencyIndexes: file_controlplane_v1_shared_message_proto_depIdxs, + EnumInfos: file_controlplane_v1_shared_message_proto_enumTypes, MessageInfos: file_controlplane_v1_shared_message_proto_msgTypes, }.Build() File_controlplane_v1_shared_message_proto = out.File diff --git a/app/controlplane/api/controlplane/v1/shared_message.proto b/app/controlplane/api/controlplane/v1/shared_message.proto index a7df1113d..8fd881bab 100644 --- a/app/controlplane/api/controlplane/v1/shared_message.proto +++ b/app/controlplane/api/controlplane/v1/shared_message.proto @@ -41,3 +41,13 @@ message IdentityReference { message: "either id or name must be provided, but not both." }; } + +// ProjectMemberRole defines the roles a member can have in a project +enum ProjectMemberRole { + // Default role for a project member + PROJECT_MEMBER_ROLE_UNSPECIFIED = 0; + // Admin role for a project member + PROJECT_MEMBER_ROLE_ADMIN = 1; + // Viewer role for a project member + PROJECT_MEMBER_ROLE_VIEWER = 2; +} diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/group.ts b/app/controlplane/api/gen/frontend/controlplane/v1/group.ts index cb445ae8f..0f1bfd1db 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/group.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/group.ts @@ -5,7 +5,12 @@ import _m0 from "protobufjs/minimal"; import { Timestamp } from "../../google/protobuf/timestamp"; import { OffsetPaginationRequest, OffsetPaginationResponse } from "./pagination"; import { User } from "./response_messages"; -import { IdentityReference } from "./shared_message"; +import { + IdentityReference, + ProjectMemberRole, + projectMemberRoleFromJSON, + projectMemberRoleToJSON, +} from "./shared_message"; export const protobufPackage = "controlplane.v1"; @@ -211,6 +216,40 @@ export interface GroupServiceUpdateMemberMaintainerStatusRequest { export interface GroupServiceUpdateMemberMaintainerStatusResponse { } +/** GroupServiceListProjectsRequest contains parameters for filtering and paginating project results for a group */ +export interface GroupServiceListProjectsRequest { + /** IdentityReference is used to specify the group by either its ID or name */ + groupReference?: IdentityReference; + /** Pagination parameters to limit and offset results */ + pagination?: OffsetPaginationRequest; +} + +/** GroupServiceListProjectsResponse contains a paginated list of projects for a group */ +export interface GroupServiceListProjectsResponse { + /** List of projects memberships matching the request criteria */ + projects: ProjectInfo[]; + /** Pagination information for the response */ + pagination?: OffsetPaginationResponse; +} + +/** ProjectInfo represents detailed information about a project that a group is a member of */ +export interface ProjectInfo { + /** Unique identifier of the project */ + id: string; + /** Name of the project */ + name: string; + /** Description of the project */ + description: string; + /** Role of the group in the project (admin or viewer) */ + role: ProjectMemberRole; + /** The latest version ID of the project, if available */ + latestVersionId?: + | string + | undefined; + /** Timestamp when the project was created */ + createdAt?: Date; +} + function createBaseGroupServiceCreateRequest(): GroupServiceCreateRequest { return { name: "", description: "" }; } @@ -1968,6 +2007,290 @@ export const GroupServiceUpdateMemberMaintainerStatusResponse = { }, }; +function createBaseGroupServiceListProjectsRequest(): GroupServiceListProjectsRequest { + return { groupReference: undefined, pagination: undefined }; +} + +export const GroupServiceListProjectsRequest = { + encode(message: GroupServiceListProjectsRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.groupReference !== undefined) { + IdentityReference.encode(message.groupReference, writer.uint32(10).fork()).ldelim(); + } + if (message.pagination !== undefined) { + OffsetPaginationRequest.encode(message.pagination, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceListProjectsRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceListProjectsRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.groupReference = IdentityReference.decode(reader, reader.uint32()); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.pagination = OffsetPaginationRequest.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupServiceListProjectsRequest { + return { + groupReference: isSet(object.groupReference) ? IdentityReference.fromJSON(object.groupReference) : undefined, + pagination: isSet(object.pagination) ? OffsetPaginationRequest.fromJSON(object.pagination) : undefined, + }; + }, + + toJSON(message: GroupServiceListProjectsRequest): unknown { + const obj: any = {}; + message.groupReference !== undefined && + (obj.groupReference = message.groupReference ? IdentityReference.toJSON(message.groupReference) : undefined); + message.pagination !== undefined && + (obj.pagination = message.pagination ? OffsetPaginationRequest.toJSON(message.pagination) : undefined); + return obj; + }, + + create, I>>(base?: I): GroupServiceListProjectsRequest { + return GroupServiceListProjectsRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): GroupServiceListProjectsRequest { + const message = createBaseGroupServiceListProjectsRequest(); + message.groupReference = (object.groupReference !== undefined && object.groupReference !== null) + ? IdentityReference.fromPartial(object.groupReference) + : undefined; + message.pagination = (object.pagination !== undefined && object.pagination !== null) + ? OffsetPaginationRequest.fromPartial(object.pagination) + : undefined; + return message; + }, +}; + +function createBaseGroupServiceListProjectsResponse(): GroupServiceListProjectsResponse { + return { projects: [], pagination: undefined }; +} + +export const GroupServiceListProjectsResponse = { + encode(message: GroupServiceListProjectsResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.projects) { + ProjectInfo.encode(v!, writer.uint32(10).fork()).ldelim(); + } + if (message.pagination !== undefined) { + OffsetPaginationResponse.encode(message.pagination, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceListProjectsResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceListProjectsResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.projects.push(ProjectInfo.decode(reader, reader.uint32())); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.pagination = OffsetPaginationResponse.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupServiceListProjectsResponse { + return { + projects: Array.isArray(object?.projects) ? object.projects.map((e: any) => ProjectInfo.fromJSON(e)) : [], + pagination: isSet(object.pagination) ? OffsetPaginationResponse.fromJSON(object.pagination) : undefined, + }; + }, + + toJSON(message: GroupServiceListProjectsResponse): unknown { + const obj: any = {}; + if (message.projects) { + obj.projects = message.projects.map((e) => e ? ProjectInfo.toJSON(e) : undefined); + } else { + obj.projects = []; + } + message.pagination !== undefined && + (obj.pagination = message.pagination ? OffsetPaginationResponse.toJSON(message.pagination) : undefined); + return obj; + }, + + create, I>>( + base?: I, + ): GroupServiceListProjectsResponse { + return GroupServiceListProjectsResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): GroupServiceListProjectsResponse { + const message = createBaseGroupServiceListProjectsResponse(); + message.projects = object.projects?.map((e) => ProjectInfo.fromPartial(e)) || []; + message.pagination = (object.pagination !== undefined && object.pagination !== null) + ? OffsetPaginationResponse.fromPartial(object.pagination) + : undefined; + return message; + }, +}; + +function createBaseProjectInfo(): ProjectInfo { + return { id: "", name: "", description: "", role: 0, latestVersionId: undefined, createdAt: undefined }; +} + +export const ProjectInfo = { + encode(message: ProjectInfo, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.name !== "") { + writer.uint32(18).string(message.name); + } + if (message.description !== "") { + writer.uint32(26).string(message.description); + } + if (message.role !== 0) { + writer.uint32(32).int32(message.role); + } + if (message.latestVersionId !== undefined) { + writer.uint32(42).string(message.latestVersionId); + } + if (message.createdAt !== undefined) { + Timestamp.encode(toTimestamp(message.createdAt), writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ProjectInfo { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseProjectInfo(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.name = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.description = reader.string(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.role = reader.int32() as any; + continue; + case 5: + if (tag !== 42) { + break; + } + + message.latestVersionId = reader.string(); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.createdAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ProjectInfo { + return { + id: isSet(object.id) ? String(object.id) : "", + name: isSet(object.name) ? String(object.name) : "", + description: isSet(object.description) ? String(object.description) : "", + role: isSet(object.role) ? projectMemberRoleFromJSON(object.role) : 0, + latestVersionId: isSet(object.latestVersionId) ? String(object.latestVersionId) : undefined, + createdAt: isSet(object.createdAt) ? fromJsonTimestamp(object.createdAt) : undefined, + }; + }, + + toJSON(message: ProjectInfo): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = message.id); + message.name !== undefined && (obj.name = message.name); + message.description !== undefined && (obj.description = message.description); + message.role !== undefined && (obj.role = projectMemberRoleToJSON(message.role)); + message.latestVersionId !== undefined && (obj.latestVersionId = message.latestVersionId); + message.createdAt !== undefined && (obj.createdAt = message.createdAt.toISOString()); + return obj; + }, + + create, I>>(base?: I): ProjectInfo { + return ProjectInfo.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): ProjectInfo { + const message = createBaseProjectInfo(); + message.id = object.id ?? ""; + message.name = object.name ?? ""; + message.description = object.description ?? ""; + message.role = object.role ?? 0; + message.latestVersionId = object.latestVersionId ?? undefined; + message.createdAt = object.createdAt ?? undefined; + return message; + }, +}; + /** GroupService provides operations for managing groups within the system */ export interface GroupService { /** Create creates a new group with the specified name and description */ @@ -2014,6 +2337,11 @@ export interface GroupService { request: DeepPartial, metadata?: grpc.Metadata, ): Promise; + /** ListProjects retrieves a paginated list of projects the group is a member of */ + ListProjects( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise; } export class GroupServiceClientImpl implements GroupService { @@ -2031,6 +2359,7 @@ export class GroupServiceClientImpl implements GroupService { this.RemoveMember = this.RemoveMember.bind(this); this.UpdateMemberMaintainerStatus = this.UpdateMemberMaintainerStatus.bind(this); this.ListPendingInvitations = this.ListPendingInvitations.bind(this); + this.ListProjects = this.ListProjects.bind(this); } Create( @@ -2104,6 +2433,13 @@ export class GroupServiceClientImpl implements GroupService { metadata, ); } + + ListProjects( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise { + return this.rpc.unary(GroupServiceListProjectsDesc, GroupServiceListProjectsRequest.fromPartial(request), metadata); + } } export const GroupServiceDesc = { serviceName: "controlplane.v1.GroupService" }; @@ -2338,6 +2674,29 @@ export const GroupServiceListPendingInvitationsDesc: UnaryMethodDefinitionish = } as any, }; +export const GroupServiceListProjectsDesc: UnaryMethodDefinitionish = { + methodName: "ListProjects", + service: GroupServiceDesc, + requestStream: false, + responseStream: false, + requestType: { + serializeBinary() { + return GroupServiceListProjectsRequest.encode(this).finish(); + }, + } as any, + responseType: { + deserializeBinary(data: Uint8Array) { + const value = GroupServiceListProjectsResponse.decode(data); + return { + ...value, + toObject() { + return value; + }, + }; + }, + } as any, +}; + interface UnaryMethodDefinitionishR extends grpc.UnaryMethodDefinition { requestStream: any; responseStream: any; diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/project.ts b/app/controlplane/api/gen/frontend/controlplane/v1/project.ts index 2e4341abb..6d4f588e5 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/project.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/project.ts @@ -6,53 +6,15 @@ import { Timestamp } from "../../google/protobuf/timestamp"; import { Group } from "./group"; import { OffsetPaginationRequest, OffsetPaginationResponse } from "./pagination"; import { User } from "./response_messages"; -import { IdentityReference } from "./shared_message"; +import { + IdentityReference, + ProjectMemberRole, + projectMemberRoleFromJSON, + projectMemberRoleToJSON, +} from "./shared_message"; export const protobufPackage = "controlplane.v1"; -/** ProjectMemberRole defines the roles a member can have in a project */ -export enum ProjectMemberRole { - /** PROJECT_MEMBER_ROLE_UNSPECIFIED - Default role for a project member */ - PROJECT_MEMBER_ROLE_UNSPECIFIED = 0, - /** PROJECT_MEMBER_ROLE_ADMIN - Admin role for a project member */ - PROJECT_MEMBER_ROLE_ADMIN = 1, - /** PROJECT_MEMBER_ROLE_VIEWER - Viewer role for a project member */ - PROJECT_MEMBER_ROLE_VIEWER = 2, - UNRECOGNIZED = -1, -} - -export function projectMemberRoleFromJSON(object: any): ProjectMemberRole { - switch (object) { - case 0: - case "PROJECT_MEMBER_ROLE_UNSPECIFIED": - return ProjectMemberRole.PROJECT_MEMBER_ROLE_UNSPECIFIED; - case 1: - case "PROJECT_MEMBER_ROLE_ADMIN": - return ProjectMemberRole.PROJECT_MEMBER_ROLE_ADMIN; - case 2: - case "PROJECT_MEMBER_ROLE_VIEWER": - return ProjectMemberRole.PROJECT_MEMBER_ROLE_VIEWER; - case -1: - case "UNRECOGNIZED": - default: - return ProjectMemberRole.UNRECOGNIZED; - } -} - -export function projectMemberRoleToJSON(object: ProjectMemberRole): string { - switch (object) { - case ProjectMemberRole.PROJECT_MEMBER_ROLE_UNSPECIFIED: - return "PROJECT_MEMBER_ROLE_UNSPECIFIED"; - case ProjectMemberRole.PROJECT_MEMBER_ROLE_ADMIN: - return "PROJECT_MEMBER_ROLE_ADMIN"; - case ProjectMemberRole.PROJECT_MEMBER_ROLE_VIEWER: - return "PROJECT_MEMBER_ROLE_VIEWER"; - case ProjectMemberRole.UNRECOGNIZED: - default: - return "UNRECOGNIZED"; - } -} - /** ProjectServiceListMembersRequest contains the information needed to list members of a project */ export interface ProjectServiceListMembersRequest { /** IdentityReference is used to specify the project by either its ID or name */ @@ -85,6 +47,8 @@ export interface ProjectMember { createdAt?: Date; /** Timestamp when the project membership was last modified */ updatedAt?: Date; + /** The ID of latest project version this member is associated with */ + latestProjectVersionId: string; } /** ProjectServiceAddMemberRequest contains the information needed to add a user to a project */ @@ -333,7 +297,14 @@ export const ProjectServiceListMembersResponse = { }; function createBaseProjectMember(): ProjectMember { - return { user: undefined, group: undefined, role: 0, createdAt: undefined, updatedAt: undefined }; + return { + user: undefined, + group: undefined, + role: 0, + createdAt: undefined, + updatedAt: undefined, + latestProjectVersionId: "", + }; } export const ProjectMember = { @@ -353,6 +324,9 @@ export const ProjectMember = { if (message.updatedAt !== undefined) { Timestamp.encode(toTimestamp(message.updatedAt), writer.uint32(42).fork()).ldelim(); } + if (message.latestProjectVersionId !== "") { + writer.uint32(50).string(message.latestProjectVersionId); + } return writer; }, @@ -398,6 +372,13 @@ export const ProjectMember = { message.updatedAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); continue; + case 6: + if (tag !== 50) { + break; + } + + message.latestProjectVersionId = reader.string(); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -414,6 +395,7 @@ export const ProjectMember = { role: isSet(object.role) ? projectMemberRoleFromJSON(object.role) : 0, createdAt: isSet(object.createdAt) ? fromJsonTimestamp(object.createdAt) : undefined, updatedAt: isSet(object.updatedAt) ? fromJsonTimestamp(object.updatedAt) : undefined, + latestProjectVersionId: isSet(object.latestProjectVersionId) ? String(object.latestProjectVersionId) : "", }; }, @@ -424,6 +406,7 @@ export const ProjectMember = { message.role !== undefined && (obj.role = projectMemberRoleToJSON(message.role)); message.createdAt !== undefined && (obj.createdAt = message.createdAt.toISOString()); message.updatedAt !== undefined && (obj.updatedAt = message.updatedAt.toISOString()); + message.latestProjectVersionId !== undefined && (obj.latestProjectVersionId = message.latestProjectVersionId); return obj; }, @@ -438,6 +421,7 @@ export const ProjectMember = { message.role = object.role ?? 0; message.createdAt = object.createdAt ?? undefined; message.updatedAt = object.updatedAt ?? undefined; + message.latestProjectVersionId = object.latestProjectVersionId ?? ""; return message; }, }; diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/shared_message.ts b/app/controlplane/api/gen/frontend/controlplane/v1/shared_message.ts index f8ccfe269..0a981073f 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/shared_message.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/shared_message.ts @@ -3,6 +3,49 @@ import _m0 from "protobufjs/minimal"; export const protobufPackage = "controlplane.v1"; +/** ProjectMemberRole defines the roles a member can have in a project */ +export enum ProjectMemberRole { + /** PROJECT_MEMBER_ROLE_UNSPECIFIED - Default role for a project member */ + PROJECT_MEMBER_ROLE_UNSPECIFIED = 0, + /** PROJECT_MEMBER_ROLE_ADMIN - Admin role for a project member */ + PROJECT_MEMBER_ROLE_ADMIN = 1, + /** PROJECT_MEMBER_ROLE_VIEWER - Viewer role for a project member */ + PROJECT_MEMBER_ROLE_VIEWER = 2, + UNRECOGNIZED = -1, +} + +export function projectMemberRoleFromJSON(object: any): ProjectMemberRole { + switch (object) { + case 0: + case "PROJECT_MEMBER_ROLE_UNSPECIFIED": + return ProjectMemberRole.PROJECT_MEMBER_ROLE_UNSPECIFIED; + case 1: + case "PROJECT_MEMBER_ROLE_ADMIN": + return ProjectMemberRole.PROJECT_MEMBER_ROLE_ADMIN; + case 2: + case "PROJECT_MEMBER_ROLE_VIEWER": + return ProjectMemberRole.PROJECT_MEMBER_ROLE_VIEWER; + case -1: + case "UNRECOGNIZED": + default: + return ProjectMemberRole.UNRECOGNIZED; + } +} + +export function projectMemberRoleToJSON(object: ProjectMemberRole): string { + switch (object) { + case ProjectMemberRole.PROJECT_MEMBER_ROLE_UNSPECIFIED: + return "PROJECT_MEMBER_ROLE_UNSPECIFIED"; + case ProjectMemberRole.PROJECT_MEMBER_ROLE_ADMIN: + return "PROJECT_MEMBER_ROLE_ADMIN"; + case ProjectMemberRole.PROJECT_MEMBER_ROLE_VIEWER: + return "PROJECT_MEMBER_ROLE_VIEWER"; + case ProjectMemberRole.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + /** IdentityReference represents a reference to an identity in the system. */ export interface IdentityReference { /** ID is optional, but if provided, it must be a valid UUID. */ diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsRequest.jsonschema.json new file mode 100644 index 000000000..f38976452 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsRequest.jsonschema.json @@ -0,0 +1,27 @@ +{ + "$id": "controlplane.v1.GroupServiceListProjectsRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceListProjectsRequest contains parameters for filtering and paginating project results for a group", + "patternProperties": { + "^(group_reference)$": { + "$ref": "controlplane.v1.IdentityReference.jsonschema.json", + "description": "IdentityReference is used to specify the group by either its ID or name" + } + }, + "properties": { + "groupReference": { + "$ref": "controlplane.v1.IdentityReference.jsonschema.json", + "description": "IdentityReference is used to specify the group by either its ID or name" + }, + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationRequest.jsonschema.json", + "description": "Pagination parameters to limit and offset results" + } + }, + "required": [ + "group_reference" + ], + "title": "Group Service List Projects Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsRequest.schema.json new file mode 100644 index 000000000..efe11ae28 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsRequest.schema.json @@ -0,0 +1,27 @@ +{ + "$id": "controlplane.v1.GroupServiceListProjectsRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceListProjectsRequest contains parameters for filtering and paginating project results for a group", + "patternProperties": { + "^(groupReference)$": { + "$ref": "controlplane.v1.IdentityReference.schema.json", + "description": "IdentityReference is used to specify the group by either its ID or name" + } + }, + "properties": { + "group_reference": { + "$ref": "controlplane.v1.IdentityReference.schema.json", + "description": "IdentityReference is used to specify the group by either its ID or name" + }, + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationRequest.schema.json", + "description": "Pagination parameters to limit and offset results" + } + }, + "required": [ + "group_reference" + ], + "title": "Group Service List Projects Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsResponse.jsonschema.json new file mode 100644 index 000000000..287fd1dc5 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsResponse.jsonschema.json @@ -0,0 +1,21 @@ +{ + "$id": "controlplane.v1.GroupServiceListProjectsResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceListProjectsResponse contains a paginated list of projects for a group", + "properties": { + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationResponse.jsonschema.json", + "description": "Pagination information for the response" + }, + "projects": { + "description": "List of projects memberships matching the request criteria", + "items": { + "$ref": "controlplane.v1.ProjectInfo.jsonschema.json" + }, + "type": "array" + } + }, + "title": "Group Service List Projects Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsResponse.schema.json new file mode 100644 index 000000000..15be117c9 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListProjectsResponse.schema.json @@ -0,0 +1,21 @@ +{ + "$id": "controlplane.v1.GroupServiceListProjectsResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceListProjectsResponse contains a paginated list of projects for a group", + "properties": { + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationResponse.schema.json", + "description": "Pagination information for the response" + }, + "projects": { + "description": "List of projects memberships matching the request criteria", + "items": { + "$ref": "controlplane.v1.ProjectInfo.schema.json" + }, + "type": "array" + } + }, + "title": "Group Service List Projects Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.Project.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.Project.jsonschema.json new file mode 100644 index 000000000..ead3f6544 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.Project.jsonschema.json @@ -0,0 +1,60 @@ +{ + "$id": "controlplane.v1.Project.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Project represents a project within the system", + "patternProperties": { + "^(created_at)$": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the project was created" + }, + "^(member_count)$": { + "description": "Count of members in the project", + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + }, + "^(organization_id)$": { + "description": "UUID of the organization that this project belongs to", + "type": "string" + }, + "^(updated_at)$": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the project was last modified" + } + }, + "properties": { + "createdAt": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the project was created" + }, + "description": { + "description": "Additional details about the project's purpose", + "type": "string" + }, + "id": { + "description": "Unique identifier for the project", + "type": "string" + }, + "memberCount": { + "description": "Count of members in the project", + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + }, + "name": { + "description": "Human-readable name of the project", + "type": "string" + }, + "organizationId": { + "description": "UUID of the organization that this project belongs to", + "type": "string" + }, + "updatedAt": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the project was last modified" + } + }, + "title": "Project", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.Project.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.Project.schema.json new file mode 100644 index 000000000..8a0902dea --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.Project.schema.json @@ -0,0 +1,60 @@ +{ + "$id": "controlplane.v1.Project.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Project represents a project within the system", + "patternProperties": { + "^(createdAt)$": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the project was created" + }, + "^(memberCount)$": { + "description": "Count of members in the project", + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + }, + "^(organizationId)$": { + "description": "UUID of the organization that this project belongs to", + "type": "string" + }, + "^(updatedAt)$": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the project was last modified" + } + }, + "properties": { + "created_at": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the project was created" + }, + "description": { + "description": "Additional details about the project's purpose", + "type": "string" + }, + "id": { + "description": "Unique identifier for the project", + "type": "string" + }, + "member_count": { + "description": "Count of members in the project", + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + }, + "name": { + "description": "Human-readable name of the project", + "type": "string" + }, + "organization_id": { + "description": "UUID of the organization that this project belongs to", + "type": "string" + }, + "updated_at": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the project was last modified" + } + }, + "title": "Project", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectInfo.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectInfo.jsonschema.json new file mode 100644 index 000000000..a628c16c6 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectInfo.jsonschema.json @@ -0,0 +1,59 @@ +{ + "$id": "controlplane.v1.ProjectInfo.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "ProjectInfo represents detailed information about a project that a group is a member of", + "patternProperties": { + "^(created_at)$": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the project was created" + }, + "^(latest_version_id)$": { + "description": "The latest version ID of the project, if available", + "type": "string" + } + }, + "properties": { + "createdAt": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the project was created" + }, + "description": { + "description": "Description of the project", + "type": "string" + }, + "id": { + "description": "Unique identifier of the project", + "type": "string" + }, + "latestVersionId": { + "description": "The latest version ID of the project, if available", + "type": "string" + }, + "name": { + "description": "Name of the project", + "type": "string" + }, + "role": { + "anyOf": [ + { + "enum": [ + "PROJECT_MEMBER_ROLE_UNSPECIFIED", + "PROJECT_MEMBER_ROLE_ADMIN", + "PROJECT_MEMBER_ROLE_VIEWER" + ], + "title": "Project Member Role", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "Role of the group in the project (admin or viewer)" + } + }, + "title": "Project Info", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectInfo.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectInfo.schema.json new file mode 100644 index 000000000..4e6a5b529 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectInfo.schema.json @@ -0,0 +1,59 @@ +{ + "$id": "controlplane.v1.ProjectInfo.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "ProjectInfo represents detailed information about a project that a group is a member of", + "patternProperties": { + "^(createdAt)$": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the project was created" + }, + "^(latestVersionId)$": { + "description": "The latest version ID of the project, if available", + "type": "string" + } + }, + "properties": { + "created_at": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the project was created" + }, + "description": { + "description": "Description of the project", + "type": "string" + }, + "id": { + "description": "Unique identifier of the project", + "type": "string" + }, + "latest_version_id": { + "description": "The latest version ID of the project, if available", + "type": "string" + }, + "name": { + "description": "Name of the project", + "type": "string" + }, + "role": { + "anyOf": [ + { + "enum": [ + "PROJECT_MEMBER_ROLE_UNSPECIFIED", + "PROJECT_MEMBER_ROLE_ADMIN", + "PROJECT_MEMBER_ROLE_VIEWER" + ], + "title": "Project Member Role", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "Role of the group in the project (admin or viewer)" + } + }, + "title": "Project Info", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.jsonschema.json index 4e044ac1e..a0fda6b8e 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.jsonschema.json @@ -8,6 +8,10 @@ "$ref": "google.protobuf.Timestamp.jsonschema.json", "description": "Timestamp when the project membership was created" }, + "^(latest_project_version_id)$": { + "description": "The ID of latest project version this member is associated with", + "type": "string" + }, "^(updated_at)$": { "$ref": "google.protobuf.Timestamp.jsonschema.json", "description": "Timestamp when the project membership was last modified" @@ -22,6 +26,10 @@ "$ref": "controlplane.v1.Group.jsonschema.json", "description": "The group who is a member of the project" }, + "latestProjectVersionId": { + "description": "The ID of latest project version this member is associated with", + "type": "string" + }, "role": { "anyOf": [ { diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.schema.json index b3965d425..c8379f59c 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.schema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.schema.json @@ -8,6 +8,10 @@ "$ref": "google.protobuf.Timestamp.schema.json", "description": "Timestamp when the project membership was created" }, + "^(latestProjectVersionId)$": { + "description": "The ID of latest project version this member is associated with", + "type": "string" + }, "^(updatedAt)$": { "$ref": "google.protobuf.Timestamp.schema.json", "description": "Timestamp when the project membership was last modified" @@ -22,6 +26,10 @@ "$ref": "controlplane.v1.Group.schema.json", "description": "The group who is a member of the project" }, + "latest_project_version_id": { + "description": "The ID of latest project version this member is associated with", + "type": "string" + }, "role": { "anyOf": [ { diff --git a/app/controlplane/cmd/wire_gen.go b/app/controlplane/cmd/wire_gen.go index c6d007b20..e78d95a63 100644 --- a/app/controlplane/cmd/wire_gen.go +++ b/app/controlplane/cmd/wire_gen.go @@ -135,7 +135,7 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l cleanup() return nil, nil, err } - groupUseCase := biz.NewGroupUseCase(logger, groupRepo, membershipRepo, userRepo, orgInvitationUseCase, auditorUseCase, orgInvitationRepo, enforcer) + groupUseCase := biz.NewGroupUseCase(logger, groupRepo, membershipRepo, userRepo, orgInvitationUseCase, auditorUseCase, orgInvitationRepo, enforcer, membershipUseCase) projectUseCase := biz.NewProjectsUseCase(logger, projectsRepo, membershipRepo, auditorUseCase, groupUseCase, membershipUseCase, orgInvitationUseCase, orgInvitationRepo, enforcer) v5 := serviceOpts(logger, enforcer, projectUseCase, groupUseCase) workflowService := service.NewWorkflowService(workflowUseCase, workflowContractUseCase, projectUseCase, v5...) diff --git a/app/controlplane/internal/service/group.go b/app/controlplane/internal/service/group.go index 585619a4e..3294ad7ef 100644 --- a/app/controlplane/internal/service/group.go +++ b/app/controlplane/internal/service/group.go @@ -479,6 +479,58 @@ func (g *GroupService) UpdateMemberMaintainerStatus(ctx context.Context, req *pb return &pb.GroupServiceUpdateMemberMaintainerStatusResponse{}, nil } +// ListProjects retrieves a paginated list of projects that a group is a member of. +func (g *GroupService) ListProjects(ctx context.Context, req *pb.GroupServiceListProjectsRequest) (*pb.GroupServiceListProjectsResponse, error) { + currentOrg, err := requireCurrentOrg(ctx) + if err != nil { + return nil, err + } + + // Parse orgID + orgUUID, err := uuid.Parse(currentOrg.ID) + if err != nil { + return nil, errors.BadRequest("invalid", "invalid organization ID") + } + + // Parse groupID and groupName from the request + id, name, err := req.GetGroupReference().Parse() + if err != nil { + return nil, errors.BadRequest("invalid", fmt.Sprintf("invalid group reference: %s", err.Error())) + } + + // Initialize the options for getting projects + groupOpts := &biz.ListProjectsByGroupOpts{ + IdentityReference: &biz.IdentityReference{ + ID: id, + Name: name, + }, + FilterByProject: g.visibleProjects(ctx), + } + + // Initialize the pagination options, with default values + paginationOpts, err := initializePaginationOpts(req.GetPagination()) + if err != nil { + return nil, handleUseCaseErr(err, g.log) + } + + // Retrieve the list of project info + projectInfoList, count, err := g.groupUseCase.ListProjectsByGroup(ctx, orgUUID, groupOpts, paginationOpts) + if err != nil { + return nil, handleUseCaseErr(err, g.log) + } + + // Convert the GroupProjectInfo to protobuf messages + result := make([]*pb.ProjectInfo, 0, len(projectInfoList)) + for _, project := range projectInfoList { + result = append(result, bizGroupProjectInfoToPb(project)) + } + + return &pb.GroupServiceListProjectsResponse{ + Projects: result, + Pagination: paginationToPb(count, paginationOpts.Offset(), paginationOpts.Limit()), + }, nil +} + // bizGroupToPb converts a biz.Group to a pb.Group protobuf message. func bizGroupToPb(gr *biz.Group) *pb.Group { base := &pb.Group{ @@ -522,3 +574,22 @@ func bizOrgInvitationToPendingGroupInvitationPb(inv *biz.OrgInvitation) *pb.Pend return base } + +// bizGroupProjectInfoToPb converts a biz.GroupProjectInfo to a pb.ProjectInfo protobuf message. +func bizGroupProjectInfoToPb(info *biz.GroupProjectInfo) *pb.ProjectInfo { + projectInfo := &pb.ProjectInfo{ + Id: info.ID.String(), + Name: info.Name, + Description: info.Description, + Role: mapAuthzRoleToProjectMemberRole(info.Role), + CreatedAt: timestamppb.New(*info.CreatedAt), + } + + // Add the latest version ID if available + if info.LatestVersionID != nil { + latestVersionID := info.LatestVersionID.String() + projectInfo.LatestVersionId = &latestVersionID + } + + return projectInfo +} diff --git a/app/controlplane/internal/service/project.go b/app/controlplane/internal/service/project.go index 0ac18fd78..69e085687 100644 --- a/app/controlplane/internal/service/project.go +++ b/app/controlplane/internal/service/project.go @@ -385,14 +385,7 @@ func bizProjectMembershipToPb(m *biz.ProjectMembership) *pb.ProjectMember { var role pb.ProjectMemberRole // Map the role string back to a protobuf enum - switch m.Role { - case authz.RoleProjectAdmin: - role = pb.ProjectMemberRole_PROJECT_MEMBER_ROLE_ADMIN - case authz.RoleProjectViewer: - role = pb.ProjectMemberRole_PROJECT_MEMBER_ROLE_VIEWER - default: - role = pb.ProjectMemberRole_PROJECT_MEMBER_ROLE_UNSPECIFIED - } + role = mapAuthzRoleToProjectMemberRole(m.Role) pbMember := &pb.ProjectMember{ Role: role, @@ -410,9 +403,14 @@ func bizProjectMembershipToPb(m *biz.ProjectMembership) *pb.ProjectMember { } } + if m.LatestProjectVersionID != nil { + pbMember.LatestProjectVersionId = m.LatestProjectVersionID.String() + } + if m.CreatedAt != nil { pbMember.CreatedAt = timestamppb.New(*m.CreatedAt) } + if m.UpdatedAt != nil { pbMember.UpdatedAt = timestamppb.New(*m.UpdatedAt) } @@ -420,6 +418,18 @@ func bizProjectMembershipToPb(m *biz.ProjectMembership) *pb.ProjectMember { return pbMember } +// mapAuthzRoleToProjectMemberRole maps an authorization role to a ProjectMemberRole protobuf enum +func mapAuthzRoleToProjectMemberRole(role authz.Role) pb.ProjectMemberRole { + switch role { + case authz.RoleProjectAdmin: + return pb.ProjectMemberRole_PROJECT_MEMBER_ROLE_ADMIN + case authz.RoleProjectViewer: + return pb.ProjectMemberRole_PROJECT_MEMBER_ROLE_VIEWER + default: + return pb.ProjectMemberRole_PROJECT_MEMBER_ROLE_UNSPECIFIED + } +} + // bizOrgInvitationToPendingProjectInvitationPb converts a biz.OrgInvitation to a pb.PendingProjectInvitation protobuf message. func bizOrgInvitationToPendingProjectInvitationPb(inv *biz.OrgInvitation) *pb.PendingProjectInvitation { base := &pb.PendingProjectInvitation{ diff --git a/app/controlplane/pkg/authz/authz.go b/app/controlplane/pkg/authz/authz.go index 7c5b04b0b..a62ad3cb3 100644 --- a/app/controlplane/pkg/authz/authz.go +++ b/app/controlplane/pkg/authz/authz.go @@ -53,6 +53,7 @@ const ( ResourceAPIToken = "api_token" ResourceProjectMembership = "project_membership" ResourceOrganizationInvitations = "organization_invitations" + ResourceGroupProjects = "group_projects" // We have for now three roles, viewer, admin and owner // The owner of an org @@ -154,6 +155,8 @@ var ( PolicyGroupAddMemberships = &Policy{ResourceGroupMembership, ActionCreate} PolicyGroupRemoveMemberships = &Policy{ResourceGroupMembership, ActionDelete} PolicyGroupUpdateMemberships = &Policy{ResourceGroupMembership, ActionUpdate} + // Group-Projects + PolicyGroupListProjects = &Policy{ResourceGroupProjects, ActionList} // API Token PolicyAPITokenList = &Policy{ResourceAPIToken, ActionList} PolicyAPITokenCreate = &Policy{ResourceAPIToken, ActionCreate} @@ -254,6 +257,9 @@ var RolesMap = map[Role][]*Policy{ PolicyGroupList, PolicyGroupRead, + // Group Projects + PolicyGroupListProjects, + // Group Memberships PolicyGroupListMemberships, @@ -393,7 +399,8 @@ var ServerOperationsMap = map[string][]*Policy{ "/controlplane.v1.GroupService/List": {PolicyGroupList}, "/controlplane.v1.GroupService/Get": {PolicyGroupRead}, // Group Memberships - "/controlplane.v1.GroupService/ListMembers": {PolicyGroupListMemberships}, + "/controlplane.v1.GroupService/ListMembers": {PolicyGroupListMemberships}, + "/controlplane.v1.GroupService/ListProjects": {PolicyGroupListProjects}, // For the following endpoints, we rely on the service layer to check the permissions // That's why we let everyone access them (empty policies) "/controlplane.v1.GroupService/AddMember": {}, diff --git a/app/controlplane/pkg/biz/group.go b/app/controlplane/pkg/biz/group.go index 8f5723a9a..3be207832 100644 --- a/app/controlplane/pkg/biz/group.go +++ b/app/controlplane/pkg/biz/group.go @@ -53,6 +53,8 @@ type GroupRepo interface { UpdateMemberMaintainerStatus(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID, userID uuid.UUID, isMaintainer bool) error // ListPendingInvitationsByGroup retrieves a list of pending invitations for a group ListPendingInvitationsByGroup(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID, paginationOpts *pagination.OffsetPaginationOpts) ([]*OrgInvitation, int, error) + // ListProjectsByGroup retrieves a list of projects that a group is a member of with pagination. + ListProjectsByGroup(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID, visibleProjectIDs []uuid.UUID, paginationOpts *pagination.OffsetPaginationOpts) ([]*GroupProjectInfo, int, error) } // GroupMembership represents a membership of a user in a group. @@ -169,6 +171,14 @@ type UpdateMemberMaintainerStatusOpts struct { IsMaintainer bool } +// ListProjectsByGroupOpts defines options for listing projects by group. +type ListProjectsByGroupOpts struct { + // Group reference + *IdentityReference + // FilterByProject is a list of project IDs to filter the results by. + FilterByProject []uuid.UUID +} + type GroupUseCase struct { // logger is used to log messages. logger *log.Helper @@ -181,9 +191,10 @@ type GroupUseCase struct { // Use Cases orgInvitationUC *OrgInvitationUseCase auditorUC *AuditorUseCase + membershipUC *MembershipUseCase } -func NewGroupUseCase(logger log.Logger, groupRepo GroupRepo, membershipRepo MembershipRepo, userRepo UserRepo, orgInvitationUC *OrgInvitationUseCase, auditorUC *AuditorUseCase, invitationRepo OrgInvitationRepo, enforcer *authz.Enforcer) *GroupUseCase { +func NewGroupUseCase(logger log.Logger, groupRepo GroupRepo, membershipRepo MembershipRepo, userRepo UserRepo, orgInvitationUC *OrgInvitationUseCase, auditorUC *AuditorUseCase, invitationRepo OrgInvitationRepo, enforcer *authz.Enforcer, membershipUseCase *MembershipUseCase) *GroupUseCase { return &GroupUseCase{ logger: log.NewHelper(log.With(logger, "component", "biz/group")), groupRepo: groupRepo, @@ -193,6 +204,7 @@ func NewGroupUseCase(logger log.Logger, groupRepo GroupRepo, membershipRepo Memb auditorUC: auditorUC, orgInvitationRepo: invitationRepo, enforcer: enforcer, + membershipUC: membershipUseCase, } } @@ -853,3 +865,36 @@ func (uc *GroupUseCase) ValidateGroupIdentifier(ctx context.Context, orgID uuid. return group.ID, nil } + +// ListProjectsByGroup retrieves a list of projects that a group is a member of with pagination. +func (uc *GroupUseCase) ListProjectsByGroup(ctx context.Context, orgID uuid.UUID, opts *ListProjectsByGroupOpts, paginationOpts *pagination.OffsetPaginationOpts) ([]*GroupProjectInfo, int, error) { + if opts == nil { + return nil, 0, NewErrValidationStr("options cannot be nil") + } + + if orgID == uuid.Nil { + return nil, 0, NewErrValidationStr("organization ID cannot be empty") + } + + resolvedGroupID, err := uc.ValidateGroupIdentifier(ctx, orgID, opts.ID, opts.Name) + if err != nil { + return nil, 0, err + } + + // Check the group exists + existingGroup, err := uc.groupRepo.FindByOrgAndID(ctx, orgID, resolvedGroupID) + if err != nil { + return nil, 0, fmt.Errorf("failed to find group: %w", err) + } + + if existingGroup == nil { + return nil, 0, NewErrNotFound("group") + } + + pgOpts := pagination.NewDefaultOffsetPaginationOpts() + if paginationOpts != nil { + pgOpts = paginationOpts + } + + return uc.groupRepo.ListProjectsByGroup(ctx, orgID, resolvedGroupID, opts.FilterByProject, pgOpts) +} diff --git a/app/controlplane/pkg/biz/project.go b/app/controlplane/pkg/biz/project.go index 8c1666c2f..d4b86b231 100644 --- a/app/controlplane/pkg/biz/project.go +++ b/app/controlplane/pkg/biz/project.go @@ -88,12 +88,30 @@ type ProjectMembership struct { MembershipType authz.MembershipType // Role represents the role of the user/group in the project (admin or viewer). Role authz.Role + // LatestProjectVersionID is the ID of the latest project version this membership is associated with. + LatestProjectVersionID *uuid.UUID // CreatedAt is the timestamp when the user/group was added to the project. CreatedAt *time.Time // UpdatedAt is the timestamp when the membership was last updated. UpdatedAt *time.Time } +// GroupProjectInfo represents detailed information about a project that a group is a member of +type GroupProjectInfo struct { + // ID is the unique identifier of the project + ID uuid.UUID + // Name is the name of the project + Name string + // Description is the description of the project + Description string + // Role represents the role of the group in the project (admin or viewer) + Role authz.Role + // LatestVersionID is the ID of the latest version of the project, if available + LatestVersionID *uuid.UUID + // CreatedAt is the timestamp when the project was created + CreatedAt *time.Time +} + // AddMemberToProjectOpts defines options for adding a member to a project. type AddMemberToProjectOpts struct { // ProjectReference is the reference to the project. diff --git a/app/controlplane/pkg/biz/testhelpers/wire_gen.go b/app/controlplane/pkg/biz/testhelpers/wire_gen.go index 786e6afb8..8346da9df 100644 --- a/app/controlplane/pkg/biz/testhelpers/wire_gen.go +++ b/app/controlplane/pkg/biz/testhelpers/wire_gen.go @@ -152,7 +152,7 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r } projectVersionRepo := data.NewProjectVersionRepo(dataData, logger) projectVersionUseCase := biz.NewProjectVersionUseCase(projectVersionRepo, logger) - groupUseCase := biz.NewGroupUseCase(logger, groupRepo, membershipRepo, userRepo, orgInvitationUseCase, auditorUseCase, orgInvitationRepo, enforcer) + groupUseCase := biz.NewGroupUseCase(logger, groupRepo, membershipRepo, userRepo, orgInvitationUseCase, auditorUseCase, orgInvitationRepo, enforcer, membershipUseCase) projectUseCase := biz.NewProjectsUseCase(logger, projectsRepo, membershipRepo, auditorUseCase, groupUseCase, membershipUseCase, orgInvitationUseCase, orgInvitationRepo, enforcer) testingRepos := &TestingRepos{ Membership: membershipRepo, diff --git a/app/controlplane/pkg/data/group.go b/app/controlplane/pkg/data/group.go index a9a1932f5..32e745f23 100644 --- a/app/controlplane/pkg/data/group.go +++ b/app/controlplane/pkg/data/group.go @@ -20,6 +20,10 @@ import ( "fmt" "time" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/projectversion" + + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/project" + "entgo.io/ent/dialect/sql/sqljson" "entgo.io/ent/dialect/sql" @@ -573,6 +577,109 @@ func (g GroupRepo) UpdateMemberMaintainerStatus(ctx context.Context, orgID uuid. }) } +// ListProjectsByGroup retrieves a list of projects that a group is a member of with pagination. +func (g GroupRepo) ListProjectsByGroup(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID, visibleProjectIDs []uuid.UUID, paginationOpts *pagination.OffsetPaginationOpts) ([]*biz.GroupProjectInfo, int, error) { + if paginationOpts == nil { + paginationOpts = pagination.NewDefaultOffsetPaginationOpts() + } + + // Get all memberships where this group is a member and the resource type is a project + membershipQuery := g.data.DB.Membership.Query(). + Where( + membership.MemberIDEQ(groupID), + membership.MembershipTypeEQ(authz.MembershipTypeGroup), + membership.ResourceTypeEQ(authz.ResourceTypeProject), + membership.HasOrganizationWith(organization.IDEQ(orgID)), + ) + + // If visibleProjectIDs is provided, filter memberships to include only those projects + if visibleProjectIDs != nil { + membershipQuery = membershipQuery.Where(membership.ResourceIDIn(visibleProjectIDs...)) + } + + // Get total count first (after applying visibility filters) + count, err := membershipQuery.Count(ctx) + if err != nil { + return nil, 0, fmt.Errorf("failed to count project memberships: %w", err) + } + + // Apply pagination to the memberships query + memberships, err := membershipQuery. + Order(ent.Desc(membership.FieldCreatedAt)). + Offset(paginationOpts.Offset()). + Limit(paginationOpts.Limit()). + All(ctx) + if err != nil { + return nil, 0, fmt.Errorf("failed to query memberships: %w", err) + } + + // If no memberships found, return empty result + if len(memberships) == 0 { + return []*biz.GroupProjectInfo{}, count, nil + } + + // Extract project IDs from memberships + projectIDs := make([]uuid.UUID, 0, len(memberships)) + for _, m := range memberships { + projectIDs = append(projectIDs, m.ResourceID) + } + + // Query the projects + entProjects, err := g.data.DB.Project.Query(). + Where( + project.IDIn(projectIDs...), + project.OrganizationID(orgID), + project.DeletedAtIsNil(), + ). + WithVersions(func(query *ent.ProjectVersionQuery) { + query.Where(projectversion.Latest(true)).Select(projectversion.FieldID) + }). + All(ctx) + if err != nil { + return nil, 0, fmt.Errorf("failed to fetch projects: %w", err) + } + + // Create a map of projects by ID for efficient lookup + projectsMap := make(map[uuid.UUID]*ent.Project, len(entProjects)) + for _, p := range entProjects { + projectsMap[p.ID] = p + } + + // Create a map to store role by project ID + projectRoles := make(map[uuid.UUID]authz.Role, len(memberships)) + for _, m := range memberships { + projectRoles[m.ResourceID] = m.Role + } + + // Build the result following the order of memberships + projectInfos := make([]*biz.GroupProjectInfo, 0, len(memberships)) + + for _, m := range memberships { + pr, exists := projectsMap[m.ResourceID] + if !exists { + // Skip projects that might have been deleted but membership still exists + continue + } + + projectInfo := &biz.GroupProjectInfo{ + ID: pr.ID, + Name: pr.Name, + Description: pr.Description, + Role: m.Role, + CreatedAt: toTimePtr(pr.CreatedAt), + } + + // If the project has versions, include the latest version ID + if len(pr.Edges.Versions) > 0 { + projectInfo.LatestVersionID = &pr.Edges.Versions[0].ID + } + + projectInfos = append(projectInfos, projectInfo) + } + + return projectInfos, count, nil +} + // entGroupToBiz converts an ent.Group to a biz.Group. func entGroupToBiz(gr *ent.Group) *biz.Group { grp := &biz.Group{