diff --git a/app/controlplane/api/controlplane/v1/group.pb.go b/app/controlplane/api/controlplane/v1/group.pb.go new file mode 100644 index 000000000..428ed5763 --- /dev/null +++ b/app/controlplane/api/controlplane/v1/group.pb.go @@ -0,0 +1,1333 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc (unknown) +// source: controlplane/v1/group.proto + +package v1 + +import ( + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// GroupServiceCreateRequest contains the information needed to create a new group +type GroupServiceCreateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Name of the group to create + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Description providing additional information about the group + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` +} + +func (x *GroupServiceCreateRequest) Reset() { + *x = GroupServiceCreateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceCreateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceCreateRequest) ProtoMessage() {} + +func (x *GroupServiceCreateRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceCreateRequest.ProtoReflect.Descriptor instead. +func (*GroupServiceCreateRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{0} +} + +func (x *GroupServiceCreateRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GroupServiceCreateRequest) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +// GroupServiceCreateResponse contains the newly created group +type GroupServiceCreateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The created group with all its attributes + Group *Group `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` +} + +func (x *GroupServiceCreateResponse) Reset() { + *x = GroupServiceCreateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceCreateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceCreateResponse) ProtoMessage() {} + +func (x *GroupServiceCreateResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceCreateResponse.ProtoReflect.Descriptor instead. +func (*GroupServiceCreateResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{1} +} + +func (x *GroupServiceCreateResponse) GetGroup() *Group { + if x != nil { + return x.Group + } + return nil +} + +// GroupServiceGetRequest contains the identifier for the group to retrieve +type GroupServiceGetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // UUID of the group to retrieve + GroupId string `protobuf:"bytes,1,opt,name=group_id,json=groupId,proto3" json:"group_id,omitempty"` +} + +func (x *GroupServiceGetRequest) Reset() { + *x = GroupServiceGetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceGetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceGetRequest) ProtoMessage() {} + +func (x *GroupServiceGetRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceGetRequest.ProtoReflect.Descriptor instead. +func (*GroupServiceGetRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{2} +} + +func (x *GroupServiceGetRequest) GetGroupId() string { + if x != nil { + return x.GroupId + } + return "" +} + +// GroupServiceGetResponse contains the requested group information +type GroupServiceGetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The requested group with all its attributes + Group *Group `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` +} + +func (x *GroupServiceGetResponse) Reset() { + *x = GroupServiceGetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceGetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceGetResponse) ProtoMessage() {} + +func (x *GroupServiceGetResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceGetResponse.ProtoReflect.Descriptor instead. +func (*GroupServiceGetResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{3} +} + +func (x *GroupServiceGetResponse) GetGroup() *Group { + if x != nil { + return x.Group + } + return nil +} + +// GroupServiceListsRequest contains parameters for filtering and paginating group results +type GroupServiceListRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Optional filter to search by group name + Name *string `protobuf:"bytes,1,opt,name=name,proto3,oneof" json:"name,omitempty"` + // Optional filter to search by group description + Description *string `protobuf:"bytes,2,opt,name=description,proto3,oneof" json:"description,omitempty"` + // Optional filter to search by member email address + MemberEmail *string `protobuf:"bytes,3,opt,name=member_email,json=memberEmail,proto3,oneof" json:"member_email,omitempty"` + // Pagination parameters to limit and offset results + Pagination *OffsetPaginationRequest `protobuf:"bytes,4,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (x *GroupServiceListRequest) Reset() { + *x = GroupServiceListRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceListRequest) ProtoMessage() {} + +func (x *GroupServiceListRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceListRequest.ProtoReflect.Descriptor instead. +func (*GroupServiceListRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{4} +} + +func (x *GroupServiceListRequest) GetName() string { + if x != nil && x.Name != nil { + return *x.Name + } + return "" +} + +func (x *GroupServiceListRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *GroupServiceListRequest) GetMemberEmail() string { + if x != nil && x.MemberEmail != nil { + return *x.MemberEmail + } + return "" +} + +func (x *GroupServiceListRequest) GetPagination() *OffsetPaginationRequest { + if x != nil { + return x.Pagination + } + return nil +} + +// GroupServiceListsResponse contains a paginated list of groups +type GroupServiceListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // List of groups matching the request criteria + Groups []*Group `protobuf:"bytes,1,rep,name=groups,proto3" json:"groups,omitempty"` + // Pagination information for the response + Pagination *OffsetPaginationResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (x *GroupServiceListResponse) Reset() { + *x = GroupServiceListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceListResponse) ProtoMessage() {} + +func (x *GroupServiceListResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceListResponse.ProtoReflect.Descriptor instead. +func (*GroupServiceListResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{5} +} + +func (x *GroupServiceListResponse) GetGroups() []*Group { + if x != nil { + return x.Groups + } + return nil +} + +func (x *GroupServiceListResponse) GetPagination() *OffsetPaginationResponse { + if x != nil { + return x.Pagination + } + return nil +} + +// GroupServiceUpdateRequest contains the fields that can be updated for a group +type GroupServiceUpdateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // UUID of the group to update + GroupId string `protobuf:"bytes,1,opt,name=group_id,json=groupId,proto3" json:"group_id,omitempty"` + // New name for the group (if provided) + Name *string `protobuf:"bytes,2,opt,name=name,proto3,oneof" json:"name,omitempty"` + // New description for the group (if provided) + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` +} + +func (x *GroupServiceUpdateRequest) Reset() { + *x = GroupServiceUpdateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceUpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceUpdateRequest) ProtoMessage() {} + +func (x *GroupServiceUpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceUpdateRequest.ProtoReflect.Descriptor instead. +func (*GroupServiceUpdateRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{6} +} + +func (x *GroupServiceUpdateRequest) GetGroupId() string { + if x != nil { + return x.GroupId + } + return "" +} + +func (x *GroupServiceUpdateRequest) GetName() string { + if x != nil && x.Name != nil { + return *x.Name + } + return "" +} + +func (x *GroupServiceUpdateRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +// GroupServiceUpdateResponse contains the updated group information +type GroupServiceUpdateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The updated group with all its attributes + Group *Group `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` +} + +func (x *GroupServiceUpdateResponse) Reset() { + *x = GroupServiceUpdateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceUpdateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceUpdateResponse) ProtoMessage() {} + +func (x *GroupServiceUpdateResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceUpdateResponse.ProtoReflect.Descriptor instead. +func (*GroupServiceUpdateResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{7} +} + +func (x *GroupServiceUpdateResponse) GetGroup() *Group { + if x != nil { + return x.Group + } + return nil +} + +// GroupServiceDeleteRequest contains the identifier for the group to delete +type GroupServiceDeleteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // UUID of the group to delete + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *GroupServiceDeleteRequest) Reset() { + *x = GroupServiceDeleteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceDeleteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceDeleteRequest) ProtoMessage() {} + +func (x *GroupServiceDeleteRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceDeleteRequest.ProtoReflect.Descriptor instead. +func (*GroupServiceDeleteRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{8} +} + +func (x *GroupServiceDeleteRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +// GroupServiceDeleteResponse is returned upon successful deletion of a group +type GroupServiceDeleteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GroupServiceDeleteResponse) Reset() { + *x = GroupServiceDeleteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceDeleteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceDeleteResponse) ProtoMessage() {} + +func (x *GroupServiceDeleteResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceDeleteResponse.ProtoReflect.Descriptor instead. +func (*GroupServiceDeleteResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{9} +} + +type GroupServiceListMembersResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // List of members in the group + Members []*GroupMember `protobuf:"bytes,1,rep,name=members,proto3" json:"members,omitempty"` + // Pagination information for the response + Pagination *OffsetPaginationResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (x *GroupServiceListMembersResponse) Reset() { + *x = GroupServiceListMembersResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceListMembersResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceListMembersResponse) ProtoMessage() {} + +func (x *GroupServiceListMembersResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceListMembersResponse.ProtoReflect.Descriptor instead. +func (*GroupServiceListMembersResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{10} +} + +func (x *GroupServiceListMembersResponse) GetMembers() []*GroupMember { + if x != nil { + return x.Members + } + return nil +} + +func (x *GroupServiceListMembersResponse) GetPagination() *OffsetPaginationResponse { + if x != nil { + return x.Pagination + } + return nil +} + +// GroupServiceListMembersRequest contains the identifier for the group whose members are to be listed +type GroupServiceListMembersRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // UUID of the group whose members are to be listed + GroupId string `protobuf:"bytes,1,opt,name=group_id,json=groupId,proto3" json:"group_id,omitempty"` + // Optional filter to search only by maintainers or not + Maintainers *bool `protobuf:"varint,2,opt,name=maintainers,proto3,oneof" json:"maintainers,omitempty"` + // Optional filter to search by member email address + MemberEmail *string `protobuf:"bytes,3,opt,name=member_email,json=memberEmail,proto3,oneof" json:"member_email,omitempty"` + // Pagination parameters to limit and offset results + Pagination *OffsetPaginationRequest `protobuf:"bytes,4,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (x *GroupServiceListMembersRequest) Reset() { + *x = GroupServiceListMembersRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupServiceListMembersRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupServiceListMembersRequest) ProtoMessage() {} + +func (x *GroupServiceListMembersRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupServiceListMembersRequest.ProtoReflect.Descriptor instead. +func (*GroupServiceListMembersRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{11} +} + +func (x *GroupServiceListMembersRequest) GetGroupId() string { + if x != nil { + return x.GroupId + } + return "" +} + +func (x *GroupServiceListMembersRequest) GetMaintainers() bool { + if x != nil && x.Maintainers != nil { + return *x.Maintainers + } + return false +} + +func (x *GroupServiceListMembersRequest) GetMemberEmail() string { + if x != nil && x.MemberEmail != nil { + return *x.MemberEmail + } + return "" +} + +func (x *GroupServiceListMembersRequest) GetPagination() *OffsetPaginationRequest { + if x != nil { + return x.Pagination + } + return nil +} + +// Group represents a collection of users with shared access to resources +type Group struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Unique identifier for the group + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Human-readable name of the group + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // Additional details about the group's purpose + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + // UUID of the organization that this group belongs to + OrganizationId string `protobuf:"bytes,4,opt,name=organization_id,json=organizationId,proto3" json:"organization_id,omitempty"` + // Timestamp when the group was created + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + // Timestamp when the group was last modified + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` +} + +func (x *Group) Reset() { + *x = Group{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Group) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Group) ProtoMessage() {} + +func (x *Group) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Group.ProtoReflect.Descriptor instead. +func (*Group) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{12} +} + +func (x *Group) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Group) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Group) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Group) GetOrganizationId() string { + if x != nil { + return x.OrganizationId + } + return "" +} + +func (x *Group) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Group) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +// GroupMember represents a user's membership within a group with their role information +type GroupMember struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The user who is a member of the group + User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` + // Indicates whether the user has maintainer (admin) privileges in the group + IsMaintainer bool `protobuf:"varint,2,opt,name=is_maintainer,json=isMaintainer,proto3" json:"is_maintainer,omitempty"` + // Timestamp when the group membership was created + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + // Timestamp when the group membership was last modified + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` +} + +func (x *GroupMember) Reset() { + *x = GroupMember{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_group_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupMember) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupMember) ProtoMessage() {} + +func (x *GroupMember) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_group_proto_msgTypes[13] + 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 GroupMember.ProtoReflect.Descriptor instead. +func (*GroupMember) Descriptor() ([]byte, []int) { + return file_controlplane_v1_group_proto_rawDescGZIP(), []int{13} +} + +func (x *GroupMember) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +func (x *GroupMember) GetIsMaintainer() bool { + if x != nil { + return x.IsMaintainer + } + return false +} + +func (x *GroupMember) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *GroupMember) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +var File_controlplane_v1_group_proto protoreflect.FileDescriptor + +var file_controlplane_v1_group_proto_rawDesc = []byte{ + 0x0a, 0x1b, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1b, + 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5a, 0x0a, 0x19, 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, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x4a, 0x0a, 0x1a, 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, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x22, + 0x40, 0x0a, 0x16, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x08, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, + 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, + 0x64, 0x22, 0x47, 0x0a, 0x17, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x05, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, + 0x6f, 0x75, 0x70, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x22, 0xf5, 0x01, 0x0a, 0x17, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, + 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x0b, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x48, + 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 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, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, + 0x69, 0x6c, 0x22, 0x95, 0x01, 0x0a, 0x18, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2e, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 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, 0xac, 0x01, 0x0a, 0x19, 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, 0x12, 0x26, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, + 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, + 0x12, 0x1f, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, + 0xba, 0x48, 0x03, 0xd0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x88, 0x01, + 0x01, 0x12, 0x2d, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xd0, 0x01, 0x01, 0x48, 0x01, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, + 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4a, 0x0a, 0x1a, 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, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x05, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x22, 0x35, 0x0a, 0x19, 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, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, + 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x1c, 0x0a, 0x1a, + 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, 0xa4, 0x01, 0x0a, 0x1f, 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, 0x12, 0x36, + 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 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, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 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, 0x92, 0x02, 0x0a, 0x1e, 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, 0x12, 0x26, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, + 0xb0, 0x01, 0x01, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x0b, + 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x42, 0x06, 0xba, 0x48, 0x03, 0xd0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x0b, 0x6d, 0x61, 0x69, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x88, 0x01, 0x01, 0x12, 0x2e, 0x0a, 0x0c, 0x6d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xd0, 0x01, 0x01, 0x48, 0x01, 0x52, 0x0b, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x48, 0x0a, 0x0a, 0x70, + 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 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, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x73, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0xec, 0x01, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, + 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, 0x27, 0x0a, 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, + 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, + 0x64, 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, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0xd3, 0x01, 0x0a, 0x0b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x29, 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, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, + 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x73, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x4d, 0x61, 0x69, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 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, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x32, 0xec, 0x04, 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, 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, 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, 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, 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 ( + file_controlplane_v1_group_proto_rawDescOnce sync.Once + file_controlplane_v1_group_proto_rawDescData = file_controlplane_v1_group_proto_rawDesc +) + +func file_controlplane_v1_group_proto_rawDescGZIP() []byte { + file_controlplane_v1_group_proto_rawDescOnce.Do(func() { + file_controlplane_v1_group_proto_rawDescData = protoimpl.X.CompressGZIP(file_controlplane_v1_group_proto_rawDescData) + }) + return file_controlplane_v1_group_proto_rawDescData +} + +var file_controlplane_v1_group_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_controlplane_v1_group_proto_goTypes = []interface{}{ + (*GroupServiceCreateRequest)(nil), // 0: controlplane.v1.GroupServiceCreateRequest + (*GroupServiceCreateResponse)(nil), // 1: controlplane.v1.GroupServiceCreateResponse + (*GroupServiceGetRequest)(nil), // 2: controlplane.v1.GroupServiceGetRequest + (*GroupServiceGetResponse)(nil), // 3: controlplane.v1.GroupServiceGetResponse + (*GroupServiceListRequest)(nil), // 4: controlplane.v1.GroupServiceListRequest + (*GroupServiceListResponse)(nil), // 5: controlplane.v1.GroupServiceListResponse + (*GroupServiceUpdateRequest)(nil), // 6: controlplane.v1.GroupServiceUpdateRequest + (*GroupServiceUpdateResponse)(nil), // 7: controlplane.v1.GroupServiceUpdateResponse + (*GroupServiceDeleteRequest)(nil), // 8: controlplane.v1.GroupServiceDeleteRequest + (*GroupServiceDeleteResponse)(nil), // 9: controlplane.v1.GroupServiceDeleteResponse + (*GroupServiceListMembersResponse)(nil), // 10: controlplane.v1.GroupServiceListMembersResponse + (*GroupServiceListMembersRequest)(nil), // 11: controlplane.v1.GroupServiceListMembersRequest + (*Group)(nil), // 12: controlplane.v1.Group + (*GroupMember)(nil), // 13: controlplane.v1.GroupMember + (*OffsetPaginationRequest)(nil), // 14: controlplane.v1.OffsetPaginationRequest + (*OffsetPaginationResponse)(nil), // 15: controlplane.v1.OffsetPaginationResponse + (*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp + (*User)(nil), // 17: controlplane.v1.User +} +var file_controlplane_v1_group_proto_depIdxs = []int32{ + 12, // 0: controlplane.v1.GroupServiceCreateResponse.group:type_name -> controlplane.v1.Group + 12, // 1: controlplane.v1.GroupServiceGetResponse.group:type_name -> controlplane.v1.Group + 14, // 2: controlplane.v1.GroupServiceListRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest + 12, // 3: controlplane.v1.GroupServiceListResponse.groups:type_name -> controlplane.v1.Group + 15, // 4: controlplane.v1.GroupServiceListResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse + 12, // 5: controlplane.v1.GroupServiceUpdateResponse.group:type_name -> controlplane.v1.Group + 13, // 6: controlplane.v1.GroupServiceListMembersResponse.members:type_name -> controlplane.v1.GroupMember + 15, // 7: controlplane.v1.GroupServiceListMembersResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse + 14, // 8: controlplane.v1.GroupServiceListMembersRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest + 16, // 9: controlplane.v1.Group.created_at:type_name -> google.protobuf.Timestamp + 16, // 10: controlplane.v1.Group.updated_at:type_name -> google.protobuf.Timestamp + 17, // 11: controlplane.v1.GroupMember.user:type_name -> controlplane.v1.User + 16, // 12: controlplane.v1.GroupMember.created_at:type_name -> google.protobuf.Timestamp + 16, // 13: controlplane.v1.GroupMember.updated_at:type_name -> google.protobuf.Timestamp + 0, // 14: controlplane.v1.GroupService.Create:input_type -> controlplane.v1.GroupServiceCreateRequest + 2, // 15: controlplane.v1.GroupService.Get:input_type -> controlplane.v1.GroupServiceGetRequest + 4, // 16: controlplane.v1.GroupService.List:input_type -> controlplane.v1.GroupServiceListRequest + 6, // 17: controlplane.v1.GroupService.Update:input_type -> controlplane.v1.GroupServiceUpdateRequest + 8, // 18: controlplane.v1.GroupService.Delete:input_type -> controlplane.v1.GroupServiceDeleteRequest + 11, // 19: controlplane.v1.GroupService.ListMembers:input_type -> controlplane.v1.GroupServiceListMembersRequest + 1, // 20: controlplane.v1.GroupService.Create:output_type -> controlplane.v1.GroupServiceCreateResponse + 3, // 21: controlplane.v1.GroupService.Get:output_type -> controlplane.v1.GroupServiceGetResponse + 5, // 22: controlplane.v1.GroupService.List:output_type -> controlplane.v1.GroupServiceListResponse + 7, // 23: controlplane.v1.GroupService.Update:output_type -> controlplane.v1.GroupServiceUpdateResponse + 9, // 24: controlplane.v1.GroupService.Delete:output_type -> controlplane.v1.GroupServiceDeleteResponse + 10, // 25: controlplane.v1.GroupService.ListMembers:output_type -> controlplane.v1.GroupServiceListMembersResponse + 20, // [20:26] is the sub-list for method output_type + 14, // [14:20] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name +} + +func init() { file_controlplane_v1_group_proto_init() } +func file_controlplane_v1_group_proto_init() { + if File_controlplane_v1_group_proto != nil { + return + } + file_controlplane_v1_pagination_proto_init() + file_controlplane_v1_response_messages_proto_init() + if !protoimpl.UnsafeEnabled { + file_controlplane_v1_group_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceCreateRequest); 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[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceCreateResponse); 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[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceGetRequest); 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[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceGetResponse); 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].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceListRequest); 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[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceListResponse); 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[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceUpdateRequest); 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[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceUpdateResponse); 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[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceDeleteRequest); 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[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceDeleteResponse); 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[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceListMembersResponse); 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[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupServiceListMembersRequest); 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[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Group); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_group_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupMember); 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{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controlplane_v1_group_proto_rawDesc, + NumEnums: 0, + NumMessages: 14, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_controlplane_v1_group_proto_goTypes, + DependencyIndexes: file_controlplane_v1_group_proto_depIdxs, + MessageInfos: file_controlplane_v1_group_proto_msgTypes, + }.Build() + File_controlplane_v1_group_proto = out.File + file_controlplane_v1_group_proto_rawDesc = nil + file_controlplane_v1_group_proto_goTypes = nil + file_controlplane_v1_group_proto_depIdxs = nil +} diff --git a/app/controlplane/api/controlplane/v1/group.proto b/app/controlplane/api/controlplane/v1/group.proto new file mode 100644 index 000000000..5125cd8c0 --- /dev/null +++ b/app/controlplane/api/controlplane/v1/group.proto @@ -0,0 +1,168 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package controlplane.v1; + +import "buf/validate/validate.proto"; +import "controlplane/v1/pagination.proto"; +import "controlplane/v1/response_messages.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1;v1"; + +// GroupService provides operations for managing groups within the system +service GroupService { + // Create creates a new group with the specified name and description + rpc Create(GroupServiceCreateRequest) returns (GroupServiceCreateResponse) {} + // Get retrieves a specific group by its ID + rpc Get(GroupServiceGetRequest) returns (GroupServiceGetResponse) {} + // List retrieves a paginated list of groups, with optional filtering + rpc List(GroupServiceListRequest) returns (GroupServiceListResponse) {} + // Update modifies an existing group's attributes + rpc Update(GroupServiceUpdateRequest) returns (GroupServiceUpdateResponse) {} + // Delete removes a group from the system + rpc Delete(GroupServiceDeleteRequest) returns (GroupServiceDeleteResponse) {} + // ListMembers retrieves the members of a specific group + rpc ListMembers(GroupServiceListMembersRequest) returns (GroupServiceListMembersResponse) {} +} + +// GroupServiceCreateRequest contains the information needed to create a new group +message GroupServiceCreateRequest { + // Name of the group to create + string name = 1 [(buf.validate.field).string.min_len = 1]; + // Description providing additional information about the group + string description = 2; +} + +// GroupServiceCreateResponse contains the newly created group +message GroupServiceCreateResponse { + // The created group with all its attributes + Group group = 1; +} + +// GroupServiceGetRequest contains the identifier for the group to retrieve +message GroupServiceGetRequest { + // UUID of the group to retrieve + string group_id = 1 [ + (buf.validate.field).string.uuid = true, + (buf.validate.field).required = true + ]; +} + +// GroupServiceGetResponse contains the requested group information +message GroupServiceGetResponse { + // The requested group with all its attributes + Group group = 1; +} + +// GroupServiceListsRequest contains parameters for filtering and paginating group results +message GroupServiceListRequest { + // Optional filter to search by group name + optional string name = 1; + // Optional filter to search by group description + optional string description = 2; + // Optional filter to search by member email address + optional string member_email = 3; + // Pagination parameters to limit and offset results + OffsetPaginationRequest pagination = 4; +} + +// GroupServiceListsResponse contains a paginated list of groups +message GroupServiceListResponse { + // List of groups matching the request criteria + repeated Group groups = 1; + // Pagination information for the response + OffsetPaginationResponse pagination = 2; +} + +// GroupServiceUpdateRequest contains the fields that can be updated for a group +message GroupServiceUpdateRequest { + // UUID of the group to update + string group_id = 1 [ + (buf.validate.field).string.uuid = true, + (buf.validate.field).required = true + ]; + // New name for the group (if provided) + optional string name = 2 [(buf.validate.field).ignore_empty = true]; + // New description for the group (if provided) + optional string description = 3 [(buf.validate.field).ignore_empty = true]; +} + +// GroupServiceUpdateResponse contains the updated group information +message GroupServiceUpdateResponse { + // The updated group with all its attributes + Group group = 1; +} + +// GroupServiceDeleteRequest contains the identifier for the group to delete +message GroupServiceDeleteRequest { + // UUID of the group to delete + string id = 1 [(buf.validate.field).string.uuid = true]; +} + +// GroupServiceDeleteResponse is returned upon successful deletion of a group +message GroupServiceDeleteResponse {} + +message GroupServiceListMembersResponse { + // List of members in the group + repeated GroupMember members = 1; + // Pagination information for the response + OffsetPaginationResponse pagination = 2; +} + +// GroupServiceListMembersRequest contains the identifier for the group whose members are to be listed +message GroupServiceListMembersRequest { + // UUID of the group whose members are to be listed + string group_id = 1 [ + (buf.validate.field).string.uuid = true, + (buf.validate.field).required = true + ]; + // Optional filter to search only by maintainers or not + optional bool maintainers = 2 [(buf.validate.field).ignore_empty = true]; + // Optional filter to search by member email address + optional string member_email = 3 [(buf.validate.field).ignore_empty = true]; + // Pagination parameters to limit and offset results + OffsetPaginationRequest pagination = 4; +} + +// Group represents a collection of users with shared access to resources +message Group { + // Unique identifier for the group + string id = 1; + // Human-readable name of the group + string name = 2; + // Additional details about the group's purpose + string description = 3; + // UUID of the organization that this group belongs to + string organization_id = 4; + // Timestamp when the group was created + google.protobuf.Timestamp created_at = 5; + // Timestamp when the group was last modified + google.protobuf.Timestamp updated_at = 6; +} + +// GroupMember represents a user's membership within a group with their role information +message GroupMember { + // The user who is a member of the group + User user = 1; + // Indicates whether the user has maintainer (admin) privileges in the group + bool is_maintainer = 2; + // Timestamp when the group membership was created + google.protobuf.Timestamp created_at = 3; + // Timestamp when the group membership was last modified + google.protobuf.Timestamp updated_at = 4; +} diff --git a/app/controlplane/api/controlplane/v1/group_grpc.pb.go b/app/controlplane/api/controlplane/v1/group_grpc.pb.go new file mode 100644 index 000000000..cfb483a9b --- /dev/null +++ b/app/controlplane/api/controlplane/v1/group_grpc.pb.go @@ -0,0 +1,321 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: controlplane/v1/group.proto + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + GroupService_Create_FullMethodName = "/controlplane.v1.GroupService/Create" + GroupService_Get_FullMethodName = "/controlplane.v1.GroupService/Get" + GroupService_List_FullMethodName = "/controlplane.v1.GroupService/List" + GroupService_Update_FullMethodName = "/controlplane.v1.GroupService/Update" + GroupService_Delete_FullMethodName = "/controlplane.v1.GroupService/Delete" + GroupService_ListMembers_FullMethodName = "/controlplane.v1.GroupService/ListMembers" +) + +// GroupServiceClient is the client API for GroupService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GroupServiceClient interface { + // Create creates a new group with the specified name and description + Create(ctx context.Context, in *GroupServiceCreateRequest, opts ...grpc.CallOption) (*GroupServiceCreateResponse, error) + // Get retrieves a specific group by its ID + Get(ctx context.Context, in *GroupServiceGetRequest, opts ...grpc.CallOption) (*GroupServiceGetResponse, error) + // List retrieves a paginated list of groups, with optional filtering + List(ctx context.Context, in *GroupServiceListRequest, opts ...grpc.CallOption) (*GroupServiceListResponse, error) + // Update modifies an existing group's attributes + Update(ctx context.Context, in *GroupServiceUpdateRequest, opts ...grpc.CallOption) (*GroupServiceUpdateResponse, error) + // Delete removes a group from the system + Delete(ctx context.Context, in *GroupServiceDeleteRequest, opts ...grpc.CallOption) (*GroupServiceDeleteResponse, error) + // ListMembers retrieves the members of a specific group + ListMembers(ctx context.Context, in *GroupServiceListMembersRequest, opts ...grpc.CallOption) (*GroupServiceListMembersResponse, error) +} + +type groupServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewGroupServiceClient(cc grpc.ClientConnInterface) GroupServiceClient { + return &groupServiceClient{cc} +} + +func (c *groupServiceClient) Create(ctx context.Context, in *GroupServiceCreateRequest, opts ...grpc.CallOption) (*GroupServiceCreateResponse, error) { + out := new(GroupServiceCreateResponse) + err := c.cc.Invoke(ctx, GroupService_Create_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) Get(ctx context.Context, in *GroupServiceGetRequest, opts ...grpc.CallOption) (*GroupServiceGetResponse, error) { + out := new(GroupServiceGetResponse) + err := c.cc.Invoke(ctx, GroupService_Get_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) List(ctx context.Context, in *GroupServiceListRequest, opts ...grpc.CallOption) (*GroupServiceListResponse, error) { + out := new(GroupServiceListResponse) + err := c.cc.Invoke(ctx, GroupService_List_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) Update(ctx context.Context, in *GroupServiceUpdateRequest, opts ...grpc.CallOption) (*GroupServiceUpdateResponse, error) { + out := new(GroupServiceUpdateResponse) + err := c.cc.Invoke(ctx, GroupService_Update_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) Delete(ctx context.Context, in *GroupServiceDeleteRequest, opts ...grpc.CallOption) (*GroupServiceDeleteResponse, error) { + out := new(GroupServiceDeleteResponse) + err := c.cc.Invoke(ctx, GroupService_Delete_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *groupServiceClient) ListMembers(ctx context.Context, in *GroupServiceListMembersRequest, opts ...grpc.CallOption) (*GroupServiceListMembersResponse, error) { + out := new(GroupServiceListMembersResponse) + err := c.cc.Invoke(ctx, GroupService_ListMembers_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 +type GroupServiceServer interface { + // Create creates a new group with the specified name and description + Create(context.Context, *GroupServiceCreateRequest) (*GroupServiceCreateResponse, error) + // Get retrieves a specific group by its ID + Get(context.Context, *GroupServiceGetRequest) (*GroupServiceGetResponse, error) + // List retrieves a paginated list of groups, with optional filtering + List(context.Context, *GroupServiceListRequest) (*GroupServiceListResponse, error) + // Update modifies an existing group's attributes + Update(context.Context, *GroupServiceUpdateRequest) (*GroupServiceUpdateResponse, error) + // Delete removes a group from the system + Delete(context.Context, *GroupServiceDeleteRequest) (*GroupServiceDeleteResponse, error) + // ListMembers retrieves the members of a specific group + ListMembers(context.Context, *GroupServiceListMembersRequest) (*GroupServiceListMembersResponse, error) + mustEmbedUnimplementedGroupServiceServer() +} + +// UnimplementedGroupServiceServer must be embedded to have forward compatible implementations. +type UnimplementedGroupServiceServer struct { +} + +func (UnimplementedGroupServiceServer) Create(context.Context, *GroupServiceCreateRequest) (*GroupServiceCreateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") +} +func (UnimplementedGroupServiceServer) Get(context.Context, *GroupServiceGetRequest) (*GroupServiceGetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (UnimplementedGroupServiceServer) List(context.Context, *GroupServiceListRequest) (*GroupServiceListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (UnimplementedGroupServiceServer) Update(context.Context, *GroupServiceUpdateRequest) (*GroupServiceUpdateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (UnimplementedGroupServiceServer) Delete(context.Context, *GroupServiceDeleteRequest) (*GroupServiceDeleteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") +} +func (UnimplementedGroupServiceServer) ListMembers(context.Context, *GroupServiceListMembersRequest) (*GroupServiceListMembersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListMembers not implemented") +} +func (UnimplementedGroupServiceServer) mustEmbedUnimplementedGroupServiceServer() {} + +// UnsafeGroupServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GroupServiceServer will +// result in compilation errors. +type UnsafeGroupServiceServer interface { + mustEmbedUnimplementedGroupServiceServer() +} + +func RegisterGroupServiceServer(s grpc.ServiceRegistrar, srv GroupServiceServer) { + s.RegisterService(&GroupService_ServiceDesc, srv) +} + +func _GroupService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupServiceCreateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).Create(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_Create_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).Create(ctx, req.(*GroupServiceCreateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupServiceGetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_Get_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).Get(ctx, req.(*GroupServiceGetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupServiceListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).List(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_List_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).List(ctx, req.(*GroupServiceListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupServiceUpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).Update(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_Update_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).Update(ctx, req.(*GroupServiceUpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupServiceDeleteRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).Delete(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_Delete_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).Delete(ctx, req.(*GroupServiceDeleteRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GroupService_ListMembers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GroupServiceListMembersRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GroupServiceServer).ListMembers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GroupService_ListMembers_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GroupServiceServer).ListMembers(ctx, req.(*GroupServiceListMembersRequest)) + } + 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) +var GroupService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "controlplane.v1.GroupService", + HandlerType: (*GroupServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Create", + Handler: _GroupService_Create_Handler, + }, + { + MethodName: "Get", + Handler: _GroupService_Get_Handler, + }, + { + MethodName: "List", + Handler: _GroupService_List_Handler, + }, + { + MethodName: "Update", + Handler: _GroupService_Update_Handler, + }, + { + MethodName: "Delete", + Handler: _GroupService_Delete_Handler, + }, + { + MethodName: "ListMembers", + Handler: _GroupService_ListMembers_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "controlplane/v1/group.proto", +} diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/group.ts b/app/controlplane/api/gen/frontend/controlplane/v1/group.ts new file mode 100644 index 000000000..9b1e1fcd6 --- /dev/null +++ b/app/controlplane/api/gen/frontend/controlplane/v1/group.ts @@ -0,0 +1,1547 @@ +/* eslint-disable */ +import { grpc } from "@improbable-eng/grpc-web"; +import { BrowserHeaders } from "browser-headers"; +import _m0 from "protobufjs/minimal"; +import { Timestamp } from "../../google/protobuf/timestamp"; +import { OffsetPaginationRequest, OffsetPaginationResponse } from "./pagination"; +import { User } from "./response_messages"; + +export const protobufPackage = "controlplane.v1"; + +/** GroupServiceCreateRequest contains the information needed to create a new group */ +export interface GroupServiceCreateRequest { + /** Name of the group to create */ + name: string; + /** Description providing additional information about the group */ + description: string; +} + +/** GroupServiceCreateResponse contains the newly created group */ +export interface GroupServiceCreateResponse { + /** The created group with all its attributes */ + group?: Group; +} + +/** GroupServiceGetRequest contains the identifier for the group to retrieve */ +export interface GroupServiceGetRequest { + /** UUID of the group to retrieve */ + groupId: string; +} + +/** GroupServiceGetResponse contains the requested group information */ +export interface GroupServiceGetResponse { + /** The requested group with all its attributes */ + group?: Group; +} + +/** GroupServiceListsRequest contains parameters for filtering and paginating group results */ +export interface GroupServiceListRequest { + /** Optional filter to search by group name */ + name?: + | string + | undefined; + /** Optional filter to search by group description */ + description?: + | string + | undefined; + /** Optional filter to search by member email address */ + memberEmail?: + | string + | undefined; + /** Pagination parameters to limit and offset results */ + pagination?: OffsetPaginationRequest; +} + +/** GroupServiceListsResponse contains a paginated list of groups */ +export interface GroupServiceListResponse { + /** List of groups matching the request criteria */ + groups: Group[]; + /** Pagination information for the response */ + pagination?: OffsetPaginationResponse; +} + +/** GroupServiceUpdateRequest contains the fields that can be updated for a group */ +export interface GroupServiceUpdateRequest { + /** UUID of the group to update */ + groupId: string; + /** New name for the group (if provided) */ + name?: + | string + | undefined; + /** New description for the group (if provided) */ + description?: string | undefined; +} + +/** GroupServiceUpdateResponse contains the updated group information */ +export interface GroupServiceUpdateResponse { + /** The updated group with all its attributes */ + group?: Group; +} + +/** GroupServiceDeleteRequest contains the identifier for the group to delete */ +export interface GroupServiceDeleteRequest { + /** UUID of the group to delete */ + id: string; +} + +/** GroupServiceDeleteResponse is returned upon successful deletion of a group */ +export interface GroupServiceDeleteResponse { +} + +export interface GroupServiceListMembersResponse { + /** List of members in the group */ + members: GroupMember[]; + /** Pagination information for the response */ + pagination?: OffsetPaginationResponse; +} + +/** GroupServiceListMembersRequest contains the identifier for the group whose members are to be listed */ +export interface GroupServiceListMembersRequest { + /** UUID of the group whose members are to be listed */ + groupId: string; + /** Optional filter to search only by maintainers or not */ + maintainers?: + | boolean + | undefined; + /** Optional filter to search by member email address */ + memberEmail?: + | string + | undefined; + /** Pagination parameters to limit and offset results */ + pagination?: OffsetPaginationRequest; +} + +/** Group represents a collection of users with shared access to resources */ +export interface Group { + /** Unique identifier for the group */ + id: string; + /** Human-readable name of the group */ + name: string; + /** Additional details about the group's purpose */ + description: string; + /** UUID of the organization that this group belongs to */ + organizationId: string; + /** Timestamp when the group was created */ + createdAt?: Date; + /** Timestamp when the group was last modified */ + updatedAt?: Date; +} + +/** GroupMember represents a user's membership within a group with their role information */ +export interface GroupMember { + /** The user who is a member of the group */ + user?: User; + /** Indicates whether the user has maintainer (admin) privileges in the group */ + isMaintainer: boolean; + /** Timestamp when the group membership was created */ + createdAt?: Date; + /** Timestamp when the group membership was last modified */ + updatedAt?: Date; +} + +function createBaseGroupServiceCreateRequest(): GroupServiceCreateRequest { + return { name: "", description: "" }; +} + +export const GroupServiceCreateRequest = { + encode(message: GroupServiceCreateRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + if (message.description !== "") { + writer.uint32(18).string(message.description); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceCreateRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceCreateRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.description = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupServiceCreateRequest { + return { + name: isSet(object.name) ? String(object.name) : "", + description: isSet(object.description) ? String(object.description) : "", + }; + }, + + toJSON(message: GroupServiceCreateRequest): unknown { + const obj: any = {}; + message.name !== undefined && (obj.name = message.name); + message.description !== undefined && (obj.description = message.description); + return obj; + }, + + create, I>>(base?: I): GroupServiceCreateRequest { + return GroupServiceCreateRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): GroupServiceCreateRequest { + const message = createBaseGroupServiceCreateRequest(); + message.name = object.name ?? ""; + message.description = object.description ?? ""; + return message; + }, +}; + +function createBaseGroupServiceCreateResponse(): GroupServiceCreateResponse { + return { group: undefined }; +} + +export const GroupServiceCreateResponse = { + encode(message: GroupServiceCreateResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.group !== undefined) { + Group.encode(message.group, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceCreateResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceCreateResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.group = Group.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupServiceCreateResponse { + return { group: isSet(object.group) ? Group.fromJSON(object.group) : undefined }; + }, + + toJSON(message: GroupServiceCreateResponse): unknown { + const obj: any = {}; + message.group !== undefined && (obj.group = message.group ? Group.toJSON(message.group) : undefined); + return obj; + }, + + create, I>>(base?: I): GroupServiceCreateResponse { + return GroupServiceCreateResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): GroupServiceCreateResponse { + const message = createBaseGroupServiceCreateResponse(); + message.group = (object.group !== undefined && object.group !== null) ? Group.fromPartial(object.group) : undefined; + return message; + }, +}; + +function createBaseGroupServiceGetRequest(): GroupServiceGetRequest { + return { groupId: "" }; +} + +export const GroupServiceGetRequest = { + encode(message: GroupServiceGetRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.groupId !== "") { + writer.uint32(10).string(message.groupId); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceGetRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceGetRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.groupId = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupServiceGetRequest { + return { groupId: isSet(object.groupId) ? String(object.groupId) : "" }; + }, + + toJSON(message: GroupServiceGetRequest): unknown { + const obj: any = {}; + message.groupId !== undefined && (obj.groupId = message.groupId); + return obj; + }, + + create, I>>(base?: I): GroupServiceGetRequest { + return GroupServiceGetRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): GroupServiceGetRequest { + const message = createBaseGroupServiceGetRequest(); + message.groupId = object.groupId ?? ""; + return message; + }, +}; + +function createBaseGroupServiceGetResponse(): GroupServiceGetResponse { + return { group: undefined }; +} + +export const GroupServiceGetResponse = { + encode(message: GroupServiceGetResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.group !== undefined) { + Group.encode(message.group, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceGetResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceGetResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.group = Group.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupServiceGetResponse { + return { group: isSet(object.group) ? Group.fromJSON(object.group) : undefined }; + }, + + toJSON(message: GroupServiceGetResponse): unknown { + const obj: any = {}; + message.group !== undefined && (obj.group = message.group ? Group.toJSON(message.group) : undefined); + return obj; + }, + + create, I>>(base?: I): GroupServiceGetResponse { + return GroupServiceGetResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): GroupServiceGetResponse { + const message = createBaseGroupServiceGetResponse(); + message.group = (object.group !== undefined && object.group !== null) ? Group.fromPartial(object.group) : undefined; + return message; + }, +}; + +function createBaseGroupServiceListRequest(): GroupServiceListRequest { + return { name: undefined, description: undefined, memberEmail: undefined, pagination: undefined }; +} + +export const GroupServiceListRequest = { + encode(message: GroupServiceListRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== undefined) { + writer.uint32(10).string(message.name); + } + if (message.description !== undefined) { + writer.uint32(18).string(message.description); + } + if (message.memberEmail !== undefined) { + writer.uint32(26).string(message.memberEmail); + } + if (message.pagination !== undefined) { + OffsetPaginationRequest.encode(message.pagination, writer.uint32(34).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceListRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceListRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.description = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.memberEmail = reader.string(); + continue; + case 4: + if (tag !== 34) { + 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): GroupServiceListRequest { + return { + name: isSet(object.name) ? String(object.name) : undefined, + description: isSet(object.description) ? String(object.description) : undefined, + memberEmail: isSet(object.memberEmail) ? String(object.memberEmail) : undefined, + pagination: isSet(object.pagination) ? OffsetPaginationRequest.fromJSON(object.pagination) : undefined, + }; + }, + + toJSON(message: GroupServiceListRequest): unknown { + const obj: any = {}; + message.name !== undefined && (obj.name = message.name); + message.description !== undefined && (obj.description = message.description); + message.memberEmail !== undefined && (obj.memberEmail = message.memberEmail); + message.pagination !== undefined && + (obj.pagination = message.pagination ? OffsetPaginationRequest.toJSON(message.pagination) : undefined); + return obj; + }, + + create, I>>(base?: I): GroupServiceListRequest { + return GroupServiceListRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): GroupServiceListRequest { + const message = createBaseGroupServiceListRequest(); + message.name = object.name ?? undefined; + message.description = object.description ?? undefined; + message.memberEmail = object.memberEmail ?? undefined; + message.pagination = (object.pagination !== undefined && object.pagination !== null) + ? OffsetPaginationRequest.fromPartial(object.pagination) + : undefined; + return message; + }, +}; + +function createBaseGroupServiceListResponse(): GroupServiceListResponse { + return { groups: [], pagination: undefined }; +} + +export const GroupServiceListResponse = { + encode(message: GroupServiceListResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.groups) { + Group.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): GroupServiceListResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceListResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.groups.push(Group.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): GroupServiceListResponse { + return { + groups: Array.isArray(object?.groups) ? object.groups.map((e: any) => Group.fromJSON(e)) : [], + pagination: isSet(object.pagination) ? OffsetPaginationResponse.fromJSON(object.pagination) : undefined, + }; + }, + + toJSON(message: GroupServiceListResponse): unknown { + const obj: any = {}; + if (message.groups) { + obj.groups = message.groups.map((e) => e ? Group.toJSON(e) : undefined); + } else { + obj.groups = []; + } + message.pagination !== undefined && + (obj.pagination = message.pagination ? OffsetPaginationResponse.toJSON(message.pagination) : undefined); + return obj; + }, + + create, I>>(base?: I): GroupServiceListResponse { + return GroupServiceListResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): GroupServiceListResponse { + const message = createBaseGroupServiceListResponse(); + message.groups = object.groups?.map((e) => Group.fromPartial(e)) || []; + message.pagination = (object.pagination !== undefined && object.pagination !== null) + ? OffsetPaginationResponse.fromPartial(object.pagination) + : undefined; + return message; + }, +}; + +function createBaseGroupServiceUpdateRequest(): GroupServiceUpdateRequest { + return { groupId: "", name: undefined, description: undefined }; +} + +export const GroupServiceUpdateRequest = { + encode(message: GroupServiceUpdateRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.groupId !== "") { + writer.uint32(10).string(message.groupId); + } + if (message.name !== undefined) { + writer.uint32(18).string(message.name); + } + if (message.description !== undefined) { + writer.uint32(26).string(message.description); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceUpdateRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceUpdateRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.groupId = 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; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupServiceUpdateRequest { + return { + groupId: isSet(object.groupId) ? String(object.groupId) : "", + name: isSet(object.name) ? String(object.name) : undefined, + description: isSet(object.description) ? String(object.description) : undefined, + }; + }, + + toJSON(message: GroupServiceUpdateRequest): unknown { + const obj: any = {}; + message.groupId !== undefined && (obj.groupId = message.groupId); + message.name !== undefined && (obj.name = message.name); + message.description !== undefined && (obj.description = message.description); + return obj; + }, + + create, I>>(base?: I): GroupServiceUpdateRequest { + return GroupServiceUpdateRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): GroupServiceUpdateRequest { + const message = createBaseGroupServiceUpdateRequest(); + message.groupId = object.groupId ?? ""; + message.name = object.name ?? undefined; + message.description = object.description ?? undefined; + return message; + }, +}; + +function createBaseGroupServiceUpdateResponse(): GroupServiceUpdateResponse { + return { group: undefined }; +} + +export const GroupServiceUpdateResponse = { + encode(message: GroupServiceUpdateResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.group !== undefined) { + Group.encode(message.group, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceUpdateResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceUpdateResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.group = Group.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupServiceUpdateResponse { + return { group: isSet(object.group) ? Group.fromJSON(object.group) : undefined }; + }, + + toJSON(message: GroupServiceUpdateResponse): unknown { + const obj: any = {}; + message.group !== undefined && (obj.group = message.group ? Group.toJSON(message.group) : undefined); + return obj; + }, + + create, I>>(base?: I): GroupServiceUpdateResponse { + return GroupServiceUpdateResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): GroupServiceUpdateResponse { + const message = createBaseGroupServiceUpdateResponse(); + message.group = (object.group !== undefined && object.group !== null) ? Group.fromPartial(object.group) : undefined; + return message; + }, +}; + +function createBaseGroupServiceDeleteRequest(): GroupServiceDeleteRequest { + return { id: "" }; +} + +export const GroupServiceDeleteRequest = { + encode(message: GroupServiceDeleteRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceDeleteRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceDeleteRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupServiceDeleteRequest { + return { id: isSet(object.id) ? String(object.id) : "" }; + }, + + toJSON(message: GroupServiceDeleteRequest): unknown { + const obj: any = {}; + message.id !== undefined && (obj.id = message.id); + return obj; + }, + + create, I>>(base?: I): GroupServiceDeleteRequest { + return GroupServiceDeleteRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): GroupServiceDeleteRequest { + const message = createBaseGroupServiceDeleteRequest(); + message.id = object.id ?? ""; + return message; + }, +}; + +function createBaseGroupServiceDeleteResponse(): GroupServiceDeleteResponse { + return {}; +} + +export const GroupServiceDeleteResponse = { + encode(_: GroupServiceDeleteResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceDeleteResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceDeleteResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): GroupServiceDeleteResponse { + return {}; + }, + + toJSON(_: GroupServiceDeleteResponse): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): GroupServiceDeleteResponse { + return GroupServiceDeleteResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>(_: I): GroupServiceDeleteResponse { + const message = createBaseGroupServiceDeleteResponse(); + return message; + }, +}; + +function createBaseGroupServiceListMembersResponse(): GroupServiceListMembersResponse { + return { members: [], pagination: undefined }; +} + +export const GroupServiceListMembersResponse = { + encode(message: GroupServiceListMembersResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.members) { + GroupMember.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): GroupServiceListMembersResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceListMembersResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.members.push(GroupMember.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): GroupServiceListMembersResponse { + return { + members: Array.isArray(object?.members) ? object.members.map((e: any) => GroupMember.fromJSON(e)) : [], + pagination: isSet(object.pagination) ? OffsetPaginationResponse.fromJSON(object.pagination) : undefined, + }; + }, + + toJSON(message: GroupServiceListMembersResponse): unknown { + const obj: any = {}; + if (message.members) { + obj.members = message.members.map((e) => e ? GroupMember.toJSON(e) : undefined); + } else { + obj.members = []; + } + message.pagination !== undefined && + (obj.pagination = message.pagination ? OffsetPaginationResponse.toJSON(message.pagination) : undefined); + return obj; + }, + + create, I>>(base?: I): GroupServiceListMembersResponse { + return GroupServiceListMembersResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): GroupServiceListMembersResponse { + const message = createBaseGroupServiceListMembersResponse(); + message.members = object.members?.map((e) => GroupMember.fromPartial(e)) || []; + message.pagination = (object.pagination !== undefined && object.pagination !== null) + ? OffsetPaginationResponse.fromPartial(object.pagination) + : undefined; + return message; + }, +}; + +function createBaseGroupServiceListMembersRequest(): GroupServiceListMembersRequest { + return { groupId: "", maintainers: undefined, memberEmail: undefined, pagination: undefined }; +} + +export const GroupServiceListMembersRequest = { + encode(message: GroupServiceListMembersRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.groupId !== "") { + writer.uint32(10).string(message.groupId); + } + if (message.maintainers !== undefined) { + writer.uint32(16).bool(message.maintainers); + } + if (message.memberEmail !== undefined) { + writer.uint32(26).string(message.memberEmail); + } + if (message.pagination !== undefined) { + OffsetPaginationRequest.encode(message.pagination, writer.uint32(34).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupServiceListMembersRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupServiceListMembersRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.groupId = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.maintainers = reader.bool(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.memberEmail = reader.string(); + continue; + case 4: + if (tag !== 34) { + 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): GroupServiceListMembersRequest { + return { + groupId: isSet(object.groupId) ? String(object.groupId) : "", + maintainers: isSet(object.maintainers) ? Boolean(object.maintainers) : undefined, + memberEmail: isSet(object.memberEmail) ? String(object.memberEmail) : undefined, + pagination: isSet(object.pagination) ? OffsetPaginationRequest.fromJSON(object.pagination) : undefined, + }; + }, + + toJSON(message: GroupServiceListMembersRequest): unknown { + const obj: any = {}; + message.groupId !== undefined && (obj.groupId = message.groupId); + message.maintainers !== undefined && (obj.maintainers = message.maintainers); + message.memberEmail !== undefined && (obj.memberEmail = message.memberEmail); + message.pagination !== undefined && + (obj.pagination = message.pagination ? OffsetPaginationRequest.toJSON(message.pagination) : undefined); + return obj; + }, + + create, I>>(base?: I): GroupServiceListMembersRequest { + return GroupServiceListMembersRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I, + ): GroupServiceListMembersRequest { + const message = createBaseGroupServiceListMembersRequest(); + message.groupId = object.groupId ?? ""; + message.maintainers = object.maintainers ?? undefined; + message.memberEmail = object.memberEmail ?? undefined; + message.pagination = (object.pagination !== undefined && object.pagination !== null) + ? OffsetPaginationRequest.fromPartial(object.pagination) + : undefined; + return message; + }, +}; + +function createBaseGroup(): Group { + return { id: "", name: "", description: "", organizationId: "", createdAt: undefined, updatedAt: undefined }; +} + +export const Group = { + encode(message: Group, 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.organizationId !== "") { + writer.uint32(34).string(message.organizationId); + } + if (message.createdAt !== undefined) { + Timestamp.encode(toTimestamp(message.createdAt), writer.uint32(42).fork()).ldelim(); + } + if (message.updatedAt !== undefined) { + Timestamp.encode(toTimestamp(message.updatedAt), writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Group { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroup(); + 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 !== 34) { + break; + } + + message.organizationId = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.createdAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.updatedAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Group { + return { + id: isSet(object.id) ? String(object.id) : "", + name: isSet(object.name) ? String(object.name) : "", + description: isSet(object.description) ? String(object.description) : "", + organizationId: isSet(object.organizationId) ? String(object.organizationId) : "", + createdAt: isSet(object.createdAt) ? fromJsonTimestamp(object.createdAt) : undefined, + updatedAt: isSet(object.updatedAt) ? fromJsonTimestamp(object.updatedAt) : undefined, + }; + }, + + toJSON(message: Group): 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.organizationId !== undefined && (obj.organizationId = message.organizationId); + message.createdAt !== undefined && (obj.createdAt = message.createdAt.toISOString()); + message.updatedAt !== undefined && (obj.updatedAt = message.updatedAt.toISOString()); + return obj; + }, + + create, I>>(base?: I): Group { + return Group.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): Group { + const message = createBaseGroup(); + message.id = object.id ?? ""; + message.name = object.name ?? ""; + message.description = object.description ?? ""; + message.organizationId = object.organizationId ?? ""; + message.createdAt = object.createdAt ?? undefined; + message.updatedAt = object.updatedAt ?? undefined; + return message; + }, +}; + +function createBaseGroupMember(): GroupMember { + return { user: undefined, isMaintainer: false, createdAt: undefined, updatedAt: undefined }; +} + +export const GroupMember = { + encode(message: GroupMember, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.user !== undefined) { + User.encode(message.user, writer.uint32(10).fork()).ldelim(); + } + if (message.isMaintainer === true) { + writer.uint32(16).bool(message.isMaintainer); + } + if (message.createdAt !== undefined) { + Timestamp.encode(toTimestamp(message.createdAt), writer.uint32(26).fork()).ldelim(); + } + if (message.updatedAt !== undefined) { + Timestamp.encode(toTimestamp(message.updatedAt), writer.uint32(34).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GroupMember { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGroupMember(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.user = User.decode(reader, reader.uint32()); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.isMaintainer = reader.bool(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.createdAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.updatedAt = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GroupMember { + return { + user: isSet(object.user) ? User.fromJSON(object.user) : undefined, + isMaintainer: isSet(object.isMaintainer) ? Boolean(object.isMaintainer) : false, + createdAt: isSet(object.createdAt) ? fromJsonTimestamp(object.createdAt) : undefined, + updatedAt: isSet(object.updatedAt) ? fromJsonTimestamp(object.updatedAt) : undefined, + }; + }, + + toJSON(message: GroupMember): unknown { + const obj: any = {}; + message.user !== undefined && (obj.user = message.user ? User.toJSON(message.user) : undefined); + message.isMaintainer !== undefined && (obj.isMaintainer = message.isMaintainer); + message.createdAt !== undefined && (obj.createdAt = message.createdAt.toISOString()); + message.updatedAt !== undefined && (obj.updatedAt = message.updatedAt.toISOString()); + return obj; + }, + + create, I>>(base?: I): GroupMember { + return GroupMember.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): GroupMember { + const message = createBaseGroupMember(); + message.user = (object.user !== undefined && object.user !== null) ? User.fromPartial(object.user) : undefined; + message.isMaintainer = object.isMaintainer ?? false; + message.createdAt = object.createdAt ?? undefined; + message.updatedAt = object.updatedAt ?? 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 */ + Create( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise; + /** Get retrieves a specific group by its ID */ + Get(request: DeepPartial, metadata?: grpc.Metadata): Promise; + /** List retrieves a paginated list of groups, with optional filtering */ + List(request: DeepPartial, metadata?: grpc.Metadata): Promise; + /** Update modifies an existing group's attributes */ + Update( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise; + /** Delete removes a group from the system */ + Delete( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise; + /** ListMembers retrieves the members of a specific group */ + ListMembers( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise; +} + +export class GroupServiceClientImpl implements GroupService { + private readonly rpc: Rpc; + + constructor(rpc: Rpc) { + this.rpc = rpc; + this.Create = this.Create.bind(this); + this.Get = this.Get.bind(this); + this.List = this.List.bind(this); + this.Update = this.Update.bind(this); + this.Delete = this.Delete.bind(this); + this.ListMembers = this.ListMembers.bind(this); + } + + Create( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise { + return this.rpc.unary(GroupServiceCreateDesc, GroupServiceCreateRequest.fromPartial(request), metadata); + } + + Get(request: DeepPartial, metadata?: grpc.Metadata): Promise { + return this.rpc.unary(GroupServiceGetDesc, GroupServiceGetRequest.fromPartial(request), metadata); + } + + List(request: DeepPartial, metadata?: grpc.Metadata): Promise { + return this.rpc.unary(GroupServiceListDesc, GroupServiceListRequest.fromPartial(request), metadata); + } + + Update( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise { + return this.rpc.unary(GroupServiceUpdateDesc, GroupServiceUpdateRequest.fromPartial(request), metadata); + } + + Delete( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise { + return this.rpc.unary(GroupServiceDeleteDesc, GroupServiceDeleteRequest.fromPartial(request), metadata); + } + + ListMembers( + request: DeepPartial, + metadata?: grpc.Metadata, + ): Promise { + return this.rpc.unary(GroupServiceListMembersDesc, GroupServiceListMembersRequest.fromPartial(request), metadata); + } +} + +export const GroupServiceDesc = { serviceName: "controlplane.v1.GroupService" }; + +export const GroupServiceCreateDesc: UnaryMethodDefinitionish = { + methodName: "Create", + service: GroupServiceDesc, + requestStream: false, + responseStream: false, + requestType: { + serializeBinary() { + return GroupServiceCreateRequest.encode(this).finish(); + }, + } as any, + responseType: { + deserializeBinary(data: Uint8Array) { + const value = GroupServiceCreateResponse.decode(data); + return { + ...value, + toObject() { + return value; + }, + }; + }, + } as any, +}; + +export const GroupServiceGetDesc: UnaryMethodDefinitionish = { + methodName: "Get", + service: GroupServiceDesc, + requestStream: false, + responseStream: false, + requestType: { + serializeBinary() { + return GroupServiceGetRequest.encode(this).finish(); + }, + } as any, + responseType: { + deserializeBinary(data: Uint8Array) { + const value = GroupServiceGetResponse.decode(data); + return { + ...value, + toObject() { + return value; + }, + }; + }, + } as any, +}; + +export const GroupServiceListDesc: UnaryMethodDefinitionish = { + methodName: "List", + service: GroupServiceDesc, + requestStream: false, + responseStream: false, + requestType: { + serializeBinary() { + return GroupServiceListRequest.encode(this).finish(); + }, + } as any, + responseType: { + deserializeBinary(data: Uint8Array) { + const value = GroupServiceListResponse.decode(data); + return { + ...value, + toObject() { + return value; + }, + }; + }, + } as any, +}; + +export const GroupServiceUpdateDesc: UnaryMethodDefinitionish = { + methodName: "Update", + service: GroupServiceDesc, + requestStream: false, + responseStream: false, + requestType: { + serializeBinary() { + return GroupServiceUpdateRequest.encode(this).finish(); + }, + } as any, + responseType: { + deserializeBinary(data: Uint8Array) { + const value = GroupServiceUpdateResponse.decode(data); + return { + ...value, + toObject() { + return value; + }, + }; + }, + } as any, +}; + +export const GroupServiceDeleteDesc: UnaryMethodDefinitionish = { + methodName: "Delete", + service: GroupServiceDesc, + requestStream: false, + responseStream: false, + requestType: { + serializeBinary() { + return GroupServiceDeleteRequest.encode(this).finish(); + }, + } as any, + responseType: { + deserializeBinary(data: Uint8Array) { + const value = GroupServiceDeleteResponse.decode(data); + return { + ...value, + toObject() { + return value; + }, + }; + }, + } as any, +}; + +export const GroupServiceListMembersDesc: UnaryMethodDefinitionish = { + methodName: "ListMembers", + service: GroupServiceDesc, + requestStream: false, + responseStream: false, + requestType: { + serializeBinary() { + return GroupServiceListMembersRequest.encode(this).finish(); + }, + } as any, + responseType: { + deserializeBinary(data: Uint8Array) { + const value = GroupServiceListMembersResponse.decode(data); + return { + ...value, + toObject() { + return value; + }, + }; + }, + } as any, +}; + +interface UnaryMethodDefinitionishR extends grpc.UnaryMethodDefinition { + requestStream: any; + responseStream: any; +} + +type UnaryMethodDefinitionish = UnaryMethodDefinitionishR; + +interface Rpc { + unary( + methodDesc: T, + request: any, + metadata: grpc.Metadata | undefined, + ): Promise; +} + +export class GrpcWebImpl { + private host: string; + private options: { + transport?: grpc.TransportFactory; + + debug?: boolean; + metadata?: grpc.Metadata; + upStreamRetryCodes?: number[]; + }; + + constructor( + host: string, + options: { + transport?: grpc.TransportFactory; + + debug?: boolean; + metadata?: grpc.Metadata; + upStreamRetryCodes?: number[]; + }, + ) { + this.host = host; + this.options = options; + } + + unary( + methodDesc: T, + _request: any, + metadata: grpc.Metadata | undefined, + ): Promise { + const request = { ..._request, ...methodDesc.requestType }; + const maybeCombinedMetadata = metadata && this.options.metadata + ? new BrowserHeaders({ ...this.options?.metadata.headersMap, ...metadata?.headersMap }) + : metadata || this.options.metadata; + return new Promise((resolve, reject) => { + grpc.unary(methodDesc, { + request, + host: this.host, + metadata: maybeCombinedMetadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (response.status === grpc.Code.OK) { + resolve(response.message!.toObject()); + } else { + const err = new GrpcWebError(response.statusMessage, response.status, response.trailers); + reject(err); + } + }, + }); + }); + } +} + +declare var self: any | undefined; +declare var window: any | undefined; +declare var global: any | undefined; +var tsProtoGlobalThis: any = (() => { + if (typeof globalThis !== "undefined") { + return globalThis; + } + if (typeof self !== "undefined") { + return self; + } + if (typeof window !== "undefined") { + return window; + } + if (typeof global !== "undefined") { + return global; + } + throw "Unable to locate global object"; +})(); + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function toTimestamp(date: Date): Timestamp { + const seconds = date.getTime() / 1_000; + const nanos = (date.getTime() % 1_000) * 1_000_000; + return { seconds, nanos }; +} + +function fromTimestamp(t: Timestamp): Date { + let millis = (t.seconds || 0) * 1_000; + millis += (t.nanos || 0) / 1_000_000; + return new Date(millis); +} + +function fromJsonTimestamp(o: any): Date { + if (o instanceof Date) { + return o; + } else if (typeof o === "string") { + return new Date(o); + } else { + return fromTimestamp(Timestamp.fromJSON(o)); + } +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export class GrpcWebError extends tsProtoGlobalThis.Error { + constructor(message: string, public code: grpc.Code, public metadata: grpc.Metadata) { + super(message); + } +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.Group.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.Group.jsonschema.json new file mode 100644 index 000000000..7b237aae8 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.Group.jsonschema.json @@ -0,0 +1,48 @@ +{ + "$id": "controlplane.v1.Group.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Group represents a collection of users with shared access to resources", + "patternProperties": { + "^(created_at)$": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the group was created" + }, + "^(organization_id)$": { + "description": "UUID of the organization that this group belongs to", + "type": "string" + }, + "^(updated_at)$": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the group was last modified" + } + }, + "properties": { + "createdAt": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the group was created" + }, + "description": { + "description": "Additional details about the group's purpose", + "type": "string" + }, + "id": { + "description": "Unique identifier for the group", + "type": "string" + }, + "name": { + "description": "Human-readable name of the group", + "type": "string" + }, + "organizationId": { + "description": "UUID of the organization that this group belongs to", + "type": "string" + }, + "updatedAt": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the group was last modified" + } + }, + "title": "Group", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.Group.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.Group.schema.json new file mode 100644 index 000000000..5afe21f14 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.Group.schema.json @@ -0,0 +1,48 @@ +{ + "$id": "controlplane.v1.Group.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Group represents a collection of users with shared access to resources", + "patternProperties": { + "^(createdAt)$": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the group was created" + }, + "^(organizationId)$": { + "description": "UUID of the organization that this group belongs to", + "type": "string" + }, + "^(updatedAt)$": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the group was last modified" + } + }, + "properties": { + "created_at": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the group was created" + }, + "description": { + "description": "Additional details about the group's purpose", + "type": "string" + }, + "id": { + "description": "Unique identifier for the group", + "type": "string" + }, + "name": { + "description": "Human-readable name of the group", + "type": "string" + }, + "organization_id": { + "description": "UUID of the organization that this group belongs to", + "type": "string" + }, + "updated_at": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the group was last modified" + } + }, + "title": "Group", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupMember.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupMember.jsonschema.json new file mode 100644 index 000000000..db1255858 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupMember.jsonschema.json @@ -0,0 +1,40 @@ +{ + "$id": "controlplane.v1.GroupMember.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupMember represents a user's membership within a group with their role information", + "patternProperties": { + "^(created_at)$": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the group membership was created" + }, + "^(is_maintainer)$": { + "description": "Indicates whether the user has maintainer (admin) privileges in the group", + "type": "boolean" + }, + "^(updated_at)$": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the group membership was last modified" + } + }, + "properties": { + "createdAt": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the group membership was created" + }, + "isMaintainer": { + "description": "Indicates whether the user has maintainer (admin) privileges in the group", + "type": "boolean" + }, + "updatedAt": { + "$ref": "google.protobuf.Timestamp.jsonschema.json", + "description": "Timestamp when the group membership was last modified" + }, + "user": { + "$ref": "controlplane.v1.User.jsonschema.json", + "description": "The user who is a member of the group" + } + }, + "title": "Group Member", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupMember.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupMember.schema.json new file mode 100644 index 000000000..a756a2fa3 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupMember.schema.json @@ -0,0 +1,40 @@ +{ + "$id": "controlplane.v1.GroupMember.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupMember represents a user's membership within a group with their role information", + "patternProperties": { + "^(createdAt)$": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the group membership was created" + }, + "^(isMaintainer)$": { + "description": "Indicates whether the user has maintainer (admin) privileges in the group", + "type": "boolean" + }, + "^(updatedAt)$": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the group membership was last modified" + } + }, + "properties": { + "created_at": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the group membership was created" + }, + "is_maintainer": { + "description": "Indicates whether the user has maintainer (admin) privileges in the group", + "type": "boolean" + }, + "updated_at": { + "$ref": "google.protobuf.Timestamp.schema.json", + "description": "Timestamp when the group membership was last modified" + }, + "user": { + "$ref": "controlplane.v1.User.schema.json", + "description": "The user who is a member of the group" + } + }, + "title": "Group Member", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateRequest.jsonschema.json new file mode 100644 index 000000000..c21a9a793 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateRequest.jsonschema.json @@ -0,0 +1,19 @@ +{ + "$id": "controlplane.v1.GroupServiceCreateRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceCreateRequest contains the information needed to create a new group", + "properties": { + "description": { + "description": "Description providing additional information about the group", + "type": "string" + }, + "name": { + "description": "Name of the group to create", + "minLength": 1, + "type": "string" + } + }, + "title": "Group Service Create Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateRequest.schema.json new file mode 100644 index 000000000..40f7edd77 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateRequest.schema.json @@ -0,0 +1,19 @@ +{ + "$id": "controlplane.v1.GroupServiceCreateRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceCreateRequest contains the information needed to create a new group", + "properties": { + "description": { + "description": "Description providing additional information about the group", + "type": "string" + }, + "name": { + "description": "Name of the group to create", + "minLength": 1, + "type": "string" + } + }, + "title": "Group Service Create Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateResponse.jsonschema.json new file mode 100644 index 000000000..0d1029788 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateResponse.jsonschema.json @@ -0,0 +1,14 @@ +{ + "$id": "controlplane.v1.GroupServiceCreateResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceCreateResponse contains the newly created group", + "properties": { + "group": { + "$ref": "controlplane.v1.Group.jsonschema.json", + "description": "The created group with all its attributes" + } + }, + "title": "Group Service Create Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateResponse.schema.json new file mode 100644 index 000000000..cbb756169 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceCreateResponse.schema.json @@ -0,0 +1,14 @@ +{ + "$id": "controlplane.v1.GroupServiceCreateResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceCreateResponse contains the newly created group", + "properties": { + "group": { + "$ref": "controlplane.v1.Group.schema.json", + "description": "The created group with all its attributes" + } + }, + "title": "Group Service Create Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteRequest.jsonschema.json new file mode 100644 index 000000000..7e27236a3 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteRequest.jsonschema.json @@ -0,0 +1,15 @@ +{ + "$id": "controlplane.v1.GroupServiceDeleteRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceDeleteRequest contains the identifier for the group to delete", + "properties": { + "id": { + "description": "UUID of the group to delete", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + } + }, + "title": "Group Service Delete Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteRequest.schema.json new file mode 100644 index 000000000..929ecd341 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteRequest.schema.json @@ -0,0 +1,15 @@ +{ + "$id": "controlplane.v1.GroupServiceDeleteRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceDeleteRequest contains the identifier for the group to delete", + "properties": { + "id": { + "description": "UUID of the group to delete", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + } + }, + "title": "Group Service Delete Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteResponse.jsonschema.json new file mode 100644 index 000000000..71c4ac9e8 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteResponse.jsonschema.json @@ -0,0 +1,9 @@ +{ + "$id": "controlplane.v1.GroupServiceDeleteResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceDeleteResponse is returned upon successful deletion of a group", + "properties": {}, + "title": "Group Service Delete Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteResponse.schema.json new file mode 100644 index 000000000..62820212a --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceDeleteResponse.schema.json @@ -0,0 +1,9 @@ +{ + "$id": "controlplane.v1.GroupServiceDeleteResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceDeleteResponse is returned upon successful deletion of a group", + "properties": {}, + "title": "Group Service Delete Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetRequest.jsonschema.json new file mode 100644 index 000000000..cb617441d --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetRequest.jsonschema.json @@ -0,0 +1,27 @@ +{ + "$id": "controlplane.v1.GroupServiceGetRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceGetRequest contains the identifier for the group to retrieve", + "patternProperties": { + "^(group_id)$": { + "description": "UUID of the group to retrieve", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + } + }, + "properties": { + "groupId": { + "description": "UUID of the group to retrieve", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + } + }, + "required": [ + "group_id" + ], + "title": "Group Service Get Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetRequest.schema.json new file mode 100644 index 000000000..e6a81743c --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetRequest.schema.json @@ -0,0 +1,27 @@ +{ + "$id": "controlplane.v1.GroupServiceGetRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceGetRequest contains the identifier for the group to retrieve", + "patternProperties": { + "^(groupId)$": { + "description": "UUID of the group to retrieve", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + } + }, + "properties": { + "group_id": { + "description": "UUID of the group to retrieve", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + } + }, + "required": [ + "group_id" + ], + "title": "Group Service Get Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetResponse.jsonschema.json new file mode 100644 index 000000000..23e5c610f --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetResponse.jsonschema.json @@ -0,0 +1,14 @@ +{ + "$id": "controlplane.v1.GroupServiceGetResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceGetResponse contains the requested group information", + "properties": { + "group": { + "$ref": "controlplane.v1.Group.jsonschema.json", + "description": "The requested group with all its attributes" + } + }, + "title": "Group Service Get Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetResponse.schema.json new file mode 100644 index 000000000..775a5cab1 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceGetResponse.schema.json @@ -0,0 +1,14 @@ +{ + "$id": "controlplane.v1.GroupServiceGetResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceGetResponse contains the requested group information", + "properties": { + "group": { + "$ref": "controlplane.v1.Group.schema.json", + "description": "The requested group with all its attributes" + } + }, + "title": "Group Service Get Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersRequest.jsonschema.json new file mode 100644 index 000000000..3de3cabb1 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersRequest.jsonschema.json @@ -0,0 +1,43 @@ +{ + "$id": "controlplane.v1.GroupServiceListMembersRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceListMembersRequest contains the identifier for the group whose members are to be listed", + "patternProperties": { + "^(group_id)$": { + "description": "UUID of the group whose members are to be listed", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + }, + "^(member_email)$": { + "description": "Optional filter to search by member email address", + "type": "string" + } + }, + "properties": { + "groupId": { + "description": "UUID of the group whose members are to be listed", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + }, + "maintainers": { + "description": "Optional filter to search only by maintainers or not", + "type": "boolean" + }, + "memberEmail": { + "description": "Optional filter to search by member email address", + "type": "string" + }, + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationRequest.jsonschema.json", + "description": "Pagination parameters to limit and offset results" + } + }, + "required": [ + "group_id" + ], + "title": "Group Service List Members Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersRequest.schema.json new file mode 100644 index 000000000..c47837205 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersRequest.schema.json @@ -0,0 +1,43 @@ +{ + "$id": "controlplane.v1.GroupServiceListMembersRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceListMembersRequest contains the identifier for the group whose members are to be listed", + "patternProperties": { + "^(groupId)$": { + "description": "UUID of the group whose members are to be listed", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + }, + "^(memberEmail)$": { + "description": "Optional filter to search by member email address", + "type": "string" + } + }, + "properties": { + "group_id": { + "description": "UUID of the group whose members are to be listed", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + }, + "maintainers": { + "description": "Optional filter to search only by maintainers or not", + "type": "boolean" + }, + "member_email": { + "description": "Optional filter to search by member email address", + "type": "string" + }, + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationRequest.schema.json", + "description": "Pagination parameters to limit and offset results" + } + }, + "required": [ + "group_id" + ], + "title": "Group Service List Members Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersResponse.jsonschema.json new file mode 100644 index 000000000..7c88cd5c2 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersResponse.jsonschema.json @@ -0,0 +1,20 @@ +{ + "$id": "controlplane.v1.GroupServiceListMembersResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "members": { + "description": "List of members in the group", + "items": { + "$ref": "controlplane.v1.GroupMember.jsonschema.json" + }, + "type": "array" + }, + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationResponse.jsonschema.json", + "description": "Pagination information for the response" + } + }, + "title": "Group Service List Members Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersResponse.schema.json new file mode 100644 index 000000000..ba17629a3 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListMembersResponse.schema.json @@ -0,0 +1,20 @@ +{ + "$id": "controlplane.v1.GroupServiceListMembersResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "members": { + "description": "List of members in the group", + "items": { + "$ref": "controlplane.v1.GroupMember.schema.json" + }, + "type": "array" + }, + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationResponse.schema.json", + "description": "Pagination information for the response" + } + }, + "title": "Group Service List Members Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListRequest.jsonschema.json new file mode 100644 index 000000000..de56e4b97 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListRequest.jsonschema.json @@ -0,0 +1,32 @@ +{ + "$id": "controlplane.v1.GroupServiceListRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceListsRequest contains parameters for filtering and paginating group results", + "patternProperties": { + "^(member_email)$": { + "description": "Optional filter to search by member email address", + "type": "string" + } + }, + "properties": { + "description": { + "description": "Optional filter to search by group description", + "type": "string" + }, + "memberEmail": { + "description": "Optional filter to search by member email address", + "type": "string" + }, + "name": { + "description": "Optional filter to search by group name", + "type": "string" + }, + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationRequest.jsonschema.json", + "description": "Pagination parameters to limit and offset results" + } + }, + "title": "Group Service List Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListRequest.schema.json new file mode 100644 index 000000000..ca8510528 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListRequest.schema.json @@ -0,0 +1,32 @@ +{ + "$id": "controlplane.v1.GroupServiceListRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceListsRequest contains parameters for filtering and paginating group results", + "patternProperties": { + "^(memberEmail)$": { + "description": "Optional filter to search by member email address", + "type": "string" + } + }, + "properties": { + "description": { + "description": "Optional filter to search by group description", + "type": "string" + }, + "member_email": { + "description": "Optional filter to search by member email address", + "type": "string" + }, + "name": { + "description": "Optional filter to search by group name", + "type": "string" + }, + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationRequest.schema.json", + "description": "Pagination parameters to limit and offset results" + } + }, + "title": "Group Service List Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListResponse.jsonschema.json new file mode 100644 index 000000000..d242f8b21 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListResponse.jsonschema.json @@ -0,0 +1,21 @@ +{ + "$id": "controlplane.v1.GroupServiceListResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceListsResponse contains a paginated list of groups", + "properties": { + "groups": { + "description": "List of groups matching the request criteria", + "items": { + "$ref": "controlplane.v1.Group.jsonschema.json" + }, + "type": "array" + }, + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationResponse.jsonschema.json", + "description": "Pagination information for the response" + } + }, + "title": "Group Service List Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListResponse.schema.json new file mode 100644 index 000000000..33aadf471 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceListResponse.schema.json @@ -0,0 +1,21 @@ +{ + "$id": "controlplane.v1.GroupServiceListResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceListsResponse contains a paginated list of groups", + "properties": { + "groups": { + "description": "List of groups matching the request criteria", + "items": { + "$ref": "controlplane.v1.Group.schema.json" + }, + "type": "array" + }, + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationResponse.schema.json", + "description": "Pagination information for the response" + } + }, + "title": "Group Service List Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateRequest.jsonschema.json new file mode 100644 index 000000000..421043cae --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateRequest.jsonschema.json @@ -0,0 +1,35 @@ +{ + "$id": "controlplane.v1.GroupServiceUpdateRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceUpdateRequest contains the fields that can be updated for a group", + "patternProperties": { + "^(group_id)$": { + "description": "UUID of the group to update", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + } + }, + "properties": { + "description": { + "description": "New description for the group (if provided)", + "type": "string" + }, + "groupId": { + "description": "UUID of the group to update", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + }, + "name": { + "description": "New name for the group (if provided)", + "type": "string" + } + }, + "required": [ + "group_id" + ], + "title": "Group Service Update Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateRequest.schema.json new file mode 100644 index 000000000..c5c880547 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateRequest.schema.json @@ -0,0 +1,35 @@ +{ + "$id": "controlplane.v1.GroupServiceUpdateRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceUpdateRequest contains the fields that can be updated for a group", + "patternProperties": { + "^(groupId)$": { + "description": "UUID of the group to update", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + } + }, + "properties": { + "description": { + "description": "New description for the group (if provided)", + "type": "string" + }, + "group_id": { + "description": "UUID of the group to update", + "minLength": 1, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "type": "string" + }, + "name": { + "description": "New name for the group (if provided)", + "type": "string" + } + }, + "required": [ + "group_id" + ], + "title": "Group Service Update Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateResponse.jsonschema.json new file mode 100644 index 000000000..459952b89 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateResponse.jsonschema.json @@ -0,0 +1,14 @@ +{ + "$id": "controlplane.v1.GroupServiceUpdateResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceUpdateResponse contains the updated group information", + "properties": { + "group": { + "$ref": "controlplane.v1.Group.jsonschema.json", + "description": "The updated group with all its attributes" + } + }, + "title": "Group Service Update Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateResponse.schema.json new file mode 100644 index 000000000..95884cd39 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.GroupServiceUpdateResponse.schema.json @@ -0,0 +1,14 @@ +{ + "$id": "controlplane.v1.GroupServiceUpdateResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "GroupServiceUpdateResponse contains the updated group information", + "properties": { + "group": { + "$ref": "controlplane.v1.Group.schema.json", + "description": "The updated group with all its attributes" + } + }, + "title": "Group Service Update Response", + "type": "object" +} diff --git a/app/controlplane/cmd/wire_gen.go b/app/controlplane/cmd/wire_gen.go index b6b77b3d2..31617bf15 100644 --- a/app/controlplane/cmd/wire_gen.go +++ b/app/controlplane/cmd/wire_gen.go @@ -227,6 +227,9 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l userService := service.NewUserService(membershipUseCase, organizationUseCase, v5...) signingService := service.NewSigningService(signingUseCase, v5...) prometheusService := service.NewPrometheusService(organizationUseCase, prometheusUseCase, v5...) + groupRepo := data.NewGroupRepo(dataData, logger) + groupUseCase := biz.NewGroupUseCase(logger, groupRepo, membershipRepo, auditorUseCase) + groupService := service.NewGroupService(groupUseCase, v5...) federatedAuthentication := bootstrap.FederatedAuthentication validator, err := newProtoValidator() if err != nil { @@ -264,6 +267,7 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l UserSvc: userService, SigningSvc: signingService, PrometheusSvc: prometheusService, + GroupSvc: groupService, Logger: logger, ServerConfig: confServer, AuthConfig: auth, diff --git a/app/controlplane/internal/server/grpc.go b/app/controlplane/internal/server/grpc.go index fa3abb656..08650de65 100644 --- a/app/controlplane/internal/server/grpc.go +++ b/app/controlplane/internal/server/grpc.go @@ -82,6 +82,7 @@ type Opts struct { UserSvc *service.UserService SigningSvc *service.SigningService PrometheusSvc *service.PrometheusService + GroupSvc *service.GroupService // Utils Logger log.Logger ServerConfig *conf.Server @@ -151,6 +152,7 @@ func NewGRPCServer(opts *Opts) (*grpc.Server, error) { v1.RegisterAttestationStateServiceServer(srv, opts.AttestationStateSvc) v1.RegisterUserServiceServer(srv, opts.UserSvc) v1.RegisterSigningServiceServer(srv, opts.SigningSvc) + v1.RegisterGroupServiceServer(srv, opts.GroupSvc) // Register Prometheus metrics grpc_prometheus.Register(srv.Server) diff --git a/app/controlplane/internal/service/group.go b/app/controlplane/internal/service/group.go new file mode 100644 index 000000000..cb4eafe1c --- /dev/null +++ b/app/controlplane/internal/service/group.go @@ -0,0 +1,295 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package service + +import ( + "context" + + pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination" + + "github.com/go-kratos/kratos/v2/errors" + "github.com/google/uuid" + "google.golang.org/protobuf/types/known/timestamppb" +) + +type GroupService struct { + pb.UnimplementedGroupServiceServer + *service + // Use Cases + groupUseCase *biz.GroupUseCase +} + +func NewGroupService(groupUseCase *biz.GroupUseCase, opts ...NewOpt) *GroupService { + return &GroupService{ + service: newService(opts...), + groupUseCase: groupUseCase, + } +} + +// Create creates a new group in the organization. +func (g GroupService) Create(ctx context.Context, req *pb.GroupServiceCreateRequest) (*pb.GroupServiceCreateResponse, error) { + currentUser, err := requireCurrentUser(ctx) + if err != nil { + return nil, err + } + 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 userID + userUUID, err := uuid.Parse(currentUser.ID) + if err != nil { + return nil, errors.BadRequest("invalid", "invalid user ID") + } + + gr, err := g.groupUseCase.Create(ctx, orgUUID, req.Name, req.Description, userUUID) + if err != nil { + return nil, handleUseCaseErr(err, g.log) + } + + return &pb.GroupServiceCreateResponse{ + Group: bizGroupToPb(gr), + }, nil +} + +// Get retrieves a group by its ID within the current organization. +func (g GroupService) Get(ctx context.Context, req *pb.GroupServiceGetRequest) (*pb.GroupServiceGetResponse, 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 + groupUUID, err := uuid.Parse(req.GetGroupId()) + if err != nil { + return nil, errors.BadRequest("invalid", "invalid group ID") + } + + gr, err := g.groupUseCase.FindByOrgAndID(ctx, orgUUID, groupUUID) + if err != nil { + return nil, handleUseCaseErr(err, g.log) + } + + return &pb.GroupServiceGetResponse{ + Group: bizGroupToPb(gr), + }, nil +} + +// List retrieves a list of groups within the current organization, with optional filters and pagination. +func (g GroupService) List(ctx context.Context, req *pb.GroupServiceListRequest) (*pb.GroupServiceListResponse, 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") + } + + // Initialize the pagination options, with default values + paginationOpts := pagination.NewDefaultOffsetPaginationOpts() + + // Override the pagination options if they are provided + if req.GetPagination() != nil { + paginationOpts, err = pagination.NewOffsetPaginationOpts( + int(req.GetPagination().GetPage()), + int(req.GetPagination().GetPageSize()), + ) + if err != nil { + return nil, handleUseCaseErr(err, g.log) + } + } + + // Initialize the filters + filters := &biz.ListGroupOpts{} + + if req.GetName() != "" { + filters.Name = req.GetName() + } + + if req.GetDescription() != "" { + filters.Description = req.GetDescription() + } + + if req.GetMemberEmail() != "" { + filters.MemberEmail = req.GetMemberEmail() + } + + grs, count, err := g.groupUseCase.List(ctx, orgUUID, filters, paginationOpts) + if err != nil { + return nil, handleUseCaseErr(err, g.log) + } + + // Convert the groups to protobuf messages + result := make([]*pb.Group, 0, len(grs)) + for _, gr := range grs { + result = append(result, bizGroupToPb(gr)) + } + return &pb.GroupServiceListResponse{ + Groups: result, + Pagination: paginationToPb(count, paginationOpts.Offset(), paginationOpts.Limit()), + }, nil +} + +// Update updates an existing group in the organization. +func (g GroupService) Update(ctx context.Context, req *pb.GroupServiceUpdateRequest) (*pb.GroupServiceUpdateResponse, 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 + groupUUID, err := uuid.Parse(req.GetGroupId()) + if err != nil { + return nil, errors.BadRequest("invalid", "invalid group ID") + } + + // Update the group with the provided options + gr, err := g.groupUseCase.Update(ctx, orgUUID, groupUUID, req.Description, req.Name) + if err != nil { + return nil, handleUseCaseErr(err, g.log) + } + + return &pb.GroupServiceUpdateResponse{ + Group: bizGroupToPb(gr), + }, nil +} + +// Delete soft-deletes a group by its ID within the current organization. +func (g GroupService) Delete(ctx context.Context, req *pb.GroupServiceDeleteRequest) (*pb.GroupServiceDeleteResponse, 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 + groupUUID, err := uuid.Parse(req.Id) + if err != nil { + return nil, errors.BadRequest("invalid", "invalid group ID") + } + + err = g.groupUseCase.SoftDelete(ctx, orgUUID, groupUUID) + if err != nil { + return nil, handleUseCaseErr(err, g.log) + } + + return &pb.GroupServiceDeleteResponse{}, nil +} + +// ListMembers retrieves a list of members in a group within the current organization, with optional filters and pagination. +func (g GroupService) ListMembers(ctx context.Context, req *pb.GroupServiceListMembersRequest) (*pb.GroupServiceListMembersResponse, 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") + } + + // Initialize the pagination options, with default values + paginationOpts := pagination.NewDefaultOffsetPaginationOpts() + + // Override the pagination options if they are provided + if req.GetPagination() != nil { + paginationOpts, err = pagination.NewOffsetPaginationOpts( + int(req.GetPagination().GetPage()), + int(req.GetPagination().GetPageSize()), + ) + if err != nil { + return nil, handleUseCaseErr(err, g.log) + } + } + + // Parse groupID + groupUUID, err := uuid.Parse(req.GetGroupId()) + if err != nil { + return nil, errors.BadRequest("invalid", "invalid group ID") + } + + grs, count, err := g.groupUseCase.ListMembers(ctx, orgUUID, groupUUID, req.Maintainers, req.MemberEmail, paginationOpts) + if err != nil { + return nil, handleUseCaseErr(err, g.log) + } + + // Convert the group members to protobuf messages + result := make([]*pb.GroupMember, 0, len(grs)) + for _, gr := range grs { + result = append(result, bizGroupMemberToPb(gr)) + } + return &pb.GroupServiceListMembersResponse{ + Members: 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{ + Id: gr.ID.String(), + Name: gr.Name, + Description: gr.Description, + CreatedAt: timestamppb.New(*gr.CreatedAt), + UpdatedAt: timestamppb.New(*gr.UpdatedAt), + } + + if gr.Organization != nil { + base.OrganizationId = gr.Organization.ID + } + + return base +} + +// bizGroupMemberToPb converts a biz.GroupMembership to a pb.GroupMember protobuf message. +func bizGroupMemberToPb(m *biz.GroupMembership) *pb.GroupMember { + return &pb.GroupMember{ + User: bizUserToPb(m.User), + IsMaintainer: m.Maintainer, + CreatedAt: timestamppb.New(*m.CreatedAt), + UpdatedAt: timestamppb.New(*m.UpdatedAt), + } +} diff --git a/app/controlplane/internal/service/service.go b/app/controlplane/internal/service/service.go index 88ce7d7a9..a9f5e7b33 100644 --- a/app/controlplane/internal/service/service.go +++ b/app/controlplane/internal/service/service.go @@ -55,6 +55,7 @@ var ProviderSet = wire.NewSet( NewUserService, NewSigningService, NewPrometheusService, + NewGroupService, wire.Struct(new(NewWorkflowRunServiceOpts), "*"), wire.Struct(new(NewAttestationServiceOpts), "*"), wire.Struct(new(NewAttestationStateServiceOpt), "*"), diff --git a/app/controlplane/pkg/auditor/events/group.go b/app/controlplane/pkg/auditor/events/group.go new file mode 100644 index 000000000..1319bef31 --- /dev/null +++ b/app/controlplane/pkg/auditor/events/group.go @@ -0,0 +1,134 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package events + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/auditor" + "github.com/google/uuid" +) + +var ( + _ auditor.LogEntry = (*GroupCreated)(nil) + _ auditor.LogEntry = (*GroupUpdated)(nil) + _ auditor.LogEntry = (*GroupDeleted)(nil) +) + +const ( + GroupType auditor.TargetType = "Group" + GroupCreatedActionType string = "GroupCreated" + GroupUpdatedActionType string = "GroupUpdated" + GroupDeletedActionType string = "GroupDeleted" +) + +// GroupBase is the base struct for group events +type GroupBase struct { + GroupID *uuid.UUID `json:"group_id,omitempty"` + GroupName string `json:"group_name,omitempty"` +} + +func (g *GroupBase) RequiresActor() bool { + return true +} + +func (g *GroupBase) TargetType() auditor.TargetType { + return GroupType +} + +func (g *GroupBase) TargetID() *uuid.UUID { + return g.GroupID +} + +func (g *GroupBase) ActionInfo() (json.RawMessage, error) { + if g.GroupID == nil || g.GroupName == "" { + return nil, errors.New("group id and name are required") + } + + return json.Marshal(&g) +} + +// GroupCreated represents the creation of a group +type GroupCreated struct { + *GroupBase + GroupDescription string `json:"group_description,omitempty"` +} + +func (g *GroupCreated) ActionType() string { + return GroupCreatedActionType +} + +func (g *GroupCreated) ActionInfo() (json.RawMessage, error) { + if _, err := g.GroupBase.ActionInfo(); err != nil { + return nil, err + } + + return json.Marshal(&g) +} + +func (g *GroupCreated) Description() string { + return fmt.Sprintf("{{ if .ActorEmail }}{{ .ActorEmail }}{{ else }}API Token {{ .ActorID }}{{ end }} has created the group %s", g.GroupName) +} + +// GroupUpdated represents an update to a group +type GroupUpdated struct { + *GroupBase + NewDescription *string `json:"new_description,omitempty"` + OldName *string `json:"old_name,omitempty"` + NewName *string `json:"new_name,omitempty"` +} + +func (g *GroupUpdated) ActionType() string { + return GroupUpdatedActionType +} + +func (g *GroupUpdated) ActionInfo() (json.RawMessage, error) { + if _, err := g.GroupBase.ActionInfo(); err != nil { + return nil, err + } + + return json.Marshal(&g) +} + +func (g *GroupUpdated) Description() string { + if g.OldName != nil && g.NewName != nil { + return fmt.Sprintf("{{ if .ActorEmail }}{{ .ActorEmail }}{{ else }}API Token {{ .ActorID }}{{ end }} has renamed the group from %s to %s", *g.OldName, *g.NewName) + } + return fmt.Sprintf("{{ if .ActorEmail }}{{ .ActorEmail }}{{ else }}API Token {{ .ActorID }}{{ end }} has updated the group %s", g.GroupName) +} + +// GroupDeleted represents the deletion of a group +type GroupDeleted struct { + *GroupBase +} + +func (g *GroupDeleted) ActionType() string { + return GroupDeletedActionType +} + +func (g *GroupDeleted) ActionInfo() (json.RawMessage, error) { + if _, err := g.GroupBase.ActionInfo(); err != nil { + return nil, err + } + + return json.Marshal(&g) +} + +func (g *GroupDeleted) Description() string { + return fmt.Sprintf("{{ if .ActorEmail }}{{ .ActorEmail }}{{ else }}API Token {{ .ActorID }}{{ end }} has deleted the group %s", g.GroupName) +} diff --git a/app/controlplane/pkg/auditor/events/group_test.go b/app/controlplane/pkg/auditor/events/group_test.go new file mode 100644 index 000000000..0255281db --- /dev/null +++ b/app/controlplane/pkg/auditor/events/group_test.go @@ -0,0 +1,234 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package events_test + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/auditor" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/auditor/events" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGroupEvents(t *testing.T) { + userUUID, err := uuid.Parse("1089bb36-e27b-428b-8009-d015c8737c54") + require.NoError(t, err) + orgUUID, err := uuid.Parse("1089bb36-e27b-428b-8009-d015c8737c54") + require.NoError(t, err) + groupUUID, err := uuid.Parse("3089bb36-e27b-428b-8009-d015c8737c56") + require.NoError(t, err) + groupName := "test-group" + groupDescription := "test description" + oldGroupName := "old-group-name" + newGroupName := "new-group-name" + + tests := []struct { + name string + event auditor.LogEntry + expected string + actor auditor.ActorType + actorID uuid.UUID + }{ + { + name: "Group created by user", + event: &events.GroupCreated{ + GroupBase: &events.GroupBase{ + GroupID: &groupUUID, + GroupName: groupName, + }, + GroupDescription: groupDescription, + }, + expected: "testdata/groups/group_created.json", + actor: auditor.ActorTypeUser, + actorID: userUUID, + }, + { + name: "Group updated by user", + event: &events.GroupUpdated{ + GroupBase: &events.GroupBase{ + GroupID: &groupUUID, + GroupName: groupName, + }, + NewDescription: &groupDescription, + }, + expected: "testdata/groups/group_updated.json", + actor: auditor.ActorTypeUser, + actorID: userUUID, + }, + { + name: "Group updated with description by user", + event: &events.GroupUpdated{ + GroupBase: &events.GroupBase{ + GroupID: &groupUUID, + GroupName: groupName, + }, + NewDescription: &groupDescription, + }, + expected: "testdata/groups/group_updated_with_description.json", + actor: auditor.ActorTypeUser, + actorID: userUUID, + }, + { + name: "Group updated with name change by user", + event: &events.GroupUpdated{ + GroupBase: &events.GroupBase{ + GroupID: &groupUUID, + GroupName: newGroupName, + }, + OldName: &oldGroupName, + NewName: &newGroupName, + }, + expected: "testdata/groups/group_renamed.json", + actor: auditor.ActorTypeUser, + actorID: userUUID, + }, + { + name: "Group deleted by user", + event: &events.GroupDeleted{ + GroupBase: &events.GroupBase{ + GroupID: &groupUUID, + GroupName: groupName, + }, + }, + expected: "testdata/groups/group_deleted.json", + actor: auditor.ActorTypeUser, + actorID: userUUID, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + opts := []auditor.GeneratorOption{ + auditor.WithOrgID(orgUUID), + } + if tt.actor == auditor.ActorTypeAPIToken { + opts = append(opts, auditor.WithActor(auditor.ActorTypeAPIToken, tt.actorID, "")) + } else { + opts = append(opts, auditor.WithActor(auditor.ActorTypeUser, tt.actorID, testEmail)) + } + + eventPayload, err := auditor.GenerateAuditEvent(tt.event, opts...) + require.NoError(t, err) + + want, err := json.MarshalIndent(eventPayload.Data, "", " ") + require.NoError(t, err) + + if updateGolden { + err := os.MkdirAll(filepath.Dir(tt.expected), 0755) + require.NoError(t, err) + err = os.WriteFile(filepath.Clean(tt.expected), want, 0600) + require.NoError(t, err) + } + + gotRaw, err := os.ReadFile(filepath.Clean(tt.expected)) + require.NoError(t, err) + + var gotPayload auditor.AuditEventPayload + err = json.Unmarshal(gotRaw, &gotPayload) + require.NoError(t, err) + got, err := json.MarshalIndent(gotPayload, "", " ") + require.NoError(t, err) + + assert.Equal(t, string(want), string(got)) + }) + } +} + +// TestGroupEventsFailed tests the behavior of group events when they are expected to fail +func TestGroupEventsFailed(t *testing.T) { + groupUUID, err := uuid.Parse("3089bb36-e27b-428b-8009-d015c8737c56") + require.NoError(t, err) + groupDescription := "test description" + + tests := []struct { + name string + event auditor.LogEntry + expectedErr string + }{ + { + name: "Group created with missing GroupID", + event: &events.GroupCreated{ + GroupBase: &events.GroupBase{ + GroupName: "test-group", + }, + GroupDescription: groupDescription, + }, + expectedErr: "group id and name are required", + }, + { + name: "Group created with missing GroupName", + event: &events.GroupCreated{ + GroupBase: &events.GroupBase{ + GroupID: &groupUUID, + }, + GroupDescription: groupDescription, + }, + expectedErr: "group id and name are required", + }, + { + name: "Group updated with missing GroupID", + event: &events.GroupUpdated{ + GroupBase: &events.GroupBase{ + GroupName: "test-group", + }, + NewDescription: &groupDescription, + }, + expectedErr: "group id and name are required", + }, + { + name: "Group updated with missing GroupName", + event: &events.GroupUpdated{ + GroupBase: &events.GroupBase{ + GroupID: &groupUUID, + }, + NewDescription: &groupDescription, + }, + expectedErr: "group id and name are required", + }, + { + name: "Group deleted with missing GroupID", + event: &events.GroupDeleted{ + GroupBase: &events.GroupBase{ + GroupName: "test-group", + }, + }, + expectedErr: "group id and name are required", + }, + { + name: "Group deleted with missing GroupName", + event: &events.GroupDeleted{ + GroupBase: &events.GroupBase{ + GroupID: &groupUUID, + }, + }, + expectedErr: "group id and name are required", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := tt.event.ActionInfo() + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.expectedErr) + }) + } +} diff --git a/app/controlplane/pkg/auditor/events/testdata/groups/group_created.json b/app/controlplane/pkg/auditor/events/testdata/groups/group_created.json new file mode 100644 index 000000000..c64a20c4b --- /dev/null +++ b/app/controlplane/pkg/auditor/events/testdata/groups/group_created.json @@ -0,0 +1,16 @@ +{ + "ActionType": "GroupCreated", + "TargetType": "Group", + "TargetID": "3089bb36-e27b-428b-8009-d015c8737c56", + "ActorType": "USER", + "ActorID": "1089bb36-e27b-428b-8009-d015c8737c54", + "ActorEmail": "john@cyberdyne.io", + "OrgID": "1089bb36-e27b-428b-8009-d015c8737c54", + "Description": "john@cyberdyne.io has created the group test-group", + "Info": { + "group_id": "3089bb36-e27b-428b-8009-d015c8737c56", + "group_name": "test-group", + "group_description": "test description" + }, + "Digest": "sha256:6404c02ca16834c24c02fdf6015ef550696723dd3babb0a20b68f2412cfbb8f4" +} \ No newline at end of file diff --git a/app/controlplane/pkg/auditor/events/testdata/groups/group_deleted.json b/app/controlplane/pkg/auditor/events/testdata/groups/group_deleted.json new file mode 100644 index 000000000..02565e7e3 --- /dev/null +++ b/app/controlplane/pkg/auditor/events/testdata/groups/group_deleted.json @@ -0,0 +1,15 @@ +{ + "ActionType": "GroupDeleted", + "TargetType": "Group", + "TargetID": "3089bb36-e27b-428b-8009-d015c8737c56", + "ActorType": "USER", + "ActorID": "1089bb36-e27b-428b-8009-d015c8737c54", + "ActorEmail": "john@cyberdyne.io", + "OrgID": "1089bb36-e27b-428b-8009-d015c8737c54", + "Description": "john@cyberdyne.io has deleted the group test-group", + "Info": { + "group_id": "3089bb36-e27b-428b-8009-d015c8737c56", + "group_name": "test-group" + }, + "Digest": "sha256:59f6c388b2b8e351e2ad1a6671aeaacc283d22e07cd8dd94ff694197e48c9556" +} \ No newline at end of file diff --git a/app/controlplane/pkg/auditor/events/testdata/groups/group_renamed.json b/app/controlplane/pkg/auditor/events/testdata/groups/group_renamed.json new file mode 100644 index 000000000..dba3a3ffb --- /dev/null +++ b/app/controlplane/pkg/auditor/events/testdata/groups/group_renamed.json @@ -0,0 +1,17 @@ +{ + "ActionType": "GroupUpdated", + "TargetType": "Group", + "TargetID": "3089bb36-e27b-428b-8009-d015c8737c56", + "ActorType": "USER", + "ActorID": "1089bb36-e27b-428b-8009-d015c8737c54", + "ActorEmail": "john@cyberdyne.io", + "OrgID": "1089bb36-e27b-428b-8009-d015c8737c54", + "Description": "john@cyberdyne.io has renamed the group from old-group-name to new-group-name", + "Info": { + "group_id": "3089bb36-e27b-428b-8009-d015c8737c56", + "group_name": "new-group-name", + "old_name": "old-group-name", + "new_name": "new-group-name" + }, + "Digest": "sha256:4c407dc69ced81f8484a41dddb207a7e7b74087faaeb1218d3fbd7675a702578" +} \ No newline at end of file diff --git a/app/controlplane/pkg/auditor/events/testdata/groups/group_updated.json b/app/controlplane/pkg/auditor/events/testdata/groups/group_updated.json new file mode 100644 index 000000000..99ed24893 --- /dev/null +++ b/app/controlplane/pkg/auditor/events/testdata/groups/group_updated.json @@ -0,0 +1,16 @@ +{ + "ActionType": "GroupUpdated", + "TargetType": "Group", + "TargetID": "3089bb36-e27b-428b-8009-d015c8737c56", + "ActorType": "USER", + "ActorID": "1089bb36-e27b-428b-8009-d015c8737c54", + "ActorEmail": "john@cyberdyne.io", + "OrgID": "1089bb36-e27b-428b-8009-d015c8737c54", + "Description": "john@cyberdyne.io has updated the group test-group", + "Info": { + "group_id": "3089bb36-e27b-428b-8009-d015c8737c56", + "group_name": "test-group", + "new_description": "test description" + }, + "Digest": "sha256:606fac0916f3980d4c661b89c5d527152e4be74c55135cf3856ec04a8d582a32" +} \ No newline at end of file diff --git a/app/controlplane/pkg/auditor/events/testdata/groups/group_updated_with_description.json b/app/controlplane/pkg/auditor/events/testdata/groups/group_updated_with_description.json new file mode 100644 index 000000000..99ed24893 --- /dev/null +++ b/app/controlplane/pkg/auditor/events/testdata/groups/group_updated_with_description.json @@ -0,0 +1,16 @@ +{ + "ActionType": "GroupUpdated", + "TargetType": "Group", + "TargetID": "3089bb36-e27b-428b-8009-d015c8737c56", + "ActorType": "USER", + "ActorID": "1089bb36-e27b-428b-8009-d015c8737c54", + "ActorEmail": "john@cyberdyne.io", + "OrgID": "1089bb36-e27b-428b-8009-d015c8737c54", + "Description": "john@cyberdyne.io has updated the group test-group", + "Info": { + "group_id": "3089bb36-e27b-428b-8009-d015c8737c56", + "group_name": "test-group", + "new_description": "test description" + }, + "Digest": "sha256:606fac0916f3980d4c661b89c5d527152e4be74c55135cf3856ec04a8d582a32" +} \ No newline at end of file diff --git a/app/controlplane/pkg/authz/authz.go b/app/controlplane/pkg/authz/authz.go index 0c378fbd4..6ec671238 100644 --- a/app/controlplane/pkg/authz/authz.go +++ b/app/controlplane/pkg/authz/authz.go @@ -58,6 +58,8 @@ const ( UserMembership = "membership_user" Organization = "organization" ResourceProject = "project" + ResourceGroup = "group" + ResourceGroupMembership = "group_membership" // We have for now three roles, viewer, admin and owner // The owner of an org @@ -130,6 +132,14 @@ var ( // User Membership PolicyOrganizationRead = &Policy{Organization, ActionRead} PolicyOrganizationListMemberships = &Policy{Organization, ActionRead} + // Groups + PolicyGroupCreate = &Policy{ResourceGroup, ActionCreate} + PolicyGroupUpdate = &Policy{ResourceGroup, ActionUpdate} + PolicyGroupDelete = &Policy{ResourceGroup, ActionDelete} + PolicyGroupList = &Policy{ResourceGroup, ActionList} + PolicyGroupRead = &Policy{ResourceGroup, ActionRead} + // Group Memberships + PolicyGroupListMemberships = &Policy{ResourceGroupMembership, ActionList} ) // List of policies for each role @@ -211,6 +221,13 @@ var rolesMap = map[Role][]*Policy{ PolicyOrgMetricsRead, PolicyReferrerRead, + + // Groups + PolicyGroupList, + PolicyGroupRead, + + // Group Memberships + PolicyGroupListMemberships, }, // RoleProjectAdmin: represents a project administrator. It's the higher role in project resources, // and it's only considered when the org-level role is `RoleOrgMember` @@ -299,6 +316,14 @@ var ServerOperationsMap = map[string][]*Policy{ "/controlplane.v1.AuthService/DeleteAccount": {}, "/controlplane.v1.OrganizationService/ListMemberships": {PolicyOrganizationListMemberships}, + // Groups + "/controlplane.v1.GroupService/List": {PolicyGroupList}, + "/controlplane.v1.GroupService/Get": {PolicyGroupRead}, + "/controlplane.v1.GroupService/Create": {PolicyGroupCreate}, + "/controlplane.v1.GroupService/Update": {PolicyGroupUpdate}, + "/controlplane.v1.GroupService/Delete": {PolicyGroupDelete}, + // Group Memberships + "/controlplane.v1.GroupService/ListMembers": {PolicyGroupListMemberships}, } type SubjectAPIToken struct { diff --git a/app/controlplane/pkg/biz/biz.go b/app/controlplane/pkg/biz/biz.go index d4a9fb9ad..675a56282 100644 --- a/app/controlplane/pkg/biz/biz.go +++ b/app/controlplane/pkg/biz/biz.go @@ -54,6 +54,7 @@ var ProviderSet = wire.NewSet( NewProjectsUseCase, NewAuditorUseCase, NewUserAccessSyncerUseCase, + NewGroupUseCase, wire.Bind(new(PromObservable), new(*PrometheusUseCase)), wire.Struct(new(NewIntegrationUseCaseOpts), "*"), wire.Struct(new(NewUserUseCaseParams), "*"), diff --git a/app/controlplane/pkg/biz/group.go b/app/controlplane/pkg/biz/group.go new file mode 100644 index 000000000..fcd1d289f --- /dev/null +++ b/app/controlplane/pkg/biz/group.go @@ -0,0 +1,276 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package biz + +import ( + "context" + "fmt" + "time" + + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/auditor/events" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination" + + "github.com/go-kratos/kratos/v2/log" + "github.com/google/uuid" +) + +type GroupRepo interface { + // List retrieves a list of groups in the organization, optionally filtered by name, description, and owner. + List(ctx context.Context, orgID uuid.UUID, filterOpts *ListGroupOpts, paginationOpts *pagination.OffsetPaginationOpts) ([]*Group, int, error) + // Create creates a new group. + Create(ctx context.Context, orgID uuid.UUID, opts *CreateGroupOpts) (*Group, error) + // Update updates an existing group. + Update(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID, opts *UpdateGroupOpts) (*Group, error) + // FindByOrgAndID finds a group by its organization ID and group ID. + FindByOrgAndID(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID) (*Group, error) + // SoftDelete soft-deletes a group by marking it as deleted. + SoftDelete(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID) error + // ListMembers retrieves a list of members in a group, optionally filtered by maintainer status. + ListMembers(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID, opts *ListMembersOpts, paginationOpts *pagination.OffsetPaginationOpts) ([]*GroupMembership, int, error) +} + +// GroupMembership represents a membership of a user in a group. +type GroupMembership struct { + // User is the user who is a member of the group. + User *User + // Maintainer indicates if the user is a maintainer of the group. + Maintainer bool + // CreatedAt is the timestamp when the user was added to the group. + CreatedAt *time.Time + // UpdatedAt is the timestamp when the membership was last updated. + UpdatedAt *time.Time + // DeletedAt is the timestamp when the membership was deleted, if applicable. + DeletedAt *time.Time +} + +type Group struct { + // ID is the unique identifier for the group. + ID uuid.UUID + // Name is the name of the group. + Name string + // The Description is a brief description of the group. + Description string + // Members is a list of group memberships, which includes the users who are members of the group. + Members []*GroupMembership + // Organization is the organization to which the group belongs. + Organization *Organization + // CreatedAt is the timestamp when the group was created. + CreatedAt *time.Time + // UpdatedAt is the timestamp when the group was last updated. + UpdatedAt *time.Time + // DeletedAt is the timestamp when the group was deleted, if applicable. + DeletedAt *time.Time +} + +type CreateGroupOpts struct { + // Name is the name of the group. + Name string + // The description is a brief description of the group. + Description string + // UserID is the ID of the user who owns the group. + UserID uuid.UUID +} + +type UpdateGroupOpts struct { + // Description is the new description of the group. + Description *string + // Name is the new name of the group. + Name *string +} + +type ListGroupOpts struct { + // Name is the name of the group to filter by. + Name string + // Description is the description of the group to filter by. + Description string + // MemberEmail is the email of the member to filter by. + MemberEmail string +} + +// ListMembersOpts defines options for listing members of a group. +type ListMembersOpts struct { + // Maintainers indicate whether to filter the members by their maintainer status. + Maintainers *bool + // MemberEmail is the email of the member to filter by. + MemberEmail *string +} + +type GroupUseCase struct { + // logger is used to log messages. + logger *log.Helper + // Repositories + groupRepo GroupRepo + membershipRepo MembershipRepo + // Auditor use case for logging events + auditorUC *AuditorUseCase +} + +func NewGroupUseCase(logger log.Logger, groupRepo GroupRepo, membershipRepo MembershipRepo, auditorUC *AuditorUseCase) *GroupUseCase { + return &GroupUseCase{ + logger: log.NewHelper(log.With(logger, "component", "biz/group")), + groupRepo: groupRepo, + membershipRepo: membershipRepo, + auditorUC: auditorUC, + } +} + +func (uc *GroupUseCase) List(ctx context.Context, orgID uuid.UUID, filterOpts *ListGroupOpts, paginationOpts *pagination.OffsetPaginationOpts) ([]*Group, int, error) { + pgOpts := pagination.NewDefaultOffsetPaginationOpts() + if paginationOpts != nil { + pgOpts = paginationOpts + } + + return uc.groupRepo.List(ctx, orgID, filterOpts, pgOpts) +} + +func (uc *GroupUseCase) ListMembers(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID, maintainers *bool, memberEmail *string, paginationOpts *pagination.OffsetPaginationOpts) ([]*GroupMembership, int, error) { + pgOpts := pagination.NewDefaultOffsetPaginationOpts() + if paginationOpts != nil { + pgOpts = paginationOpts + } + + return uc.groupRepo.ListMembers(ctx, orgID, groupID, &ListMembersOpts{Maintainers: maintainers, MemberEmail: memberEmail}, pgOpts) +} + +// Create creates a new group in the organization. +func (uc *GroupUseCase) Create(ctx context.Context, orgID uuid.UUID, name string, description string, userID uuid.UUID) (*Group, error) { + if name == "" { + return nil, NewErrValidationStr("name cannot be empty") + } + + if orgID == uuid.Nil || userID == uuid.Nil { + return nil, NewErrValidationStr("organization ID and user ID cannot be empty") + } + + // Check if the user is a member of the organization + m, err := uc.membershipRepo.FindByOrgAndUser(ctx, orgID, userID) + if err != nil { + return nil, fmt.Errorf("failed to find membership: %w", err) + } else if m == nil { + return nil, NewErrNotFound("membership") + } + + group, err := uc.groupRepo.Create(ctx, orgID, &CreateGroupOpts{ + Name: name, + Description: description, + UserID: userID, + }) + if err != nil { + return nil, fmt.Errorf("failed to create group: %w", err) + } + + // Dispatch event to the audit log for group creation + uc.auditorUC.Dispatch(ctx, &events.GroupCreated{ + GroupBase: &events.GroupBase{ + GroupID: &group.ID, + GroupName: group.Name, + }, + GroupDescription: description, + }, &orgID) + + return group, nil +} + +// Update updates an existing group in the organization. +func (uc *GroupUseCase) Update(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID, description *string, name *string) (*Group, error) { + if orgID == uuid.Nil || groupID == uuid.Nil { + return nil, NewErrValidationStr("organization ID and group ID cannot be empty") + } + + // Check the group exists + existingGroup, err := uc.groupRepo.FindByOrgAndID(ctx, orgID, groupID) + if err != nil { + return nil, fmt.Errorf("failed to find group: %w", err) + } + + if existingGroup == nil { + return nil, NewErrNotFound("group") + } + + updatedGroup, err := uc.groupRepo.Update(ctx, orgID, groupID, &UpdateGroupOpts{ + Description: description, + Name: name, + }) + if err != nil { + return nil, fmt.Errorf("failed to update group: %w", err) + } + + // Dispatch event to the audit log for group update + event := &events.GroupUpdated{ + GroupBase: &events.GroupBase{ + GroupID: &updatedGroup.ID, + GroupName: updatedGroup.Name, + }, + NewDescription: description, + } + + // Add old and new name only if the name was changed + if name != nil && existingGroup.Name != *name { + event.OldName = &existingGroup.Name + event.NewName = name + } + + uc.auditorUC.Dispatch(ctx, event, &orgID) + + return updatedGroup, nil +} + +// FindByOrgAndID retrieves a group by its organization ID and group ID. +func (uc *GroupUseCase) FindByOrgAndID(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID) (*Group, error) { + if orgID == uuid.Nil || groupID == uuid.Nil { + return nil, NewErrValidationStr("organization ID and group ID cannot be empty") + } + + group, err := uc.groupRepo.FindByOrgAndID(ctx, orgID, groupID) + if err != nil { + return nil, fmt.Errorf("failed to find group: %w", err) + } else if group == nil { + return nil, NewErrNotFound("group") + } + + return group, nil +} + +// SoftDelete marks a group as deleted by setting the DeletedAt timestamp. +func (uc *GroupUseCase) SoftDelete(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID) error { + if orgID == uuid.Nil || groupID == uuid.Nil { + return NewErrValidationStr("organization ID and group ID cannot be empty") + } + + // Check the group exists + existingGroup, err := uc.groupRepo.FindByOrgAndID(ctx, orgID, groupID) + if err != nil { + return fmt.Errorf("failed to find group: %w", err) + } + + if existingGroup == nil { + return NewErrNotFound("group") + } + + if err := uc.groupRepo.SoftDelete(ctx, orgID, groupID); err != nil { + return fmt.Errorf("failed to soft-delete group: %w", err) + } + + // Dispatch event to the audit log for group deletion + uc.auditorUC.Dispatch(ctx, &events.GroupDeleted{ + GroupBase: &events.GroupBase{ + GroupID: &existingGroup.ID, + GroupName: existingGroup.Name, + }, + }, &orgID) + + return nil +} diff --git a/app/controlplane/pkg/biz/group_integration_test.go b/app/controlplane/pkg/biz/group_integration_test.go new file mode 100644 index 000000000..fb23b9ace --- /dev/null +++ b/app/controlplane/pkg/biz/group_integration_test.go @@ -0,0 +1,562 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package biz_test + +import ( + "context" + "fmt" + "testing" + + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz/testhelpers" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +// Run the tests +func TestGroupUseCase(t *testing.T) { + suite.Run(t, new(groupIntegrationTestSuite)) + suite.Run(t, new(groupListIntegrationTestSuite)) + suite.Run(t, new(groupMembersIntegrationTestSuite)) +} + +// Utility struct to hold the base test suite +type groupIntegrationTestSuite struct { + testhelpers.UseCasesEachTestSuite + org *biz.Organization + user *biz.User +} + +func (s *groupIntegrationTestSuite) SetupTest() { + var err error + assert := assert.New(s.T()) + s.TestingUseCases = testhelpers.NewTestingUseCases(s.T()) + + ctx := context.Background() + s.org, err = s.Organization.CreateWithRandomName(ctx) + assert.NoError(err) + + // Create a user for membership tests + s.user, err = s.User.UpsertByEmail(ctx, fmt.Sprintf("test-user-%s@example.com", uuid.New().String()), nil) + assert.NoError(err) + + // Add user to organization + _, err = s.Membership.Create(ctx, s.org.ID, s.user.ID) + assert.NoError(err) +} + +// TearDown cleans up resources after all tests in the suite have completed +func (s *groupIntegrationTestSuite) TearDownTest() { + ctx := context.Background() + // Clean up any test groups created during testing + _, _ = s.Data.DB.Group.Delete().Exec(ctx) +} + +// Test creating groups +func (s *groupIntegrationTestSuite) TestCreate() { + ctx := context.Background() + localDescription := "A test group" + testCases := []struct { + name string + opts *biz.CreateGroupOpts + expectError bool + errorMsg string + }{ + { + name: "successful creation", + opts: &biz.CreateGroupOpts{ + Name: "test-group", + Description: localDescription, + UserID: uuid.MustParse(s.user.ID), + }, + expectError: false, + }, + { + name: "empty name", + opts: &biz.CreateGroupOpts{ + Name: "", + Description: localDescription, + UserID: uuid.MustParse(s.user.ID), + }, + expectError: true, + errorMsg: "name cannot be empty", + }, + { + name: "nil user ID", + opts: &biz.CreateGroupOpts{ + Name: "test-group", + Description: localDescription, + UserID: uuid.Nil, + }, + expectError: true, + errorMsg: "organization ID and user ID cannot be empty", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + group, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), tc.opts.Name, tc.opts.Description, tc.opts.UserID) + + if tc.expectError { + s.Error(err) + if tc.errorMsg != "" { + s.Contains(err.Error(), tc.errorMsg) + } + return + } + + s.NoError(err) + s.NotNil(group) + s.Equal(tc.opts.Name, group.Name) + s.Equal(tc.opts.Description, group.Description) + s.NotEmpty(group.ID) + s.NotNil(group.CreatedAt) + s.NotNil(group.Organization) + s.Equal(s.org.ID, group.Organization.ID) + }) + } +} + +// Test creating duplicate groups +func (s *groupIntegrationTestSuite) TestCreateDuplicate() { + ctx := context.Background() + + // Create initial group + name := "duplicate-test-group" + description := "This is a test group for duplicate tests" + differentDescription := "Different description" + + group, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), name, description, uuid.MustParse(s.user.ID)) + s.NoError(err) + s.NotNil(group) + + // Try to create another group with the same name + _, err = s.Group.Create(ctx, uuid.MustParse(s.org.ID), name, differentDescription, uuid.MustParse(s.user.ID)) + s.Error(err) + s.Contains(err.Error(), "duplicated") + + // Create a group with the same name in a different organization + org2, err := s.Organization.CreateWithRandomName(ctx) + require.NoError(s.T(), err) + + // Add user to second organization + _, err = s.Membership.Create(ctx, org2.ID, s.user.ID) + require.NoError(s.T(), err) + + // Should succeed because it's in a different organization + group2, err := s.Group.Create(ctx, uuid.MustParse(org2.ID), name, description, uuid.MustParse(s.user.ID)) + s.NoError(err) + s.NotNil(group2) + s.Equal(name, group2.Name) +} + +// Test finding groups by ID +func (s *groupIntegrationTestSuite) TestFindByID() { + ctx := context.Background() + + // Create a group + name := "test-find-group" + groupDescription := "This is a test group for finding by ID" + + group, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), name, groupDescription, uuid.MustParse(s.user.ID)) + s.NoError(err) + s.NotNil(group) + + // Test finding the group by ID + s.Run("find existing group", func() { + foundGroup, err := s.Group.FindByOrgAndID(ctx, uuid.MustParse(s.org.ID), group.ID) + s.NoError(err) + s.NotNil(foundGroup) + s.Equal(group.ID, foundGroup.ID) + s.Equal(name, foundGroup.Name) + s.Equal(groupDescription, foundGroup.Description) + }) + + s.Run("try to find in wrong organization", func() { + org2, org2Err := s.Organization.CreateWithRandomName(ctx) + require.NoError(s.T(), org2Err) + + _, expectedErr := s.Group.FindByOrgAndID(ctx, uuid.MustParse(org2.ID), group.ID) + s.Error(expectedErr) + s.True(biz.IsNotFound(expectedErr)) + }) + + s.Run("try to find non-existent group", func() { + _, err := s.Group.FindByOrgAndID(ctx, uuid.MustParse(s.org.ID), uuid.New()) + s.Error(err) + s.True(biz.IsNotFound(err)) + }) +} + +// Test updating groups +func (s *groupIntegrationTestSuite) TestUpdate() { + ctx := context.Background() + + // Create a group + name := "test-update-group" + description := "This is a test group for updating" + + group, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), name, description, uuid.MustParse(s.user.ID)) + s.NoError(err) + s.NotNil(group) + + // Test updating the group + s.Run("update description", func() { + newDescription := "Updated description" + descPtr := &newDescription + + updatedGroup, err := s.Group.Update(ctx, uuid.MustParse(s.org.ID), group.ID, descPtr, nil) + + s.NoError(err) + s.NotNil(updatedGroup) + s.Equal(newDescription, updatedGroup.Description) + s.Equal(name, updatedGroup.Name) // Name should not change + }) + + s.Run("try to update in wrong organization", func() { + org2, err := s.Organization.CreateWithRandomName(ctx) + require.NoError(s.T(), err) + + newDescription := "Updated description" + descPtr := &newDescription + + _, err = s.Group.Update(ctx, uuid.MustParse(org2.ID), group.ID, descPtr, nil) + + s.Error(err) + s.True(biz.IsNotFound(err)) + }) + + s.Run("try to update non-existent group", func() { + nonExistentGroupID := uuid.New() + newDescription := "Updated description for non-existent group" + descPtr := &newDescription + + _, err := s.Group.Update(ctx, uuid.MustParse(s.org.ID), nonExistentGroupID, descPtr, nil) + + s.Error(err) + s.True(biz.IsNotFound(err)) + }) +} + +// Test soft deleting groups +func (s *groupIntegrationTestSuite) TestSoftDelete() { + ctx := context.Background() + + // Create a group + name := "test-delete-group" + description := "This is a test group for deleting" + + group, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), name, description, uuid.MustParse(s.user.ID)) + s.NoError(err) + s.NotNil(group) + + // Test deleting the group + s.Run("delete existing group", func() { + err := s.Group.SoftDelete(ctx, uuid.MustParse(s.org.ID), group.ID) + s.NoError(err) + + // Try to find it after deletion + _, err = s.Group.FindByOrgAndID(ctx, uuid.MustParse(s.org.ID), group.ID) + s.Error(err) + s.True(biz.IsNotFound(err)) + + // We should be able to create a new group with the same name after deletion + newGroup, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), name, description, uuid.MustParse(s.user.ID)) + s.NoError(err) + s.NotNil(newGroup) + }) + + s.Run("try to delete in wrong organization", func() { + org2, err := s.Organization.CreateWithRandomName(ctx) + require.NoError(s.T(), err) + + group, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), "org-specific-group", description, uuid.MustParse(s.user.ID)) + s.NoError(err) + + err = s.Group.SoftDelete(ctx, uuid.MustParse(org2.ID), group.ID) + s.Error(err) + s.True(biz.IsNotFound(err)) + }) + + s.Run("try to delete non-existent group", func() { + err := s.Group.SoftDelete(ctx, uuid.MustParse(s.org.ID), uuid.New()) + s.Error(err) + s.True(biz.IsNotFound(err)) + }) +} + +// Utility struct for listing tests +type groupListIntegrationTestSuite struct { + testhelpers.UseCasesEachTestSuite + org *biz.Organization + user *biz.User +} + +func (s *groupListIntegrationTestSuite) SetupTest() { + var err error + assert := assert.New(s.T()) + s.TestingUseCases = testhelpers.NewTestingUseCases(s.T()) + + ctx := context.Background() + s.org, err = s.Organization.CreateWithRandomName(ctx) + assert.NoError(err) + + // Create a user for membership tests + s.user, err = s.User.UpsertByEmail(ctx, fmt.Sprintf("test-user-%s@example.com", uuid.New().String()), nil) + assert.NoError(err) + + // Add user to organization + _, err = s.Membership.Create(ctx, s.org.ID, s.user.ID) + assert.NoError(err) +} + +// TearDown cleans up resources after all tests in the suite have completed +func (s *groupListIntegrationTestSuite) TearDownSubTest() { + ctx := context.Background() + // Clean up the database after each test + _, _ = s.Data.DB.Group.Delete().Exec(ctx) +} + +// Test listing groups with various filters +func (s *groupListIntegrationTestSuite) TestList() { + ctx := context.Background() + + s.Run("no groups", func() { + groups, count, err := s.Group.List(ctx, uuid.MustParse(s.org.ID), nil, nil) + s.NoError(err) + s.Empty(groups) + s.Equal(0, count) + }) + + s.Run("list groups without filters", func() { + // Create a few groups + desc1 := "Description 1" + _, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), "group-1", desc1, uuid.MustParse(s.user.ID)) + require.NoError(s.T(), err) + + desc2 := "Description 2" + _, err = s.Group.Create(ctx, uuid.MustParse(s.org.ID), "group-2", desc2, uuid.MustParse(s.user.ID)) + require.NoError(s.T(), err) + + groups, count, err := s.Group.List(ctx, uuid.MustParse(s.org.ID), nil, nil) + s.NoError(err) + s.Equal(2, len(groups)) + s.Equal(2, count) + }) + + s.Run("list groups with name filter", func() { + devDescription := "Development Team" + opsDescription := "Operations Team" + // Create groups with different names + _, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), "dev-team", devDescription, uuid.MustParse(s.user.ID)) + require.NoError(s.T(), err) + _, err = s.Group.Create(ctx, uuid.MustParse(s.org.ID), "ops-team", opsDescription, uuid.MustParse(s.user.ID)) + require.NoError(s.T(), err) + + // Filter by name + filterOpts := &biz.ListGroupOpts{Name: "dev"} + groups, count, err := s.Group.List(ctx, uuid.MustParse(s.org.ID), filterOpts, nil) + s.NoError(err) + s.Equal(1, len(groups)) + s.Equal(1, count) + s.Contains(groups[0].Name, "dev") + }) + + s.Run("list groups with description filter", func() { + teamADescription := "This is the A team" + teamBDescription := "This is the B team" + // Create groups with different descriptions + _, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), "team-a", teamADescription, uuid.MustParse(s.user.ID)) + require.NoError(s.T(), err) + _, err = s.Group.Create(ctx, uuid.MustParse(s.org.ID), "team-b", teamBDescription, uuid.MustParse(s.user.ID)) + require.NoError(s.T(), err) + + // Filter by description + filterOpts := &biz.ListGroupOpts{Description: "A team"} + groups, count, err := s.Group.List(ctx, uuid.MustParse(s.org.ID), filterOpts, nil) + s.NoError(err) + s.Equal(1, len(groups)) + s.Equal(1, count) + s.Contains(groups[0].Description, "A team") + }) + + s.Run("list groups with member email filter", func() { + // Create a second user + user2, err := s.User.UpsertByEmail(ctx, "user2@example.com", nil) + require.NoError(s.T(), err) + + // Add user2 to organization + _, err = s.Membership.Create(ctx, s.org.ID, user2.ID) + require.NoError(s.T(), err) + + // Create a group with user as maintainer + groupWithUser1 := "Group with user 1" + _, err = s.Group.Create(ctx, uuid.MustParse(s.org.ID), "group-with-user1", groupWithUser1, uuid.MustParse(s.user.ID)) + require.NoError(s.T(), err) + + // Create a group with user2 as maintainer + groupWithUser2 := "Group with user 2" + group2, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), "group-with-user2", groupWithUser2, uuid.MustParse(user2.ID)) + require.NoError(s.T(), err) + + // Filter by member email + filterOpts := &biz.ListGroupOpts{MemberEmail: "user2@example.com"} + groups, count, err := s.Group.List(ctx, uuid.MustParse(s.org.ID), filterOpts, nil) + s.NoError(err) + s.Equal(1, len(groups)) + s.Equal(1, count) + s.Equal(group2.ID, groups[0].ID) + }) + + s.Run("list groups with pagination", func() { + // Create several groups + for i := 1; i <= 5; i++ { + lDescription := fmt.Sprintf("Description %d", i) + _, err := s.Group.Create(ctx, uuid.MustParse(s.org.ID), fmt.Sprintf("the-group-%d", i), lDescription, uuid.MustParse(s.user.ID)) + require.NoError(s.T(), err) + } + + // Test with offset pagination + paginationOpts, err := pagination.NewOffsetPaginationOpts(0, 2) + require.NoError(s.T(), err) + + groups, count, err := s.Group.List(ctx, uuid.MustParse(s.org.ID), nil, paginationOpts) + s.NoError(err) + s.Equal(2, len(groups)) + s.Equal(5, count) // Total count should be 5 + + // Get the next page + paginationOpts, err = pagination.NewOffsetPaginationOpts(2, 2) + require.NoError(s.T(), err) + + groups, count, err = s.Group.List(ctx, uuid.MustParse(s.org.ID), nil, paginationOpts) + s.NoError(err) + s.Equal(2, len(groups)) + s.Equal(5, count) + }) +} + +// Utility struct for group members tests +type groupMembersIntegrationTestSuite struct { + testhelpers.UseCasesEachTestSuite + org *biz.Organization + user *biz.User + group *biz.Group +} + +func (s *groupMembersIntegrationTestSuite) SetupTest() { + var err error + assert := assert.New(s.T()) + s.TestingUseCases = testhelpers.NewTestingUseCases(s.T()) + + ctx := context.Background() + s.org, err = s.Organization.CreateWithRandomName(ctx) + assert.NoError(err) + + // Create a user for membership tests + s.user, err = s.User.UpsertByEmail(ctx, fmt.Sprintf("test-user-%s@example.com", uuid.New().String()), nil) + assert.NoError(err) + + // Add user to organization + _, err = s.Membership.Create(ctx, s.org.ID, s.user.ID) + assert.NoError(err) + + // Create a group for membership tests + membersDescription := "Group for testing members" + s.group, err = s.Group.Create(ctx, uuid.MustParse(s.org.ID), "test-members-group", membersDescription, uuid.MustParse(s.user.ID)) + assert.NoError(err) +} + +func (s *groupMembersIntegrationTestSuite) TearDownTest() { + ctx := context.Background() + // Clean up the database after each test + _, _ = s.Data.DB.Group.Delete().Exec(ctx) +} + +// Test group membership operations +func (s *groupMembersIntegrationTestSuite) TestListMembers() { + ctx := context.Background() + + // Create additional users + user2, err := s.User.UpsertByEmail(ctx, "user2@example.com", nil) + require.NoError(s.T(), err) + + user3, err := s.User.UpsertByEmail(ctx, "user3@example.com", nil) + require.NoError(s.T(), err) + + // Add users to organization + _, err = s.Membership.Create(ctx, s.org.ID, user2.ID) + require.NoError(s.T(), err) + _, err = s.Membership.Create(ctx, s.org.ID, user3.ID) + require.NoError(s.T(), err) + + s.Run("initial group has creator as maintainer", func() { + members, count, err := s.Group.ListMembers(ctx, uuid.MustParse(s.org.ID), s.group.ID, nil, nil, nil) + s.NoError(err) + s.Equal(1, len(members)) + s.Equal(1, count) + s.Equal(s.user.ID, members[0].User.ID) + s.True(members[0].Maintainer) + }) + + // TODO: Add tests for adding members to groups once that functionality is implemented + + s.Run("filter members by maintainer status", func() { + isTrue := true + members, count, err := s.Group.ListMembers(ctx, uuid.MustParse(s.org.ID), s.group.ID, &isTrue, nil, nil) + s.NoError(err) + s.Equal(1, len(members)) + s.Equal(1, count) + s.True(members[0].Maintainer) + + isFalse := false + members, count, err = s.Group.ListMembers(ctx, uuid.MustParse(s.org.ID), s.group.ID, &isFalse, nil, nil) + s.NoError(err) + s.Equal(0, len(members)) + s.Equal(0, count) + }) + + s.Run("filter members by email", func() { + email := s.user.Email + members, count, err := s.Group.ListMembers(ctx, uuid.MustParse(s.org.ID), s.group.ID, nil, &email, nil) + s.NoError(err) + s.Equal(1, len(members)) + s.Equal(1, count) + s.Equal(s.user.Email, members[0].User.Email) + + nonExistentEmail := "nonexistent@example.com" + members, count, err = s.Group.ListMembers(ctx, uuid.MustParse(s.org.ID), s.group.ID, nil, &nonExistentEmail, nil) + s.NoError(err) + s.Equal(0, len(members)) + s.Equal(0, count) + }) + + s.Run("list members with pagination", func() { + // TODO: Add more members to the group once that functionality is implemented + + paginationOpts, err := pagination.NewOffsetPaginationOpts(0, 1) + require.NoError(s.T(), err) + + members, count, err := s.Group.ListMembers(ctx, uuid.MustParse(s.org.ID), s.group.ID, nil, nil, paginationOpts) + s.NoError(err) + s.Equal(1, len(members)) + s.Equal(1, count) + }) +} diff --git a/app/controlplane/pkg/biz/testhelpers/database.go b/app/controlplane/pkg/biz/testhelpers/database.go index 2219d8ef1..e62cd9c5a 100644 --- a/app/controlplane/pkg/biz/testhelpers/database.go +++ b/app/controlplane/pkg/biz/testhelpers/database.go @@ -76,6 +76,7 @@ type TestingUseCases struct { ProjectVersion *biz.ProjectVersionUseCase Project *biz.ProjectUseCase OrgMetrics *biz.OrgMetricsUseCase + Group *biz.GroupUseCase // Repositories that can be used for custom crafting of use-cases Repos *TestingRepos } @@ -87,6 +88,7 @@ type TestingRepos struct { WorkflowRunRepo biz.WorkflowRunRepo AttestationState biz.AttestationStateRepo OrganizationRepo biz.OrganizationRepo + GroupRepo biz.GroupRepo } type newTestingOpts struct { diff --git a/app/controlplane/pkg/biz/testhelpers/wire_gen.go b/app/controlplane/pkg/biz/testhelpers/wire_gen.go index a8f286a6e..c3db5e05a 100644 --- a/app/controlplane/pkg/biz/testhelpers/wire_gen.go +++ b/app/controlplane/pkg/biz/testhelpers/wire_gen.go @@ -151,6 +151,8 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r projectVersionRepo := data.NewProjectVersionRepo(dataData, logger) projectVersionUseCase := biz.NewProjectVersionUseCase(projectVersionRepo, logger) projectUseCase := biz.NewProjectsUseCase(logger, projectsRepo) + groupRepo := data.NewGroupRepo(dataData, logger) + groupUseCase := biz.NewGroupUseCase(logger, groupRepo, membershipRepo, auditorUseCase) testingRepos := &TestingRepos{ Membership: membershipRepo, Referrer: referrerRepo, @@ -158,6 +160,7 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r WorkflowRunRepo: workflowRunRepo, AttestationState: attestationStateRepo, OrganizationRepo: organizationRepo, + GroupRepo: groupRepo, } testingUseCases := &TestingUseCases{ DB: testDatabase, @@ -183,6 +186,7 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r ProjectVersion: projectVersionUseCase, Project: projectUseCase, OrgMetrics: orgMetricsUseCase, + Group: groupUseCase, Repos: testingRepos, } return testingUseCases, func() { diff --git a/app/controlplane/pkg/data/data.go b/app/controlplane/pkg/data/data.go index ea7349a3b..2d2069bdd 100644 --- a/app/controlplane/pkg/data/data.go +++ b/app/controlplane/pkg/data/data.go @@ -57,6 +57,7 @@ var ProviderSet = wire.NewSet( NewAttestationStateRepo, NewProjectVersionRepo, NewProjectsRepo, + NewGroupRepo, ) // Data . diff --git a/app/controlplane/pkg/data/ent/client.go b/app/controlplane/pkg/data/ent/client.go index 8467e7c73..5438d6100 100644 --- a/app/controlplane/pkg/data/ent/client.go +++ b/app/controlplane/pkg/data/ent/client.go @@ -20,6 +20,8 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/attestation" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casbackend" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casmapping" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integration" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integrationattachment" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/membership" @@ -49,6 +51,10 @@ type Client struct { CASBackend *CASBackendClient // CASMapping is the client for interacting with the CASMapping builders. CASMapping *CASMappingClient + // Group is the client for interacting with the Group builders. + Group *GroupClient + // GroupMembership is the client for interacting with the GroupMembership builders. + GroupMembership *GroupMembershipClient // Integration is the client for interacting with the Integration builders. Integration *IntegrationClient // IntegrationAttachment is the client for interacting with the IntegrationAttachment builders. @@ -92,6 +98,8 @@ func (c *Client) init() { c.Attestation = NewAttestationClient(c.config) c.CASBackend = NewCASBackendClient(c.config) c.CASMapping = NewCASMappingClient(c.config) + c.Group = NewGroupClient(c.config) + c.GroupMembership = NewGroupMembershipClient(c.config) c.Integration = NewIntegrationClient(c.config) c.IntegrationAttachment = NewIntegrationAttachmentClient(c.config) c.Membership = NewMembershipClient(c.config) @@ -202,6 +210,8 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { Attestation: NewAttestationClient(cfg), CASBackend: NewCASBackendClient(cfg), CASMapping: NewCASMappingClient(cfg), + Group: NewGroupClient(cfg), + GroupMembership: NewGroupMembershipClient(cfg), Integration: NewIntegrationClient(cfg), IntegrationAttachment: NewIntegrationAttachmentClient(cfg), Membership: NewMembershipClient(cfg), @@ -239,6 +249,8 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) Attestation: NewAttestationClient(cfg), CASBackend: NewCASBackendClient(cfg), CASMapping: NewCASMappingClient(cfg), + Group: NewGroupClient(cfg), + GroupMembership: NewGroupMembershipClient(cfg), Integration: NewIntegrationClient(cfg), IntegrationAttachment: NewIntegrationAttachmentClient(cfg), Membership: NewMembershipClient(cfg), @@ -282,10 +294,11 @@ func (c *Client) Close() error { // In order to add hooks to a specific client, call: `client.Node.Use(...)`. func (c *Client) Use(hooks ...Hook) { for _, n := range []interface{ Use(...Hook) }{ - c.APIToken, c.Attestation, c.CASBackend, c.CASMapping, c.Integration, - c.IntegrationAttachment, c.Membership, c.OrgInvitation, c.Organization, - c.Project, c.ProjectVersion, c.Referrer, c.RobotAccount, c.User, c.Workflow, - c.WorkflowContract, c.WorkflowContractVersion, c.WorkflowRun, + c.APIToken, c.Attestation, c.CASBackend, c.CASMapping, c.Group, + c.GroupMembership, c.Integration, c.IntegrationAttachment, c.Membership, + c.OrgInvitation, c.Organization, c.Project, c.ProjectVersion, c.Referrer, + c.RobotAccount, c.User, c.Workflow, c.WorkflowContract, + c.WorkflowContractVersion, c.WorkflowRun, } { n.Use(hooks...) } @@ -295,10 +308,11 @@ func (c *Client) Use(hooks ...Hook) { // In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. func (c *Client) Intercept(interceptors ...Interceptor) { for _, n := range []interface{ Intercept(...Interceptor) }{ - c.APIToken, c.Attestation, c.CASBackend, c.CASMapping, c.Integration, - c.IntegrationAttachment, c.Membership, c.OrgInvitation, c.Organization, - c.Project, c.ProjectVersion, c.Referrer, c.RobotAccount, c.User, c.Workflow, - c.WorkflowContract, c.WorkflowContractVersion, c.WorkflowRun, + c.APIToken, c.Attestation, c.CASBackend, c.CASMapping, c.Group, + c.GroupMembership, c.Integration, c.IntegrationAttachment, c.Membership, + c.OrgInvitation, c.Organization, c.Project, c.ProjectVersion, c.Referrer, + c.RobotAccount, c.User, c.Workflow, c.WorkflowContract, + c.WorkflowContractVersion, c.WorkflowRun, } { n.Intercept(interceptors...) } @@ -315,6 +329,10 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.CASBackend.mutate(ctx, m) case *CASMappingMutation: return c.CASMapping.mutate(ctx, m) + case *GroupMutation: + return c.Group.mutate(ctx, m) + case *GroupMembershipMutation: + return c.GroupMembership.mutate(ctx, m) case *IntegrationMutation: return c.Integration.mutate(ctx, m) case *IntegrationAttachmentMutation: @@ -976,6 +994,352 @@ func (c *CASMappingClient) mutate(ctx context.Context, m *CASMappingMutation) (V } } +// GroupClient is a client for the Group schema. +type GroupClient struct { + config +} + +// NewGroupClient returns a client for the Group from the given config. +func NewGroupClient(c config) *GroupClient { + return &GroupClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `group.Hooks(f(g(h())))`. +func (c *GroupClient) Use(hooks ...Hook) { + c.hooks.Group = append(c.hooks.Group, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `group.Intercept(f(g(h())))`. +func (c *GroupClient) Intercept(interceptors ...Interceptor) { + c.inters.Group = append(c.inters.Group, interceptors...) +} + +// Create returns a builder for creating a Group entity. +func (c *GroupClient) Create() *GroupCreate { + mutation := newGroupMutation(c.config, OpCreate) + return &GroupCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Group entities. +func (c *GroupClient) CreateBulk(builders ...*GroupCreate) *GroupCreateBulk { + return &GroupCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *GroupClient) MapCreateBulk(slice any, setFunc func(*GroupCreate, int)) *GroupCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &GroupCreateBulk{err: fmt.Errorf("calling to GroupClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*GroupCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &GroupCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Group. +func (c *GroupClient) Update() *GroupUpdate { + mutation := newGroupMutation(c.config, OpUpdate) + return &GroupUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *GroupClient) UpdateOne(gr *Group) *GroupUpdateOne { + mutation := newGroupMutation(c.config, OpUpdateOne, withGroup(gr)) + return &GroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *GroupClient) UpdateOneID(id uuid.UUID) *GroupUpdateOne { + mutation := newGroupMutation(c.config, OpUpdateOne, withGroupID(id)) + return &GroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Group. +func (c *GroupClient) Delete() *GroupDelete { + mutation := newGroupMutation(c.config, OpDelete) + return &GroupDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *GroupClient) DeleteOne(gr *Group) *GroupDeleteOne { + return c.DeleteOneID(gr.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *GroupClient) DeleteOneID(id uuid.UUID) *GroupDeleteOne { + builder := c.Delete().Where(group.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &GroupDeleteOne{builder} +} + +// Query returns a query builder for Group. +func (c *GroupClient) Query() *GroupQuery { + return &GroupQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeGroup}, + inters: c.Interceptors(), + } +} + +// Get returns a Group entity by its id. +func (c *GroupClient) Get(ctx context.Context, id uuid.UUID) (*Group, error) { + return c.Query().Where(group.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *GroupClient) GetX(ctx context.Context, id uuid.UUID) *Group { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryMembers queries the members edge of a Group. +func (c *GroupClient) QueryMembers(gr *Group) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := gr.ID + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, group.MembersTable, group.MembersPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(gr.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryOrganization queries the organization edge of a Group. +func (c *GroupClient) QueryOrganization(gr *Group) *OrganizationQuery { + query := (&OrganizationClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := gr.ID + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, id), + sqlgraph.To(organization.Table, organization.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, group.OrganizationTable, group.OrganizationColumn), + ) + fromV = sqlgraph.Neighbors(gr.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryGroupUsers queries the group_users edge of a Group. +func (c *GroupClient) QueryGroupUsers(gr *Group) *GroupMembershipQuery { + query := (&GroupMembershipClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := gr.ID + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, id), + sqlgraph.To(groupmembership.Table, groupmembership.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, group.GroupUsersTable, group.GroupUsersColumn), + ) + fromV = sqlgraph.Neighbors(gr.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *GroupClient) Hooks() []Hook { + return c.hooks.Group +} + +// Interceptors returns the client interceptors. +func (c *GroupClient) Interceptors() []Interceptor { + return c.inters.Group +} + +func (c *GroupClient) mutate(ctx context.Context, m *GroupMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&GroupCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&GroupUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&GroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&GroupDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Group mutation op: %q", m.Op()) + } +} + +// GroupMembershipClient is a client for the GroupMembership schema. +type GroupMembershipClient struct { + config +} + +// NewGroupMembershipClient returns a client for the GroupMembership from the given config. +func NewGroupMembershipClient(c config) *GroupMembershipClient { + return &GroupMembershipClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `groupmembership.Hooks(f(g(h())))`. +func (c *GroupMembershipClient) Use(hooks ...Hook) { + c.hooks.GroupMembership = append(c.hooks.GroupMembership, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `groupmembership.Intercept(f(g(h())))`. +func (c *GroupMembershipClient) Intercept(interceptors ...Interceptor) { + c.inters.GroupMembership = append(c.inters.GroupMembership, interceptors...) +} + +// Create returns a builder for creating a GroupMembership entity. +func (c *GroupMembershipClient) Create() *GroupMembershipCreate { + mutation := newGroupMembershipMutation(c.config, OpCreate) + return &GroupMembershipCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of GroupMembership entities. +func (c *GroupMembershipClient) CreateBulk(builders ...*GroupMembershipCreate) *GroupMembershipCreateBulk { + return &GroupMembershipCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *GroupMembershipClient) MapCreateBulk(slice any, setFunc func(*GroupMembershipCreate, int)) *GroupMembershipCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &GroupMembershipCreateBulk{err: fmt.Errorf("calling to GroupMembershipClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*GroupMembershipCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &GroupMembershipCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for GroupMembership. +func (c *GroupMembershipClient) Update() *GroupMembershipUpdate { + mutation := newGroupMembershipMutation(c.config, OpUpdate) + return &GroupMembershipUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *GroupMembershipClient) UpdateOne(gm *GroupMembership) *GroupMembershipUpdateOne { + mutation := newGroupMembershipMutation(c.config, OpUpdateOne, withGroupMembership(gm)) + return &GroupMembershipUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *GroupMembershipClient) UpdateOneID(id uuid.UUID) *GroupMembershipUpdateOne { + mutation := newGroupMembershipMutation(c.config, OpUpdateOne, withGroupMembershipID(id)) + return &GroupMembershipUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for GroupMembership. +func (c *GroupMembershipClient) Delete() *GroupMembershipDelete { + mutation := newGroupMembershipMutation(c.config, OpDelete) + return &GroupMembershipDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *GroupMembershipClient) DeleteOne(gm *GroupMembership) *GroupMembershipDeleteOne { + return c.DeleteOneID(gm.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *GroupMembershipClient) DeleteOneID(id uuid.UUID) *GroupMembershipDeleteOne { + builder := c.Delete().Where(groupmembership.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &GroupMembershipDeleteOne{builder} +} + +// Query returns a query builder for GroupMembership. +func (c *GroupMembershipClient) Query() *GroupMembershipQuery { + return &GroupMembershipQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeGroupMembership}, + inters: c.Interceptors(), + } +} + +// Get returns a GroupMembership entity by its id. +func (c *GroupMembershipClient) Get(ctx context.Context, id uuid.UUID) (*GroupMembership, error) { + return c.Query().Where(groupmembership.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *GroupMembershipClient) GetX(ctx context.Context, id uuid.UUID) *GroupMembership { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryGroup queries the group edge of a GroupMembership. +func (c *GroupMembershipClient) QueryGroup(gm *GroupMembership) *GroupQuery { + query := (&GroupClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := gm.ID + step := sqlgraph.NewStep( + sqlgraph.From(groupmembership.Table, groupmembership.FieldID, id), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, groupmembership.GroupTable, groupmembership.GroupColumn), + ) + fromV = sqlgraph.Neighbors(gm.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryUser queries the user edge of a GroupMembership. +func (c *GroupMembershipClient) QueryUser(gm *GroupMembership) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := gm.ID + step := sqlgraph.NewStep( + sqlgraph.From(groupmembership.Table, groupmembership.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, groupmembership.UserTable, groupmembership.UserColumn), + ) + fromV = sqlgraph.Neighbors(gm.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *GroupMembershipClient) Hooks() []Hook { + return c.hooks.GroupMembership +} + +// Interceptors returns the client interceptors. +func (c *GroupMembershipClient) Interceptors() []Interceptor { + return c.inters.GroupMembership +} + +func (c *GroupMembershipClient) mutate(ctx context.Context, m *GroupMembershipMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&GroupMembershipCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&GroupMembershipUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&GroupMembershipUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&GroupMembershipDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown GroupMembership mutation op: %q", m.Op()) + } +} + // IntegrationClient is a client for the Integration schema. type IntegrationClient struct { config @@ -1856,6 +2220,22 @@ func (c *OrganizationClient) QueryProjects(o *Organization) *ProjectQuery { return query } +// QueryGroups queries the groups edge of a Organization. +func (c *OrganizationClient) QueryGroups(o *Organization) *GroupQuery { + query := (&GroupClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := o.ID + step := sqlgraph.NewStep( + sqlgraph.From(organization.Table, organization.FieldID, id), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, organization.GroupsTable, organization.GroupsColumn), + ) + fromV = sqlgraph.Neighbors(o.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *OrganizationClient) Hooks() []Hook { return c.hooks.Organization @@ -2681,6 +3061,38 @@ func (c *UserClient) QueryMemberships(u *User) *MembershipQuery { return query } +// QueryGroup queries the group edge of a User. +func (c *UserClient) QueryGroup(u *User) *GroupQuery { + query := (&GroupClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := u.ID + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, id), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, user.GroupTable, user.GroupPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryGroupUsers queries the group_users edge of a User. +func (c *UserClient) QueryGroupUsers(u *User) *GroupMembershipQuery { + query := (&GroupMembershipClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := u.ID + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, id), + sqlgraph.To(groupmembership.Table, groupmembership.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, user.GroupUsersTable, user.GroupUsersColumn), + ) + fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *UserClient) Hooks() []Hook { return c.hooks.User @@ -3513,15 +3925,15 @@ func (c *WorkflowRunClient) mutate(ctx context.Context, m *WorkflowRunMutation) // hooks and interceptors per client, for fast access. type ( hooks struct { - APIToken, Attestation, CASBackend, CASMapping, Integration, - IntegrationAttachment, Membership, OrgInvitation, Organization, Project, - ProjectVersion, Referrer, RobotAccount, User, Workflow, WorkflowContract, - WorkflowContractVersion, WorkflowRun []ent.Hook + APIToken, Attestation, CASBackend, CASMapping, Group, GroupMembership, + Integration, IntegrationAttachment, Membership, OrgInvitation, Organization, + Project, ProjectVersion, Referrer, RobotAccount, User, Workflow, + WorkflowContract, WorkflowContractVersion, WorkflowRun []ent.Hook } inters struct { - APIToken, Attestation, CASBackend, CASMapping, Integration, - IntegrationAttachment, Membership, OrgInvitation, Organization, Project, - ProjectVersion, Referrer, RobotAccount, User, Workflow, WorkflowContract, - WorkflowContractVersion, WorkflowRun []ent.Interceptor + APIToken, Attestation, CASBackend, CASMapping, Group, GroupMembership, + Integration, IntegrationAttachment, Membership, OrgInvitation, Organization, + Project, ProjectVersion, Referrer, RobotAccount, User, Workflow, + WorkflowContract, WorkflowContractVersion, WorkflowRun []ent.Interceptor } ) diff --git a/app/controlplane/pkg/data/ent/ent.go b/app/controlplane/pkg/data/ent/ent.go index f6974ac57..2ce1290cf 100644 --- a/app/controlplane/pkg/data/ent/ent.go +++ b/app/controlplane/pkg/data/ent/ent.go @@ -16,6 +16,8 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/attestation" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casbackend" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casmapping" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integration" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integrationattachment" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/membership" @@ -94,6 +96,8 @@ func checkColumn(table, column string) error { attestation.Table: attestation.ValidColumn, casbackend.Table: casbackend.ValidColumn, casmapping.Table: casmapping.ValidColumn, + group.Table: group.ValidColumn, + groupmembership.Table: groupmembership.ValidColumn, integration.Table: integration.ValidColumn, integrationattachment.Table: integrationattachment.ValidColumn, membership.Table: membership.ValidColumn, diff --git a/app/controlplane/pkg/data/ent/group.go b/app/controlplane/pkg/data/ent/group.go new file mode 100644 index 000000000..3279900a5 --- /dev/null +++ b/app/controlplane/pkg/data/ent/group.go @@ -0,0 +1,223 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" + "github.com/google/uuid" +) + +// Group is the model entity for the Group schema. +type Group struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Description holds the value of the "description" field. + Description string `json:"description,omitempty"` + // OrganizationID holds the value of the "organization_id" field. + OrganizationID uuid.UUID `json:"organization_id,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // DeletedAt holds the value of the "deleted_at" field. + DeletedAt time.Time `json:"deleted_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the GroupQuery when eager-loading is set. + Edges GroupEdges `json:"edges"` + selectValues sql.SelectValues +} + +// GroupEdges holds the relations/edges for other nodes in the graph. +type GroupEdges struct { + // Members holds the value of the members edge. + Members []*User `json:"members,omitempty"` + // Organization holds the value of the organization edge. + Organization *Organization `json:"organization,omitempty"` + // GroupUsers holds the value of the group_users edge. + GroupUsers []*GroupMembership `json:"group_users,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [3]bool +} + +// MembersOrErr returns the Members value or an error if the edge +// was not loaded in eager-loading. +func (e GroupEdges) MembersOrErr() ([]*User, error) { + if e.loadedTypes[0] { + return e.Members, nil + } + return nil, &NotLoadedError{edge: "members"} +} + +// OrganizationOrErr returns the Organization value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e GroupEdges) OrganizationOrErr() (*Organization, error) { + if e.Organization != nil { + return e.Organization, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: organization.Label} + } + return nil, &NotLoadedError{edge: "organization"} +} + +// GroupUsersOrErr returns the GroupUsers value or an error if the edge +// was not loaded in eager-loading. +func (e GroupEdges) GroupUsersOrErr() ([]*GroupMembership, error) { + if e.loadedTypes[2] { + return e.GroupUsers, nil + } + return nil, &NotLoadedError{edge: "group_users"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Group) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case group.FieldName, group.FieldDescription: + values[i] = new(sql.NullString) + case group.FieldCreatedAt, group.FieldUpdatedAt, group.FieldDeletedAt: + values[i] = new(sql.NullTime) + case group.FieldID, group.FieldOrganizationID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Group fields. +func (gr *Group) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case group.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + gr.ID = *value + } + case group.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + gr.Name = value.String + } + case group.FieldDescription: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field description", values[i]) + } else if value.Valid { + gr.Description = value.String + } + case group.FieldOrganizationID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field organization_id", values[i]) + } else if value != nil { + gr.OrganizationID = *value + } + case group.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + gr.CreatedAt = value.Time + } + case group.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + gr.UpdatedAt = value.Time + } + case group.FieldDeletedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field deleted_at", values[i]) + } else if value.Valid { + gr.DeletedAt = value.Time + } + default: + gr.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Group. +// This includes values selected through modifiers, order, etc. +func (gr *Group) Value(name string) (ent.Value, error) { + return gr.selectValues.Get(name) +} + +// QueryMembers queries the "members" edge of the Group entity. +func (gr *Group) QueryMembers() *UserQuery { + return NewGroupClient(gr.config).QueryMembers(gr) +} + +// QueryOrganization queries the "organization" edge of the Group entity. +func (gr *Group) QueryOrganization() *OrganizationQuery { + return NewGroupClient(gr.config).QueryOrganization(gr) +} + +// QueryGroupUsers queries the "group_users" edge of the Group entity. +func (gr *Group) QueryGroupUsers() *GroupMembershipQuery { + return NewGroupClient(gr.config).QueryGroupUsers(gr) +} + +// Update returns a builder for updating this Group. +// Note that you need to call Group.Unwrap() before calling this method if this Group +// was returned from a transaction, and the transaction was committed or rolled back. +func (gr *Group) Update() *GroupUpdateOne { + return NewGroupClient(gr.config).UpdateOne(gr) +} + +// Unwrap unwraps the Group entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (gr *Group) Unwrap() *Group { + _tx, ok := gr.config.driver.(*txDriver) + if !ok { + panic("ent: Group is not a transactional entity") + } + gr.config.driver = _tx.drv + return gr +} + +// String implements the fmt.Stringer. +func (gr *Group) String() string { + var builder strings.Builder + builder.WriteString("Group(") + builder.WriteString(fmt.Sprintf("id=%v, ", gr.ID)) + builder.WriteString("name=") + builder.WriteString(gr.Name) + builder.WriteString(", ") + builder.WriteString("description=") + builder.WriteString(gr.Description) + builder.WriteString(", ") + builder.WriteString("organization_id=") + builder.WriteString(fmt.Sprintf("%v", gr.OrganizationID)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(gr.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(gr.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("deleted_at=") + builder.WriteString(gr.DeletedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// Groups is a parsable slice of Group. +type Groups []*Group diff --git a/app/controlplane/pkg/data/ent/group/group.go b/app/controlplane/pkg/data/ent/group/group.go new file mode 100644 index 000000000..d7500eddb --- /dev/null +++ b/app/controlplane/pkg/data/ent/group/group.go @@ -0,0 +1,189 @@ +// Code generated by ent, DO NOT EDIT. + +package group + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the group type in the database. + Label = "group" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldDescription holds the string denoting the description field in the database. + FieldDescription = "description" + // FieldOrganizationID holds the string denoting the organization_id field in the database. + FieldOrganizationID = "organization_id" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // FieldDeletedAt holds the string denoting the deleted_at field in the database. + FieldDeletedAt = "deleted_at" + // EdgeMembers holds the string denoting the members edge name in mutations. + EdgeMembers = "members" + // EdgeOrganization holds the string denoting the organization edge name in mutations. + EdgeOrganization = "organization" + // EdgeGroupUsers holds the string denoting the group_users edge name in mutations. + EdgeGroupUsers = "group_users" + // Table holds the table name of the group in the database. + Table = "groups" + // MembersTable is the table that holds the members relation/edge. The primary key declared below. + MembersTable = "group_memberships" + // MembersInverseTable is the table name for the User entity. + // It exists in this package in order to avoid circular dependency with the "user" package. + MembersInverseTable = "users" + // OrganizationTable is the table that holds the organization relation/edge. + OrganizationTable = "groups" + // OrganizationInverseTable is the table name for the Organization entity. + // It exists in this package in order to avoid circular dependency with the "organization" package. + OrganizationInverseTable = "organizations" + // OrganizationColumn is the table column denoting the organization relation/edge. + OrganizationColumn = "organization_id" + // GroupUsersTable is the table that holds the group_users relation/edge. + GroupUsersTable = "group_memberships" + // GroupUsersInverseTable is the table name for the GroupMembership entity. + // It exists in this package in order to avoid circular dependency with the "groupmembership" package. + GroupUsersInverseTable = "group_memberships" + // GroupUsersColumn is the table column denoting the group_users relation/edge. + GroupUsersColumn = "group_id" +) + +// Columns holds all SQL columns for group fields. +var Columns = []string{ + FieldID, + FieldName, + FieldDescription, + FieldOrganizationID, + FieldCreatedAt, + FieldUpdatedAt, + FieldDeletedAt, +} + +var ( + // MembersPrimaryKey and MembersColumn2 are the table columns denoting the + // primary key for the members relation (M2M). + MembersPrimaryKey = []string{"group_id", "user_id"} +) + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the Group queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByDescription orders the results by the description field. +func ByDescription(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDescription, opts...).ToFunc() +} + +// ByOrganizationID orders the results by the organization_id field. +func ByOrganizationID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldOrganizationID, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByDeletedAt orders the results by the deleted_at field. +func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDeletedAt, opts...).ToFunc() +} + +// ByMembersCount orders the results by members count. +func ByMembersCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newMembersStep(), opts...) + } +} + +// ByMembers orders the results by members terms. +func ByMembers(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newMembersStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByOrganizationField orders the results by organization field. +func ByOrganizationField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newOrganizationStep(), sql.OrderByField(field, opts...)) + } +} + +// ByGroupUsersCount orders the results by group_users count. +func ByGroupUsersCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newGroupUsersStep(), opts...) + } +} + +// ByGroupUsers orders the results by group_users terms. +func ByGroupUsers(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newGroupUsersStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newMembersStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(MembersInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, MembersTable, MembersPrimaryKey...), + ) +} +func newOrganizationStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(OrganizationInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, OrganizationTable, OrganizationColumn), + ) +} +func newGroupUsersStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(GroupUsersInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, GroupUsersTable, GroupUsersColumn), + ) +} diff --git a/app/controlplane/pkg/data/ent/group/where.go b/app/controlplane/pkg/data/ent/group/where.go new file mode 100644 index 000000000..076926ba7 --- /dev/null +++ b/app/controlplane/pkg/data/ent/group/where.go @@ -0,0 +1,461 @@ +// Code generated by ent, DO NOT EDIT. + +package group + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldLTE(FieldID, id)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldName, v)) +} + +// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. +func Description(v string) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldDescription, v)) +} + +// OrganizationID applies equality check predicate on the "organization_id" field. It's identical to OrganizationIDEQ. +func OrganizationID(v uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldOrganizationID, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ. +func DeletedAt(v time.Time) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldDeletedAt, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.Group { + return predicate.Group(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.Group { + return predicate.Group(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.Group { + return predicate.Group(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.Group { + return predicate.Group(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.Group { + return predicate.Group(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.Group { + return predicate.Group(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.Group { + return predicate.Group(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.Group { + return predicate.Group(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.Group { + return predicate.Group(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.Group { + return predicate.Group(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.Group { + return predicate.Group(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.Group { + return predicate.Group(sql.FieldContainsFold(FieldName, v)) +} + +// DescriptionEQ applies the EQ predicate on the "description" field. +func DescriptionEQ(v string) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldDescription, v)) +} + +// DescriptionNEQ applies the NEQ predicate on the "description" field. +func DescriptionNEQ(v string) predicate.Group { + return predicate.Group(sql.FieldNEQ(FieldDescription, v)) +} + +// DescriptionIn applies the In predicate on the "description" field. +func DescriptionIn(vs ...string) predicate.Group { + return predicate.Group(sql.FieldIn(FieldDescription, vs...)) +} + +// DescriptionNotIn applies the NotIn predicate on the "description" field. +func DescriptionNotIn(vs ...string) predicate.Group { + return predicate.Group(sql.FieldNotIn(FieldDescription, vs...)) +} + +// DescriptionGT applies the GT predicate on the "description" field. +func DescriptionGT(v string) predicate.Group { + return predicate.Group(sql.FieldGT(FieldDescription, v)) +} + +// DescriptionGTE applies the GTE predicate on the "description" field. +func DescriptionGTE(v string) predicate.Group { + return predicate.Group(sql.FieldGTE(FieldDescription, v)) +} + +// DescriptionLT applies the LT predicate on the "description" field. +func DescriptionLT(v string) predicate.Group { + return predicate.Group(sql.FieldLT(FieldDescription, v)) +} + +// DescriptionLTE applies the LTE predicate on the "description" field. +func DescriptionLTE(v string) predicate.Group { + return predicate.Group(sql.FieldLTE(FieldDescription, v)) +} + +// DescriptionContains applies the Contains predicate on the "description" field. +func DescriptionContains(v string) predicate.Group { + return predicate.Group(sql.FieldContains(FieldDescription, v)) +} + +// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field. +func DescriptionHasPrefix(v string) predicate.Group { + return predicate.Group(sql.FieldHasPrefix(FieldDescription, v)) +} + +// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field. +func DescriptionHasSuffix(v string) predicate.Group { + return predicate.Group(sql.FieldHasSuffix(FieldDescription, v)) +} + +// DescriptionIsNil applies the IsNil predicate on the "description" field. +func DescriptionIsNil() predicate.Group { + return predicate.Group(sql.FieldIsNull(FieldDescription)) +} + +// DescriptionNotNil applies the NotNil predicate on the "description" field. +func DescriptionNotNil() predicate.Group { + return predicate.Group(sql.FieldNotNull(FieldDescription)) +} + +// DescriptionEqualFold applies the EqualFold predicate on the "description" field. +func DescriptionEqualFold(v string) predicate.Group { + return predicate.Group(sql.FieldEqualFold(FieldDescription, v)) +} + +// DescriptionContainsFold applies the ContainsFold predicate on the "description" field. +func DescriptionContainsFold(v string) predicate.Group { + return predicate.Group(sql.FieldContainsFold(FieldDescription, v)) +} + +// OrganizationIDEQ applies the EQ predicate on the "organization_id" field. +func OrganizationIDEQ(v uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldOrganizationID, v)) +} + +// OrganizationIDNEQ applies the NEQ predicate on the "organization_id" field. +func OrganizationIDNEQ(v uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldNEQ(FieldOrganizationID, v)) +} + +// OrganizationIDIn applies the In predicate on the "organization_id" field. +func OrganizationIDIn(vs ...uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldIn(FieldOrganizationID, vs...)) +} + +// OrganizationIDNotIn applies the NotIn predicate on the "organization_id" field. +func OrganizationIDNotIn(vs ...uuid.UUID) predicate.Group { + return predicate.Group(sql.FieldNotIn(FieldOrganizationID, vs...)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.Group { + return predicate.Group(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.Group { + return predicate.Group(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.Group { + return predicate.Group(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.Group { + return predicate.Group(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.Group { + return predicate.Group(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.Group { + return predicate.Group(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.Group { + return predicate.Group(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.Group { + return predicate.Group(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.Group { + return predicate.Group(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.Group { + return predicate.Group(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.Group { + return predicate.Group(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.Group { + return predicate.Group(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.Group { + return predicate.Group(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.Group { + return predicate.Group(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// DeletedAtEQ applies the EQ predicate on the "deleted_at" field. +func DeletedAtEQ(v time.Time) predicate.Group { + return predicate.Group(sql.FieldEQ(FieldDeletedAt, v)) +} + +// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field. +func DeletedAtNEQ(v time.Time) predicate.Group { + return predicate.Group(sql.FieldNEQ(FieldDeletedAt, v)) +} + +// DeletedAtIn applies the In predicate on the "deleted_at" field. +func DeletedAtIn(vs ...time.Time) predicate.Group { + return predicate.Group(sql.FieldIn(FieldDeletedAt, vs...)) +} + +// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field. +func DeletedAtNotIn(vs ...time.Time) predicate.Group { + return predicate.Group(sql.FieldNotIn(FieldDeletedAt, vs...)) +} + +// DeletedAtGT applies the GT predicate on the "deleted_at" field. +func DeletedAtGT(v time.Time) predicate.Group { + return predicate.Group(sql.FieldGT(FieldDeletedAt, v)) +} + +// DeletedAtGTE applies the GTE predicate on the "deleted_at" field. +func DeletedAtGTE(v time.Time) predicate.Group { + return predicate.Group(sql.FieldGTE(FieldDeletedAt, v)) +} + +// DeletedAtLT applies the LT predicate on the "deleted_at" field. +func DeletedAtLT(v time.Time) predicate.Group { + return predicate.Group(sql.FieldLT(FieldDeletedAt, v)) +} + +// DeletedAtLTE applies the LTE predicate on the "deleted_at" field. +func DeletedAtLTE(v time.Time) predicate.Group { + return predicate.Group(sql.FieldLTE(FieldDeletedAt, v)) +} + +// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field. +func DeletedAtIsNil() predicate.Group { + return predicate.Group(sql.FieldIsNull(FieldDeletedAt)) +} + +// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field. +func DeletedAtNotNil() predicate.Group { + return predicate.Group(sql.FieldNotNull(FieldDeletedAt)) +} + +// HasMembers applies the HasEdge predicate on the "members" edge. +func HasMembers() predicate.Group { + return predicate.Group(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, MembersTable, MembersPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasMembersWith applies the HasEdge predicate on the "members" edge with a given conditions (other predicates). +func HasMembersWith(preds ...predicate.User) predicate.Group { + return predicate.Group(func(s *sql.Selector) { + step := newMembersStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasOrganization applies the HasEdge predicate on the "organization" edge. +func HasOrganization() predicate.Group { + return predicate.Group(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, OrganizationTable, OrganizationColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasOrganizationWith applies the HasEdge predicate on the "organization" edge with a given conditions (other predicates). +func HasOrganizationWith(preds ...predicate.Organization) predicate.Group { + return predicate.Group(func(s *sql.Selector) { + step := newOrganizationStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasGroupUsers applies the HasEdge predicate on the "group_users" edge. +func HasGroupUsers() predicate.Group { + return predicate.Group(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, GroupUsersTable, GroupUsersColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasGroupUsersWith applies the HasEdge predicate on the "group_users" edge with a given conditions (other predicates). +func HasGroupUsersWith(preds ...predicate.GroupMembership) predicate.Group { + return predicate.Group(func(s *sql.Selector) { + step := newGroupUsersStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Group) predicate.Group { + return predicate.Group(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Group) predicate.Group { + return predicate.Group(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Group) predicate.Group { + return predicate.Group(sql.NotPredicates(p)) +} diff --git a/app/controlplane/pkg/data/ent/group_create.go b/app/controlplane/pkg/data/ent/group_create.go new file mode 100644 index 000000000..9db96d507 --- /dev/null +++ b/app/controlplane/pkg/data/ent/group_create.go @@ -0,0 +1,912 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" + "github.com/google/uuid" +) + +// GroupCreate is the builder for creating a Group entity. +type GroupCreate struct { + config + mutation *GroupMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetName sets the "name" field. +func (gc *GroupCreate) SetName(s string) *GroupCreate { + gc.mutation.SetName(s) + return gc +} + +// SetDescription sets the "description" field. +func (gc *GroupCreate) SetDescription(s string) *GroupCreate { + gc.mutation.SetDescription(s) + return gc +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (gc *GroupCreate) SetNillableDescription(s *string) *GroupCreate { + if s != nil { + gc.SetDescription(*s) + } + return gc +} + +// SetOrganizationID sets the "organization_id" field. +func (gc *GroupCreate) SetOrganizationID(u uuid.UUID) *GroupCreate { + gc.mutation.SetOrganizationID(u) + return gc +} + +// SetCreatedAt sets the "created_at" field. +func (gc *GroupCreate) SetCreatedAt(t time.Time) *GroupCreate { + gc.mutation.SetCreatedAt(t) + return gc +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (gc *GroupCreate) SetNillableCreatedAt(t *time.Time) *GroupCreate { + if t != nil { + gc.SetCreatedAt(*t) + } + return gc +} + +// SetUpdatedAt sets the "updated_at" field. +func (gc *GroupCreate) SetUpdatedAt(t time.Time) *GroupCreate { + gc.mutation.SetUpdatedAt(t) + return gc +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (gc *GroupCreate) SetNillableUpdatedAt(t *time.Time) *GroupCreate { + if t != nil { + gc.SetUpdatedAt(*t) + } + return gc +} + +// SetDeletedAt sets the "deleted_at" field. +func (gc *GroupCreate) SetDeletedAt(t time.Time) *GroupCreate { + gc.mutation.SetDeletedAt(t) + return gc +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (gc *GroupCreate) SetNillableDeletedAt(t *time.Time) *GroupCreate { + if t != nil { + gc.SetDeletedAt(*t) + } + return gc +} + +// SetID sets the "id" field. +func (gc *GroupCreate) SetID(u uuid.UUID) *GroupCreate { + gc.mutation.SetID(u) + return gc +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (gc *GroupCreate) SetNillableID(u *uuid.UUID) *GroupCreate { + if u != nil { + gc.SetID(*u) + } + return gc +} + +// AddMemberIDs adds the "members" edge to the User entity by IDs. +func (gc *GroupCreate) AddMemberIDs(ids ...uuid.UUID) *GroupCreate { + gc.mutation.AddMemberIDs(ids...) + return gc +} + +// AddMembers adds the "members" edges to the User entity. +func (gc *GroupCreate) AddMembers(u ...*User) *GroupCreate { + ids := make([]uuid.UUID, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return gc.AddMemberIDs(ids...) +} + +// SetOrganization sets the "organization" edge to the Organization entity. +func (gc *GroupCreate) SetOrganization(o *Organization) *GroupCreate { + return gc.SetOrganizationID(o.ID) +} + +// AddGroupUserIDs adds the "group_users" edge to the GroupMembership entity by IDs. +func (gc *GroupCreate) AddGroupUserIDs(ids ...uuid.UUID) *GroupCreate { + gc.mutation.AddGroupUserIDs(ids...) + return gc +} + +// AddGroupUsers adds the "group_users" edges to the GroupMembership entity. +func (gc *GroupCreate) AddGroupUsers(g ...*GroupMembership) *GroupCreate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return gc.AddGroupUserIDs(ids...) +} + +// Mutation returns the GroupMutation object of the builder. +func (gc *GroupCreate) Mutation() *GroupMutation { + return gc.mutation +} + +// Save creates the Group in the database. +func (gc *GroupCreate) Save(ctx context.Context) (*Group, error) { + gc.defaults() + return withHooks(ctx, gc.sqlSave, gc.mutation, gc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (gc *GroupCreate) SaveX(ctx context.Context) *Group { + v, err := gc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (gc *GroupCreate) Exec(ctx context.Context) error { + _, err := gc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (gc *GroupCreate) ExecX(ctx context.Context) { + if err := gc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (gc *GroupCreate) defaults() { + if _, ok := gc.mutation.CreatedAt(); !ok { + v := group.DefaultCreatedAt() + gc.mutation.SetCreatedAt(v) + } + if _, ok := gc.mutation.UpdatedAt(); !ok { + v := group.DefaultUpdatedAt() + gc.mutation.SetUpdatedAt(v) + } + if _, ok := gc.mutation.ID(); !ok { + v := group.DefaultID() + gc.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (gc *GroupCreate) check() error { + if _, ok := gc.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Group.name"`)} + } + if v, ok := gc.mutation.Name(); ok { + if err := group.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Group.name": %w`, err)} + } + } + if _, ok := gc.mutation.OrganizationID(); !ok { + return &ValidationError{Name: "organization_id", err: errors.New(`ent: missing required field "Group.organization_id"`)} + } + if _, ok := gc.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "Group.created_at"`)} + } + if _, ok := gc.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "Group.updated_at"`)} + } + if len(gc.mutation.OrganizationIDs()) == 0 { + return &ValidationError{Name: "organization", err: errors.New(`ent: missing required edge "Group.organization"`)} + } + return nil +} + +func (gc *GroupCreate) sqlSave(ctx context.Context) (*Group, error) { + if err := gc.check(); err != nil { + return nil, err + } + _node, _spec := gc.createSpec() + if err := sqlgraph.CreateNode(ctx, gc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + gc.mutation.id = &_node.ID + gc.mutation.done = true + return _node, nil +} + +func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) { + var ( + _node = &Group{config: gc.config} + _spec = sqlgraph.NewCreateSpec(group.Table, sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = gc.conflict + if id, ok := gc.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := gc.mutation.Name(); ok { + _spec.SetField(group.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := gc.mutation.Description(); ok { + _spec.SetField(group.FieldDescription, field.TypeString, value) + _node.Description = value + } + if value, ok := gc.mutation.CreatedAt(); ok { + _spec.SetField(group.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := gc.mutation.UpdatedAt(); ok { + _spec.SetField(group.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := gc.mutation.DeletedAt(); ok { + _spec.SetField(group.FieldDeletedAt, field.TypeTime, value) + _node.DeletedAt = value + } + if nodes := gc.mutation.MembersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: group.MembersTable, + Columns: group.MembersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &GroupMembershipCreate{config: gc.config, mutation: newGroupMembershipMutation(gc.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := gc.mutation.OrganizationIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: group.OrganizationTable, + Columns: []string{group.OrganizationColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.OrganizationID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := gc.mutation.GroupUsersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.GroupUsersTable, + Columns: []string{group.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Group.Create(). +// SetName(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GroupUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (gc *GroupCreate) OnConflict(opts ...sql.ConflictOption) *GroupUpsertOne { + gc.conflict = opts + return &GroupUpsertOne{ + create: gc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (gc *GroupCreate) OnConflictColumns(columns ...string) *GroupUpsertOne { + gc.conflict = append(gc.conflict, sql.ConflictColumns(columns...)) + return &GroupUpsertOne{ + create: gc, + } +} + +type ( + // GroupUpsertOne is the builder for "upsert"-ing + // one Group node. + GroupUpsertOne struct { + create *GroupCreate + } + + // GroupUpsert is the "OnConflict" setter. + GroupUpsert struct { + *sql.UpdateSet + } +) + +// SetName sets the "name" field. +func (u *GroupUpsert) SetName(v string) *GroupUpsert { + u.Set(group.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *GroupUpsert) UpdateName() *GroupUpsert { + u.SetExcluded(group.FieldName) + return u +} + +// SetDescription sets the "description" field. +func (u *GroupUpsert) SetDescription(v string) *GroupUpsert { + u.Set(group.FieldDescription, v) + return u +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *GroupUpsert) UpdateDescription() *GroupUpsert { + u.SetExcluded(group.FieldDescription) + return u +} + +// ClearDescription clears the value of the "description" field. +func (u *GroupUpsert) ClearDescription() *GroupUpsert { + u.SetNull(group.FieldDescription) + return u +} + +// SetOrganizationID sets the "organization_id" field. +func (u *GroupUpsert) SetOrganizationID(v uuid.UUID) *GroupUpsert { + u.Set(group.FieldOrganizationID, v) + return u +} + +// UpdateOrganizationID sets the "organization_id" field to the value that was provided on create. +func (u *GroupUpsert) UpdateOrganizationID() *GroupUpsert { + u.SetExcluded(group.FieldOrganizationID) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *GroupUpsert) SetUpdatedAt(v time.Time) *GroupUpsert { + u.Set(group.FieldUpdatedAt, v) + return u +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *GroupUpsert) UpdateUpdatedAt() *GroupUpsert { + u.SetExcluded(group.FieldUpdatedAt) + return u +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *GroupUpsert) SetDeletedAt(v time.Time) *GroupUpsert { + u.Set(group.FieldDeletedAt, v) + return u +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *GroupUpsert) UpdateDeletedAt() *GroupUpsert { + u.SetExcluded(group.FieldDeletedAt) + return u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *GroupUpsert) ClearDeletedAt() *GroupUpsert { + u.SetNull(group.FieldDeletedAt) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(group.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GroupUpsertOne) UpdateNewValues() *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(group.FieldID) + } + if _, exists := u.create.mutation.CreatedAt(); exists { + s.SetIgnore(group.FieldCreatedAt) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GroupUpsertOne) Ignore() *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GroupUpsertOne) DoNothing() *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GroupCreate.OnConflict +// documentation for more info. +func (u *GroupUpsertOne) Update(set func(*GroupUpsert)) *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GroupUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *GroupUpsertOne) SetName(v string) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateName() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateName() + }) +} + +// SetDescription sets the "description" field. +func (u *GroupUpsertOne) SetDescription(v string) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateDescription() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *GroupUpsertOne) ClearDescription() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.ClearDescription() + }) +} + +// SetOrganizationID sets the "organization_id" field. +func (u *GroupUpsertOne) SetOrganizationID(v uuid.UUID) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetOrganizationID(v) + }) +} + +// UpdateOrganizationID sets the "organization_id" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateOrganizationID() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateOrganizationID() + }) +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *GroupUpsertOne) SetUpdatedAt(v time.Time) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateUpdatedAt() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *GroupUpsertOne) SetDeletedAt(v time.Time) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateDeletedAt() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *GroupUpsertOne) ClearDeletedAt() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.ClearDeletedAt() + }) +} + +// Exec executes the query. +func (u *GroupUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GroupCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GroupUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *GroupUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: GroupUpsertOne.ID is not supported by MySQL driver. Use GroupUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *GroupUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// GroupCreateBulk is the builder for creating many Group entities in bulk. +type GroupCreateBulk struct { + config + err error + builders []*GroupCreate + conflict []sql.ConflictOption +} + +// Save creates the Group entities in the database. +func (gcb *GroupCreateBulk) Save(ctx context.Context) ([]*Group, error) { + if gcb.err != nil { + return nil, gcb.err + } + specs := make([]*sqlgraph.CreateSpec, len(gcb.builders)) + nodes := make([]*Group, len(gcb.builders)) + mutators := make([]Mutator, len(gcb.builders)) + for i := range gcb.builders { + func(i int, root context.Context) { + builder := gcb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*GroupMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, gcb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = gcb.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, gcb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, gcb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (gcb *GroupCreateBulk) SaveX(ctx context.Context) []*Group { + v, err := gcb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (gcb *GroupCreateBulk) Exec(ctx context.Context) error { + _, err := gcb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (gcb *GroupCreateBulk) ExecX(ctx context.Context) { + if err := gcb.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Group.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GroupUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (gcb *GroupCreateBulk) OnConflict(opts ...sql.ConflictOption) *GroupUpsertBulk { + gcb.conflict = opts + return &GroupUpsertBulk{ + create: gcb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (gcb *GroupCreateBulk) OnConflictColumns(columns ...string) *GroupUpsertBulk { + gcb.conflict = append(gcb.conflict, sql.ConflictColumns(columns...)) + return &GroupUpsertBulk{ + create: gcb, + } +} + +// GroupUpsertBulk is the builder for "upsert"-ing +// a bulk of Group nodes. +type GroupUpsertBulk struct { + create *GroupCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(group.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GroupUpsertBulk) UpdateNewValues() *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(group.FieldID) + } + if _, exists := b.mutation.CreatedAt(); exists { + s.SetIgnore(group.FieldCreatedAt) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GroupUpsertBulk) Ignore() *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GroupUpsertBulk) DoNothing() *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GroupCreateBulk.OnConflict +// documentation for more info. +func (u *GroupUpsertBulk) Update(set func(*GroupUpsert)) *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GroupUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *GroupUpsertBulk) SetName(v string) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateName() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateName() + }) +} + +// SetDescription sets the "description" field. +func (u *GroupUpsertBulk) SetDescription(v string) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateDescription() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *GroupUpsertBulk) ClearDescription() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.ClearDescription() + }) +} + +// SetOrganizationID sets the "organization_id" field. +func (u *GroupUpsertBulk) SetOrganizationID(v uuid.UUID) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetOrganizationID(v) + }) +} + +// UpdateOrganizationID sets the "organization_id" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateOrganizationID() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateOrganizationID() + }) +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *GroupUpsertBulk) SetUpdatedAt(v time.Time) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateUpdatedAt() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *GroupUpsertBulk) SetDeletedAt(v time.Time) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateDeletedAt() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *GroupUpsertBulk) ClearDeletedAt() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.ClearDeletedAt() + }) +} + +// Exec executes the query. +func (u *GroupUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the GroupCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GroupCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GroupUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/app/controlplane/pkg/data/ent/group_delete.go b/app/controlplane/pkg/data/ent/group_delete.go new file mode 100644 index 000000000..67f4594b4 --- /dev/null +++ b/app/controlplane/pkg/data/ent/group_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" +) + +// GroupDelete is the builder for deleting a Group entity. +type GroupDelete struct { + config + hooks []Hook + mutation *GroupMutation +} + +// Where appends a list predicates to the GroupDelete builder. +func (gd *GroupDelete) Where(ps ...predicate.Group) *GroupDelete { + gd.mutation.Where(ps...) + return gd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (gd *GroupDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, gd.sqlExec, gd.mutation, gd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (gd *GroupDelete) ExecX(ctx context.Context) int { + n, err := gd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (gd *GroupDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(group.Table, sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID)) + if ps := gd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, gd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + gd.mutation.done = true + return affected, err +} + +// GroupDeleteOne is the builder for deleting a single Group entity. +type GroupDeleteOne struct { + gd *GroupDelete +} + +// Where appends a list predicates to the GroupDelete builder. +func (gdo *GroupDeleteOne) Where(ps ...predicate.Group) *GroupDeleteOne { + gdo.gd.mutation.Where(ps...) + return gdo +} + +// Exec executes the deletion query. +func (gdo *GroupDeleteOne) Exec(ctx context.Context) error { + n, err := gdo.gd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{group.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (gdo *GroupDeleteOne) ExecX(ctx context.Context) { + if err := gdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/app/controlplane/pkg/data/ent/group_query.go b/app/controlplane/pkg/data/ent/group_query.go new file mode 100644 index 000000000..84c0a7956 --- /dev/null +++ b/app/controlplane/pkg/data/ent/group_query.go @@ -0,0 +1,837 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" + "github.com/google/uuid" +) + +// GroupQuery is the builder for querying Group entities. +type GroupQuery struct { + config + ctx *QueryContext + order []group.OrderOption + inters []Interceptor + predicates []predicate.Group + withMembers *UserQuery + withOrganization *OrganizationQuery + withGroupUsers *GroupMembershipQuery + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the GroupQuery builder. +func (gq *GroupQuery) Where(ps ...predicate.Group) *GroupQuery { + gq.predicates = append(gq.predicates, ps...) + return gq +} + +// Limit the number of records to be returned by this query. +func (gq *GroupQuery) Limit(limit int) *GroupQuery { + gq.ctx.Limit = &limit + return gq +} + +// Offset to start from. +func (gq *GroupQuery) Offset(offset int) *GroupQuery { + gq.ctx.Offset = &offset + return gq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (gq *GroupQuery) Unique(unique bool) *GroupQuery { + gq.ctx.Unique = &unique + return gq +} + +// Order specifies how the records should be ordered. +func (gq *GroupQuery) Order(o ...group.OrderOption) *GroupQuery { + gq.order = append(gq.order, o...) + return gq +} + +// QueryMembers chains the current query on the "members" edge. +func (gq *GroupQuery) QueryMembers() *UserQuery { + query := (&UserClient{config: gq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := gq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := gq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, selector), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, group.MembersTable, group.MembersPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(gq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryOrganization chains the current query on the "organization" edge. +func (gq *GroupQuery) QueryOrganization() *OrganizationQuery { + query := (&OrganizationClient{config: gq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := gq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := gq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, selector), + sqlgraph.To(organization.Table, organization.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, group.OrganizationTable, group.OrganizationColumn), + ) + fromU = sqlgraph.SetNeighbors(gq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryGroupUsers chains the current query on the "group_users" edge. +func (gq *GroupQuery) QueryGroupUsers() *GroupMembershipQuery { + query := (&GroupMembershipClient{config: gq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := gq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := gq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, selector), + sqlgraph.To(groupmembership.Table, groupmembership.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, group.GroupUsersTable, group.GroupUsersColumn), + ) + fromU = sqlgraph.SetNeighbors(gq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Group entity from the query. +// Returns a *NotFoundError when no Group was found. +func (gq *GroupQuery) First(ctx context.Context) (*Group, error) { + nodes, err := gq.Limit(1).All(setContextOp(ctx, gq.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{group.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (gq *GroupQuery) FirstX(ctx context.Context) *Group { + node, err := gq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Group ID from the query. +// Returns a *NotFoundError when no Group ID was found. +func (gq *GroupQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = gq.Limit(1).IDs(setContextOp(ctx, gq.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{group.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (gq *GroupQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := gq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Group entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Group entity is found. +// Returns a *NotFoundError when no Group entities are found. +func (gq *GroupQuery) Only(ctx context.Context) (*Group, error) { + nodes, err := gq.Limit(2).All(setContextOp(ctx, gq.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{group.Label} + default: + return nil, &NotSingularError{group.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (gq *GroupQuery) OnlyX(ctx context.Context) *Group { + node, err := gq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Group ID in the query. +// Returns a *NotSingularError when more than one Group ID is found. +// Returns a *NotFoundError when no entities are found. +func (gq *GroupQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = gq.Limit(2).IDs(setContextOp(ctx, gq.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{group.Label} + default: + err = &NotSingularError{group.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (gq *GroupQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := gq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Groups. +func (gq *GroupQuery) All(ctx context.Context) ([]*Group, error) { + ctx = setContextOp(ctx, gq.ctx, ent.OpQueryAll) + if err := gq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Group, *GroupQuery]() + return withInterceptors[[]*Group](ctx, gq, qr, gq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (gq *GroupQuery) AllX(ctx context.Context) []*Group { + nodes, err := gq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Group IDs. +func (gq *GroupQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if gq.ctx.Unique == nil && gq.path != nil { + gq.Unique(true) + } + ctx = setContextOp(ctx, gq.ctx, ent.OpQueryIDs) + if err = gq.Select(group.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (gq *GroupQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := gq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (gq *GroupQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, gq.ctx, ent.OpQueryCount) + if err := gq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, gq, querierCount[*GroupQuery](), gq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (gq *GroupQuery) CountX(ctx context.Context) int { + count, err := gq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (gq *GroupQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, gq.ctx, ent.OpQueryExist) + switch _, err := gq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (gq *GroupQuery) ExistX(ctx context.Context) bool { + exist, err := gq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the GroupQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (gq *GroupQuery) Clone() *GroupQuery { + if gq == nil { + return nil + } + return &GroupQuery{ + config: gq.config, + ctx: gq.ctx.Clone(), + order: append([]group.OrderOption{}, gq.order...), + inters: append([]Interceptor{}, gq.inters...), + predicates: append([]predicate.Group{}, gq.predicates...), + withMembers: gq.withMembers.Clone(), + withOrganization: gq.withOrganization.Clone(), + withGroupUsers: gq.withGroupUsers.Clone(), + // clone intermediate query. + sql: gq.sql.Clone(), + path: gq.path, + modifiers: append([]func(*sql.Selector){}, gq.modifiers...), + } +} + +// WithMembers tells the query-builder to eager-load the nodes that are connected to +// the "members" edge. The optional arguments are used to configure the query builder of the edge. +func (gq *GroupQuery) WithMembers(opts ...func(*UserQuery)) *GroupQuery { + query := (&UserClient{config: gq.config}).Query() + for _, opt := range opts { + opt(query) + } + gq.withMembers = query + return gq +} + +// WithOrganization tells the query-builder to eager-load the nodes that are connected to +// the "organization" edge. The optional arguments are used to configure the query builder of the edge. +func (gq *GroupQuery) WithOrganization(opts ...func(*OrganizationQuery)) *GroupQuery { + query := (&OrganizationClient{config: gq.config}).Query() + for _, opt := range opts { + opt(query) + } + gq.withOrganization = query + return gq +} + +// WithGroupUsers tells the query-builder to eager-load the nodes that are connected to +// the "group_users" edge. The optional arguments are used to configure the query builder of the edge. +func (gq *GroupQuery) WithGroupUsers(opts ...func(*GroupMembershipQuery)) *GroupQuery { + query := (&GroupMembershipClient{config: gq.config}).Query() + for _, opt := range opts { + opt(query) + } + gq.withGroupUsers = query + return gq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Group.Query(). +// GroupBy(group.FieldName). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (gq *GroupQuery) GroupBy(field string, fields ...string) *GroupGroupBy { + gq.ctx.Fields = append([]string{field}, fields...) + grbuild := &GroupGroupBy{build: gq} + grbuild.flds = &gq.ctx.Fields + grbuild.label = group.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// } +// +// client.Group.Query(). +// Select(group.FieldName). +// Scan(ctx, &v) +func (gq *GroupQuery) Select(fields ...string) *GroupSelect { + gq.ctx.Fields = append(gq.ctx.Fields, fields...) + sbuild := &GroupSelect{GroupQuery: gq} + sbuild.label = group.Label + sbuild.flds, sbuild.scan = &gq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a GroupSelect configured with the given aggregations. +func (gq *GroupQuery) Aggregate(fns ...AggregateFunc) *GroupSelect { + return gq.Select().Aggregate(fns...) +} + +func (gq *GroupQuery) prepareQuery(ctx context.Context) error { + for _, inter := range gq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, gq); err != nil { + return err + } + } + } + for _, f := range gq.ctx.Fields { + if !group.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if gq.path != nil { + prev, err := gq.path(ctx) + if err != nil { + return err + } + gq.sql = prev + } + return nil +} + +func (gq *GroupQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Group, error) { + var ( + nodes = []*Group{} + _spec = gq.querySpec() + loadedTypes = [3]bool{ + gq.withMembers != nil, + gq.withOrganization != nil, + gq.withGroupUsers != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Group).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Group{config: gq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + if len(gq.modifiers) > 0 { + _spec.Modifiers = gq.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, gq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := gq.withMembers; query != nil { + if err := gq.loadMembers(ctx, query, nodes, + func(n *Group) { n.Edges.Members = []*User{} }, + func(n *Group, e *User) { n.Edges.Members = append(n.Edges.Members, e) }); err != nil { + return nil, err + } + } + if query := gq.withOrganization; query != nil { + if err := gq.loadOrganization(ctx, query, nodes, nil, + func(n *Group, e *Organization) { n.Edges.Organization = e }); err != nil { + return nil, err + } + } + if query := gq.withGroupUsers; query != nil { + if err := gq.loadGroupUsers(ctx, query, nodes, + func(n *Group) { n.Edges.GroupUsers = []*GroupMembership{} }, + func(n *Group, e *GroupMembership) { n.Edges.GroupUsers = append(n.Edges.GroupUsers, e) }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (gq *GroupQuery) loadMembers(ctx context.Context, query *UserQuery, nodes []*Group, init func(*Group), assign func(*Group, *User)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[uuid.UUID]*Group) + nids := make(map[uuid.UUID]map[*Group]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(group.MembersTable) + s.Join(joinT).On(s.C(user.FieldID), joinT.C(group.MembersPrimaryKey[1])) + s.Where(sql.InValues(joinT.C(group.MembersPrimaryKey[0]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(group.MembersPrimaryKey[0])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(uuid.UUID)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := *values[0].(*uuid.UUID) + inValue := *values[1].(*uuid.UUID) + if nids[inValue] == nil { + nids[inValue] = map[*Group]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*User](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "members" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} +func (gq *GroupQuery) loadOrganization(ctx context.Context, query *OrganizationQuery, nodes []*Group, init func(*Group), assign func(*Group, *Organization)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*Group) + for i := range nodes { + fk := nodes[i].OrganizationID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(organization.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "organization_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (gq *GroupQuery) loadGroupUsers(ctx context.Context, query *GroupMembershipQuery, nodes []*Group, init func(*Group), assign func(*Group, *GroupMembership)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*Group) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(groupmembership.FieldGroupID) + } + query.Where(predicate.GroupMembership(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(group.GroupUsersColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.GroupID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "group_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} + +func (gq *GroupQuery) sqlCount(ctx context.Context) (int, error) { + _spec := gq.querySpec() + if len(gq.modifiers) > 0 { + _spec.Modifiers = gq.modifiers + } + _spec.Node.Columns = gq.ctx.Fields + if len(gq.ctx.Fields) > 0 { + _spec.Unique = gq.ctx.Unique != nil && *gq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, gq.driver, _spec) +} + +func (gq *GroupQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(group.Table, group.Columns, sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID)) + _spec.From = gq.sql + if unique := gq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if gq.path != nil { + _spec.Unique = true + } + if fields := gq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, group.FieldID) + for i := range fields { + if fields[i] != group.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if gq.withOrganization != nil { + _spec.Node.AddColumnOnce(group.FieldOrganizationID) + } + } + if ps := gq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := gq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := gq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := gq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (gq *GroupQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(gq.driver.Dialect()) + t1 := builder.Table(group.Table) + columns := gq.ctx.Fields + if len(columns) == 0 { + columns = group.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if gq.sql != nil { + selector = gq.sql + selector.Select(selector.Columns(columns...)...) + } + if gq.ctx.Unique != nil && *gq.ctx.Unique { + selector.Distinct() + } + for _, m := range gq.modifiers { + m(selector) + } + for _, p := range gq.predicates { + p(selector) + } + for _, p := range gq.order { + p(selector) + } + if offset := gq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := gq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (gq *GroupQuery) ForUpdate(opts ...sql.LockOption) *GroupQuery { + if gq.driver.Dialect() == dialect.Postgres { + gq.Unique(false) + } + gq.modifiers = append(gq.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return gq +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (gq *GroupQuery) ForShare(opts ...sql.LockOption) *GroupQuery { + if gq.driver.Dialect() == dialect.Postgres { + gq.Unique(false) + } + gq.modifiers = append(gq.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return gq +} + +// Modify adds a query modifier for attaching custom logic to queries. +func (gq *GroupQuery) Modify(modifiers ...func(s *sql.Selector)) *GroupSelect { + gq.modifiers = append(gq.modifiers, modifiers...) + return gq.Select() +} + +// GroupGroupBy is the group-by builder for Group entities. +type GroupGroupBy struct { + selector + build *GroupQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (ggb *GroupGroupBy) Aggregate(fns ...AggregateFunc) *GroupGroupBy { + ggb.fns = append(ggb.fns, fns...) + return ggb +} + +// Scan applies the selector query and scans the result into the given value. +func (ggb *GroupGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, ggb.build.ctx, ent.OpQueryGroupBy) + if err := ggb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*GroupQuery, *GroupGroupBy](ctx, ggb.build, ggb, ggb.build.inters, v) +} + +func (ggb *GroupGroupBy) sqlScan(ctx context.Context, root *GroupQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(ggb.fns)) + for _, fn := range ggb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*ggb.flds)+len(ggb.fns)) + for _, f := range *ggb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*ggb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := ggb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// GroupSelect is the builder for selecting fields of Group entities. +type GroupSelect struct { + *GroupQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (gs *GroupSelect) Aggregate(fns ...AggregateFunc) *GroupSelect { + gs.fns = append(gs.fns, fns...) + return gs +} + +// Scan applies the selector query and scans the result into the given value. +func (gs *GroupSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, gs.ctx, ent.OpQuerySelect) + if err := gs.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*GroupQuery, *GroupSelect](ctx, gs.GroupQuery, gs, gs.inters, v) +} + +func (gs *GroupSelect) sqlScan(ctx context.Context, root *GroupQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(gs.fns)) + for _, fn := range gs.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*gs.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := gs.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// Modify adds a query modifier for attaching custom logic to queries. +func (gs *GroupSelect) Modify(modifiers ...func(s *sql.Selector)) *GroupSelect { + gs.modifiers = append(gs.modifiers, modifiers...) + return gs +} diff --git a/app/controlplane/pkg/data/ent/group_update.go b/app/controlplane/pkg/data/ent/group_update.go new file mode 100644 index 000000000..e0d44b571 --- /dev/null +++ b/app/controlplane/pkg/data/ent/group_update.go @@ -0,0 +1,874 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" + "github.com/google/uuid" +) + +// GroupUpdate is the builder for updating Group entities. +type GroupUpdate struct { + config + hooks []Hook + mutation *GroupMutation + modifiers []func(*sql.UpdateBuilder) +} + +// Where appends a list predicates to the GroupUpdate builder. +func (gu *GroupUpdate) Where(ps ...predicate.Group) *GroupUpdate { + gu.mutation.Where(ps...) + return gu +} + +// SetName sets the "name" field. +func (gu *GroupUpdate) SetName(s string) *GroupUpdate { + gu.mutation.SetName(s) + return gu +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (gu *GroupUpdate) SetNillableName(s *string) *GroupUpdate { + if s != nil { + gu.SetName(*s) + } + return gu +} + +// SetDescription sets the "description" field. +func (gu *GroupUpdate) SetDescription(s string) *GroupUpdate { + gu.mutation.SetDescription(s) + return gu +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (gu *GroupUpdate) SetNillableDescription(s *string) *GroupUpdate { + if s != nil { + gu.SetDescription(*s) + } + return gu +} + +// ClearDescription clears the value of the "description" field. +func (gu *GroupUpdate) ClearDescription() *GroupUpdate { + gu.mutation.ClearDescription() + return gu +} + +// SetOrganizationID sets the "organization_id" field. +func (gu *GroupUpdate) SetOrganizationID(u uuid.UUID) *GroupUpdate { + gu.mutation.SetOrganizationID(u) + return gu +} + +// SetNillableOrganizationID sets the "organization_id" field if the given value is not nil. +func (gu *GroupUpdate) SetNillableOrganizationID(u *uuid.UUID) *GroupUpdate { + if u != nil { + gu.SetOrganizationID(*u) + } + return gu +} + +// SetUpdatedAt sets the "updated_at" field. +func (gu *GroupUpdate) SetUpdatedAt(t time.Time) *GroupUpdate { + gu.mutation.SetUpdatedAt(t) + return gu +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (gu *GroupUpdate) SetNillableUpdatedAt(t *time.Time) *GroupUpdate { + if t != nil { + gu.SetUpdatedAt(*t) + } + return gu +} + +// SetDeletedAt sets the "deleted_at" field. +func (gu *GroupUpdate) SetDeletedAt(t time.Time) *GroupUpdate { + gu.mutation.SetDeletedAt(t) + return gu +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (gu *GroupUpdate) SetNillableDeletedAt(t *time.Time) *GroupUpdate { + if t != nil { + gu.SetDeletedAt(*t) + } + return gu +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (gu *GroupUpdate) ClearDeletedAt() *GroupUpdate { + gu.mutation.ClearDeletedAt() + return gu +} + +// AddMemberIDs adds the "members" edge to the User entity by IDs. +func (gu *GroupUpdate) AddMemberIDs(ids ...uuid.UUID) *GroupUpdate { + gu.mutation.AddMemberIDs(ids...) + return gu +} + +// AddMembers adds the "members" edges to the User entity. +func (gu *GroupUpdate) AddMembers(u ...*User) *GroupUpdate { + ids := make([]uuid.UUID, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return gu.AddMemberIDs(ids...) +} + +// SetOrganization sets the "organization" edge to the Organization entity. +func (gu *GroupUpdate) SetOrganization(o *Organization) *GroupUpdate { + return gu.SetOrganizationID(o.ID) +} + +// AddGroupUserIDs adds the "group_users" edge to the GroupMembership entity by IDs. +func (gu *GroupUpdate) AddGroupUserIDs(ids ...uuid.UUID) *GroupUpdate { + gu.mutation.AddGroupUserIDs(ids...) + return gu +} + +// AddGroupUsers adds the "group_users" edges to the GroupMembership entity. +func (gu *GroupUpdate) AddGroupUsers(g ...*GroupMembership) *GroupUpdate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return gu.AddGroupUserIDs(ids...) +} + +// Mutation returns the GroupMutation object of the builder. +func (gu *GroupUpdate) Mutation() *GroupMutation { + return gu.mutation +} + +// ClearMembers clears all "members" edges to the User entity. +func (gu *GroupUpdate) ClearMembers() *GroupUpdate { + gu.mutation.ClearMembers() + return gu +} + +// RemoveMemberIDs removes the "members" edge to User entities by IDs. +func (gu *GroupUpdate) RemoveMemberIDs(ids ...uuid.UUID) *GroupUpdate { + gu.mutation.RemoveMemberIDs(ids...) + return gu +} + +// RemoveMembers removes "members" edges to User entities. +func (gu *GroupUpdate) RemoveMembers(u ...*User) *GroupUpdate { + ids := make([]uuid.UUID, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return gu.RemoveMemberIDs(ids...) +} + +// ClearOrganization clears the "organization" edge to the Organization entity. +func (gu *GroupUpdate) ClearOrganization() *GroupUpdate { + gu.mutation.ClearOrganization() + return gu +} + +// ClearGroupUsers clears all "group_users" edges to the GroupMembership entity. +func (gu *GroupUpdate) ClearGroupUsers() *GroupUpdate { + gu.mutation.ClearGroupUsers() + return gu +} + +// RemoveGroupUserIDs removes the "group_users" edge to GroupMembership entities by IDs. +func (gu *GroupUpdate) RemoveGroupUserIDs(ids ...uuid.UUID) *GroupUpdate { + gu.mutation.RemoveGroupUserIDs(ids...) + return gu +} + +// RemoveGroupUsers removes "group_users" edges to GroupMembership entities. +func (gu *GroupUpdate) RemoveGroupUsers(g ...*GroupMembership) *GroupUpdate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return gu.RemoveGroupUserIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (gu *GroupUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, gu.sqlSave, gu.mutation, gu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (gu *GroupUpdate) SaveX(ctx context.Context) int { + affected, err := gu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (gu *GroupUpdate) Exec(ctx context.Context) error { + _, err := gu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (gu *GroupUpdate) ExecX(ctx context.Context) { + if err := gu.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (gu *GroupUpdate) check() error { + if v, ok := gu.mutation.Name(); ok { + if err := group.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Group.name": %w`, err)} + } + } + if gu.mutation.OrganizationCleared() && len(gu.mutation.OrganizationIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Group.organization"`) + } + return nil +} + +// Modify adds a statement modifier for attaching custom logic to the UPDATE statement. +func (gu *GroupUpdate) Modify(modifiers ...func(u *sql.UpdateBuilder)) *GroupUpdate { + gu.modifiers = append(gu.modifiers, modifiers...) + return gu +} + +func (gu *GroupUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := gu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(group.Table, group.Columns, sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID)) + if ps := gu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := gu.mutation.Name(); ok { + _spec.SetField(group.FieldName, field.TypeString, value) + } + if value, ok := gu.mutation.Description(); ok { + _spec.SetField(group.FieldDescription, field.TypeString, value) + } + if gu.mutation.DescriptionCleared() { + _spec.ClearField(group.FieldDescription, field.TypeString) + } + if value, ok := gu.mutation.UpdatedAt(); ok { + _spec.SetField(group.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := gu.mutation.DeletedAt(); ok { + _spec.SetField(group.FieldDeletedAt, field.TypeTime, value) + } + if gu.mutation.DeletedAtCleared() { + _spec.ClearField(group.FieldDeletedAt, field.TypeTime) + } + if gu.mutation.MembersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: group.MembersTable, + Columns: group.MembersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + createE := &GroupMembershipCreate{config: gu.config, mutation: newGroupMembershipMutation(gu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gu.mutation.RemovedMembersIDs(); len(nodes) > 0 && !gu.mutation.MembersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: group.MembersTable, + Columns: group.MembersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &GroupMembershipCreate{config: gu.config, mutation: newGroupMembershipMutation(gu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gu.mutation.MembersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: group.MembersTable, + Columns: group.MembersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &GroupMembershipCreate{config: gu.config, mutation: newGroupMembershipMutation(gu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if gu.mutation.OrganizationCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: group.OrganizationTable, + Columns: []string{group.OrganizationColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gu.mutation.OrganizationIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: group.OrganizationTable, + Columns: []string{group.OrganizationColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if gu.mutation.GroupUsersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.GroupUsersTable, + Columns: []string{group.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gu.mutation.RemovedGroupUsersIDs(); len(nodes) > 0 && !gu.mutation.GroupUsersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.GroupUsersTable, + Columns: []string{group.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gu.mutation.GroupUsersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.GroupUsersTable, + Columns: []string{group.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _spec.AddModifiers(gu.modifiers...) + if n, err = sqlgraph.UpdateNodes(ctx, gu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{group.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + gu.mutation.done = true + return n, nil +} + +// GroupUpdateOne is the builder for updating a single Group entity. +type GroupUpdateOne struct { + config + fields []string + hooks []Hook + mutation *GroupMutation + modifiers []func(*sql.UpdateBuilder) +} + +// SetName sets the "name" field. +func (guo *GroupUpdateOne) SetName(s string) *GroupUpdateOne { + guo.mutation.SetName(s) + return guo +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (guo *GroupUpdateOne) SetNillableName(s *string) *GroupUpdateOne { + if s != nil { + guo.SetName(*s) + } + return guo +} + +// SetDescription sets the "description" field. +func (guo *GroupUpdateOne) SetDescription(s string) *GroupUpdateOne { + guo.mutation.SetDescription(s) + return guo +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (guo *GroupUpdateOne) SetNillableDescription(s *string) *GroupUpdateOne { + if s != nil { + guo.SetDescription(*s) + } + return guo +} + +// ClearDescription clears the value of the "description" field. +func (guo *GroupUpdateOne) ClearDescription() *GroupUpdateOne { + guo.mutation.ClearDescription() + return guo +} + +// SetOrganizationID sets the "organization_id" field. +func (guo *GroupUpdateOne) SetOrganizationID(u uuid.UUID) *GroupUpdateOne { + guo.mutation.SetOrganizationID(u) + return guo +} + +// SetNillableOrganizationID sets the "organization_id" field if the given value is not nil. +func (guo *GroupUpdateOne) SetNillableOrganizationID(u *uuid.UUID) *GroupUpdateOne { + if u != nil { + guo.SetOrganizationID(*u) + } + return guo +} + +// SetUpdatedAt sets the "updated_at" field. +func (guo *GroupUpdateOne) SetUpdatedAt(t time.Time) *GroupUpdateOne { + guo.mutation.SetUpdatedAt(t) + return guo +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (guo *GroupUpdateOne) SetNillableUpdatedAt(t *time.Time) *GroupUpdateOne { + if t != nil { + guo.SetUpdatedAt(*t) + } + return guo +} + +// SetDeletedAt sets the "deleted_at" field. +func (guo *GroupUpdateOne) SetDeletedAt(t time.Time) *GroupUpdateOne { + guo.mutation.SetDeletedAt(t) + return guo +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (guo *GroupUpdateOne) SetNillableDeletedAt(t *time.Time) *GroupUpdateOne { + if t != nil { + guo.SetDeletedAt(*t) + } + return guo +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (guo *GroupUpdateOne) ClearDeletedAt() *GroupUpdateOne { + guo.mutation.ClearDeletedAt() + return guo +} + +// AddMemberIDs adds the "members" edge to the User entity by IDs. +func (guo *GroupUpdateOne) AddMemberIDs(ids ...uuid.UUID) *GroupUpdateOne { + guo.mutation.AddMemberIDs(ids...) + return guo +} + +// AddMembers adds the "members" edges to the User entity. +func (guo *GroupUpdateOne) AddMembers(u ...*User) *GroupUpdateOne { + ids := make([]uuid.UUID, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return guo.AddMemberIDs(ids...) +} + +// SetOrganization sets the "organization" edge to the Organization entity. +func (guo *GroupUpdateOne) SetOrganization(o *Organization) *GroupUpdateOne { + return guo.SetOrganizationID(o.ID) +} + +// AddGroupUserIDs adds the "group_users" edge to the GroupMembership entity by IDs. +func (guo *GroupUpdateOne) AddGroupUserIDs(ids ...uuid.UUID) *GroupUpdateOne { + guo.mutation.AddGroupUserIDs(ids...) + return guo +} + +// AddGroupUsers adds the "group_users" edges to the GroupMembership entity. +func (guo *GroupUpdateOne) AddGroupUsers(g ...*GroupMembership) *GroupUpdateOne { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return guo.AddGroupUserIDs(ids...) +} + +// Mutation returns the GroupMutation object of the builder. +func (guo *GroupUpdateOne) Mutation() *GroupMutation { + return guo.mutation +} + +// ClearMembers clears all "members" edges to the User entity. +func (guo *GroupUpdateOne) ClearMembers() *GroupUpdateOne { + guo.mutation.ClearMembers() + return guo +} + +// RemoveMemberIDs removes the "members" edge to User entities by IDs. +func (guo *GroupUpdateOne) RemoveMemberIDs(ids ...uuid.UUID) *GroupUpdateOne { + guo.mutation.RemoveMemberIDs(ids...) + return guo +} + +// RemoveMembers removes "members" edges to User entities. +func (guo *GroupUpdateOne) RemoveMembers(u ...*User) *GroupUpdateOne { + ids := make([]uuid.UUID, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return guo.RemoveMemberIDs(ids...) +} + +// ClearOrganization clears the "organization" edge to the Organization entity. +func (guo *GroupUpdateOne) ClearOrganization() *GroupUpdateOne { + guo.mutation.ClearOrganization() + return guo +} + +// ClearGroupUsers clears all "group_users" edges to the GroupMembership entity. +func (guo *GroupUpdateOne) ClearGroupUsers() *GroupUpdateOne { + guo.mutation.ClearGroupUsers() + return guo +} + +// RemoveGroupUserIDs removes the "group_users" edge to GroupMembership entities by IDs. +func (guo *GroupUpdateOne) RemoveGroupUserIDs(ids ...uuid.UUID) *GroupUpdateOne { + guo.mutation.RemoveGroupUserIDs(ids...) + return guo +} + +// RemoveGroupUsers removes "group_users" edges to GroupMembership entities. +func (guo *GroupUpdateOne) RemoveGroupUsers(g ...*GroupMembership) *GroupUpdateOne { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return guo.RemoveGroupUserIDs(ids...) +} + +// Where appends a list predicates to the GroupUpdate builder. +func (guo *GroupUpdateOne) Where(ps ...predicate.Group) *GroupUpdateOne { + guo.mutation.Where(ps...) + return guo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (guo *GroupUpdateOne) Select(field string, fields ...string) *GroupUpdateOne { + guo.fields = append([]string{field}, fields...) + return guo +} + +// Save executes the query and returns the updated Group entity. +func (guo *GroupUpdateOne) Save(ctx context.Context) (*Group, error) { + return withHooks(ctx, guo.sqlSave, guo.mutation, guo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (guo *GroupUpdateOne) SaveX(ctx context.Context) *Group { + node, err := guo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (guo *GroupUpdateOne) Exec(ctx context.Context) error { + _, err := guo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (guo *GroupUpdateOne) ExecX(ctx context.Context) { + if err := guo.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (guo *GroupUpdateOne) check() error { + if v, ok := guo.mutation.Name(); ok { + if err := group.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Group.name": %w`, err)} + } + } + if guo.mutation.OrganizationCleared() && len(guo.mutation.OrganizationIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Group.organization"`) + } + return nil +} + +// Modify adds a statement modifier for attaching custom logic to the UPDATE statement. +func (guo *GroupUpdateOne) Modify(modifiers ...func(u *sql.UpdateBuilder)) *GroupUpdateOne { + guo.modifiers = append(guo.modifiers, modifiers...) + return guo +} + +func (guo *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error) { + if err := guo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(group.Table, group.Columns, sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID)) + id, ok := guo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Group.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := guo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, group.FieldID) + for _, f := range fields { + if !group.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != group.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := guo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := guo.mutation.Name(); ok { + _spec.SetField(group.FieldName, field.TypeString, value) + } + if value, ok := guo.mutation.Description(); ok { + _spec.SetField(group.FieldDescription, field.TypeString, value) + } + if guo.mutation.DescriptionCleared() { + _spec.ClearField(group.FieldDescription, field.TypeString) + } + if value, ok := guo.mutation.UpdatedAt(); ok { + _spec.SetField(group.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := guo.mutation.DeletedAt(); ok { + _spec.SetField(group.FieldDeletedAt, field.TypeTime, value) + } + if guo.mutation.DeletedAtCleared() { + _spec.ClearField(group.FieldDeletedAt, field.TypeTime) + } + if guo.mutation.MembersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: group.MembersTable, + Columns: group.MembersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + createE := &GroupMembershipCreate{config: guo.config, mutation: newGroupMembershipMutation(guo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := guo.mutation.RemovedMembersIDs(); len(nodes) > 0 && !guo.mutation.MembersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: group.MembersTable, + Columns: group.MembersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &GroupMembershipCreate{config: guo.config, mutation: newGroupMembershipMutation(guo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := guo.mutation.MembersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: group.MembersTable, + Columns: group.MembersPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &GroupMembershipCreate{config: guo.config, mutation: newGroupMembershipMutation(guo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if guo.mutation.OrganizationCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: group.OrganizationTable, + Columns: []string{group.OrganizationColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := guo.mutation.OrganizationIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: group.OrganizationTable, + Columns: []string{group.OrganizationColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(organization.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if guo.mutation.GroupUsersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.GroupUsersTable, + Columns: []string{group.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := guo.mutation.RemovedGroupUsersIDs(); len(nodes) > 0 && !guo.mutation.GroupUsersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.GroupUsersTable, + Columns: []string{group.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := guo.mutation.GroupUsersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: group.GroupUsersTable, + Columns: []string{group.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _spec.AddModifiers(guo.modifiers...) + _node = &Group{config: guo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, guo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{group.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + guo.mutation.done = true + return _node, nil +} diff --git a/app/controlplane/pkg/data/ent/groupmembership.go b/app/controlplane/pkg/data/ent/groupmembership.go new file mode 100644 index 000000000..f5ff6ddb9 --- /dev/null +++ b/app/controlplane/pkg/data/ent/groupmembership.go @@ -0,0 +1,210 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" + "github.com/google/uuid" +) + +// GroupMembership is the model entity for the GroupMembership schema. +type GroupMembership struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // GroupID holds the value of the "group_id" field. + GroupID uuid.UUID `json:"group_id,omitempty"` + // UserID holds the value of the "user_id" field. + UserID uuid.UUID `json:"user_id,omitempty"` + // Maintainer holds the value of the "maintainer" field. + Maintainer bool `json:"maintainer,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // DeletedAt holds the value of the "deleted_at" field. + DeletedAt time.Time `json:"deleted_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the GroupMembershipQuery when eager-loading is set. + Edges GroupMembershipEdges `json:"edges"` + selectValues sql.SelectValues +} + +// GroupMembershipEdges holds the relations/edges for other nodes in the graph. +type GroupMembershipEdges struct { + // Group holds the value of the group edge. + Group *Group `json:"group,omitempty"` + // User holds the value of the user edge. + User *User `json:"user,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// GroupOrErr returns the Group value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e GroupMembershipEdges) GroupOrErr() (*Group, error) { + if e.Group != nil { + return e.Group, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: group.Label} + } + return nil, &NotLoadedError{edge: "group"} +} + +// UserOrErr returns the User value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e GroupMembershipEdges) UserOrErr() (*User, error) { + if e.User != nil { + return e.User, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: user.Label} + } + return nil, &NotLoadedError{edge: "user"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*GroupMembership) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case groupmembership.FieldMaintainer: + values[i] = new(sql.NullBool) + case groupmembership.FieldCreatedAt, groupmembership.FieldUpdatedAt, groupmembership.FieldDeletedAt: + values[i] = new(sql.NullTime) + case groupmembership.FieldID, groupmembership.FieldGroupID, groupmembership.FieldUserID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the GroupMembership fields. +func (gm *GroupMembership) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case groupmembership.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + gm.ID = *value + } + case groupmembership.FieldGroupID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field group_id", values[i]) + } else if value != nil { + gm.GroupID = *value + } + case groupmembership.FieldUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field user_id", values[i]) + } else if value != nil { + gm.UserID = *value + } + case groupmembership.FieldMaintainer: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field maintainer", values[i]) + } else if value.Valid { + gm.Maintainer = value.Bool + } + case groupmembership.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + gm.CreatedAt = value.Time + } + case groupmembership.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + gm.UpdatedAt = value.Time + } + case groupmembership.FieldDeletedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field deleted_at", values[i]) + } else if value.Valid { + gm.DeletedAt = value.Time + } + default: + gm.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the GroupMembership. +// This includes values selected through modifiers, order, etc. +func (gm *GroupMembership) Value(name string) (ent.Value, error) { + return gm.selectValues.Get(name) +} + +// QueryGroup queries the "group" edge of the GroupMembership entity. +func (gm *GroupMembership) QueryGroup() *GroupQuery { + return NewGroupMembershipClient(gm.config).QueryGroup(gm) +} + +// QueryUser queries the "user" edge of the GroupMembership entity. +func (gm *GroupMembership) QueryUser() *UserQuery { + return NewGroupMembershipClient(gm.config).QueryUser(gm) +} + +// Update returns a builder for updating this GroupMembership. +// Note that you need to call GroupMembership.Unwrap() before calling this method if this GroupMembership +// was returned from a transaction, and the transaction was committed or rolled back. +func (gm *GroupMembership) Update() *GroupMembershipUpdateOne { + return NewGroupMembershipClient(gm.config).UpdateOne(gm) +} + +// Unwrap unwraps the GroupMembership entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (gm *GroupMembership) Unwrap() *GroupMembership { + _tx, ok := gm.config.driver.(*txDriver) + if !ok { + panic("ent: GroupMembership is not a transactional entity") + } + gm.config.driver = _tx.drv + return gm +} + +// String implements the fmt.Stringer. +func (gm *GroupMembership) String() string { + var builder strings.Builder + builder.WriteString("GroupMembership(") + builder.WriteString(fmt.Sprintf("id=%v, ", gm.ID)) + builder.WriteString("group_id=") + builder.WriteString(fmt.Sprintf("%v", gm.GroupID)) + builder.WriteString(", ") + builder.WriteString("user_id=") + builder.WriteString(fmt.Sprintf("%v", gm.UserID)) + builder.WriteString(", ") + builder.WriteString("maintainer=") + builder.WriteString(fmt.Sprintf("%v", gm.Maintainer)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(gm.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(gm.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("deleted_at=") + builder.WriteString(gm.DeletedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// GroupMemberships is a parsable slice of GroupMembership. +type GroupMemberships []*GroupMembership diff --git a/app/controlplane/pkg/data/ent/groupmembership/groupmembership.go b/app/controlplane/pkg/data/ent/groupmembership/groupmembership.go new file mode 100644 index 000000000..fc0a81ab4 --- /dev/null +++ b/app/controlplane/pkg/data/ent/groupmembership/groupmembership.go @@ -0,0 +1,152 @@ +// Code generated by ent, DO NOT EDIT. + +package groupmembership + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the groupmembership type in the database. + Label = "group_membership" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldGroupID holds the string denoting the group_id field in the database. + FieldGroupID = "group_id" + // FieldUserID holds the string denoting the user_id field in the database. + FieldUserID = "user_id" + // FieldMaintainer holds the string denoting the maintainer field in the database. + FieldMaintainer = "maintainer" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // FieldDeletedAt holds the string denoting the deleted_at field in the database. + FieldDeletedAt = "deleted_at" + // EdgeGroup holds the string denoting the group edge name in mutations. + EdgeGroup = "group" + // EdgeUser holds the string denoting the user edge name in mutations. + EdgeUser = "user" + // Table holds the table name of the groupmembership in the database. + Table = "group_memberships" + // GroupTable is the table that holds the group relation/edge. + GroupTable = "group_memberships" + // GroupInverseTable is the table name for the Group entity. + // It exists in this package in order to avoid circular dependency with the "group" package. + GroupInverseTable = "groups" + // GroupColumn is the table column denoting the group relation/edge. + GroupColumn = "group_id" + // UserTable is the table that holds the user relation/edge. + UserTable = "group_memberships" + // UserInverseTable is the table name for the User entity. + // It exists in this package in order to avoid circular dependency with the "user" package. + UserInverseTable = "users" + // UserColumn is the table column denoting the user relation/edge. + UserColumn = "user_id" +) + +// Columns holds all SQL columns for groupmembership fields. +var Columns = []string{ + FieldID, + FieldGroupID, + FieldUserID, + FieldMaintainer, + FieldCreatedAt, + FieldUpdatedAt, + FieldDeletedAt, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultGroupID holds the default value on creation for the "group_id" field. + DefaultGroupID func() uuid.UUID + // DefaultUserID holds the default value on creation for the "user_id" field. + DefaultUserID func() uuid.UUID + // DefaultMaintainer holds the default value on creation for the "maintainer" field. + DefaultMaintainer bool + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the GroupMembership queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByGroupID orders the results by the group_id field. +func ByGroupID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldGroupID, opts...).ToFunc() +} + +// ByUserID orders the results by the user_id field. +func ByUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUserID, opts...).ToFunc() +} + +// ByMaintainer orders the results by the maintainer field. +func ByMaintainer(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldMaintainer, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByDeletedAt orders the results by the deleted_at field. +func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDeletedAt, opts...).ToFunc() +} + +// ByGroupField orders the results by group field. +func ByGroupField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newGroupStep(), sql.OrderByField(field, opts...)) + } +} + +// ByUserField orders the results by user field. +func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...)) + } +} +func newGroupStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(GroupInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, GroupTable, GroupColumn), + ) +} +func newUserStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(UserInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn), + ) +} diff --git a/app/controlplane/pkg/data/ent/groupmembership/where.go b/app/controlplane/pkg/data/ent/groupmembership/where.go new file mode 100644 index 000000000..7454cdb04 --- /dev/null +++ b/app/controlplane/pkg/data/ent/groupmembership/where.go @@ -0,0 +1,328 @@ +// Code generated by ent, DO NOT EDIT. + +package groupmembership + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldLTE(FieldID, id)) +} + +// GroupID applies equality check predicate on the "group_id" field. It's identical to GroupIDEQ. +func GroupID(v uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldGroupID, v)) +} + +// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ. +func UserID(v uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldUserID, v)) +} + +// Maintainer applies equality check predicate on the "maintainer" field. It's identical to MaintainerEQ. +func Maintainer(v bool) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldMaintainer, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ. +func DeletedAt(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldDeletedAt, v)) +} + +// GroupIDEQ applies the EQ predicate on the "group_id" field. +func GroupIDEQ(v uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldGroupID, v)) +} + +// GroupIDNEQ applies the NEQ predicate on the "group_id" field. +func GroupIDNEQ(v uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNEQ(FieldGroupID, v)) +} + +// GroupIDIn applies the In predicate on the "group_id" field. +func GroupIDIn(vs ...uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldIn(FieldGroupID, vs...)) +} + +// GroupIDNotIn applies the NotIn predicate on the "group_id" field. +func GroupIDNotIn(vs ...uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNotIn(FieldGroupID, vs...)) +} + +// UserIDEQ applies the EQ predicate on the "user_id" field. +func UserIDEQ(v uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldUserID, v)) +} + +// UserIDNEQ applies the NEQ predicate on the "user_id" field. +func UserIDNEQ(v uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNEQ(FieldUserID, v)) +} + +// UserIDIn applies the In predicate on the "user_id" field. +func UserIDIn(vs ...uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldIn(FieldUserID, vs...)) +} + +// UserIDNotIn applies the NotIn predicate on the "user_id" field. +func UserIDNotIn(vs ...uuid.UUID) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNotIn(FieldUserID, vs...)) +} + +// MaintainerEQ applies the EQ predicate on the "maintainer" field. +func MaintainerEQ(v bool) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldMaintainer, v)) +} + +// MaintainerNEQ applies the NEQ predicate on the "maintainer" field. +func MaintainerNEQ(v bool) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNEQ(FieldMaintainer, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// DeletedAtEQ applies the EQ predicate on the "deleted_at" field. +func DeletedAtEQ(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldEQ(FieldDeletedAt, v)) +} + +// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field. +func DeletedAtNEQ(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNEQ(FieldDeletedAt, v)) +} + +// DeletedAtIn applies the In predicate on the "deleted_at" field. +func DeletedAtIn(vs ...time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldIn(FieldDeletedAt, vs...)) +} + +// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field. +func DeletedAtNotIn(vs ...time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNotIn(FieldDeletedAt, vs...)) +} + +// DeletedAtGT applies the GT predicate on the "deleted_at" field. +func DeletedAtGT(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldGT(FieldDeletedAt, v)) +} + +// DeletedAtGTE applies the GTE predicate on the "deleted_at" field. +func DeletedAtGTE(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldGTE(FieldDeletedAt, v)) +} + +// DeletedAtLT applies the LT predicate on the "deleted_at" field. +func DeletedAtLT(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldLT(FieldDeletedAt, v)) +} + +// DeletedAtLTE applies the LTE predicate on the "deleted_at" field. +func DeletedAtLTE(v time.Time) predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldLTE(FieldDeletedAt, v)) +} + +// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field. +func DeletedAtIsNil() predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldIsNull(FieldDeletedAt)) +} + +// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field. +func DeletedAtNotNil() predicate.GroupMembership { + return predicate.GroupMembership(sql.FieldNotNull(FieldDeletedAt)) +} + +// HasGroup applies the HasEdge predicate on the "group" edge. +func HasGroup() predicate.GroupMembership { + return predicate.GroupMembership(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, GroupTable, GroupColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasGroupWith applies the HasEdge predicate on the "group" edge with a given conditions (other predicates). +func HasGroupWith(preds ...predicate.Group) predicate.GroupMembership { + return predicate.GroupMembership(func(s *sql.Selector) { + step := newGroupStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasUser applies the HasEdge predicate on the "user" edge. +func HasUser() predicate.GroupMembership { + return predicate.GroupMembership(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates). +func HasUserWith(preds ...predicate.User) predicate.GroupMembership { + return predicate.GroupMembership(func(s *sql.Selector) { + step := newUserStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.GroupMembership) predicate.GroupMembership { + return predicate.GroupMembership(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.GroupMembership) predicate.GroupMembership { + return predicate.GroupMembership(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.GroupMembership) predicate.GroupMembership { + return predicate.GroupMembership(sql.NotPredicates(p)) +} diff --git a/app/controlplane/pkg/data/ent/groupmembership_create.go b/app/controlplane/pkg/data/ent/groupmembership_create.go new file mode 100644 index 000000000..385ccb917 --- /dev/null +++ b/app/controlplane/pkg/data/ent/groupmembership_create.go @@ -0,0 +1,869 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" + "github.com/google/uuid" +) + +// GroupMembershipCreate is the builder for creating a GroupMembership entity. +type GroupMembershipCreate struct { + config + mutation *GroupMembershipMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetGroupID sets the "group_id" field. +func (gmc *GroupMembershipCreate) SetGroupID(u uuid.UUID) *GroupMembershipCreate { + gmc.mutation.SetGroupID(u) + return gmc +} + +// SetNillableGroupID sets the "group_id" field if the given value is not nil. +func (gmc *GroupMembershipCreate) SetNillableGroupID(u *uuid.UUID) *GroupMembershipCreate { + if u != nil { + gmc.SetGroupID(*u) + } + return gmc +} + +// SetUserID sets the "user_id" field. +func (gmc *GroupMembershipCreate) SetUserID(u uuid.UUID) *GroupMembershipCreate { + gmc.mutation.SetUserID(u) + return gmc +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (gmc *GroupMembershipCreate) SetNillableUserID(u *uuid.UUID) *GroupMembershipCreate { + if u != nil { + gmc.SetUserID(*u) + } + return gmc +} + +// SetMaintainer sets the "maintainer" field. +func (gmc *GroupMembershipCreate) SetMaintainer(b bool) *GroupMembershipCreate { + gmc.mutation.SetMaintainer(b) + return gmc +} + +// SetNillableMaintainer sets the "maintainer" field if the given value is not nil. +func (gmc *GroupMembershipCreate) SetNillableMaintainer(b *bool) *GroupMembershipCreate { + if b != nil { + gmc.SetMaintainer(*b) + } + return gmc +} + +// SetCreatedAt sets the "created_at" field. +func (gmc *GroupMembershipCreate) SetCreatedAt(t time.Time) *GroupMembershipCreate { + gmc.mutation.SetCreatedAt(t) + return gmc +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (gmc *GroupMembershipCreate) SetNillableCreatedAt(t *time.Time) *GroupMembershipCreate { + if t != nil { + gmc.SetCreatedAt(*t) + } + return gmc +} + +// SetUpdatedAt sets the "updated_at" field. +func (gmc *GroupMembershipCreate) SetUpdatedAt(t time.Time) *GroupMembershipCreate { + gmc.mutation.SetUpdatedAt(t) + return gmc +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (gmc *GroupMembershipCreate) SetNillableUpdatedAt(t *time.Time) *GroupMembershipCreate { + if t != nil { + gmc.SetUpdatedAt(*t) + } + return gmc +} + +// SetDeletedAt sets the "deleted_at" field. +func (gmc *GroupMembershipCreate) SetDeletedAt(t time.Time) *GroupMembershipCreate { + gmc.mutation.SetDeletedAt(t) + return gmc +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (gmc *GroupMembershipCreate) SetNillableDeletedAt(t *time.Time) *GroupMembershipCreate { + if t != nil { + gmc.SetDeletedAt(*t) + } + return gmc +} + +// SetID sets the "id" field. +func (gmc *GroupMembershipCreate) SetID(u uuid.UUID) *GroupMembershipCreate { + gmc.mutation.SetID(u) + return gmc +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (gmc *GroupMembershipCreate) SetNillableID(u *uuid.UUID) *GroupMembershipCreate { + if u != nil { + gmc.SetID(*u) + } + return gmc +} + +// SetGroup sets the "group" edge to the Group entity. +func (gmc *GroupMembershipCreate) SetGroup(g *Group) *GroupMembershipCreate { + return gmc.SetGroupID(g.ID) +} + +// SetUser sets the "user" edge to the User entity. +func (gmc *GroupMembershipCreate) SetUser(u *User) *GroupMembershipCreate { + return gmc.SetUserID(u.ID) +} + +// Mutation returns the GroupMembershipMutation object of the builder. +func (gmc *GroupMembershipCreate) Mutation() *GroupMembershipMutation { + return gmc.mutation +} + +// Save creates the GroupMembership in the database. +func (gmc *GroupMembershipCreate) Save(ctx context.Context) (*GroupMembership, error) { + gmc.defaults() + return withHooks(ctx, gmc.sqlSave, gmc.mutation, gmc.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (gmc *GroupMembershipCreate) SaveX(ctx context.Context) *GroupMembership { + v, err := gmc.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (gmc *GroupMembershipCreate) Exec(ctx context.Context) error { + _, err := gmc.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (gmc *GroupMembershipCreate) ExecX(ctx context.Context) { + if err := gmc.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (gmc *GroupMembershipCreate) defaults() { + if _, ok := gmc.mutation.GroupID(); !ok { + v := groupmembership.DefaultGroupID() + gmc.mutation.SetGroupID(v) + } + if _, ok := gmc.mutation.UserID(); !ok { + v := groupmembership.DefaultUserID() + gmc.mutation.SetUserID(v) + } + if _, ok := gmc.mutation.Maintainer(); !ok { + v := groupmembership.DefaultMaintainer + gmc.mutation.SetMaintainer(v) + } + if _, ok := gmc.mutation.CreatedAt(); !ok { + v := groupmembership.DefaultCreatedAt() + gmc.mutation.SetCreatedAt(v) + } + if _, ok := gmc.mutation.UpdatedAt(); !ok { + v := groupmembership.DefaultUpdatedAt() + gmc.mutation.SetUpdatedAt(v) + } + if _, ok := gmc.mutation.ID(); !ok { + v := groupmembership.DefaultID() + gmc.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (gmc *GroupMembershipCreate) check() error { + if _, ok := gmc.mutation.GroupID(); !ok { + return &ValidationError{Name: "group_id", err: errors.New(`ent: missing required field "GroupMembership.group_id"`)} + } + if _, ok := gmc.mutation.UserID(); !ok { + return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "GroupMembership.user_id"`)} + } + if _, ok := gmc.mutation.Maintainer(); !ok { + return &ValidationError{Name: "maintainer", err: errors.New(`ent: missing required field "GroupMembership.maintainer"`)} + } + if _, ok := gmc.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "GroupMembership.created_at"`)} + } + if _, ok := gmc.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "GroupMembership.updated_at"`)} + } + if len(gmc.mutation.GroupIDs()) == 0 { + return &ValidationError{Name: "group", err: errors.New(`ent: missing required edge "GroupMembership.group"`)} + } + if len(gmc.mutation.UserIDs()) == 0 { + return &ValidationError{Name: "user", err: errors.New(`ent: missing required edge "GroupMembership.user"`)} + } + return nil +} + +func (gmc *GroupMembershipCreate) sqlSave(ctx context.Context) (*GroupMembership, error) { + if err := gmc.check(); err != nil { + return nil, err + } + _node, _spec := gmc.createSpec() + if err := sqlgraph.CreateNode(ctx, gmc.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + gmc.mutation.id = &_node.ID + gmc.mutation.done = true + return _node, nil +} + +func (gmc *GroupMembershipCreate) createSpec() (*GroupMembership, *sqlgraph.CreateSpec) { + var ( + _node = &GroupMembership{config: gmc.config} + _spec = sqlgraph.NewCreateSpec(groupmembership.Table, sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = gmc.conflict + if id, ok := gmc.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := gmc.mutation.Maintainer(); ok { + _spec.SetField(groupmembership.FieldMaintainer, field.TypeBool, value) + _node.Maintainer = value + } + if value, ok := gmc.mutation.CreatedAt(); ok { + _spec.SetField(groupmembership.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := gmc.mutation.UpdatedAt(); ok { + _spec.SetField(groupmembership.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := gmc.mutation.DeletedAt(); ok { + _spec.SetField(groupmembership.FieldDeletedAt, field.TypeTime, value) + _node.DeletedAt = value + } + if nodes := gmc.mutation.GroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: groupmembership.GroupTable, + Columns: []string{groupmembership.GroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.GroupID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := gmc.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: groupmembership.UserTable, + Columns: []string{groupmembership.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.UserID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.GroupMembership.Create(). +// SetGroupID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GroupMembershipUpsert) { +// SetGroupID(v+v). +// }). +// Exec(ctx) +func (gmc *GroupMembershipCreate) OnConflict(opts ...sql.ConflictOption) *GroupMembershipUpsertOne { + gmc.conflict = opts + return &GroupMembershipUpsertOne{ + create: gmc, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (gmc *GroupMembershipCreate) OnConflictColumns(columns ...string) *GroupMembershipUpsertOne { + gmc.conflict = append(gmc.conflict, sql.ConflictColumns(columns...)) + return &GroupMembershipUpsertOne{ + create: gmc, + } +} + +type ( + // GroupMembershipUpsertOne is the builder for "upsert"-ing + // one GroupMembership node. + GroupMembershipUpsertOne struct { + create *GroupMembershipCreate + } + + // GroupMembershipUpsert is the "OnConflict" setter. + GroupMembershipUpsert struct { + *sql.UpdateSet + } +) + +// SetGroupID sets the "group_id" field. +func (u *GroupMembershipUpsert) SetGroupID(v uuid.UUID) *GroupMembershipUpsert { + u.Set(groupmembership.FieldGroupID, v) + return u +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *GroupMembershipUpsert) UpdateGroupID() *GroupMembershipUpsert { + u.SetExcluded(groupmembership.FieldGroupID) + return u +} + +// SetUserID sets the "user_id" field. +func (u *GroupMembershipUpsert) SetUserID(v uuid.UUID) *GroupMembershipUpsert { + u.Set(groupmembership.FieldUserID, v) + return u +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *GroupMembershipUpsert) UpdateUserID() *GroupMembershipUpsert { + u.SetExcluded(groupmembership.FieldUserID) + return u +} + +// SetMaintainer sets the "maintainer" field. +func (u *GroupMembershipUpsert) SetMaintainer(v bool) *GroupMembershipUpsert { + u.Set(groupmembership.FieldMaintainer, v) + return u +} + +// UpdateMaintainer sets the "maintainer" field to the value that was provided on create. +func (u *GroupMembershipUpsert) UpdateMaintainer() *GroupMembershipUpsert { + u.SetExcluded(groupmembership.FieldMaintainer) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *GroupMembershipUpsert) SetUpdatedAt(v time.Time) *GroupMembershipUpsert { + u.Set(groupmembership.FieldUpdatedAt, v) + return u +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *GroupMembershipUpsert) UpdateUpdatedAt() *GroupMembershipUpsert { + u.SetExcluded(groupmembership.FieldUpdatedAt) + return u +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *GroupMembershipUpsert) SetDeletedAt(v time.Time) *GroupMembershipUpsert { + u.Set(groupmembership.FieldDeletedAt, v) + return u +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *GroupMembershipUpsert) UpdateDeletedAt() *GroupMembershipUpsert { + u.SetExcluded(groupmembership.FieldDeletedAt) + return u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *GroupMembershipUpsert) ClearDeletedAt() *GroupMembershipUpsert { + u.SetNull(groupmembership.FieldDeletedAt) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(groupmembership.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GroupMembershipUpsertOne) UpdateNewValues() *GroupMembershipUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(groupmembership.FieldID) + } + if _, exists := u.create.mutation.CreatedAt(); exists { + s.SetIgnore(groupmembership.FieldCreatedAt) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GroupMembershipUpsertOne) Ignore() *GroupMembershipUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GroupMembershipUpsertOne) DoNothing() *GroupMembershipUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GroupMembershipCreate.OnConflict +// documentation for more info. +func (u *GroupMembershipUpsertOne) Update(set func(*GroupMembershipUpsert)) *GroupMembershipUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GroupMembershipUpsert{UpdateSet: update}) + })) + return u +} + +// SetGroupID sets the "group_id" field. +func (u *GroupMembershipUpsertOne) SetGroupID(v uuid.UUID) *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *GroupMembershipUpsertOne) UpdateGroupID() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateGroupID() + }) +} + +// SetUserID sets the "user_id" field. +func (u *GroupMembershipUpsertOne) SetUserID(v uuid.UUID) *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *GroupMembershipUpsertOne) UpdateUserID() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateUserID() + }) +} + +// SetMaintainer sets the "maintainer" field. +func (u *GroupMembershipUpsertOne) SetMaintainer(v bool) *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetMaintainer(v) + }) +} + +// UpdateMaintainer sets the "maintainer" field to the value that was provided on create. +func (u *GroupMembershipUpsertOne) UpdateMaintainer() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateMaintainer() + }) +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *GroupMembershipUpsertOne) SetUpdatedAt(v time.Time) *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *GroupMembershipUpsertOne) UpdateUpdatedAt() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *GroupMembershipUpsertOne) SetDeletedAt(v time.Time) *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *GroupMembershipUpsertOne) UpdateDeletedAt() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *GroupMembershipUpsertOne) ClearDeletedAt() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.ClearDeletedAt() + }) +} + +// Exec executes the query. +func (u *GroupMembershipUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GroupMembershipCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GroupMembershipUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *GroupMembershipUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: GroupMembershipUpsertOne.ID is not supported by MySQL driver. Use GroupMembershipUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *GroupMembershipUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// GroupMembershipCreateBulk is the builder for creating many GroupMembership entities in bulk. +type GroupMembershipCreateBulk struct { + config + err error + builders []*GroupMembershipCreate + conflict []sql.ConflictOption +} + +// Save creates the GroupMembership entities in the database. +func (gmcb *GroupMembershipCreateBulk) Save(ctx context.Context) ([]*GroupMembership, error) { + if gmcb.err != nil { + return nil, gmcb.err + } + specs := make([]*sqlgraph.CreateSpec, len(gmcb.builders)) + nodes := make([]*GroupMembership, len(gmcb.builders)) + mutators := make([]Mutator, len(gmcb.builders)) + for i := range gmcb.builders { + func(i int, root context.Context) { + builder := gmcb.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*GroupMembershipMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, gmcb.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = gmcb.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, gmcb.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, gmcb.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (gmcb *GroupMembershipCreateBulk) SaveX(ctx context.Context) []*GroupMembership { + v, err := gmcb.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (gmcb *GroupMembershipCreateBulk) Exec(ctx context.Context) error { + _, err := gmcb.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (gmcb *GroupMembershipCreateBulk) ExecX(ctx context.Context) { + if err := gmcb.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.GroupMembership.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GroupMembershipUpsert) { +// SetGroupID(v+v). +// }). +// Exec(ctx) +func (gmcb *GroupMembershipCreateBulk) OnConflict(opts ...sql.ConflictOption) *GroupMembershipUpsertBulk { + gmcb.conflict = opts + return &GroupMembershipUpsertBulk{ + create: gmcb, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (gmcb *GroupMembershipCreateBulk) OnConflictColumns(columns ...string) *GroupMembershipUpsertBulk { + gmcb.conflict = append(gmcb.conflict, sql.ConflictColumns(columns...)) + return &GroupMembershipUpsertBulk{ + create: gmcb, + } +} + +// GroupMembershipUpsertBulk is the builder for "upsert"-ing +// a bulk of GroupMembership nodes. +type GroupMembershipUpsertBulk struct { + create *GroupMembershipCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(groupmembership.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GroupMembershipUpsertBulk) UpdateNewValues() *GroupMembershipUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(groupmembership.FieldID) + } + if _, exists := b.mutation.CreatedAt(); exists { + s.SetIgnore(groupmembership.FieldCreatedAt) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GroupMembershipUpsertBulk) Ignore() *GroupMembershipUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GroupMembershipUpsertBulk) DoNothing() *GroupMembershipUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GroupMembershipCreateBulk.OnConflict +// documentation for more info. +func (u *GroupMembershipUpsertBulk) Update(set func(*GroupMembershipUpsert)) *GroupMembershipUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GroupMembershipUpsert{UpdateSet: update}) + })) + return u +} + +// SetGroupID sets the "group_id" field. +func (u *GroupMembershipUpsertBulk) SetGroupID(v uuid.UUID) *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *GroupMembershipUpsertBulk) UpdateGroupID() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateGroupID() + }) +} + +// SetUserID sets the "user_id" field. +func (u *GroupMembershipUpsertBulk) SetUserID(v uuid.UUID) *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *GroupMembershipUpsertBulk) UpdateUserID() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateUserID() + }) +} + +// SetMaintainer sets the "maintainer" field. +func (u *GroupMembershipUpsertBulk) SetMaintainer(v bool) *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetMaintainer(v) + }) +} + +// UpdateMaintainer sets the "maintainer" field to the value that was provided on create. +func (u *GroupMembershipUpsertBulk) UpdateMaintainer() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateMaintainer() + }) +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *GroupMembershipUpsertBulk) SetUpdatedAt(v time.Time) *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *GroupMembershipUpsertBulk) UpdateUpdatedAt() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *GroupMembershipUpsertBulk) SetDeletedAt(v time.Time) *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *GroupMembershipUpsertBulk) UpdateDeletedAt() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *GroupMembershipUpsertBulk) ClearDeletedAt() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.ClearDeletedAt() + }) +} + +// Exec executes the query. +func (u *GroupMembershipUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the GroupMembershipCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GroupMembershipCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GroupMembershipUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/app/controlplane/pkg/data/ent/groupmembership_delete.go b/app/controlplane/pkg/data/ent/groupmembership_delete.go new file mode 100644 index 000000000..1207481a9 --- /dev/null +++ b/app/controlplane/pkg/data/ent/groupmembership_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" +) + +// GroupMembershipDelete is the builder for deleting a GroupMembership entity. +type GroupMembershipDelete struct { + config + hooks []Hook + mutation *GroupMembershipMutation +} + +// Where appends a list predicates to the GroupMembershipDelete builder. +func (gmd *GroupMembershipDelete) Where(ps ...predicate.GroupMembership) *GroupMembershipDelete { + gmd.mutation.Where(ps...) + return gmd +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (gmd *GroupMembershipDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, gmd.sqlExec, gmd.mutation, gmd.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (gmd *GroupMembershipDelete) ExecX(ctx context.Context) int { + n, err := gmd.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (gmd *GroupMembershipDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(groupmembership.Table, sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID)) + if ps := gmd.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, gmd.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + gmd.mutation.done = true + return affected, err +} + +// GroupMembershipDeleteOne is the builder for deleting a single GroupMembership entity. +type GroupMembershipDeleteOne struct { + gmd *GroupMembershipDelete +} + +// Where appends a list predicates to the GroupMembershipDelete builder. +func (gmdo *GroupMembershipDeleteOne) Where(ps ...predicate.GroupMembership) *GroupMembershipDeleteOne { + gmdo.gmd.mutation.Where(ps...) + return gmdo +} + +// Exec executes the deletion query. +func (gmdo *GroupMembershipDeleteOne) Exec(ctx context.Context) error { + n, err := gmdo.gmd.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{groupmembership.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (gmdo *GroupMembershipDeleteOne) ExecX(ctx context.Context) { + if err := gmdo.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/app/controlplane/pkg/data/ent/groupmembership_query.go b/app/controlplane/pkg/data/ent/groupmembership_query.go new file mode 100644 index 000000000..64839af3b --- /dev/null +++ b/app/controlplane/pkg/data/ent/groupmembership_query.go @@ -0,0 +1,732 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" + "github.com/google/uuid" +) + +// GroupMembershipQuery is the builder for querying GroupMembership entities. +type GroupMembershipQuery struct { + config + ctx *QueryContext + order []groupmembership.OrderOption + inters []Interceptor + predicates []predicate.GroupMembership + withGroup *GroupQuery + withUser *UserQuery + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the GroupMembershipQuery builder. +func (gmq *GroupMembershipQuery) Where(ps ...predicate.GroupMembership) *GroupMembershipQuery { + gmq.predicates = append(gmq.predicates, ps...) + return gmq +} + +// Limit the number of records to be returned by this query. +func (gmq *GroupMembershipQuery) Limit(limit int) *GroupMembershipQuery { + gmq.ctx.Limit = &limit + return gmq +} + +// Offset to start from. +func (gmq *GroupMembershipQuery) Offset(offset int) *GroupMembershipQuery { + gmq.ctx.Offset = &offset + return gmq +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (gmq *GroupMembershipQuery) Unique(unique bool) *GroupMembershipQuery { + gmq.ctx.Unique = &unique + return gmq +} + +// Order specifies how the records should be ordered. +func (gmq *GroupMembershipQuery) Order(o ...groupmembership.OrderOption) *GroupMembershipQuery { + gmq.order = append(gmq.order, o...) + return gmq +} + +// QueryGroup chains the current query on the "group" edge. +func (gmq *GroupMembershipQuery) QueryGroup() *GroupQuery { + query := (&GroupClient{config: gmq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := gmq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := gmq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(groupmembership.Table, groupmembership.FieldID, selector), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, groupmembership.GroupTable, groupmembership.GroupColumn), + ) + fromU = sqlgraph.SetNeighbors(gmq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryUser chains the current query on the "user" edge. +func (gmq *GroupMembershipQuery) QueryUser() *UserQuery { + query := (&UserClient{config: gmq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := gmq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := gmq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(groupmembership.Table, groupmembership.FieldID, selector), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, groupmembership.UserTable, groupmembership.UserColumn), + ) + fromU = sqlgraph.SetNeighbors(gmq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first GroupMembership entity from the query. +// Returns a *NotFoundError when no GroupMembership was found. +func (gmq *GroupMembershipQuery) First(ctx context.Context) (*GroupMembership, error) { + nodes, err := gmq.Limit(1).All(setContextOp(ctx, gmq.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{groupmembership.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (gmq *GroupMembershipQuery) FirstX(ctx context.Context) *GroupMembership { + node, err := gmq.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first GroupMembership ID from the query. +// Returns a *NotFoundError when no GroupMembership ID was found. +func (gmq *GroupMembershipQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = gmq.Limit(1).IDs(setContextOp(ctx, gmq.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{groupmembership.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (gmq *GroupMembershipQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := gmq.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single GroupMembership entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one GroupMembership entity is found. +// Returns a *NotFoundError when no GroupMembership entities are found. +func (gmq *GroupMembershipQuery) Only(ctx context.Context) (*GroupMembership, error) { + nodes, err := gmq.Limit(2).All(setContextOp(ctx, gmq.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{groupmembership.Label} + default: + return nil, &NotSingularError{groupmembership.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (gmq *GroupMembershipQuery) OnlyX(ctx context.Context) *GroupMembership { + node, err := gmq.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only GroupMembership ID in the query. +// Returns a *NotSingularError when more than one GroupMembership ID is found. +// Returns a *NotFoundError when no entities are found. +func (gmq *GroupMembershipQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = gmq.Limit(2).IDs(setContextOp(ctx, gmq.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{groupmembership.Label} + default: + err = &NotSingularError{groupmembership.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (gmq *GroupMembershipQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := gmq.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of GroupMemberships. +func (gmq *GroupMembershipQuery) All(ctx context.Context) ([]*GroupMembership, error) { + ctx = setContextOp(ctx, gmq.ctx, ent.OpQueryAll) + if err := gmq.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*GroupMembership, *GroupMembershipQuery]() + return withInterceptors[[]*GroupMembership](ctx, gmq, qr, gmq.inters) +} + +// AllX is like All, but panics if an error occurs. +func (gmq *GroupMembershipQuery) AllX(ctx context.Context) []*GroupMembership { + nodes, err := gmq.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of GroupMembership IDs. +func (gmq *GroupMembershipQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if gmq.ctx.Unique == nil && gmq.path != nil { + gmq.Unique(true) + } + ctx = setContextOp(ctx, gmq.ctx, ent.OpQueryIDs) + if err = gmq.Select(groupmembership.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (gmq *GroupMembershipQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := gmq.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (gmq *GroupMembershipQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, gmq.ctx, ent.OpQueryCount) + if err := gmq.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, gmq, querierCount[*GroupMembershipQuery](), gmq.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (gmq *GroupMembershipQuery) CountX(ctx context.Context) int { + count, err := gmq.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (gmq *GroupMembershipQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, gmq.ctx, ent.OpQueryExist) + switch _, err := gmq.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (gmq *GroupMembershipQuery) ExistX(ctx context.Context) bool { + exist, err := gmq.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the GroupMembershipQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (gmq *GroupMembershipQuery) Clone() *GroupMembershipQuery { + if gmq == nil { + return nil + } + return &GroupMembershipQuery{ + config: gmq.config, + ctx: gmq.ctx.Clone(), + order: append([]groupmembership.OrderOption{}, gmq.order...), + inters: append([]Interceptor{}, gmq.inters...), + predicates: append([]predicate.GroupMembership{}, gmq.predicates...), + withGroup: gmq.withGroup.Clone(), + withUser: gmq.withUser.Clone(), + // clone intermediate query. + sql: gmq.sql.Clone(), + path: gmq.path, + modifiers: append([]func(*sql.Selector){}, gmq.modifiers...), + } +} + +// WithGroup tells the query-builder to eager-load the nodes that are connected to +// the "group" edge. The optional arguments are used to configure the query builder of the edge. +func (gmq *GroupMembershipQuery) WithGroup(opts ...func(*GroupQuery)) *GroupMembershipQuery { + query := (&GroupClient{config: gmq.config}).Query() + for _, opt := range opts { + opt(query) + } + gmq.withGroup = query + return gmq +} + +// WithUser tells the query-builder to eager-load the nodes that are connected to +// the "user" edge. The optional arguments are used to configure the query builder of the edge. +func (gmq *GroupMembershipQuery) WithUser(opts ...func(*UserQuery)) *GroupMembershipQuery { + query := (&UserClient{config: gmq.config}).Query() + for _, opt := range opts { + opt(query) + } + gmq.withUser = query + return gmq +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// GroupID uuid.UUID `json:"group_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.GroupMembership.Query(). +// GroupBy(groupmembership.FieldGroupID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (gmq *GroupMembershipQuery) GroupBy(field string, fields ...string) *GroupMembershipGroupBy { + gmq.ctx.Fields = append([]string{field}, fields...) + grbuild := &GroupMembershipGroupBy{build: gmq} + grbuild.flds = &gmq.ctx.Fields + grbuild.label = groupmembership.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// GroupID uuid.UUID `json:"group_id,omitempty"` +// } +// +// client.GroupMembership.Query(). +// Select(groupmembership.FieldGroupID). +// Scan(ctx, &v) +func (gmq *GroupMembershipQuery) Select(fields ...string) *GroupMembershipSelect { + gmq.ctx.Fields = append(gmq.ctx.Fields, fields...) + sbuild := &GroupMembershipSelect{GroupMembershipQuery: gmq} + sbuild.label = groupmembership.Label + sbuild.flds, sbuild.scan = &gmq.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a GroupMembershipSelect configured with the given aggregations. +func (gmq *GroupMembershipQuery) Aggregate(fns ...AggregateFunc) *GroupMembershipSelect { + return gmq.Select().Aggregate(fns...) +} + +func (gmq *GroupMembershipQuery) prepareQuery(ctx context.Context) error { + for _, inter := range gmq.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, gmq); err != nil { + return err + } + } + } + for _, f := range gmq.ctx.Fields { + if !groupmembership.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if gmq.path != nil { + prev, err := gmq.path(ctx) + if err != nil { + return err + } + gmq.sql = prev + } + return nil +} + +func (gmq *GroupMembershipQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*GroupMembership, error) { + var ( + nodes = []*GroupMembership{} + _spec = gmq.querySpec() + loadedTypes = [2]bool{ + gmq.withGroup != nil, + gmq.withUser != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*GroupMembership).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &GroupMembership{config: gmq.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + if len(gmq.modifiers) > 0 { + _spec.Modifiers = gmq.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, gmq.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := gmq.withGroup; query != nil { + if err := gmq.loadGroup(ctx, query, nodes, nil, + func(n *GroupMembership, e *Group) { n.Edges.Group = e }); err != nil { + return nil, err + } + } + if query := gmq.withUser; query != nil { + if err := gmq.loadUser(ctx, query, nodes, nil, + func(n *GroupMembership, e *User) { n.Edges.User = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (gmq *GroupMembershipQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes []*GroupMembership, init func(*GroupMembership), assign func(*GroupMembership, *Group)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*GroupMembership) + for i := range nodes { + fk := nodes[i].GroupID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(group.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "group_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (gmq *GroupMembershipQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*GroupMembership, init func(*GroupMembership), assign func(*GroupMembership, *User)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*GroupMembership) + for i := range nodes { + fk := nodes[i].UserID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "user_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (gmq *GroupMembershipQuery) sqlCount(ctx context.Context) (int, error) { + _spec := gmq.querySpec() + if len(gmq.modifiers) > 0 { + _spec.Modifiers = gmq.modifiers + } + _spec.Node.Columns = gmq.ctx.Fields + if len(gmq.ctx.Fields) > 0 { + _spec.Unique = gmq.ctx.Unique != nil && *gmq.ctx.Unique + } + return sqlgraph.CountNodes(ctx, gmq.driver, _spec) +} + +func (gmq *GroupMembershipQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(groupmembership.Table, groupmembership.Columns, sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID)) + _spec.From = gmq.sql + if unique := gmq.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if gmq.path != nil { + _spec.Unique = true + } + if fields := gmq.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, groupmembership.FieldID) + for i := range fields { + if fields[i] != groupmembership.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if gmq.withGroup != nil { + _spec.Node.AddColumnOnce(groupmembership.FieldGroupID) + } + if gmq.withUser != nil { + _spec.Node.AddColumnOnce(groupmembership.FieldUserID) + } + } + if ps := gmq.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := gmq.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := gmq.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := gmq.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (gmq *GroupMembershipQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(gmq.driver.Dialect()) + t1 := builder.Table(groupmembership.Table) + columns := gmq.ctx.Fields + if len(columns) == 0 { + columns = groupmembership.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if gmq.sql != nil { + selector = gmq.sql + selector.Select(selector.Columns(columns...)...) + } + if gmq.ctx.Unique != nil && *gmq.ctx.Unique { + selector.Distinct() + } + for _, m := range gmq.modifiers { + m(selector) + } + for _, p := range gmq.predicates { + p(selector) + } + for _, p := range gmq.order { + p(selector) + } + if offset := gmq.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := gmq.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (gmq *GroupMembershipQuery) ForUpdate(opts ...sql.LockOption) *GroupMembershipQuery { + if gmq.driver.Dialect() == dialect.Postgres { + gmq.Unique(false) + } + gmq.modifiers = append(gmq.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return gmq +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (gmq *GroupMembershipQuery) ForShare(opts ...sql.LockOption) *GroupMembershipQuery { + if gmq.driver.Dialect() == dialect.Postgres { + gmq.Unique(false) + } + gmq.modifiers = append(gmq.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return gmq +} + +// Modify adds a query modifier for attaching custom logic to queries. +func (gmq *GroupMembershipQuery) Modify(modifiers ...func(s *sql.Selector)) *GroupMembershipSelect { + gmq.modifiers = append(gmq.modifiers, modifiers...) + return gmq.Select() +} + +// GroupMembershipGroupBy is the group-by builder for GroupMembership entities. +type GroupMembershipGroupBy struct { + selector + build *GroupMembershipQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (gmgb *GroupMembershipGroupBy) Aggregate(fns ...AggregateFunc) *GroupMembershipGroupBy { + gmgb.fns = append(gmgb.fns, fns...) + return gmgb +} + +// Scan applies the selector query and scans the result into the given value. +func (gmgb *GroupMembershipGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, gmgb.build.ctx, ent.OpQueryGroupBy) + if err := gmgb.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*GroupMembershipQuery, *GroupMembershipGroupBy](ctx, gmgb.build, gmgb, gmgb.build.inters, v) +} + +func (gmgb *GroupMembershipGroupBy) sqlScan(ctx context.Context, root *GroupMembershipQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(gmgb.fns)) + for _, fn := range gmgb.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*gmgb.flds)+len(gmgb.fns)) + for _, f := range *gmgb.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*gmgb.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := gmgb.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// GroupMembershipSelect is the builder for selecting fields of GroupMembership entities. +type GroupMembershipSelect struct { + *GroupMembershipQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (gms *GroupMembershipSelect) Aggregate(fns ...AggregateFunc) *GroupMembershipSelect { + gms.fns = append(gms.fns, fns...) + return gms +} + +// Scan applies the selector query and scans the result into the given value. +func (gms *GroupMembershipSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, gms.ctx, ent.OpQuerySelect) + if err := gms.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*GroupMembershipQuery, *GroupMembershipSelect](ctx, gms.GroupMembershipQuery, gms, gms.inters, v) +} + +func (gms *GroupMembershipSelect) sqlScan(ctx context.Context, root *GroupMembershipQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(gms.fns)) + for _, fn := range gms.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*gms.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := gms.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// Modify adds a query modifier for attaching custom logic to queries. +func (gms *GroupMembershipSelect) Modify(modifiers ...func(s *sql.Selector)) *GroupMembershipSelect { + gms.modifiers = append(gms.modifiers, modifiers...) + return gms +} diff --git a/app/controlplane/pkg/data/ent/groupmembership_update.go b/app/controlplane/pkg/data/ent/groupmembership_update.go new file mode 100644 index 000000000..368939ddc --- /dev/null +++ b/app/controlplane/pkg/data/ent/groupmembership_update.go @@ -0,0 +1,559 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" + "github.com/google/uuid" +) + +// GroupMembershipUpdate is the builder for updating GroupMembership entities. +type GroupMembershipUpdate struct { + config + hooks []Hook + mutation *GroupMembershipMutation + modifiers []func(*sql.UpdateBuilder) +} + +// Where appends a list predicates to the GroupMembershipUpdate builder. +func (gmu *GroupMembershipUpdate) Where(ps ...predicate.GroupMembership) *GroupMembershipUpdate { + gmu.mutation.Where(ps...) + return gmu +} + +// SetGroupID sets the "group_id" field. +func (gmu *GroupMembershipUpdate) SetGroupID(u uuid.UUID) *GroupMembershipUpdate { + gmu.mutation.SetGroupID(u) + return gmu +} + +// SetNillableGroupID sets the "group_id" field if the given value is not nil. +func (gmu *GroupMembershipUpdate) SetNillableGroupID(u *uuid.UUID) *GroupMembershipUpdate { + if u != nil { + gmu.SetGroupID(*u) + } + return gmu +} + +// SetUserID sets the "user_id" field. +func (gmu *GroupMembershipUpdate) SetUserID(u uuid.UUID) *GroupMembershipUpdate { + gmu.mutation.SetUserID(u) + return gmu +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (gmu *GroupMembershipUpdate) SetNillableUserID(u *uuid.UUID) *GroupMembershipUpdate { + if u != nil { + gmu.SetUserID(*u) + } + return gmu +} + +// SetMaintainer sets the "maintainer" field. +func (gmu *GroupMembershipUpdate) SetMaintainer(b bool) *GroupMembershipUpdate { + gmu.mutation.SetMaintainer(b) + return gmu +} + +// SetNillableMaintainer sets the "maintainer" field if the given value is not nil. +func (gmu *GroupMembershipUpdate) SetNillableMaintainer(b *bool) *GroupMembershipUpdate { + if b != nil { + gmu.SetMaintainer(*b) + } + return gmu +} + +// SetUpdatedAt sets the "updated_at" field. +func (gmu *GroupMembershipUpdate) SetUpdatedAt(t time.Time) *GroupMembershipUpdate { + gmu.mutation.SetUpdatedAt(t) + return gmu +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (gmu *GroupMembershipUpdate) SetNillableUpdatedAt(t *time.Time) *GroupMembershipUpdate { + if t != nil { + gmu.SetUpdatedAt(*t) + } + return gmu +} + +// SetDeletedAt sets the "deleted_at" field. +func (gmu *GroupMembershipUpdate) SetDeletedAt(t time.Time) *GroupMembershipUpdate { + gmu.mutation.SetDeletedAt(t) + return gmu +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (gmu *GroupMembershipUpdate) SetNillableDeletedAt(t *time.Time) *GroupMembershipUpdate { + if t != nil { + gmu.SetDeletedAt(*t) + } + return gmu +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (gmu *GroupMembershipUpdate) ClearDeletedAt() *GroupMembershipUpdate { + gmu.mutation.ClearDeletedAt() + return gmu +} + +// SetGroup sets the "group" edge to the Group entity. +func (gmu *GroupMembershipUpdate) SetGroup(g *Group) *GroupMembershipUpdate { + return gmu.SetGroupID(g.ID) +} + +// SetUser sets the "user" edge to the User entity. +func (gmu *GroupMembershipUpdate) SetUser(u *User) *GroupMembershipUpdate { + return gmu.SetUserID(u.ID) +} + +// Mutation returns the GroupMembershipMutation object of the builder. +func (gmu *GroupMembershipUpdate) Mutation() *GroupMembershipMutation { + return gmu.mutation +} + +// ClearGroup clears the "group" edge to the Group entity. +func (gmu *GroupMembershipUpdate) ClearGroup() *GroupMembershipUpdate { + gmu.mutation.ClearGroup() + return gmu +} + +// ClearUser clears the "user" edge to the User entity. +func (gmu *GroupMembershipUpdate) ClearUser() *GroupMembershipUpdate { + gmu.mutation.ClearUser() + return gmu +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (gmu *GroupMembershipUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, gmu.sqlSave, gmu.mutation, gmu.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (gmu *GroupMembershipUpdate) SaveX(ctx context.Context) int { + affected, err := gmu.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (gmu *GroupMembershipUpdate) Exec(ctx context.Context) error { + _, err := gmu.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (gmu *GroupMembershipUpdate) ExecX(ctx context.Context) { + if err := gmu.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (gmu *GroupMembershipUpdate) check() error { + if gmu.mutation.GroupCleared() && len(gmu.mutation.GroupIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "GroupMembership.group"`) + } + if gmu.mutation.UserCleared() && len(gmu.mutation.UserIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "GroupMembership.user"`) + } + return nil +} + +// Modify adds a statement modifier for attaching custom logic to the UPDATE statement. +func (gmu *GroupMembershipUpdate) Modify(modifiers ...func(u *sql.UpdateBuilder)) *GroupMembershipUpdate { + gmu.modifiers = append(gmu.modifiers, modifiers...) + return gmu +} + +func (gmu *GroupMembershipUpdate) sqlSave(ctx context.Context) (n int, err error) { + if err := gmu.check(); err != nil { + return n, err + } + _spec := sqlgraph.NewUpdateSpec(groupmembership.Table, groupmembership.Columns, sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID)) + if ps := gmu.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := gmu.mutation.Maintainer(); ok { + _spec.SetField(groupmembership.FieldMaintainer, field.TypeBool, value) + } + if value, ok := gmu.mutation.UpdatedAt(); ok { + _spec.SetField(groupmembership.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := gmu.mutation.DeletedAt(); ok { + _spec.SetField(groupmembership.FieldDeletedAt, field.TypeTime, value) + } + if gmu.mutation.DeletedAtCleared() { + _spec.ClearField(groupmembership.FieldDeletedAt, field.TypeTime) + } + if gmu.mutation.GroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: groupmembership.GroupTable, + Columns: []string{groupmembership.GroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gmu.mutation.GroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: groupmembership.GroupTable, + Columns: []string{groupmembership.GroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if gmu.mutation.UserCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: groupmembership.UserTable, + Columns: []string{groupmembership.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gmu.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: groupmembership.UserTable, + Columns: []string{groupmembership.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _spec.AddModifiers(gmu.modifiers...) + if n, err = sqlgraph.UpdateNodes(ctx, gmu.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{groupmembership.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + gmu.mutation.done = true + return n, nil +} + +// GroupMembershipUpdateOne is the builder for updating a single GroupMembership entity. +type GroupMembershipUpdateOne struct { + config + fields []string + hooks []Hook + mutation *GroupMembershipMutation + modifiers []func(*sql.UpdateBuilder) +} + +// SetGroupID sets the "group_id" field. +func (gmuo *GroupMembershipUpdateOne) SetGroupID(u uuid.UUID) *GroupMembershipUpdateOne { + gmuo.mutation.SetGroupID(u) + return gmuo +} + +// SetNillableGroupID sets the "group_id" field if the given value is not nil. +func (gmuo *GroupMembershipUpdateOne) SetNillableGroupID(u *uuid.UUID) *GroupMembershipUpdateOne { + if u != nil { + gmuo.SetGroupID(*u) + } + return gmuo +} + +// SetUserID sets the "user_id" field. +func (gmuo *GroupMembershipUpdateOne) SetUserID(u uuid.UUID) *GroupMembershipUpdateOne { + gmuo.mutation.SetUserID(u) + return gmuo +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (gmuo *GroupMembershipUpdateOne) SetNillableUserID(u *uuid.UUID) *GroupMembershipUpdateOne { + if u != nil { + gmuo.SetUserID(*u) + } + return gmuo +} + +// SetMaintainer sets the "maintainer" field. +func (gmuo *GroupMembershipUpdateOne) SetMaintainer(b bool) *GroupMembershipUpdateOne { + gmuo.mutation.SetMaintainer(b) + return gmuo +} + +// SetNillableMaintainer sets the "maintainer" field if the given value is not nil. +func (gmuo *GroupMembershipUpdateOne) SetNillableMaintainer(b *bool) *GroupMembershipUpdateOne { + if b != nil { + gmuo.SetMaintainer(*b) + } + return gmuo +} + +// SetUpdatedAt sets the "updated_at" field. +func (gmuo *GroupMembershipUpdateOne) SetUpdatedAt(t time.Time) *GroupMembershipUpdateOne { + gmuo.mutation.SetUpdatedAt(t) + return gmuo +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (gmuo *GroupMembershipUpdateOne) SetNillableUpdatedAt(t *time.Time) *GroupMembershipUpdateOne { + if t != nil { + gmuo.SetUpdatedAt(*t) + } + return gmuo +} + +// SetDeletedAt sets the "deleted_at" field. +func (gmuo *GroupMembershipUpdateOne) SetDeletedAt(t time.Time) *GroupMembershipUpdateOne { + gmuo.mutation.SetDeletedAt(t) + return gmuo +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (gmuo *GroupMembershipUpdateOne) SetNillableDeletedAt(t *time.Time) *GroupMembershipUpdateOne { + if t != nil { + gmuo.SetDeletedAt(*t) + } + return gmuo +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (gmuo *GroupMembershipUpdateOne) ClearDeletedAt() *GroupMembershipUpdateOne { + gmuo.mutation.ClearDeletedAt() + return gmuo +} + +// SetGroup sets the "group" edge to the Group entity. +func (gmuo *GroupMembershipUpdateOne) SetGroup(g *Group) *GroupMembershipUpdateOne { + return gmuo.SetGroupID(g.ID) +} + +// SetUser sets the "user" edge to the User entity. +func (gmuo *GroupMembershipUpdateOne) SetUser(u *User) *GroupMembershipUpdateOne { + return gmuo.SetUserID(u.ID) +} + +// Mutation returns the GroupMembershipMutation object of the builder. +func (gmuo *GroupMembershipUpdateOne) Mutation() *GroupMembershipMutation { + return gmuo.mutation +} + +// ClearGroup clears the "group" edge to the Group entity. +func (gmuo *GroupMembershipUpdateOne) ClearGroup() *GroupMembershipUpdateOne { + gmuo.mutation.ClearGroup() + return gmuo +} + +// ClearUser clears the "user" edge to the User entity. +func (gmuo *GroupMembershipUpdateOne) ClearUser() *GroupMembershipUpdateOne { + gmuo.mutation.ClearUser() + return gmuo +} + +// Where appends a list predicates to the GroupMembershipUpdate builder. +func (gmuo *GroupMembershipUpdateOne) Where(ps ...predicate.GroupMembership) *GroupMembershipUpdateOne { + gmuo.mutation.Where(ps...) + return gmuo +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (gmuo *GroupMembershipUpdateOne) Select(field string, fields ...string) *GroupMembershipUpdateOne { + gmuo.fields = append([]string{field}, fields...) + return gmuo +} + +// Save executes the query and returns the updated GroupMembership entity. +func (gmuo *GroupMembershipUpdateOne) Save(ctx context.Context) (*GroupMembership, error) { + return withHooks(ctx, gmuo.sqlSave, gmuo.mutation, gmuo.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (gmuo *GroupMembershipUpdateOne) SaveX(ctx context.Context) *GroupMembership { + node, err := gmuo.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (gmuo *GroupMembershipUpdateOne) Exec(ctx context.Context) error { + _, err := gmuo.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (gmuo *GroupMembershipUpdateOne) ExecX(ctx context.Context) { + if err := gmuo.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (gmuo *GroupMembershipUpdateOne) check() error { + if gmuo.mutation.GroupCleared() && len(gmuo.mutation.GroupIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "GroupMembership.group"`) + } + if gmuo.mutation.UserCleared() && len(gmuo.mutation.UserIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "GroupMembership.user"`) + } + return nil +} + +// Modify adds a statement modifier for attaching custom logic to the UPDATE statement. +func (gmuo *GroupMembershipUpdateOne) Modify(modifiers ...func(u *sql.UpdateBuilder)) *GroupMembershipUpdateOne { + gmuo.modifiers = append(gmuo.modifiers, modifiers...) + return gmuo +} + +func (gmuo *GroupMembershipUpdateOne) sqlSave(ctx context.Context) (_node *GroupMembership, err error) { + if err := gmuo.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(groupmembership.Table, groupmembership.Columns, sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID)) + id, ok := gmuo.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "GroupMembership.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := gmuo.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, groupmembership.FieldID) + for _, f := range fields { + if !groupmembership.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != groupmembership.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := gmuo.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := gmuo.mutation.Maintainer(); ok { + _spec.SetField(groupmembership.FieldMaintainer, field.TypeBool, value) + } + if value, ok := gmuo.mutation.UpdatedAt(); ok { + _spec.SetField(groupmembership.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := gmuo.mutation.DeletedAt(); ok { + _spec.SetField(groupmembership.FieldDeletedAt, field.TypeTime, value) + } + if gmuo.mutation.DeletedAtCleared() { + _spec.ClearField(groupmembership.FieldDeletedAt, field.TypeTime) + } + if gmuo.mutation.GroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: groupmembership.GroupTable, + Columns: []string{groupmembership.GroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gmuo.mutation.GroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: groupmembership.GroupTable, + Columns: []string{groupmembership.GroupColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if gmuo.mutation.UserCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: groupmembership.UserTable, + Columns: []string{groupmembership.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := gmuo.mutation.UserIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: groupmembership.UserTable, + Columns: []string{groupmembership.UserColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _spec.AddModifiers(gmuo.modifiers...) + _node = &GroupMembership{config: gmuo.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, gmuo.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{groupmembership.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + gmuo.mutation.done = true + return _node, nil +} diff --git a/app/controlplane/pkg/data/ent/hook/hook.go b/app/controlplane/pkg/data/ent/hook/hook.go index bfd43098c..0015ac265 100644 --- a/app/controlplane/pkg/data/ent/hook/hook.go +++ b/app/controlplane/pkg/data/ent/hook/hook.go @@ -57,6 +57,30 @@ func (f CASMappingFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.CASMappingMutation", m) } +// The GroupFunc type is an adapter to allow the use of ordinary +// function as Group mutator. +type GroupFunc func(context.Context, *ent.GroupMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f GroupFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.GroupMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.GroupMutation", m) +} + +// The GroupMembershipFunc type is an adapter to allow the use of ordinary +// function as GroupMembership mutator. +type GroupMembershipFunc func(context.Context, *ent.GroupMembershipMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f GroupMembershipFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.GroupMembershipMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.GroupMembershipMutation", m) +} + // The IntegrationFunc type is an adapter to allow the use of ordinary // function as Integration mutator. type IntegrationFunc func(context.Context, *ent.IntegrationMutation) (ent.Value, error) diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/20250626061546.sql b/app/controlplane/pkg/data/ent/migrate/migrations/20250626061546.sql new file mode 100644 index 000000000..636a2479e --- /dev/null +++ b/app/controlplane/pkg/data/ent/migrate/migrations/20250626061546.sql @@ -0,0 +1,8 @@ +-- Create "groups" table +CREATE TABLE "groups" ("id" uuid NOT NULL, "name" character varying NOT NULL, "description" character varying NULL, "created_at" timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted_at" timestamptz NULL, "organization_id" uuid NOT NULL, PRIMARY KEY ("id"), CONSTRAINT "groups_organizations_groups" FOREIGN KEY ("organization_id") REFERENCES "organizations" ("id") ON UPDATE NO ACTION ON DELETE CASCADE); +-- Create index "group_name_organization_id" to table: "groups" +CREATE UNIQUE INDEX "group_name_organization_id" ON "groups" ("name", "organization_id") WHERE (deleted_at IS NULL); +-- Create "group_memberships" table +CREATE TABLE "group_memberships" ("id" uuid NOT NULL, "maintainer" boolean NOT NULL DEFAULT false, "created_at" timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted_at" timestamptz NULL, "group_id" uuid NOT NULL, "user_id" uuid NOT NULL, PRIMARY KEY ("id"), CONSTRAINT "group_memberships_groups_group" FOREIGN KEY ("group_id") REFERENCES "groups" ("id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "group_memberships_users_user" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON UPDATE NO ACTION ON DELETE CASCADE); +-- Create index "groupmembership_group_id_user_id" to table: "group_memberships" +CREATE UNIQUE INDEX "groupmembership_group_id_user_id" ON "group_memberships" ("group_id", "user_id"); diff --git a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum index 3045a7375..41caeab51 100644 --- a/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum +++ b/app/controlplane/pkg/data/ent/migrate/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:h/gZpSCHr10MO/31l3/0hc+a9Gq7JOAN1HBdLSYujZ4= +h1:vK06zG9gBHFaCg/1y+KI+CeDOp/rjLd1pTWjHNRXKfQ= 20230706165452_init-schema.sql h1:VvqbNFEQnCvUVyj2iDYVQQxDM0+sSXqocpt/5H64k8M= 20230710111950-cas-backend.sql h1:A8iBuSzZIEbdsv9ipBtscZQuaBp3V5/VMw7eZH6GX+g= 20230712094107-cas-backends-workflow-runs.sql h1:a5rzxpVGyd56nLRSsKrmCFc9sebg65RWzLghKHh5xvI= @@ -88,3 +88,4 @@ h1:h/gZpSCHr10MO/31l3/0hc+a9Gq7JOAN1HBdLSYujZ4= 20250616182009.sql h1:xmLOXknF6GTJP6UgYj6nO6NY0qQ3xfpQ4Awx/KqkSTA= 20250616182058.sql h1:fg5r2AZPj/n9y+FeCRaieUrj0UdFzBooLg7xJsNz8P0= 20250617182716.sql h1:APJGiHfWf95qNV622cc367xde2kEn217BUH+za58vxc= +20250626061546.sql h1:eJSVav8H0qrar65KBkmTPkQTxmNExdo+zmVSpF2Zt5E= diff --git a/app/controlplane/pkg/data/ent/migrate/schema.go b/app/controlplane/pkg/data/ent/migrate/schema.go index 6f84c97f4..b6f14698b 100644 --- a/app/controlplane/pkg/data/ent/migrate/schema.go +++ b/app/controlplane/pkg/data/ent/migrate/schema.go @@ -151,6 +151,77 @@ var ( }, }, } + // GroupsColumns holds the columns for the "groups" table. + GroupsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID, Unique: true}, + {Name: "name", Type: field.TypeString}, + {Name: "description", Type: field.TypeString, Nullable: true}, + {Name: "created_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, + {Name: "updated_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, + {Name: "deleted_at", Type: field.TypeTime, Nullable: true}, + {Name: "organization_id", Type: field.TypeUUID}, + } + // GroupsTable holds the schema information for the "groups" table. + GroupsTable = &schema.Table{ + Name: "groups", + Columns: GroupsColumns, + PrimaryKey: []*schema.Column{GroupsColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "groups_organizations_groups", + Columns: []*schema.Column{GroupsColumns[6]}, + RefColumns: []*schema.Column{OrganizationsColumns[0]}, + OnDelete: schema.Cascade, + }, + }, + Indexes: []*schema.Index{ + { + Name: "group_name_organization_id", + Unique: true, + Columns: []*schema.Column{GroupsColumns[1], GroupsColumns[6]}, + Annotation: &entsql.IndexAnnotation{ + Where: "deleted_at IS NULL", + }, + }, + }, + } + // GroupMembershipsColumns holds the columns for the "group_memberships" table. + GroupMembershipsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID, Unique: true}, + {Name: "maintainer", Type: field.TypeBool, Default: false}, + {Name: "created_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, + {Name: "updated_at", Type: field.TypeTime, Default: "CURRENT_TIMESTAMP"}, + {Name: "deleted_at", Type: field.TypeTime, Nullable: true}, + {Name: "group_id", Type: field.TypeUUID}, + {Name: "user_id", Type: field.TypeUUID}, + } + // GroupMembershipsTable holds the schema information for the "group_memberships" table. + GroupMembershipsTable = &schema.Table{ + Name: "group_memberships", + Columns: GroupMembershipsColumns, + PrimaryKey: []*schema.Column{GroupMembershipsColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "group_memberships_groups_group", + Columns: []*schema.Column{GroupMembershipsColumns[5]}, + RefColumns: []*schema.Column{GroupsColumns[0]}, + OnDelete: schema.Cascade, + }, + { + Symbol: "group_memberships_users_user", + Columns: []*schema.Column{GroupMembershipsColumns[6]}, + RefColumns: []*schema.Column{UsersColumns[0]}, + OnDelete: schema.Cascade, + }, + }, + Indexes: []*schema.Index{ + { + Name: "groupmembership_group_id_user_id", + Unique: true, + Columns: []*schema.Column{GroupMembershipsColumns[5], GroupMembershipsColumns[6]}, + }, + }, + } // IntegrationsColumns holds the columns for the "integrations" table. IntegrationsColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID, Unique: true}, @@ -786,6 +857,8 @@ var ( AttestationsTable, CasBackendsTable, CasMappingsTable, + GroupsTable, + GroupMembershipsTable, IntegrationsTable, IntegrationAttachmentsTable, MembershipsTable, @@ -812,6 +885,9 @@ func init() { CasBackendsTable.ForeignKeys[0].RefTable = OrganizationsTable CasMappingsTable.ForeignKeys[0].RefTable = CasBackendsTable CasMappingsTable.ForeignKeys[1].RefTable = OrganizationsTable + GroupsTable.ForeignKeys[0].RefTable = OrganizationsTable + GroupMembershipsTable.ForeignKeys[0].RefTable = GroupsTable + GroupMembershipsTable.ForeignKeys[1].RefTable = UsersTable IntegrationsTable.ForeignKeys[0].RefTable = OrganizationsTable IntegrationAttachmentsTable.ForeignKeys[0].RefTable = IntegrationsTable IntegrationAttachmentsTable.ForeignKeys[1].RefTable = WorkflowsTable diff --git a/app/controlplane/pkg/data/ent/mutation.go b/app/controlplane/pkg/data/ent/mutation.go index b2d88d479..53505e6d6 100644 --- a/app/controlplane/pkg/data/ent/mutation.go +++ b/app/controlplane/pkg/data/ent/mutation.go @@ -17,6 +17,8 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/attestation" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casbackend" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casmapping" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integration" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integrationattachment" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/membership" @@ -50,6 +52,8 @@ const ( TypeAttestation = "Attestation" TypeCASBackend = "CASBackend" TypeCASMapping = "CASMapping" + TypeGroup = "Group" + TypeGroupMembership = "GroupMembership" TypeIntegration = "Integration" TypeIntegrationAttachment = "IntegrationAttachment" TypeMembership = "Membership" @@ -3060,6 +3064,1595 @@ func (m *CASMappingMutation) ResetEdge(name string) error { return fmt.Errorf("unknown CASMapping edge %s", name) } +// GroupMutation represents an operation that mutates the Group nodes in the graph. +type GroupMutation struct { + config + op Op + typ string + id *uuid.UUID + name *string + description *string + created_at *time.Time + updated_at *time.Time + deleted_at *time.Time + clearedFields map[string]struct{} + members map[uuid.UUID]struct{} + removedmembers map[uuid.UUID]struct{} + clearedmembers bool + organization *uuid.UUID + clearedorganization bool + group_users map[uuid.UUID]struct{} + removedgroup_users map[uuid.UUID]struct{} + clearedgroup_users bool + done bool + oldValue func(context.Context) (*Group, error) + predicates []predicate.Group +} + +var _ ent.Mutation = (*GroupMutation)(nil) + +// groupOption allows management of the mutation configuration using functional options. +type groupOption func(*GroupMutation) + +// newGroupMutation creates new mutation for the Group entity. +func newGroupMutation(c config, op Op, opts ...groupOption) *GroupMutation { + m := &GroupMutation{ + config: c, + op: op, + typ: TypeGroup, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withGroupID sets the ID field of the mutation. +func withGroupID(id uuid.UUID) groupOption { + return func(m *GroupMutation) { + var ( + err error + once sync.Once + value *Group + ) + m.oldValue = func(ctx context.Context) (*Group, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Group.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withGroup sets the old Group of the mutation. +func withGroup(node *Group) groupOption { + return func(m *GroupMutation) { + m.oldValue = func(context.Context) (*Group, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m GroupMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m GroupMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of Group entities. +func (m *GroupMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *GroupMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *GroupMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Group.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetName sets the "name" field. +func (m *GroupMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *GroupMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *GroupMutation) ResetName() { + m.name = nil +} + +// SetDescription sets the "description" field. +func (m *GroupMutation) SetDescription(s string) { + m.description = &s +} + +// Description returns the value of the "description" field in the mutation. +func (m *GroupMutation) Description() (r string, exists bool) { + v := m.description + if v == nil { + return + } + return *v, true +} + +// OldDescription returns the old "description" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldDescription(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDescription is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDescription requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDescription: %w", err) + } + return oldValue.Description, nil +} + +// ClearDescription clears the value of the "description" field. +func (m *GroupMutation) ClearDescription() { + m.description = nil + m.clearedFields[group.FieldDescription] = struct{}{} +} + +// DescriptionCleared returns if the "description" field was cleared in this mutation. +func (m *GroupMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[group.FieldDescription] + return ok +} + +// ResetDescription resets all changes to the "description" field. +func (m *GroupMutation) ResetDescription() { + m.description = nil + delete(m.clearedFields, group.FieldDescription) +} + +// SetOrganizationID sets the "organization_id" field. +func (m *GroupMutation) SetOrganizationID(u uuid.UUID) { + m.organization = &u +} + +// OrganizationID returns the value of the "organization_id" field in the mutation. +func (m *GroupMutation) OrganizationID() (r uuid.UUID, exists bool) { + v := m.organization + if v == nil { + return + } + return *v, true +} + +// OldOrganizationID returns the old "organization_id" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldOrganizationID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldOrganizationID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldOrganizationID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldOrganizationID: %w", err) + } + return oldValue.OrganizationID, nil +} + +// ResetOrganizationID resets all changes to the "organization_id" field. +func (m *GroupMutation) ResetOrganizationID() { + m.organization = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *GroupMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *GroupMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *GroupMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *GroupMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *GroupMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *GroupMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetDeletedAt sets the "deleted_at" field. +func (m *GroupMutation) SetDeletedAt(t time.Time) { + m.deleted_at = &t +} + +// DeletedAt returns the value of the "deleted_at" field in the mutation. +func (m *GroupMutation) DeletedAt() (r time.Time, exists bool) { + v := m.deleted_at + if v == nil { + return + } + return *v, true +} + +// OldDeletedAt returns the old "deleted_at" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldDeletedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDeletedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDeletedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDeletedAt: %w", err) + } + return oldValue.DeletedAt, nil +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (m *GroupMutation) ClearDeletedAt() { + m.deleted_at = nil + m.clearedFields[group.FieldDeletedAt] = struct{}{} +} + +// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation. +func (m *GroupMutation) DeletedAtCleared() bool { + _, ok := m.clearedFields[group.FieldDeletedAt] + return ok +} + +// ResetDeletedAt resets all changes to the "deleted_at" field. +func (m *GroupMutation) ResetDeletedAt() { + m.deleted_at = nil + delete(m.clearedFields, group.FieldDeletedAt) +} + +// AddMemberIDs adds the "members" edge to the User entity by ids. +func (m *GroupMutation) AddMemberIDs(ids ...uuid.UUID) { + if m.members == nil { + m.members = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.members[ids[i]] = struct{}{} + } +} + +// ClearMembers clears the "members" edge to the User entity. +func (m *GroupMutation) ClearMembers() { + m.clearedmembers = true +} + +// MembersCleared reports if the "members" edge to the User entity was cleared. +func (m *GroupMutation) MembersCleared() bool { + return m.clearedmembers +} + +// RemoveMemberIDs removes the "members" edge to the User entity by IDs. +func (m *GroupMutation) RemoveMemberIDs(ids ...uuid.UUID) { + if m.removedmembers == nil { + m.removedmembers = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.members, ids[i]) + m.removedmembers[ids[i]] = struct{}{} + } +} + +// RemovedMembers returns the removed IDs of the "members" edge to the User entity. +func (m *GroupMutation) RemovedMembersIDs() (ids []uuid.UUID) { + for id := range m.removedmembers { + ids = append(ids, id) + } + return +} + +// MembersIDs returns the "members" edge IDs in the mutation. +func (m *GroupMutation) MembersIDs() (ids []uuid.UUID) { + for id := range m.members { + ids = append(ids, id) + } + return +} + +// ResetMembers resets all changes to the "members" edge. +func (m *GroupMutation) ResetMembers() { + m.members = nil + m.clearedmembers = false + m.removedmembers = nil +} + +// ClearOrganization clears the "organization" edge to the Organization entity. +func (m *GroupMutation) ClearOrganization() { + m.clearedorganization = true + m.clearedFields[group.FieldOrganizationID] = struct{}{} +} + +// OrganizationCleared reports if the "organization" edge to the Organization entity was cleared. +func (m *GroupMutation) OrganizationCleared() bool { + return m.clearedorganization +} + +// OrganizationIDs returns the "organization" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// OrganizationID instead. It exists only for internal usage by the builders. +func (m *GroupMutation) OrganizationIDs() (ids []uuid.UUID) { + if id := m.organization; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetOrganization resets all changes to the "organization" edge. +func (m *GroupMutation) ResetOrganization() { + m.organization = nil + m.clearedorganization = false +} + +// AddGroupUserIDs adds the "group_users" edge to the GroupMembership entity by ids. +func (m *GroupMutation) AddGroupUserIDs(ids ...uuid.UUID) { + if m.group_users == nil { + m.group_users = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.group_users[ids[i]] = struct{}{} + } +} + +// ClearGroupUsers clears the "group_users" edge to the GroupMembership entity. +func (m *GroupMutation) ClearGroupUsers() { + m.clearedgroup_users = true +} + +// GroupUsersCleared reports if the "group_users" edge to the GroupMembership entity was cleared. +func (m *GroupMutation) GroupUsersCleared() bool { + return m.clearedgroup_users +} + +// RemoveGroupUserIDs removes the "group_users" edge to the GroupMembership entity by IDs. +func (m *GroupMutation) RemoveGroupUserIDs(ids ...uuid.UUID) { + if m.removedgroup_users == nil { + m.removedgroup_users = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.group_users, ids[i]) + m.removedgroup_users[ids[i]] = struct{}{} + } +} + +// RemovedGroupUsers returns the removed IDs of the "group_users" edge to the GroupMembership entity. +func (m *GroupMutation) RemovedGroupUsersIDs() (ids []uuid.UUID) { + for id := range m.removedgroup_users { + ids = append(ids, id) + } + return +} + +// GroupUsersIDs returns the "group_users" edge IDs in the mutation. +func (m *GroupMutation) GroupUsersIDs() (ids []uuid.UUID) { + for id := range m.group_users { + ids = append(ids, id) + } + return +} + +// ResetGroupUsers resets all changes to the "group_users" edge. +func (m *GroupMutation) ResetGroupUsers() { + m.group_users = nil + m.clearedgroup_users = false + m.removedgroup_users = nil +} + +// Where appends a list predicates to the GroupMutation builder. +func (m *GroupMutation) Where(ps ...predicate.Group) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the GroupMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *GroupMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Group, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *GroupMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *GroupMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Group). +func (m *GroupMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *GroupMutation) Fields() []string { + fields := make([]string, 0, 6) + if m.name != nil { + fields = append(fields, group.FieldName) + } + if m.description != nil { + fields = append(fields, group.FieldDescription) + } + if m.organization != nil { + fields = append(fields, group.FieldOrganizationID) + } + if m.created_at != nil { + fields = append(fields, group.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, group.FieldUpdatedAt) + } + if m.deleted_at != nil { + fields = append(fields, group.FieldDeletedAt) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *GroupMutation) Field(name string) (ent.Value, bool) { + switch name { + case group.FieldName: + return m.Name() + case group.FieldDescription: + return m.Description() + case group.FieldOrganizationID: + return m.OrganizationID() + case group.FieldCreatedAt: + return m.CreatedAt() + case group.FieldUpdatedAt: + return m.UpdatedAt() + case group.FieldDeletedAt: + return m.DeletedAt() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *GroupMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case group.FieldName: + return m.OldName(ctx) + case group.FieldDescription: + return m.OldDescription(ctx) + case group.FieldOrganizationID: + return m.OldOrganizationID(ctx) + case group.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case group.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case group.FieldDeletedAt: + return m.OldDeletedAt(ctx) + } + return nil, fmt.Errorf("unknown Group field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GroupMutation) SetField(name string, value ent.Value) error { + switch name { + case group.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case group.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case group.FieldOrganizationID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetOrganizationID(v) + return nil + case group.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case group.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case group.FieldDeletedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDeletedAt(v) + return nil + } + return fmt.Errorf("unknown Group field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *GroupMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *GroupMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GroupMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown Group numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *GroupMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(group.FieldDescription) { + fields = append(fields, group.FieldDescription) + } + if m.FieldCleared(group.FieldDeletedAt) { + fields = append(fields, group.FieldDeletedAt) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *GroupMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *GroupMutation) ClearField(name string) error { + switch name { + case group.FieldDescription: + m.ClearDescription() + return nil + case group.FieldDeletedAt: + m.ClearDeletedAt() + return nil + } + return fmt.Errorf("unknown Group nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *GroupMutation) ResetField(name string) error { + switch name { + case group.FieldName: + m.ResetName() + return nil + case group.FieldDescription: + m.ResetDescription() + return nil + case group.FieldOrganizationID: + m.ResetOrganizationID() + return nil + case group.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case group.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case group.FieldDeletedAt: + m.ResetDeletedAt() + return nil + } + return fmt.Errorf("unknown Group field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *GroupMutation) AddedEdges() []string { + edges := make([]string, 0, 3) + if m.members != nil { + edges = append(edges, group.EdgeMembers) + } + if m.organization != nil { + edges = append(edges, group.EdgeOrganization) + } + if m.group_users != nil { + edges = append(edges, group.EdgeGroupUsers) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *GroupMutation) AddedIDs(name string) []ent.Value { + switch name { + case group.EdgeMembers: + ids := make([]ent.Value, 0, len(m.members)) + for id := range m.members { + ids = append(ids, id) + } + return ids + case group.EdgeOrganization: + if id := m.organization; id != nil { + return []ent.Value{*id} + } + case group.EdgeGroupUsers: + ids := make([]ent.Value, 0, len(m.group_users)) + for id := range m.group_users { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *GroupMutation) RemovedEdges() []string { + edges := make([]string, 0, 3) + if m.removedmembers != nil { + edges = append(edges, group.EdgeMembers) + } + if m.removedgroup_users != nil { + edges = append(edges, group.EdgeGroupUsers) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *GroupMutation) RemovedIDs(name string) []ent.Value { + switch name { + case group.EdgeMembers: + ids := make([]ent.Value, 0, len(m.removedmembers)) + for id := range m.removedmembers { + ids = append(ids, id) + } + return ids + case group.EdgeGroupUsers: + ids := make([]ent.Value, 0, len(m.removedgroup_users)) + for id := range m.removedgroup_users { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *GroupMutation) ClearedEdges() []string { + edges := make([]string, 0, 3) + if m.clearedmembers { + edges = append(edges, group.EdgeMembers) + } + if m.clearedorganization { + edges = append(edges, group.EdgeOrganization) + } + if m.clearedgroup_users { + edges = append(edges, group.EdgeGroupUsers) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *GroupMutation) EdgeCleared(name string) bool { + switch name { + case group.EdgeMembers: + return m.clearedmembers + case group.EdgeOrganization: + return m.clearedorganization + case group.EdgeGroupUsers: + return m.clearedgroup_users + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *GroupMutation) ClearEdge(name string) error { + switch name { + case group.EdgeOrganization: + m.ClearOrganization() + return nil + } + return fmt.Errorf("unknown Group unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *GroupMutation) ResetEdge(name string) error { + switch name { + case group.EdgeMembers: + m.ResetMembers() + return nil + case group.EdgeOrganization: + m.ResetOrganization() + return nil + case group.EdgeGroupUsers: + m.ResetGroupUsers() + return nil + } + return fmt.Errorf("unknown Group edge %s", name) +} + +// GroupMembershipMutation represents an operation that mutates the GroupMembership nodes in the graph. +type GroupMembershipMutation struct { + config + op Op + typ string + id *uuid.UUID + maintainer *bool + created_at *time.Time + updated_at *time.Time + deleted_at *time.Time + clearedFields map[string]struct{} + group *uuid.UUID + clearedgroup bool + user *uuid.UUID + cleareduser bool + done bool + oldValue func(context.Context) (*GroupMembership, error) + predicates []predicate.GroupMembership +} + +var _ ent.Mutation = (*GroupMembershipMutation)(nil) + +// groupmembershipOption allows management of the mutation configuration using functional options. +type groupmembershipOption func(*GroupMembershipMutation) + +// newGroupMembershipMutation creates new mutation for the GroupMembership entity. +func newGroupMembershipMutation(c config, op Op, opts ...groupmembershipOption) *GroupMembershipMutation { + m := &GroupMembershipMutation{ + config: c, + op: op, + typ: TypeGroupMembership, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withGroupMembershipID sets the ID field of the mutation. +func withGroupMembershipID(id uuid.UUID) groupmembershipOption { + return func(m *GroupMembershipMutation) { + var ( + err error + once sync.Once + value *GroupMembership + ) + m.oldValue = func(ctx context.Context) (*GroupMembership, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().GroupMembership.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withGroupMembership sets the old GroupMembership of the mutation. +func withGroupMembership(node *GroupMembership) groupmembershipOption { + return func(m *GroupMembershipMutation) { + m.oldValue = func(context.Context) (*GroupMembership, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m GroupMembershipMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m GroupMembershipMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of GroupMembership entities. +func (m *GroupMembershipMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *GroupMembershipMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *GroupMembershipMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().GroupMembership.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetGroupID sets the "group_id" field. +func (m *GroupMembershipMutation) SetGroupID(u uuid.UUID) { + m.group = &u +} + +// GroupID returns the value of the "group_id" field in the mutation. +func (m *GroupMembershipMutation) GroupID() (r uuid.UUID, exists bool) { + v := m.group + if v == nil { + return + } + return *v, true +} + +// OldGroupID returns the old "group_id" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldGroupID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGroupID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGroupID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGroupID: %w", err) + } + return oldValue.GroupID, nil +} + +// ResetGroupID resets all changes to the "group_id" field. +func (m *GroupMembershipMutation) ResetGroupID() { + m.group = nil +} + +// SetUserID sets the "user_id" field. +func (m *GroupMembershipMutation) SetUserID(u uuid.UUID) { + m.user = &u +} + +// UserID returns the value of the "user_id" field in the mutation. +func (m *GroupMembershipMutation) UserID() (r uuid.UUID, exists bool) { + v := m.user + if v == nil { + return + } + return *v, true +} + +// OldUserID returns the old "user_id" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldUserID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUserID: %w", err) + } + return oldValue.UserID, nil +} + +// ResetUserID resets all changes to the "user_id" field. +func (m *GroupMembershipMutation) ResetUserID() { + m.user = nil +} + +// SetMaintainer sets the "maintainer" field. +func (m *GroupMembershipMutation) SetMaintainer(b bool) { + m.maintainer = &b +} + +// Maintainer returns the value of the "maintainer" field in the mutation. +func (m *GroupMembershipMutation) Maintainer() (r bool, exists bool) { + v := m.maintainer + if v == nil { + return + } + return *v, true +} + +// OldMaintainer returns the old "maintainer" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldMaintainer(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMaintainer is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMaintainer requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMaintainer: %w", err) + } + return oldValue.Maintainer, nil +} + +// ResetMaintainer resets all changes to the "maintainer" field. +func (m *GroupMembershipMutation) ResetMaintainer() { + m.maintainer = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *GroupMembershipMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *GroupMembershipMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *GroupMembershipMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *GroupMembershipMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *GroupMembershipMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *GroupMembershipMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetDeletedAt sets the "deleted_at" field. +func (m *GroupMembershipMutation) SetDeletedAt(t time.Time) { + m.deleted_at = &t +} + +// DeletedAt returns the value of the "deleted_at" field in the mutation. +func (m *GroupMembershipMutation) DeletedAt() (r time.Time, exists bool) { + v := m.deleted_at + if v == nil { + return + } + return *v, true +} + +// OldDeletedAt returns the old "deleted_at" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldDeletedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDeletedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDeletedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDeletedAt: %w", err) + } + return oldValue.DeletedAt, nil +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (m *GroupMembershipMutation) ClearDeletedAt() { + m.deleted_at = nil + m.clearedFields[groupmembership.FieldDeletedAt] = struct{}{} +} + +// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation. +func (m *GroupMembershipMutation) DeletedAtCleared() bool { + _, ok := m.clearedFields[groupmembership.FieldDeletedAt] + return ok +} + +// ResetDeletedAt resets all changes to the "deleted_at" field. +func (m *GroupMembershipMutation) ResetDeletedAt() { + m.deleted_at = nil + delete(m.clearedFields, groupmembership.FieldDeletedAt) +} + +// ClearGroup clears the "group" edge to the Group entity. +func (m *GroupMembershipMutation) ClearGroup() { + m.clearedgroup = true + m.clearedFields[groupmembership.FieldGroupID] = struct{}{} +} + +// GroupCleared reports if the "group" edge to the Group entity was cleared. +func (m *GroupMembershipMutation) GroupCleared() bool { + return m.clearedgroup +} + +// GroupIDs returns the "group" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// GroupID instead. It exists only for internal usage by the builders. +func (m *GroupMembershipMutation) GroupIDs() (ids []uuid.UUID) { + if id := m.group; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetGroup resets all changes to the "group" edge. +func (m *GroupMembershipMutation) ResetGroup() { + m.group = nil + m.clearedgroup = false +} + +// ClearUser clears the "user" edge to the User entity. +func (m *GroupMembershipMutation) ClearUser() { + m.cleareduser = true + m.clearedFields[groupmembership.FieldUserID] = struct{}{} +} + +// UserCleared reports if the "user" edge to the User entity was cleared. +func (m *GroupMembershipMutation) UserCleared() bool { + return m.cleareduser +} + +// UserIDs returns the "user" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// UserID instead. It exists only for internal usage by the builders. +func (m *GroupMembershipMutation) UserIDs() (ids []uuid.UUID) { + if id := m.user; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetUser resets all changes to the "user" edge. +func (m *GroupMembershipMutation) ResetUser() { + m.user = nil + m.cleareduser = false +} + +// Where appends a list predicates to the GroupMembershipMutation builder. +func (m *GroupMembershipMutation) Where(ps ...predicate.GroupMembership) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the GroupMembershipMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *GroupMembershipMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.GroupMembership, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *GroupMembershipMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *GroupMembershipMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (GroupMembership). +func (m *GroupMembershipMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *GroupMembershipMutation) Fields() []string { + fields := make([]string, 0, 6) + if m.group != nil { + fields = append(fields, groupmembership.FieldGroupID) + } + if m.user != nil { + fields = append(fields, groupmembership.FieldUserID) + } + if m.maintainer != nil { + fields = append(fields, groupmembership.FieldMaintainer) + } + if m.created_at != nil { + fields = append(fields, groupmembership.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, groupmembership.FieldUpdatedAt) + } + if m.deleted_at != nil { + fields = append(fields, groupmembership.FieldDeletedAt) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *GroupMembershipMutation) Field(name string) (ent.Value, bool) { + switch name { + case groupmembership.FieldGroupID: + return m.GroupID() + case groupmembership.FieldUserID: + return m.UserID() + case groupmembership.FieldMaintainer: + return m.Maintainer() + case groupmembership.FieldCreatedAt: + return m.CreatedAt() + case groupmembership.FieldUpdatedAt: + return m.UpdatedAt() + case groupmembership.FieldDeletedAt: + return m.DeletedAt() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *GroupMembershipMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case groupmembership.FieldGroupID: + return m.OldGroupID(ctx) + case groupmembership.FieldUserID: + return m.OldUserID(ctx) + case groupmembership.FieldMaintainer: + return m.OldMaintainer(ctx) + case groupmembership.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case groupmembership.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case groupmembership.FieldDeletedAt: + return m.OldDeletedAt(ctx) + } + return nil, fmt.Errorf("unknown GroupMembership field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GroupMembershipMutation) SetField(name string, value ent.Value) error { + switch name { + case groupmembership.FieldGroupID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGroupID(v) + return nil + case groupmembership.FieldUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUserID(v) + return nil + case groupmembership.FieldMaintainer: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMaintainer(v) + return nil + case groupmembership.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case groupmembership.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case groupmembership.FieldDeletedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDeletedAt(v) + return nil + } + return fmt.Errorf("unknown GroupMembership field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *GroupMembershipMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *GroupMembershipMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GroupMembershipMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown GroupMembership numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *GroupMembershipMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(groupmembership.FieldDeletedAt) { + fields = append(fields, groupmembership.FieldDeletedAt) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *GroupMembershipMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *GroupMembershipMutation) ClearField(name string) error { + switch name { + case groupmembership.FieldDeletedAt: + m.ClearDeletedAt() + return nil + } + return fmt.Errorf("unknown GroupMembership nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *GroupMembershipMutation) ResetField(name string) error { + switch name { + case groupmembership.FieldGroupID: + m.ResetGroupID() + return nil + case groupmembership.FieldUserID: + m.ResetUserID() + return nil + case groupmembership.FieldMaintainer: + m.ResetMaintainer() + return nil + case groupmembership.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case groupmembership.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case groupmembership.FieldDeletedAt: + m.ResetDeletedAt() + return nil + } + return fmt.Errorf("unknown GroupMembership field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *GroupMembershipMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.group != nil { + edges = append(edges, groupmembership.EdgeGroup) + } + if m.user != nil { + edges = append(edges, groupmembership.EdgeUser) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *GroupMembershipMutation) AddedIDs(name string) []ent.Value { + switch name { + case groupmembership.EdgeGroup: + if id := m.group; id != nil { + return []ent.Value{*id} + } + case groupmembership.EdgeUser: + if id := m.user; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *GroupMembershipMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *GroupMembershipMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *GroupMembershipMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedgroup { + edges = append(edges, groupmembership.EdgeGroup) + } + if m.cleareduser { + edges = append(edges, groupmembership.EdgeUser) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *GroupMembershipMutation) EdgeCleared(name string) bool { + switch name { + case groupmembership.EdgeGroup: + return m.clearedgroup + case groupmembership.EdgeUser: + return m.cleareduser + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *GroupMembershipMutation) ClearEdge(name string) error { + switch name { + case groupmembership.EdgeGroup: + m.ClearGroup() + return nil + case groupmembership.EdgeUser: + m.ClearUser() + return nil + } + return fmt.Errorf("unknown GroupMembership unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *GroupMembershipMutation) ResetEdge(name string) error { + switch name { + case groupmembership.EdgeGroup: + m.ResetGroup() + return nil + case groupmembership.EdgeUser: + m.ResetUser() + return nil + } + return fmt.Errorf("unknown GroupMembership edge %s", name) +} + // IntegrationMutation represents an operation that mutates the Integration nodes in the graph. type IntegrationMutation struct { config @@ -6319,6 +7912,9 @@ type OrganizationMutation struct { projects map[uuid.UUID]struct{} removedprojects map[uuid.UUID]struct{} clearedprojects bool + groups map[uuid.UUID]struct{} + removedgroups map[uuid.UUID]struct{} + clearedgroups bool done bool oldValue func(context.Context) (*Organization, error) predicates []predicate.Organization @@ -6914,6 +8510,60 @@ func (m *OrganizationMutation) ResetProjects() { m.removedprojects = nil } +// AddGroupIDs adds the "groups" edge to the Group entity by ids. +func (m *OrganizationMutation) AddGroupIDs(ids ...uuid.UUID) { + if m.groups == nil { + m.groups = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.groups[ids[i]] = struct{}{} + } +} + +// ClearGroups clears the "groups" edge to the Group entity. +func (m *OrganizationMutation) ClearGroups() { + m.clearedgroups = true +} + +// GroupsCleared reports if the "groups" edge to the Group entity was cleared. +func (m *OrganizationMutation) GroupsCleared() bool { + return m.clearedgroups +} + +// RemoveGroupIDs removes the "groups" edge to the Group entity by IDs. +func (m *OrganizationMutation) RemoveGroupIDs(ids ...uuid.UUID) { + if m.removedgroups == nil { + m.removedgroups = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.groups, ids[i]) + m.removedgroups[ids[i]] = struct{}{} + } +} + +// RemovedGroups returns the removed IDs of the "groups" edge to the Group entity. +func (m *OrganizationMutation) RemovedGroupsIDs() (ids []uuid.UUID) { + for id := range m.removedgroups { + ids = append(ids, id) + } + return +} + +// GroupsIDs returns the "groups" edge IDs in the mutation. +func (m *OrganizationMutation) GroupsIDs() (ids []uuid.UUID) { + for id := range m.groups { + ids = append(ids, id) + } + return +} + +// ResetGroups resets all changes to the "groups" edge. +func (m *OrganizationMutation) ResetGroups() { + m.groups = nil + m.clearedgroups = false + m.removedgroups = nil +} + // Where appends a list predicates to the OrganizationMutation builder. func (m *OrganizationMutation) Where(ps ...predicate.Organization) { m.predicates = append(m.predicates, ps...) @@ -7081,7 +8731,7 @@ func (m *OrganizationMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *OrganizationMutation) AddedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.memberships != nil { edges = append(edges, organization.EdgeMemberships) } @@ -7103,6 +8753,9 @@ func (m *OrganizationMutation) AddedEdges() []string { if m.projects != nil { edges = append(edges, organization.EdgeProjects) } + if m.groups != nil { + edges = append(edges, organization.EdgeGroups) + } return edges } @@ -7152,13 +8805,19 @@ func (m *OrganizationMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case organization.EdgeGroups: + ids := make([]ent.Value, 0, len(m.groups)) + for id := range m.groups { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *OrganizationMutation) RemovedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.removedmemberships != nil { edges = append(edges, organization.EdgeMemberships) } @@ -7180,6 +8839,9 @@ func (m *OrganizationMutation) RemovedEdges() []string { if m.removedprojects != nil { edges = append(edges, organization.EdgeProjects) } + if m.removedgroups != nil { + edges = append(edges, organization.EdgeGroups) + } return edges } @@ -7229,13 +8891,19 @@ func (m *OrganizationMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case organization.EdgeGroups: + ids := make([]ent.Value, 0, len(m.removedgroups)) + for id := range m.removedgroups { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *OrganizationMutation) ClearedEdges() []string { - edges := make([]string, 0, 7) + edges := make([]string, 0, 8) if m.clearedmemberships { edges = append(edges, organization.EdgeMemberships) } @@ -7257,6 +8925,9 @@ func (m *OrganizationMutation) ClearedEdges() []string { if m.clearedprojects { edges = append(edges, organization.EdgeProjects) } + if m.clearedgroups { + edges = append(edges, organization.EdgeGroups) + } return edges } @@ -7278,6 +8949,8 @@ func (m *OrganizationMutation) EdgeCleared(name string) bool { return m.clearedapi_tokens case organization.EdgeProjects: return m.clearedprojects + case organization.EdgeGroups: + return m.clearedgroups } return false } @@ -7315,6 +8988,9 @@ func (m *OrganizationMutation) ResetEdge(name string) error { case organization.EdgeProjects: m.ResetProjects() return nil + case organization.EdgeGroups: + m.ResetGroups() + return nil } return fmt.Errorf("unknown Organization edge %s", name) } @@ -10556,6 +12232,12 @@ type UserMutation struct { memberships map[uuid.UUID]struct{} removedmemberships map[uuid.UUID]struct{} clearedmemberships bool + group map[uuid.UUID]struct{} + removedgroup map[uuid.UUID]struct{} + clearedgroup bool + group_users map[uuid.UUID]struct{} + removedgroup_users map[uuid.UUID]struct{} + clearedgroup_users bool done bool oldValue func(context.Context) (*User, error) predicates []predicate.User @@ -10938,6 +12620,114 @@ func (m *UserMutation) ResetMemberships() { m.removedmemberships = nil } +// AddGroupIDs adds the "group" edge to the Group entity by ids. +func (m *UserMutation) AddGroupIDs(ids ...uuid.UUID) { + if m.group == nil { + m.group = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.group[ids[i]] = struct{}{} + } +} + +// ClearGroup clears the "group" edge to the Group entity. +func (m *UserMutation) ClearGroup() { + m.clearedgroup = true +} + +// GroupCleared reports if the "group" edge to the Group entity was cleared. +func (m *UserMutation) GroupCleared() bool { + return m.clearedgroup +} + +// RemoveGroupIDs removes the "group" edge to the Group entity by IDs. +func (m *UserMutation) RemoveGroupIDs(ids ...uuid.UUID) { + if m.removedgroup == nil { + m.removedgroup = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.group, ids[i]) + m.removedgroup[ids[i]] = struct{}{} + } +} + +// RemovedGroup returns the removed IDs of the "group" edge to the Group entity. +func (m *UserMutation) RemovedGroupIDs() (ids []uuid.UUID) { + for id := range m.removedgroup { + ids = append(ids, id) + } + return +} + +// GroupIDs returns the "group" edge IDs in the mutation. +func (m *UserMutation) GroupIDs() (ids []uuid.UUID) { + for id := range m.group { + ids = append(ids, id) + } + return +} + +// ResetGroup resets all changes to the "group" edge. +func (m *UserMutation) ResetGroup() { + m.group = nil + m.clearedgroup = false + m.removedgroup = nil +} + +// AddGroupUserIDs adds the "group_users" edge to the GroupMembership entity by ids. +func (m *UserMutation) AddGroupUserIDs(ids ...uuid.UUID) { + if m.group_users == nil { + m.group_users = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.group_users[ids[i]] = struct{}{} + } +} + +// ClearGroupUsers clears the "group_users" edge to the GroupMembership entity. +func (m *UserMutation) ClearGroupUsers() { + m.clearedgroup_users = true +} + +// GroupUsersCleared reports if the "group_users" edge to the GroupMembership entity was cleared. +func (m *UserMutation) GroupUsersCleared() bool { + return m.clearedgroup_users +} + +// RemoveGroupUserIDs removes the "group_users" edge to the GroupMembership entity by IDs. +func (m *UserMutation) RemoveGroupUserIDs(ids ...uuid.UUID) { + if m.removedgroup_users == nil { + m.removedgroup_users = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.group_users, ids[i]) + m.removedgroup_users[ids[i]] = struct{}{} + } +} + +// RemovedGroupUsers returns the removed IDs of the "group_users" edge to the GroupMembership entity. +func (m *UserMutation) RemovedGroupUsersIDs() (ids []uuid.UUID) { + for id := range m.removedgroup_users { + ids = append(ids, id) + } + return +} + +// GroupUsersIDs returns the "group_users" edge IDs in the mutation. +func (m *UserMutation) GroupUsersIDs() (ids []uuid.UUID) { + for id := range m.group_users { + ids = append(ids, id) + } + return +} + +// ResetGroupUsers resets all changes to the "group_users" edge. +func (m *UserMutation) ResetGroupUsers() { + m.group_users = nil + m.clearedgroup_users = false + m.removedgroup_users = nil +} + // Where appends a list predicates to the UserMutation builder. func (m *UserMutation) Where(ps ...predicate.User) { m.predicates = append(m.predicates, ps...) @@ -11160,10 +12950,16 @@ func (m *UserMutation) ResetField(name string) error { // AddedEdges returns all edge names that were set/added in this mutation. func (m *UserMutation) AddedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 3) if m.memberships != nil { edges = append(edges, user.EdgeMemberships) } + if m.group != nil { + edges = append(edges, user.EdgeGroup) + } + if m.group_users != nil { + edges = append(edges, user.EdgeGroupUsers) + } return edges } @@ -11177,16 +12973,34 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeGroup: + ids := make([]ent.Value, 0, len(m.group)) + for id := range m.group { + ids = append(ids, id) + } + return ids + case user.EdgeGroupUsers: + ids := make([]ent.Value, 0, len(m.group_users)) + for id := range m.group_users { + ids = append(ids, id) + } + return ids } return nil } // RemovedEdges returns all edge names that were removed in this mutation. func (m *UserMutation) RemovedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 3) if m.removedmemberships != nil { edges = append(edges, user.EdgeMemberships) } + if m.removedgroup != nil { + edges = append(edges, user.EdgeGroup) + } + if m.removedgroup_users != nil { + edges = append(edges, user.EdgeGroupUsers) + } return edges } @@ -11200,16 +13014,34 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value { ids = append(ids, id) } return ids + case user.EdgeGroup: + ids := make([]ent.Value, 0, len(m.removedgroup)) + for id := range m.removedgroup { + ids = append(ids, id) + } + return ids + case user.EdgeGroupUsers: + ids := make([]ent.Value, 0, len(m.removedgroup_users)) + for id := range m.removedgroup_users { + ids = append(ids, id) + } + return ids } return nil } // ClearedEdges returns all edge names that were cleared in this mutation. func (m *UserMutation) ClearedEdges() []string { - edges := make([]string, 0, 1) + edges := make([]string, 0, 3) if m.clearedmemberships { edges = append(edges, user.EdgeMemberships) } + if m.clearedgroup { + edges = append(edges, user.EdgeGroup) + } + if m.clearedgroup_users { + edges = append(edges, user.EdgeGroupUsers) + } return edges } @@ -11219,6 +13051,10 @@ func (m *UserMutation) EdgeCleared(name string) bool { switch name { case user.EdgeMemberships: return m.clearedmemberships + case user.EdgeGroup: + return m.clearedgroup + case user.EdgeGroupUsers: + return m.clearedgroup_users } return false } @@ -11238,6 +13074,12 @@ func (m *UserMutation) ResetEdge(name string) error { case user.EdgeMemberships: m.ResetMemberships() return nil + case user.EdgeGroup: + m.ResetGroup() + return nil + case user.EdgeGroupUsers: + m.ResetGroupUsers() + return nil } return fmt.Errorf("unknown User edge %s", name) } diff --git a/app/controlplane/pkg/data/ent/organization.go b/app/controlplane/pkg/data/ent/organization.go index 648df83a6..2253e071e 100644 --- a/app/controlplane/pkg/data/ent/organization.go +++ b/app/controlplane/pkg/data/ent/organization.go @@ -46,9 +46,11 @@ type OrganizationEdges struct { APITokens []*APIToken `json:"api_tokens,omitempty"` // Projects holds the value of the projects edge. Projects []*Project `json:"projects,omitempty"` + // Groups holds the value of the groups edge. + Groups []*Group `json:"groups,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [7]bool + loadedTypes [8]bool } // MembershipsOrErr returns the Memberships value or an error if the edge @@ -114,6 +116,15 @@ func (e OrganizationEdges) ProjectsOrErr() ([]*Project, error) { return nil, &NotLoadedError{edge: "projects"} } +// GroupsOrErr returns the Groups value or an error if the edge +// was not loaded in eager-loading. +func (e OrganizationEdges) GroupsOrErr() ([]*Group, error) { + if e.loadedTypes[7] { + return e.Groups, nil + } + return nil, &NotLoadedError{edge: "groups"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*Organization) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -214,6 +225,11 @@ func (o *Organization) QueryProjects() *ProjectQuery { return NewOrganizationClient(o.config).QueryProjects(o) } +// QueryGroups queries the "groups" edge of the Organization entity. +func (o *Organization) QueryGroups() *GroupQuery { + return NewOrganizationClient(o.config).QueryGroups(o) +} + // Update returns a builder for updating this Organization. // Note that you need to call Organization.Unwrap() before calling this method if this Organization // was returned from a transaction, and the transaction was committed or rolled back. diff --git a/app/controlplane/pkg/data/ent/organization/organization.go b/app/controlplane/pkg/data/ent/organization/organization.go index db15e5ffd..6aa0ada04 100644 --- a/app/controlplane/pkg/data/ent/organization/organization.go +++ b/app/controlplane/pkg/data/ent/organization/organization.go @@ -35,6 +35,8 @@ const ( EdgeAPITokens = "api_tokens" // EdgeProjects holds the string denoting the projects edge name in mutations. EdgeProjects = "projects" + // EdgeGroups holds the string denoting the groups edge name in mutations. + EdgeGroups = "groups" // Table holds the table name of the organization in the database. Table = "organizations" // MembershipsTable is the table that holds the memberships relation/edge. @@ -86,6 +88,13 @@ const ( ProjectsInverseTable = "projects" // ProjectsColumn is the table column denoting the projects relation/edge. ProjectsColumn = "organization_id" + // GroupsTable is the table that holds the groups relation/edge. + GroupsTable = "groups" + // GroupsInverseTable is the table name for the Group entity. + // It exists in this package in order to avoid circular dependency with the "group" package. + GroupsInverseTable = "groups" + // GroupsColumn is the table column denoting the groups relation/edge. + GroupsColumn = "organization_id" ) // Columns holds all SQL columns for organization fields. @@ -235,6 +244,20 @@ func ByProjects(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { sqlgraph.OrderByNeighborTerms(s, newProjectsStep(), append([]sql.OrderTerm{term}, terms...)...) } } + +// ByGroupsCount orders the results by groups count. +func ByGroupsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newGroupsStep(), opts...) + } +} + +// ByGroups orders the results by groups terms. +func ByGroups(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newGroupsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} func newMembershipsStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -284,3 +307,10 @@ func newProjectsStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, false, ProjectsTable, ProjectsColumn), ) } +func newGroupsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(GroupsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, GroupsTable, GroupsColumn), + ) +} diff --git a/app/controlplane/pkg/data/ent/organization/where.go b/app/controlplane/pkg/data/ent/organization/where.go index 30c2702e1..722ed2d81 100644 --- a/app/controlplane/pkg/data/ent/organization/where.go +++ b/app/controlplane/pkg/data/ent/organization/where.go @@ -347,6 +347,29 @@ func HasProjectsWith(preds ...predicate.Project) predicate.Organization { }) } +// HasGroups applies the HasEdge predicate on the "groups" edge. +func HasGroups() predicate.Organization { + return predicate.Organization(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, GroupsTable, GroupsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasGroupsWith applies the HasEdge predicate on the "groups" edge with a given conditions (other predicates). +func HasGroupsWith(preds ...predicate.Group) predicate.Organization { + return predicate.Organization(func(s *sql.Selector) { + step := newGroupsStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.Organization) predicate.Organization { return predicate.Organization(sql.AndPredicates(predicates...)) diff --git a/app/controlplane/pkg/data/ent/organization_create.go b/app/controlplane/pkg/data/ent/organization_create.go index 51c32dc77..88d3fd5f5 100644 --- a/app/controlplane/pkg/data/ent/organization_create.go +++ b/app/controlplane/pkg/data/ent/organization_create.go @@ -14,6 +14,7 @@ import ( "entgo.io/ent/schema/field" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/apitoken" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casbackend" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integration" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" @@ -184,6 +185,21 @@ func (oc *OrganizationCreate) AddProjects(p ...*Project) *OrganizationCreate { return oc.AddProjectIDs(ids...) } +// AddGroupIDs adds the "groups" edge to the Group entity by IDs. +func (oc *OrganizationCreate) AddGroupIDs(ids ...uuid.UUID) *OrganizationCreate { + oc.mutation.AddGroupIDs(ids...) + return oc +} + +// AddGroups adds the "groups" edges to the Group entity. +func (oc *OrganizationCreate) AddGroups(g ...*Group) *OrganizationCreate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return oc.AddGroupIDs(ids...) +} + // Mutation returns the OrganizationMutation object of the builder. func (oc *OrganizationCreate) Mutation() *OrganizationMutation { return oc.mutation @@ -404,6 +420,22 @@ func (oc *OrganizationCreate) createSpec() (*Organization, *sqlgraph.CreateSpec) } _spec.Edges = append(_spec.Edges, edge) } + if nodes := oc.mutation.GroupsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: organization.GroupsTable, + Columns: []string{organization.GroupsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/app/controlplane/pkg/data/ent/organization_query.go b/app/controlplane/pkg/data/ent/organization_query.go index 894701c50..75aeb023e 100644 --- a/app/controlplane/pkg/data/ent/organization_query.go +++ b/app/controlplane/pkg/data/ent/organization_query.go @@ -15,6 +15,7 @@ import ( "entgo.io/ent/schema/field" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/apitoken" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casbackend" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integration" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" @@ -39,6 +40,7 @@ type OrganizationQuery struct { withIntegrations *IntegrationQuery withAPITokens *APITokenQuery withProjects *ProjectQuery + withGroups *GroupQuery modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector @@ -230,6 +232,28 @@ func (oq *OrganizationQuery) QueryProjects() *ProjectQuery { return query } +// QueryGroups chains the current query on the "groups" edge. +func (oq *OrganizationQuery) QueryGroups() *GroupQuery { + query := (&GroupClient{config: oq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := oq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := oq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(organization.Table, organization.FieldID, selector), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, organization.GroupsTable, organization.GroupsColumn), + ) + fromU = sqlgraph.SetNeighbors(oq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first Organization entity from the query. // Returns a *NotFoundError when no Organization was found. func (oq *OrganizationQuery) First(ctx context.Context) (*Organization, error) { @@ -429,6 +453,7 @@ func (oq *OrganizationQuery) Clone() *OrganizationQuery { withIntegrations: oq.withIntegrations.Clone(), withAPITokens: oq.withAPITokens.Clone(), withProjects: oq.withProjects.Clone(), + withGroups: oq.withGroups.Clone(), // clone intermediate query. sql: oq.sql.Clone(), path: oq.path, @@ -513,6 +538,17 @@ func (oq *OrganizationQuery) WithProjects(opts ...func(*ProjectQuery)) *Organiza return oq } +// WithGroups tells the query-builder to eager-load the nodes that are connected to +// the "groups" edge. The optional arguments are used to configure the query builder of the edge. +func (oq *OrganizationQuery) WithGroups(opts ...func(*GroupQuery)) *OrganizationQuery { + query := (&GroupClient{config: oq.config}).Query() + for _, opt := range opts { + opt(query) + } + oq.withGroups = query + return oq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -591,7 +627,7 @@ func (oq *OrganizationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([] var ( nodes = []*Organization{} _spec = oq.querySpec() - loadedTypes = [7]bool{ + loadedTypes = [8]bool{ oq.withMemberships != nil, oq.withWorkflowContracts != nil, oq.withWorkflows != nil, @@ -599,6 +635,7 @@ func (oq *OrganizationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([] oq.withIntegrations != nil, oq.withAPITokens != nil, oq.withProjects != nil, + oq.withGroups != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -673,6 +710,13 @@ func (oq *OrganizationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([] return nil, err } } + if query := oq.withGroups; query != nil { + if err := oq.loadGroups(ctx, query, nodes, + func(n *Organization) { n.Edges.Groups = []*Group{} }, + func(n *Organization, e *Group) { n.Edges.Groups = append(n.Edges.Groups, e) }); err != nil { + return nil, err + } + } return nodes, nil } @@ -891,6 +935,36 @@ func (oq *OrganizationQuery) loadProjects(ctx context.Context, query *ProjectQue } return nil } +func (oq *OrganizationQuery) loadGroups(ctx context.Context, query *GroupQuery, nodes []*Organization, init func(*Organization), assign func(*Organization, *Group)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*Organization) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(group.FieldOrganizationID) + } + query.Where(predicate.Group(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(organization.GroupsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.OrganizationID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "organization_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} func (oq *OrganizationQuery) sqlCount(ctx context.Context) (int, error) { _spec := oq.querySpec() diff --git a/app/controlplane/pkg/data/ent/organization_update.go b/app/controlplane/pkg/data/ent/organization_update.go index b396d89c1..ca85e2418 100644 --- a/app/controlplane/pkg/data/ent/organization_update.go +++ b/app/controlplane/pkg/data/ent/organization_update.go @@ -12,6 +12,7 @@ import ( "entgo.io/ent/schema/field" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/apitoken" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casbackend" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integration" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" @@ -169,6 +170,21 @@ func (ou *OrganizationUpdate) AddProjects(p ...*Project) *OrganizationUpdate { return ou.AddProjectIDs(ids...) } +// AddGroupIDs adds the "groups" edge to the Group entity by IDs. +func (ou *OrganizationUpdate) AddGroupIDs(ids ...uuid.UUID) *OrganizationUpdate { + ou.mutation.AddGroupIDs(ids...) + return ou +} + +// AddGroups adds the "groups" edges to the Group entity. +func (ou *OrganizationUpdate) AddGroups(g ...*Group) *OrganizationUpdate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return ou.AddGroupIDs(ids...) +} + // Mutation returns the OrganizationMutation object of the builder. func (ou *OrganizationUpdate) Mutation() *OrganizationMutation { return ou.mutation @@ -321,6 +337,27 @@ func (ou *OrganizationUpdate) RemoveProjects(p ...*Project) *OrganizationUpdate return ou.RemoveProjectIDs(ids...) } +// ClearGroups clears all "groups" edges to the Group entity. +func (ou *OrganizationUpdate) ClearGroups() *OrganizationUpdate { + ou.mutation.ClearGroups() + return ou +} + +// RemoveGroupIDs removes the "groups" edge to Group entities by IDs. +func (ou *OrganizationUpdate) RemoveGroupIDs(ids ...uuid.UUID) *OrganizationUpdate { + ou.mutation.RemoveGroupIDs(ids...) + return ou +} + +// RemoveGroups removes "groups" edges to Group entities. +func (ou *OrganizationUpdate) RemoveGroups(g ...*Group) *OrganizationUpdate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return ou.RemoveGroupIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (ou *OrganizationUpdate) Save(ctx context.Context) (int, error) { return withHooks(ctx, ou.sqlSave, ou.mutation, ou.hooks) @@ -684,6 +721,51 @@ func (ou *OrganizationUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if ou.mutation.GroupsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: organization.GroupsTable, + Columns: []string{organization.GroupsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ou.mutation.RemovedGroupsIDs(); len(nodes) > 0 && !ou.mutation.GroupsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: organization.GroupsTable, + Columns: []string{organization.GroupsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ou.mutation.GroupsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: organization.GroupsTable, + Columns: []string{organization.GroupsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _spec.AddModifiers(ou.modifiers...) if n, err = sqlgraph.UpdateNodes(ctx, ou.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { @@ -839,6 +921,21 @@ func (ouo *OrganizationUpdateOne) AddProjects(p ...*Project) *OrganizationUpdate return ouo.AddProjectIDs(ids...) } +// AddGroupIDs adds the "groups" edge to the Group entity by IDs. +func (ouo *OrganizationUpdateOne) AddGroupIDs(ids ...uuid.UUID) *OrganizationUpdateOne { + ouo.mutation.AddGroupIDs(ids...) + return ouo +} + +// AddGroups adds the "groups" edges to the Group entity. +func (ouo *OrganizationUpdateOne) AddGroups(g ...*Group) *OrganizationUpdateOne { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return ouo.AddGroupIDs(ids...) +} + // Mutation returns the OrganizationMutation object of the builder. func (ouo *OrganizationUpdateOne) Mutation() *OrganizationMutation { return ouo.mutation @@ -991,6 +1088,27 @@ func (ouo *OrganizationUpdateOne) RemoveProjects(p ...*Project) *OrganizationUpd return ouo.RemoveProjectIDs(ids...) } +// ClearGroups clears all "groups" edges to the Group entity. +func (ouo *OrganizationUpdateOne) ClearGroups() *OrganizationUpdateOne { + ouo.mutation.ClearGroups() + return ouo +} + +// RemoveGroupIDs removes the "groups" edge to Group entities by IDs. +func (ouo *OrganizationUpdateOne) RemoveGroupIDs(ids ...uuid.UUID) *OrganizationUpdateOne { + ouo.mutation.RemoveGroupIDs(ids...) + return ouo +} + +// RemoveGroups removes "groups" edges to Group entities. +func (ouo *OrganizationUpdateOne) RemoveGroups(g ...*Group) *OrganizationUpdateOne { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return ouo.RemoveGroupIDs(ids...) +} + // Where appends a list predicates to the OrganizationUpdate builder. func (ouo *OrganizationUpdateOne) Where(ps ...predicate.Organization) *OrganizationUpdateOne { ouo.mutation.Where(ps...) @@ -1384,6 +1502,51 @@ func (ouo *OrganizationUpdateOne) sqlSave(ctx context.Context) (_node *Organizat } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if ouo.mutation.GroupsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: organization.GroupsTable, + Columns: []string{organization.GroupsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ouo.mutation.RemovedGroupsIDs(); len(nodes) > 0 && !ouo.mutation.GroupsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: organization.GroupsTable, + Columns: []string{organization.GroupsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := ouo.mutation.GroupsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: organization.GroupsTable, + Columns: []string{organization.GroupsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _spec.AddModifiers(ouo.modifiers...) _node = &Organization{config: ouo.config} _spec.Assign = _node.assignValues diff --git a/app/controlplane/pkg/data/ent/predicate/predicate.go b/app/controlplane/pkg/data/ent/predicate/predicate.go index 53faf216e..4545014e7 100644 --- a/app/controlplane/pkg/data/ent/predicate/predicate.go +++ b/app/controlplane/pkg/data/ent/predicate/predicate.go @@ -18,6 +18,12 @@ type CASBackend func(*sql.Selector) // CASMapping is the predicate function for casmapping builders. type CASMapping func(*sql.Selector) +// Group is the predicate function for group builders. +type Group func(*sql.Selector) + +// GroupMembership is the predicate function for groupmembership builders. +type GroupMembership func(*sql.Selector) + // Integration is the predicate function for integration builders. type Integration func(*sql.Selector) diff --git a/app/controlplane/pkg/data/ent/runtime.go b/app/controlplane/pkg/data/ent/runtime.go index d6f0b63bb..6dedfb60a 100644 --- a/app/controlplane/pkg/data/ent/runtime.go +++ b/app/controlplane/pkg/data/ent/runtime.go @@ -9,6 +9,8 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/attestation" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casbackend" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/casmapping" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integration" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/integrationattachment" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/membership" @@ -87,6 +89,50 @@ func init() { casmappingDescID := casmappingFields[0].Descriptor() // casmapping.DefaultID holds the default value on creation for the id field. casmapping.DefaultID = casmappingDescID.Default.(func() uuid.UUID) + groupFields := schema.Group{}.Fields() + _ = groupFields + // groupDescName is the schema descriptor for name field. + groupDescName := groupFields[1].Descriptor() + // group.NameValidator is a validator for the "name" field. It is called by the builders before save. + group.NameValidator = groupDescName.Validators[0].(func(string) error) + // groupDescCreatedAt is the schema descriptor for created_at field. + groupDescCreatedAt := groupFields[4].Descriptor() + // group.DefaultCreatedAt holds the default value on creation for the created_at field. + group.DefaultCreatedAt = groupDescCreatedAt.Default.(func() time.Time) + // groupDescUpdatedAt is the schema descriptor for updated_at field. + groupDescUpdatedAt := groupFields[5].Descriptor() + // group.DefaultUpdatedAt holds the default value on creation for the updated_at field. + group.DefaultUpdatedAt = groupDescUpdatedAt.Default.(func() time.Time) + // groupDescID is the schema descriptor for id field. + groupDescID := groupFields[0].Descriptor() + // group.DefaultID holds the default value on creation for the id field. + group.DefaultID = groupDescID.Default.(func() uuid.UUID) + groupmembershipFields := schema.GroupMembership{}.Fields() + _ = groupmembershipFields + // groupmembershipDescGroupID is the schema descriptor for group_id field. + groupmembershipDescGroupID := groupmembershipFields[1].Descriptor() + // groupmembership.DefaultGroupID holds the default value on creation for the group_id field. + groupmembership.DefaultGroupID = groupmembershipDescGroupID.Default.(func() uuid.UUID) + // groupmembershipDescUserID is the schema descriptor for user_id field. + groupmembershipDescUserID := groupmembershipFields[2].Descriptor() + // groupmembership.DefaultUserID holds the default value on creation for the user_id field. + groupmembership.DefaultUserID = groupmembershipDescUserID.Default.(func() uuid.UUID) + // groupmembershipDescMaintainer is the schema descriptor for maintainer field. + groupmembershipDescMaintainer := groupmembershipFields[3].Descriptor() + // groupmembership.DefaultMaintainer holds the default value on creation for the maintainer field. + groupmembership.DefaultMaintainer = groupmembershipDescMaintainer.Default.(bool) + // groupmembershipDescCreatedAt is the schema descriptor for created_at field. + groupmembershipDescCreatedAt := groupmembershipFields[4].Descriptor() + // groupmembership.DefaultCreatedAt holds the default value on creation for the created_at field. + groupmembership.DefaultCreatedAt = groupmembershipDescCreatedAt.Default.(func() time.Time) + // groupmembershipDescUpdatedAt is the schema descriptor for updated_at field. + groupmembershipDescUpdatedAt := groupmembershipFields[5].Descriptor() + // groupmembership.DefaultUpdatedAt holds the default value on creation for the updated_at field. + groupmembership.DefaultUpdatedAt = groupmembershipDescUpdatedAt.Default.(func() time.Time) + // groupmembershipDescID is the schema descriptor for id field. + groupmembershipDescID := groupmembershipFields[0].Descriptor() + // groupmembership.DefaultID holds the default value on creation for the id field. + groupmembership.DefaultID = groupmembershipDescID.Default.(func() uuid.UUID) integrationFields := schema.Integration{}.Fields() _ = integrationFields // integrationDescCreatedAt is the schema descriptor for created_at field. diff --git a/app/controlplane/pkg/data/ent/schema-viz.html b/app/controlplane/pkg/data/ent/schema-viz.html index 02ef11249..20d8ad658 100644 --- a/app/controlplane/pkg/data/ent/schema-viz.html +++ b/app/controlplane/pkg/data/ent/schema-viz.html @@ -70,7 +70,7 @@ } - const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"APIToken\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"expires_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Attestation\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"bundle\",\"type\":\"[]byte\"},{\"name\":\"workflowrun_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"},{\"name\":\"max_blob_size_bytes\",\"type\":\"int64\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_run_id\",\"type\":\"uuid.UUID\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"role\",\"type\":\"authz.Role\"},{\"name\":\"membership_type\",\"type\":\"authz.MembershipType\"},{\"name\":\"member_id\",\"type\":\"uuid.UUID\"},{\"name\":\"resource_type\",\"type\":\"authz.ResourceType\"},{\"name\":\"resource_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"block_on_policy_violation\",\"type\":\"bool\"}]},{\"id\":\"Project\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"ProjectVersion\",\"fields\":[{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"prerelease\",\"type\":\"bool\"},{\"name\":\"workflow_run_count\",\"type\":\"int\"},{\"name\":\"released_at\",\"type\":\"time.Time\"},{\"name\":\"latest\",\"type\":\"bool\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"metadata\",\"type\":\"map[string]string\"},{\"name\":\"annotations\",\"type\":\"map[string]string\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"has_restricted_access\",\"type\":\"bool\"},{\"name\":\"first_name\",\"type\":\"string\"},{\"name\":\"last_name\",\"type\":\"string\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project_old\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"latest_run\",\"type\":\"uuid.UUID\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"metadata\",\"type\":\"map[string]interface {}\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"raw_body\",\"type\":\"[]byte\"},{\"name\":\"raw_body_format\",\"type\":\"unmarshal.RawFormat\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"},{\"name\":\"attestation_state\",\"type\":\"[]byte\"},{\"name\":\"contract_revision_used\",\"type\":\"int\"},{\"name\":\"contract_revision_latest\",\"type\":\"int\"},{\"name\":\"version_id\",\"type\":\"uuid.UUID\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]}],\"edges\":[{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Organization\",\"to\":\"APIToken\",\"label\":\"api_tokens\"},{\"from\":\"Organization\",\"to\":\"Project\",\"label\":\"projects\"},{\"from\":\"Project\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Project\",\"to\":\"ProjectVersion\",\"label\":\"versions\"},{\"from\":\"ProjectVersion\",\"to\":\"WorkflowRun\",\"label\":\"runs\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"latest_workflow_run\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"WorkflowRun\",\"to\":\"Attestation\",\"label\":\"attestation_bundle\"}]}"); + const entGraph = JSON.parse("{\"nodes\":[{\"id\":\"APIToken\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"expires_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Attestation\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"bundle\",\"type\":\"[]byte\"},{\"name\":\"workflowrun_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"CASBackend\",\"fields\":[{\"name\":\"location\",\"type\":\"string\"},{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"provider\",\"type\":\"biz.CASBackendProvider\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"validation_status\",\"type\":\"biz.CASBackendValidationStatus\"},{\"name\":\"validated_at\",\"type\":\"time.Time\"},{\"name\":\"default\",\"type\":\"bool\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"fallback\",\"type\":\"bool\"},{\"name\":\"max_blob_size_bytes\",\"type\":\"int64\"}]},{\"id\":\"CASMapping\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_run_id\",\"type\":\"uuid.UUID\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Group\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"GroupMembership\",\"fields\":[{\"name\":\"group_id\",\"type\":\"uuid.UUID\"},{\"name\":\"user_id\",\"type\":\"uuid.UUID\"},{\"name\":\"maintainer\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"Integration\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"secret_name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"}]},{\"id\":\"IntegrationAttachment\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"configuration\",\"type\":\"[]byte\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"Membership\",\"fields\":[{\"name\":\"current\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"role\",\"type\":\"authz.Role\"},{\"name\":\"membership_type\",\"type\":\"authz.MembershipType\"},{\"name\":\"member_id\",\"type\":\"uuid.UUID\"},{\"name\":\"resource_type\",\"type\":\"authz.ResourceType\"},{\"name\":\"resource_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"OrgInvitation\",\"fields\":[{\"name\":\"receiver_email\",\"type\":\"string\"},{\"name\":\"status\",\"type\":\"biz.OrgInvitationStatus\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"sender_id\",\"type\":\"uuid.UUID\"},{\"name\":\"role\",\"type\":\"authz.Role\"}]},{\"id\":\"Organization\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"block_on_policy_violation\",\"type\":\"bool\"}]},{\"id\":\"Project\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"}]},{\"id\":\"ProjectVersion\",\"fields\":[{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"prerelease\",\"type\":\"bool\"},{\"name\":\"workflow_run_count\",\"type\":\"int\"},{\"name\":\"released_at\",\"type\":\"time.Time\"},{\"name\":\"latest\",\"type\":\"bool\"}]},{\"id\":\"Referrer\",\"fields\":[{\"name\":\"digest\",\"type\":\"string\"},{\"name\":\"kind\",\"type\":\"string\"},{\"name\":\"downloadable\",\"type\":\"bool\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"metadata\",\"type\":\"map[string]string\"},{\"name\":\"annotations\",\"type\":\"map[string]string\"}]},{\"id\":\"RobotAccount\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"revoked_at\",\"type\":\"time.Time\"}]},{\"id\":\"User\",\"fields\":[{\"name\":\"email\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"has_restricted_access\",\"type\":\"bool\"},{\"name\":\"first_name\",\"type\":\"string\"},{\"name\":\"last_name\",\"type\":\"string\"}]},{\"id\":\"Workflow\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"project_old\",\"type\":\"string\"},{\"name\":\"team\",\"type\":\"string\"},{\"name\":\"runs_count\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"updated_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"public\",\"type\":\"bool\"},{\"name\":\"organization_id\",\"type\":\"uuid.UUID\"},{\"name\":\"project_id\",\"type\":\"uuid.UUID\"},{\"name\":\"latest_run\",\"type\":\"uuid.UUID\"},{\"name\":\"description\",\"type\":\"string\"},{\"name\":\"metadata\",\"type\":\"map[string]interface {}\"}]},{\"id\":\"WorkflowContract\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"deleted_at\",\"type\":\"time.Time\"},{\"name\":\"description\",\"type\":\"string\"}]},{\"id\":\"WorkflowContractVersion\",\"fields\":[{\"name\":\"body\",\"type\":\"[]byte\"},{\"name\":\"raw_body\",\"type\":\"[]byte\"},{\"name\":\"raw_body_format\",\"type\":\"unmarshal.RawFormat\"},{\"name\":\"revision\",\"type\":\"int\"},{\"name\":\"created_at\",\"type\":\"time.Time\"}]},{\"id\":\"WorkflowRun\",\"fields\":[{\"name\":\"created_at\",\"type\":\"time.Time\"},{\"name\":\"finished_at\",\"type\":\"time.Time\"},{\"name\":\"state\",\"type\":\"biz.WorkflowRunStatus\"},{\"name\":\"reason\",\"type\":\"string\"},{\"name\":\"run_url\",\"type\":\"string\"},{\"name\":\"runner_type\",\"type\":\"string\"},{\"name\":\"attestation\",\"type\":\"*dsse.Envelope\"},{\"name\":\"attestation_digest\",\"type\":\"string\"},{\"name\":\"attestation_state\",\"type\":\"[]byte\"},{\"name\":\"contract_revision_used\",\"type\":\"int\"},{\"name\":\"contract_revision_latest\",\"type\":\"int\"},{\"name\":\"version_id\",\"type\":\"uuid.UUID\"},{\"name\":\"workflow_id\",\"type\":\"uuid.UUID\"}]}],\"edges\":[{\"from\":\"CASMapping\",\"to\":\"CASBackend\",\"label\":\"cas_backend\"},{\"from\":\"CASMapping\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"Group\",\"to\":\"User\",\"label\":\"members\"},{\"from\":\"GroupMembership\",\"to\":\"Group\",\"label\":\"group\"},{\"from\":\"GroupMembership\",\"to\":\"User\",\"label\":\"user\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Integration\",\"label\":\"integration\"},{\"from\":\"IntegrationAttachment\",\"to\":\"Workflow\",\"label\":\"workflow\"},{\"from\":\"OrgInvitation\",\"to\":\"Organization\",\"label\":\"organization\"},{\"from\":\"OrgInvitation\",\"to\":\"User\",\"label\":\"sender\"},{\"from\":\"Organization\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Organization\",\"to\":\"WorkflowContract\",\"label\":\"workflow_contracts\"},{\"from\":\"Organization\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Organization\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"Organization\",\"to\":\"Integration\",\"label\":\"integrations\"},{\"from\":\"Organization\",\"to\":\"APIToken\",\"label\":\"api_tokens\"},{\"from\":\"Organization\",\"to\":\"Project\",\"label\":\"projects\"},{\"from\":\"Organization\",\"to\":\"Group\",\"label\":\"groups\"},{\"from\":\"Project\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"Project\",\"to\":\"ProjectVersion\",\"label\":\"versions\"},{\"from\":\"ProjectVersion\",\"to\":\"WorkflowRun\",\"label\":\"runs\"},{\"from\":\"Referrer\",\"to\":\"Referrer\",\"label\":\"references\"},{\"from\":\"Referrer\",\"to\":\"Workflow\",\"label\":\"workflows\"},{\"from\":\"User\",\"to\":\"Membership\",\"label\":\"memberships\"},{\"from\":\"Workflow\",\"to\":\"RobotAccount\",\"label\":\"robotaccounts\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"workflowruns\"},{\"from\":\"Workflow\",\"to\":\"WorkflowContract\",\"label\":\"contract\"},{\"from\":\"Workflow\",\"to\":\"WorkflowRun\",\"label\":\"latest_workflow_run\"},{\"from\":\"WorkflowContract\",\"to\":\"WorkflowContractVersion\",\"label\":\"versions\"},{\"from\":\"WorkflowRun\",\"to\":\"WorkflowContractVersion\",\"label\":\"contract_version\"},{\"from\":\"WorkflowRun\",\"to\":\"CASBackend\",\"label\":\"cas_backends\"},{\"from\":\"WorkflowRun\",\"to\":\"Attestation\",\"label\":\"attestation_bundle\"}]}"); const nodes = new vis.DataSet((entGraph.nodes || []).map(n => ({ id: n.id, diff --git a/app/controlplane/pkg/data/ent/schema/group.go b/app/controlplane/pkg/data/ent/schema/group.go new file mode 100644 index 000000000..00be63800 --- /dev/null +++ b/app/controlplane/pkg/data/ent/schema/group.go @@ -0,0 +1,71 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/index" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type Group struct { + ent.Schema +} + +func (Group) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Unique(), + field.String("name").NotEmpty(), + field.String("description").Optional(), + field.UUID("organization_id", uuid.UUID{}), + field.Time("created_at"). + Default(time.Now). + Immutable(). + Annotations(&entsql.Annotation{Default: "CURRENT_TIMESTAMP"}), + field.Time("updated_at"). + Default(time.Now). + Annotations(&entsql.Annotation{Default: "CURRENT_TIMESTAMP"}), + field.Time("deleted_at").Optional(), + } +} + +func (Group) Edges() []ent.Edge { + return []ent.Edge{ + // The members of the group + edge.To("members", User.Type).Through("group_users", GroupMembership.Type), + // The organization this group belongs to + edge.From("organization", Organization.Type). + Field("organization_id"). + Ref("groups"). + Unique(). + Required(), + } +} + +func (Group) Indexes() []ent.Index { + return []ent.Index{ + // names are unique within an organization and affects only to non-deleted items + index.Fields("name").Edges("organization").Unique().Annotations( + entsql.IndexWhere("deleted_at IS NULL"), + ), + } +} diff --git a/app/controlplane/pkg/data/ent/schema/group_membership.go b/app/controlplane/pkg/data/ent/schema/group_membership.go new file mode 100644 index 000000000..4301f3c6d --- /dev/null +++ b/app/controlplane/pkg/data/ent/schema/group_membership.go @@ -0,0 +1,69 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type GroupMembership struct { + ent.Schema +} + +func (GroupMembership) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Unique(), + field.UUID("group_id", uuid.UUID{}). + Default(uuid.New). + Unique(), + field.UUID("user_id", uuid.UUID{}). + Default(uuid.New). + Unique(), + field.Bool("maintainer").Default(false), + field.Time("created_at"). + Default(time.Now). + Immutable(). + Annotations(&entsql.Annotation{Default: "CURRENT_TIMESTAMP"}), + field.Time("updated_at"). + Default(time.Now). + Annotations(&entsql.Annotation{Default: "CURRENT_TIMESTAMP"}), + field.Time("deleted_at").Optional(), + } +} + +// Edges of the GroupMembership. +func (GroupMembership) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("group", Group.Type). + Required(). + Unique(). + Field("group_id"). + Annotations(entsql.Annotation{OnDelete: entsql.Cascade}), + edge.To("user", User.Type). + Required(). + Unique(). + Field("user_id"). + Annotations(entsql.Annotation{OnDelete: entsql.Cascade}), + } +} diff --git a/app/controlplane/pkg/data/ent/schema/organization.go b/app/controlplane/pkg/data/ent/schema/organization.go index 86181f2d3..ae3be3483 100644 --- a/app/controlplane/pkg/data/ent/schema/organization.go +++ b/app/controlplane/pkg/data/ent/schema/organization.go @@ -56,5 +56,6 @@ func (Organization) Edges() []ent.Edge { edge.To("integrations", Integration.Type).Annotations(entsql.Annotation{OnDelete: entsql.Cascade}), edge.To("api_tokens", APIToken.Type).Annotations(entsql.Annotation{OnDelete: entsql.Cascade}), edge.To("projects", Project.Type).Annotations(entsql.Annotation{OnDelete: entsql.Cascade}), + edge.To("groups", Group.Type).Annotations(entsql.Annotation{OnDelete: entsql.Cascade}), } } diff --git a/app/controlplane/pkg/data/ent/schema/user.go b/app/controlplane/pkg/data/ent/schema/user.go index 5a45efee5..7012318aa 100644 --- a/app/controlplane/pkg/data/ent/schema/user.go +++ b/app/controlplane/pkg/data/ent/schema/user.go @@ -59,6 +59,7 @@ func (User) Fields() []ent.Field { func (User) Edges() []ent.Edge { return []ent.Edge{ edge.To("memberships", Membership.Type).Annotations(entsql.Annotation{OnDelete: entsql.Cascade}), + edge.From("group", Group.Type).Ref("members").Through("group_users", GroupMembership.Type), } } diff --git a/app/controlplane/pkg/data/ent/tx.go b/app/controlplane/pkg/data/ent/tx.go index 9036c7fe0..21663ad8b 100644 --- a/app/controlplane/pkg/data/ent/tx.go +++ b/app/controlplane/pkg/data/ent/tx.go @@ -20,6 +20,10 @@ type Tx struct { CASBackend *CASBackendClient // CASMapping is the client for interacting with the CASMapping builders. CASMapping *CASMappingClient + // Group is the client for interacting with the Group builders. + Group *GroupClient + // GroupMembership is the client for interacting with the GroupMembership builders. + GroupMembership *GroupMembershipClient // Integration is the client for interacting with the Integration builders. Integration *IntegrationClient // IntegrationAttachment is the client for interacting with the IntegrationAttachment builders. @@ -183,6 +187,8 @@ func (tx *Tx) init() { tx.Attestation = NewAttestationClient(tx.config) tx.CASBackend = NewCASBackendClient(tx.config) tx.CASMapping = NewCASMappingClient(tx.config) + tx.Group = NewGroupClient(tx.config) + tx.GroupMembership = NewGroupMembershipClient(tx.config) tx.Integration = NewIntegrationClient(tx.config) tx.IntegrationAttachment = NewIntegrationAttachmentClient(tx.config) tx.Membership = NewMembershipClient(tx.config) diff --git a/app/controlplane/pkg/data/ent/user.go b/app/controlplane/pkg/data/ent/user.go index f5378f8da..f926658e7 100644 --- a/app/controlplane/pkg/data/ent/user.go +++ b/app/controlplane/pkg/data/ent/user.go @@ -38,9 +38,13 @@ type User struct { type UserEdges struct { // Memberships holds the value of the memberships edge. Memberships []*Membership `json:"memberships,omitempty"` + // Group holds the value of the group edge. + Group []*Group `json:"group,omitempty"` + // GroupUsers holds the value of the group_users edge. + GroupUsers []*GroupMembership `json:"group_users,omitempty"` // loadedTypes holds the information for reporting if a // type was loaded (or requested) in eager-loading or not. - loadedTypes [1]bool + loadedTypes [3]bool } // MembershipsOrErr returns the Memberships value or an error if the edge @@ -52,6 +56,24 @@ func (e UserEdges) MembershipsOrErr() ([]*Membership, error) { return nil, &NotLoadedError{edge: "memberships"} } +// GroupOrErr returns the Group value or an error if the edge +// was not loaded in eager-loading. +func (e UserEdges) GroupOrErr() ([]*Group, error) { + if e.loadedTypes[1] { + return e.Group, nil + } + return nil, &NotLoadedError{edge: "group"} +} + +// GroupUsersOrErr returns the GroupUsers value or an error if the edge +// was not loaded in eager-loading. +func (e UserEdges) GroupUsersOrErr() ([]*GroupMembership, error) { + if e.loadedTypes[2] { + return e.GroupUsers, nil + } + return nil, &NotLoadedError{edge: "group_users"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -135,6 +157,16 @@ func (u *User) QueryMemberships() *MembershipQuery { return NewUserClient(u.config).QueryMemberships(u) } +// QueryGroup queries the "group" edge of the User entity. +func (u *User) QueryGroup() *GroupQuery { + return NewUserClient(u.config).QueryGroup(u) +} + +// QueryGroupUsers queries the "group_users" edge of the User entity. +func (u *User) QueryGroupUsers() *GroupMembershipQuery { + return NewUserClient(u.config).QueryGroupUsers(u) +} + // Update returns a builder for updating this User. // Note that you need to call User.Unwrap() before calling this method if this User // was returned from a transaction, and the transaction was committed or rolled back. diff --git a/app/controlplane/pkg/data/ent/user/user.go b/app/controlplane/pkg/data/ent/user/user.go index 967a96484..4fdcdf9d0 100644 --- a/app/controlplane/pkg/data/ent/user/user.go +++ b/app/controlplane/pkg/data/ent/user/user.go @@ -27,6 +27,10 @@ const ( FieldLastName = "last_name" // EdgeMemberships holds the string denoting the memberships edge name in mutations. EdgeMemberships = "memberships" + // EdgeGroup holds the string denoting the group edge name in mutations. + EdgeGroup = "group" + // EdgeGroupUsers holds the string denoting the group_users edge name in mutations. + EdgeGroupUsers = "group_users" // Table holds the table name of the user in the database. Table = "users" // MembershipsTable is the table that holds the memberships relation/edge. @@ -36,6 +40,18 @@ const ( MembershipsInverseTable = "memberships" // MembershipsColumn is the table column denoting the memberships relation/edge. MembershipsColumn = "user_memberships" + // GroupTable is the table that holds the group relation/edge. The primary key declared below. + GroupTable = "group_memberships" + // GroupInverseTable is the table name for the Group entity. + // It exists in this package in order to avoid circular dependency with the "group" package. + GroupInverseTable = "groups" + // GroupUsersTable is the table that holds the group_users relation/edge. + GroupUsersTable = "group_memberships" + // GroupUsersInverseTable is the table name for the GroupMembership entity. + // It exists in this package in order to avoid circular dependency with the "groupmembership" package. + GroupUsersInverseTable = "group_memberships" + // GroupUsersColumn is the table column denoting the group_users relation/edge. + GroupUsersColumn = "user_id" ) // Columns holds all SQL columns for user fields. @@ -48,6 +64,12 @@ var Columns = []string{ FieldLastName, } +var ( + // GroupPrimaryKey and GroupColumn2 are the table columns denoting the + // primary key for the group relation (M2M). + GroupPrimaryKey = []string{"group_id", "user_id"} +) + // ValidColumn reports if the column name is valid (part of the table columns). func ValidColumn(column string) bool { for i := range Columns { @@ -113,6 +135,34 @@ func ByMemberships(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { sqlgraph.OrderByNeighborTerms(s, newMembershipsStep(), append([]sql.OrderTerm{term}, terms...)...) } } + +// ByGroupCount orders the results by group count. +func ByGroupCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newGroupStep(), opts...) + } +} + +// ByGroup orders the results by group terms. +func ByGroup(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newGroupStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByGroupUsersCount orders the results by group_users count. +func ByGroupUsersCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newGroupUsersStep(), opts...) + } +} + +// ByGroupUsers orders the results by group_users terms. +func ByGroupUsers(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newGroupUsersStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} func newMembershipsStep() *sqlgraph.Step { return sqlgraph.NewStep( sqlgraph.From(Table, FieldID), @@ -120,3 +170,17 @@ func newMembershipsStep() *sqlgraph.Step { sqlgraph.Edge(sqlgraph.O2M, false, MembershipsTable, MembershipsColumn), ) } +func newGroupStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(GroupInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, GroupTable, GroupPrimaryKey...), + ) +} +func newGroupUsersStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(GroupUsersInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, GroupUsersTable, GroupUsersColumn), + ) +} diff --git a/app/controlplane/pkg/data/ent/user/where.go b/app/controlplane/pkg/data/ent/user/where.go index c007d1c6e..bb37f5e3f 100644 --- a/app/controlplane/pkg/data/ent/user/where.go +++ b/app/controlplane/pkg/data/ent/user/where.go @@ -379,6 +379,52 @@ func HasMembershipsWith(preds ...predicate.Membership) predicate.User { }) } +// HasGroup applies the HasEdge predicate on the "group" edge. +func HasGroup() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, GroupTable, GroupPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasGroupWith applies the HasEdge predicate on the "group" edge with a given conditions (other predicates). +func HasGroupWith(preds ...predicate.Group) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := newGroupStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasGroupUsers applies the HasEdge predicate on the "group_users" edge. +func HasGroupUsers() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, GroupUsersTable, GroupUsersColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasGroupUsersWith applies the HasEdge predicate on the "group_users" edge with a given conditions (other predicates). +func HasGroupUsersWith(preds ...predicate.GroupMembership) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := newGroupUsersStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.User) predicate.User { return predicate.User(sql.AndPredicates(predicates...)) diff --git a/app/controlplane/pkg/data/ent/user_create.go b/app/controlplane/pkg/data/ent/user_create.go index 38ea762fb..01dd05e2f 100644 --- a/app/controlplane/pkg/data/ent/user_create.go +++ b/app/controlplane/pkg/data/ent/user_create.go @@ -12,6 +12,8 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" "github.com/google/uuid" @@ -116,6 +118,36 @@ func (uc *UserCreate) AddMemberships(m ...*Membership) *UserCreate { return uc.AddMembershipIDs(ids...) } +// AddGroupIDs adds the "group" edge to the Group entity by IDs. +func (uc *UserCreate) AddGroupIDs(ids ...uuid.UUID) *UserCreate { + uc.mutation.AddGroupIDs(ids...) + return uc +} + +// AddGroup adds the "group" edges to the Group entity. +func (uc *UserCreate) AddGroup(g ...*Group) *UserCreate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uc.AddGroupIDs(ids...) +} + +// AddGroupUserIDs adds the "group_users" edge to the GroupMembership entity by IDs. +func (uc *UserCreate) AddGroupUserIDs(ids ...uuid.UUID) *UserCreate { + uc.mutation.AddGroupUserIDs(ids...) + return uc +} + +// AddGroupUsers adds the "group_users" edges to the GroupMembership entity. +func (uc *UserCreate) AddGroupUsers(g ...*GroupMembership) *UserCreate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uc.AddGroupUserIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uc *UserCreate) Mutation() *UserMutation { return uc.mutation @@ -246,6 +278,45 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { } _spec.Edges = append(_spec.Edges, edge) } + if nodes := uc.mutation.GroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: user.GroupTable, + Columns: user.GroupPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &GroupMembershipCreate{config: uc.config, mutation: newGroupMembershipMutation(uc.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := uc.mutation.GroupUsersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.GroupUsersTable, + Columns: []string{user.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/app/controlplane/pkg/data/ent/user_query.go b/app/controlplane/pkg/data/ent/user_query.go index d3eb7d9d4..7b208b01b 100644 --- a/app/controlplane/pkg/data/ent/user_query.go +++ b/app/controlplane/pkg/data/ent/user_query.go @@ -13,6 +13,8 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" @@ -27,6 +29,8 @@ type UserQuery struct { inters []Interceptor predicates []predicate.User withMemberships *MembershipQuery + withGroup *GroupQuery + withGroupUsers *GroupMembershipQuery modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector @@ -86,6 +90,50 @@ func (uq *UserQuery) QueryMemberships() *MembershipQuery { return query } +// QueryGroup chains the current query on the "group" edge. +func (uq *UserQuery) QueryGroup() *GroupQuery { + query := (&GroupClient{config: uq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := uq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := uq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, selector), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, user.GroupTable, user.GroupPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryGroupUsers chains the current query on the "group_users" edge. +func (uq *UserQuery) QueryGroupUsers() *GroupMembershipQuery { + query := (&GroupMembershipClient{config: uq.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := uq.prepareQuery(ctx); err != nil { + return nil, err + } + selector := uq.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, selector), + sqlgraph.To(groupmembership.Table, groupmembership.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, user.GroupUsersTable, user.GroupUsersColumn), + ) + fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first User entity from the query. // Returns a *NotFoundError when no User was found. func (uq *UserQuery) First(ctx context.Context) (*User, error) { @@ -279,6 +327,8 @@ func (uq *UserQuery) Clone() *UserQuery { inters: append([]Interceptor{}, uq.inters...), predicates: append([]predicate.User{}, uq.predicates...), withMemberships: uq.withMemberships.Clone(), + withGroup: uq.withGroup.Clone(), + withGroupUsers: uq.withGroupUsers.Clone(), // clone intermediate query. sql: uq.sql.Clone(), path: uq.path, @@ -297,6 +347,28 @@ func (uq *UserQuery) WithMemberships(opts ...func(*MembershipQuery)) *UserQuery return uq } +// WithGroup tells the query-builder to eager-load the nodes that are connected to +// the "group" edge. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithGroup(opts ...func(*GroupQuery)) *UserQuery { + query := (&GroupClient{config: uq.config}).Query() + for _, opt := range opts { + opt(query) + } + uq.withGroup = query + return uq +} + +// WithGroupUsers tells the query-builder to eager-load the nodes that are connected to +// the "group_users" edge. The optional arguments are used to configure the query builder of the edge. +func (uq *UserQuery) WithGroupUsers(opts ...func(*GroupMembershipQuery)) *UserQuery { + query := (&GroupMembershipClient{config: uq.config}).Query() + for _, opt := range opts { + opt(query) + } + uq.withGroupUsers = query + return uq +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -375,8 +447,10 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e var ( nodes = []*User{} _spec = uq.querySpec() - loadedTypes = [1]bool{ + loadedTypes = [3]bool{ uq.withMemberships != nil, + uq.withGroup != nil, + uq.withGroupUsers != nil, } ) _spec.ScanValues = func(columns []string) ([]any, error) { @@ -407,6 +481,20 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e return nil, err } } + if query := uq.withGroup; query != nil { + if err := uq.loadGroup(ctx, query, nodes, + func(n *User) { n.Edges.Group = []*Group{} }, + func(n *User, e *Group) { n.Edges.Group = append(n.Edges.Group, e) }); err != nil { + return nil, err + } + } + if query := uq.withGroupUsers; query != nil { + if err := uq.loadGroupUsers(ctx, query, nodes, + func(n *User) { n.Edges.GroupUsers = []*GroupMembership{} }, + func(n *User, e *GroupMembership) { n.Edges.GroupUsers = append(n.Edges.GroupUsers, e) }); err != nil { + return nil, err + } + } return nodes, nil } @@ -441,6 +529,97 @@ func (uq *UserQuery) loadMemberships(ctx context.Context, query *MembershipQuery } return nil } +func (uq *UserQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes []*User, init func(*User), assign func(*User, *Group)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[uuid.UUID]*User) + nids := make(map[uuid.UUID]map[*User]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(user.GroupTable) + s.Join(joinT).On(s.C(group.FieldID), joinT.C(user.GroupPrimaryKey[0])) + s.Where(sql.InValues(joinT.C(user.GroupPrimaryKey[1]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(user.GroupPrimaryKey[1])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(uuid.UUID)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := *values[0].(*uuid.UUID) + inValue := *values[1].(*uuid.UUID) + if nids[inValue] == nil { + nids[inValue] = map[*User]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*Group](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "group" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} +func (uq *UserQuery) loadGroupUsers(ctx context.Context, query *GroupMembershipQuery, nodes []*User, init func(*User), assign func(*User, *GroupMembership)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(groupmembership.FieldUserID) + } + query.Where(predicate.GroupMembership(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(user.GroupUsersColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.UserID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "user_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) { _spec := uq.querySpec() diff --git a/app/controlplane/pkg/data/ent/user_update.go b/app/controlplane/pkg/data/ent/user_update.go index b2184c8fe..4c20d693f 100644 --- a/app/controlplane/pkg/data/ent/user_update.go +++ b/app/controlplane/pkg/data/ent/user_update.go @@ -10,6 +10,8 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/membership" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" @@ -119,6 +121,36 @@ func (uu *UserUpdate) AddMemberships(m ...*Membership) *UserUpdate { return uu.AddMembershipIDs(ids...) } +// AddGroupIDs adds the "group" edge to the Group entity by IDs. +func (uu *UserUpdate) AddGroupIDs(ids ...uuid.UUID) *UserUpdate { + uu.mutation.AddGroupIDs(ids...) + return uu +} + +// AddGroup adds the "group" edges to the Group entity. +func (uu *UserUpdate) AddGroup(g ...*Group) *UserUpdate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uu.AddGroupIDs(ids...) +} + +// AddGroupUserIDs adds the "group_users" edge to the GroupMembership entity by IDs. +func (uu *UserUpdate) AddGroupUserIDs(ids ...uuid.UUID) *UserUpdate { + uu.mutation.AddGroupUserIDs(ids...) + return uu +} + +// AddGroupUsers adds the "group_users" edges to the GroupMembership entity. +func (uu *UserUpdate) AddGroupUsers(g ...*GroupMembership) *UserUpdate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uu.AddGroupUserIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uu *UserUpdate) Mutation() *UserMutation { return uu.mutation @@ -145,6 +177,48 @@ func (uu *UserUpdate) RemoveMemberships(m ...*Membership) *UserUpdate { return uu.RemoveMembershipIDs(ids...) } +// ClearGroup clears all "group" edges to the Group entity. +func (uu *UserUpdate) ClearGroup() *UserUpdate { + uu.mutation.ClearGroup() + return uu +} + +// RemoveGroupIDs removes the "group" edge to Group entities by IDs. +func (uu *UserUpdate) RemoveGroupIDs(ids ...uuid.UUID) *UserUpdate { + uu.mutation.RemoveGroupIDs(ids...) + return uu +} + +// RemoveGroup removes "group" edges to Group entities. +func (uu *UserUpdate) RemoveGroup(g ...*Group) *UserUpdate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uu.RemoveGroupIDs(ids...) +} + +// ClearGroupUsers clears all "group_users" edges to the GroupMembership entity. +func (uu *UserUpdate) ClearGroupUsers() *UserUpdate { + uu.mutation.ClearGroupUsers() + return uu +} + +// RemoveGroupUserIDs removes the "group_users" edge to GroupMembership entities by IDs. +func (uu *UserUpdate) RemoveGroupUserIDs(ids ...uuid.UUID) *UserUpdate { + uu.mutation.RemoveGroupUserIDs(ids...) + return uu +} + +// RemoveGroupUsers removes "group_users" edges to GroupMembership entities. +func (uu *UserUpdate) RemoveGroupUsers(g ...*GroupMembership) *UserUpdate { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uu.RemoveGroupUserIDs(ids...) +} + // Save executes the query and returns the number of nodes affected by the update operation. func (uu *UserUpdate) Save(ctx context.Context) (int, error) { return withHooks(ctx, uu.sqlSave, uu.mutation, uu.hooks) @@ -266,6 +340,117 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uu.mutation.GroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: user.GroupTable, + Columns: user.GroupPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + createE := &GroupMembershipCreate{config: uu.config, mutation: newGroupMembershipMutation(uu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.RemovedGroupIDs(); len(nodes) > 0 && !uu.mutation.GroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: user.GroupTable, + Columns: user.GroupPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &GroupMembershipCreate{config: uu.config, mutation: newGroupMembershipMutation(uu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.GroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: user.GroupTable, + Columns: user.GroupPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &GroupMembershipCreate{config: uu.config, mutation: newGroupMembershipMutation(uu.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if uu.mutation.GroupUsersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.GroupUsersTable, + Columns: []string{user.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.RemovedGroupUsersIDs(); len(nodes) > 0 && !uu.mutation.GroupUsersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.GroupUsersTable, + Columns: []string{user.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uu.mutation.GroupUsersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.GroupUsersTable, + Columns: []string{user.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _spec.AddModifiers(uu.modifiers...) if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { @@ -377,6 +562,36 @@ func (uuo *UserUpdateOne) AddMemberships(m ...*Membership) *UserUpdateOne { return uuo.AddMembershipIDs(ids...) } +// AddGroupIDs adds the "group" edge to the Group entity by IDs. +func (uuo *UserUpdateOne) AddGroupIDs(ids ...uuid.UUID) *UserUpdateOne { + uuo.mutation.AddGroupIDs(ids...) + return uuo +} + +// AddGroup adds the "group" edges to the Group entity. +func (uuo *UserUpdateOne) AddGroup(g ...*Group) *UserUpdateOne { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uuo.AddGroupIDs(ids...) +} + +// AddGroupUserIDs adds the "group_users" edge to the GroupMembership entity by IDs. +func (uuo *UserUpdateOne) AddGroupUserIDs(ids ...uuid.UUID) *UserUpdateOne { + uuo.mutation.AddGroupUserIDs(ids...) + return uuo +} + +// AddGroupUsers adds the "group_users" edges to the GroupMembership entity. +func (uuo *UserUpdateOne) AddGroupUsers(g ...*GroupMembership) *UserUpdateOne { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uuo.AddGroupUserIDs(ids...) +} + // Mutation returns the UserMutation object of the builder. func (uuo *UserUpdateOne) Mutation() *UserMutation { return uuo.mutation @@ -403,6 +618,48 @@ func (uuo *UserUpdateOne) RemoveMemberships(m ...*Membership) *UserUpdateOne { return uuo.RemoveMembershipIDs(ids...) } +// ClearGroup clears all "group" edges to the Group entity. +func (uuo *UserUpdateOne) ClearGroup() *UserUpdateOne { + uuo.mutation.ClearGroup() + return uuo +} + +// RemoveGroupIDs removes the "group" edge to Group entities by IDs. +func (uuo *UserUpdateOne) RemoveGroupIDs(ids ...uuid.UUID) *UserUpdateOne { + uuo.mutation.RemoveGroupIDs(ids...) + return uuo +} + +// RemoveGroup removes "group" edges to Group entities. +func (uuo *UserUpdateOne) RemoveGroup(g ...*Group) *UserUpdateOne { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uuo.RemoveGroupIDs(ids...) +} + +// ClearGroupUsers clears all "group_users" edges to the GroupMembership entity. +func (uuo *UserUpdateOne) ClearGroupUsers() *UserUpdateOne { + uuo.mutation.ClearGroupUsers() + return uuo +} + +// RemoveGroupUserIDs removes the "group_users" edge to GroupMembership entities by IDs. +func (uuo *UserUpdateOne) RemoveGroupUserIDs(ids ...uuid.UUID) *UserUpdateOne { + uuo.mutation.RemoveGroupUserIDs(ids...) + return uuo +} + +// RemoveGroupUsers removes "group_users" edges to GroupMembership entities. +func (uuo *UserUpdateOne) RemoveGroupUsers(g ...*GroupMembership) *UserUpdateOne { + ids := make([]uuid.UUID, len(g)) + for i := range g { + ids[i] = g[i].ID + } + return uuo.RemoveGroupUserIDs(ids...) +} + // Where appends a list predicates to the UserUpdate builder. func (uuo *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne { uuo.mutation.Where(ps...) @@ -554,6 +811,117 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) } _spec.Edges.Add = append(_spec.Edges.Add, edge) } + if uuo.mutation.GroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: user.GroupTable, + Columns: user.GroupPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + createE := &GroupMembershipCreate{config: uuo.config, mutation: newGroupMembershipMutation(uuo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.RemovedGroupIDs(); len(nodes) > 0 && !uuo.mutation.GroupCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: user.GroupTable, + Columns: user.GroupPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &GroupMembershipCreate{config: uuo.config, mutation: newGroupMembershipMutation(uuo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.GroupIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: user.GroupTable, + Columns: user.GroupPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + createE := &GroupMembershipCreate{config: uuo.config, mutation: newGroupMembershipMutation(uuo.config, OpCreate)} + createE.defaults() + _, specE := createE.createSpec() + edge.Target.Fields = specE.Fields + if specE.ID.Value != nil { + edge.Target.Fields = append(edge.Target.Fields, specE.ID) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if uuo.mutation.GroupUsersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.GroupUsersTable, + Columns: []string{user.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.RemovedGroupUsersIDs(); len(nodes) > 0 && !uuo.mutation.GroupUsersCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.GroupUsersTable, + Columns: []string{user.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := uuo.mutation.GroupUsersIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: user.GroupUsersTable, + Columns: []string{user.GroupUsersColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _spec.AddModifiers(uuo.modifiers...) _node = &User{config: uuo.config} _spec.Assign = _node.assignValues diff --git a/app/controlplane/pkg/data/group.go b/app/controlplane/pkg/data/group.go new file mode 100644 index 000000000..f9b17fc0a --- /dev/null +++ b/app/controlplane/pkg/data/group.go @@ -0,0 +1,280 @@ +// +// Copyright 2025 The Chainloop Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package data + +import ( + "context" + "fmt" + "time" + + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/group" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/groupmembership" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/user" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/workflow" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination" + + "github.com/go-kratos/kratos/v2/log" + "github.com/google/uuid" +) + +type GroupRepo struct { + data *Data + log *log.Helper +} + +func NewGroupRepo(data *Data, logger log.Logger) biz.GroupRepo { + return &GroupRepo{ + data: data, + log: log.NewHelper(logger), + } +} + +func (g GroupRepo) List(ctx context.Context, orgID uuid.UUID, filterOpts *biz.ListGroupOpts, paginationOpts *pagination.OffsetPaginationOpts) ([]*biz.Group, int, error) { + if filterOpts == nil { + filterOpts = &biz.ListGroupOpts{} + } + + query := g.data.DB.Group.Query(). + Where(group.DeletedAtIsNil(), group.OrganizationIDEQ(orgID)). + WithMembers().WithOrganization() + + if filterOpts.Name != "" { + query.Where(group.NameContains(filterOpts.Name)) + } + + if filterOpts.Description != "" { + query.Where(group.DescriptionContains(filterOpts.Description)) + } + + if filterOpts.MemberEmail != "" { + query.Where(group.HasMembersWith(user.EmailContains(filterOpts.MemberEmail))) + } + + // Get the count of all filtered rows without the limit and offset + count, err := query.Count(ctx) + if err != nil { + return nil, 0, err + } + + // Apply pagination options and execute the query + groups, err := query. + Order(ent.Desc(group.FieldCreatedAt)). + Limit(paginationOpts.Limit()). + Offset(paginationOpts.Offset()). + All(ctx) + if err != nil { + return nil, 0, err + } + + bizGroups := make([]*biz.Group, 0, len(groups)) + for _, entGroup := range groups { + bizGroups = append(bizGroups, entGroupToBiz(entGroup)) + } + + return bizGroups, count, nil +} + +// ListMembers retrieves the members of a group, optionally filtering by maintainers. +func (g GroupRepo) ListMembers(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID, opts *biz.ListMembersOpts, paginationOpts *pagination.OffsetPaginationOpts) ([]*biz.GroupMembership, int, error) { + if paginationOpts == nil { + paginationOpts = pagination.NewDefaultOffsetPaginationOpts() + } + + // Check the group exists in the organization + _, err := g.data.DB.Group.Query(). + Where(group.ID(groupID), group.OrganizationIDEQ(orgID), group.DeletedAtIsNil()). + Only(ctx) + if err != nil { + if ent.IsNotFound(err) { + return nil, 0, biz.NewErrNotFound("group") + } + return nil, 0, err + } + + // Build the query to list members of the group + query := g.data.DB.GroupMembership.Query(). + Where(groupmembership.GroupID(groupID), groupmembership.DeletedAtIsNil()) + + if opts != nil && opts.Maintainers != nil { + query.Where(groupmembership.MaintainerEQ(*opts.Maintainers)) + } + + if opts != nil && opts.MemberEmail != nil { + query.Where(groupmembership.HasUserWith(user.EmailContains(*opts.MemberEmail))) + } + + // Get the count of all filtered rows without the limit and offset + count, err := query.Count(ctx) + if err != nil { + return nil, 0, err + } + + members, err := query. + Order(ent.Desc(workflow.FieldCreatedAt)). + WithUser(). + Limit(paginationOpts.Limit()). + Offset(paginationOpts.Offset()). + All(ctx) + if err != nil { + return nil, 0, err + } + + bizMembers := make([]*biz.GroupMembership, 0, len(members)) + for _, member := range members { + bizMembers = append(bizMembers, entGroupMembershipToBiz(member)) + } + + return bizMembers, count, nil +} + +// Create creates a new group in the specified organization. +func (g GroupRepo) Create(ctx context.Context, orgID uuid.UUID, opts *biz.CreateGroupOpts) (*biz.Group, error) { + if opts == nil { + return nil, biz.NewErrValidationStr("create group options cannot be nil") + } + + var entGroup ent.Group + + err := WithTx(ctx, g.data.DB, func(tx *ent.Tx) error { + // Create the group with the provided options + gr, err := tx.Group.Create(). + SetName(opts.Name). + SetDescription(opts.Description). + AddMemberIDs(opts.UserID). + SetOrganizationID(orgID). + Save(ctx) + if err != nil { + if ent.IsConstraintError(err) { + return biz.NewErrAlreadyExists(err) + } + return err + } + + // Update the group-user member to set it's a group maintainer + if _, grUerr := tx.GroupMembership.Update(). + Where( + groupmembership.GroupIDEQ(gr.ID), + groupmembership.UserIDEQ(opts.UserID), + ). + SetMaintainer(true). + Save(ctx); grUerr != nil { + if ent.IsNotFound(grUerr) { + return biz.NewErrNotFound("group user") + } + return grUerr + } + + entGroup = *gr + + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to create group: %w", err) + } + + return g.FindByOrgAndID(ctx, orgID, entGroup.ID) +} + +// FindByOrgAndID retrieves a group by its ID and org, ensuring it is not deleted. +func (g GroupRepo) FindByOrgAndID(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID) (*biz.Group, error) { + entGroup, err := g.data.DB.Group.Query(). + Where(group.DeletedAtIsNil(), group.ID(groupID), group.OrganizationID(orgID)). + WithOrganization(). + Only(ctx) + if err != nil { + if ent.IsNotFound(err) { + return nil, biz.NewErrNotFound("group") + } + return nil, err + } + + return entGroupToBiz(entGroup), nil +} + +// Update updates an existing group in the specified organization. +func (g GroupRepo) Update(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID, opts *biz.UpdateGroupOpts) (*biz.Group, error) { + if opts == nil { + return nil, biz.NewErrValidationStr("update group options cannot be nil") + } + + // Update the group with the provided options + entGroup, err := g.data.DB.Group.UpdateOneID(groupID). + SetNillableName(opts.Name). + SetNillableDescription(opts.Description). + Where(group.OrganizationIDEQ(orgID), group.DeletedAtIsNil()). + Save(ctx) + if err != nil { + if ent.IsNotFound(err) { + return nil, biz.NewErrNotFound("group") + } + if ent.IsConstraintError(err) { + return nil, biz.NewErrAlreadyExists(err) + } + + return nil, err + } + + return g.FindByOrgAndID(ctx, orgID, entGroup.ID) +} + +// SoftDelete soft-deletes a group by setting the DeletedAt field to the current time. +func (g GroupRepo) SoftDelete(ctx context.Context, orgID uuid.UUID, groupID uuid.UUID) error { + // Softly delete the group by setting the DeletedAt field + _, err := g.data.DB.Group.UpdateOneID(groupID). + SetDeletedAt(time.Now()). + Where(group.OrganizationIDEQ(orgID), group.DeletedAtIsNil()). + Save(ctx) + if err != nil { + if ent.IsNotFound(err) { + return biz.NewErrNotFound("group") + } + return err + } + + return nil +} + +// entGroupToBiz converts an ent.Group to a biz.Group. +func entGroupToBiz(gr *ent.Group) *biz.Group { + grp := &biz.Group{ + ID: gr.ID, + Name: gr.Name, + Description: gr.Description, + CreatedAt: toTimePtr(gr.CreatedAt), + UpdatedAt: toTimePtr(gr.UpdatedAt), + DeletedAt: toTimePtr(gr.DeletedAt), + } + + // Include the organization to which the group belongs + if gr.Edges.Organization != nil { + grp.Organization = entOrgToBizOrg(gr.Edges.Organization) + } + + return grp +} + +// entGroupMembershipToBiz converts an ent.GroupMembership to a biz.GroupMembership. +func entGroupMembershipToBiz(gu *ent.GroupMembership) *biz.GroupMembership { + return &biz.GroupMembership{ + User: entUserToBizUser(gu.Edges.User), + Maintainer: gu.Maintainer, + CreatedAt: toTimePtr(gu.CreatedAt), + UpdatedAt: toTimePtr(gu.UpdatedAt), + DeletedAt: toTimePtr(gu.DeletedAt), + } +} diff --git a/app/controlplane/plugins/sdk/v1/plugin/api/fanout.proto b/app/controlplane/plugins/sdk/v1/plugin/api/fanout.proto index 8d4fdbcb3..dda1c18b2 100644 --- a/app/controlplane/plugins/sdk/v1/plugin/api/fanout.proto +++ b/app/controlplane/plugins/sdk/v1/plugin/api/fanout.proto @@ -17,10 +17,10 @@ syntax = "proto3"; package api; -option go_package = "github.com/chainloop-dev/chainloop/app/controlplane/plugins/sdk/v1/plugin/api;api"; - import "google/protobuf/timestamp.proto"; +option go_package = "github.com/chainloop-dev/chainloop/app/controlplane/plugins/sdk/v1/plugin/api;api"; + service FanoutService { // Core / Shared rpc Describe(DescribeRequest) returns (DescribeResponse); @@ -146,4 +146,4 @@ message IsSubscribedToRequest { message IsSubscribedToResponse { bool subscribed = 1; -} \ No newline at end of file +}