From 78d1a09f297bd1217cc7576e5355859d03ad746e Mon Sep 17 00:00:00 2001 From: "Jose I. Paris" Date: Mon, 28 Jul 2025 16:28:30 +0200 Subject: [PATCH] add ParentId to project membership response Signed-off-by: Jose I. Paris --- .../api/controlplane/v1/project.pb.go | 18 +++++++++++++++--- .../api/controlplane/v1/project.proto | 2 ++ .../gen/frontend/controlplane/v1/project.ts | 16 ++++++++++++++++ ...ntrolplane.v1.ProjectMember.jsonschema.json | 8 ++++++++ .../controlplane.v1.ProjectMember.schema.json | 8 ++++++++ app/controlplane/internal/service/project.go | 9 +++++---- app/controlplane/pkg/biz/project.go | 2 ++ app/controlplane/pkg/data/project.go | 1 + 8 files changed, 57 insertions(+), 7 deletions(-) diff --git a/app/controlplane/api/controlplane/v1/project.pb.go b/app/controlplane/api/controlplane/v1/project.pb.go index ca961d15e..2f94786a8 100644 --- a/app/controlplane/api/controlplane/v1/project.pb.go +++ b/app/controlplane/api/controlplane/v1/project.pb.go @@ -174,6 +174,8 @@ type ProjectMember struct { UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` // The ID of latest project version this member is associated with LatestProjectVersionId string `protobuf:"bytes,6,opt,name=latest_project_version_id,json=latestProjectVersionId,proto3" json:"latest_project_version_id,omitempty"` + // Optional parent ID for nested project memberships + ParentId *string `protobuf:"bytes,7,opt,name=parent_id,json=parentId,proto3,oneof" json:"parent_id,omitempty"` } func (x *ProjectMember) Reset() { @@ -257,6 +259,13 @@ func (x *ProjectMember) GetLatestProjectVersionId() string { return "" } +func (x *ProjectMember) GetParentId() string { + if x != nil && x.ParentId != nil { + return *x.ParentId + } + return "" +} + type isProjectMember_Subject interface { isProjectMember_Subject() } @@ -903,7 +912,7 @@ var file_controlplane_v1_project_proto_rawDesc = []byte{ 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0xe0, 0x02, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x90, 0x03, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x2e, @@ -924,8 +933,11 @@ var file_controlplane_v1_project_proto_rawDesc = []byte{ 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x22, 0xa1, 0x02, 0x0a, 0x1e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x08, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x73, 0x75, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, + 0x69, 0x64, 0x22, 0xa1, 0x02, 0x0a, 0x1e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, diff --git a/app/controlplane/api/controlplane/v1/project.proto b/app/controlplane/api/controlplane/v1/project.proto index 756880981..5ef081c27 100644 --- a/app/controlplane/api/controlplane/v1/project.proto +++ b/app/controlplane/api/controlplane/v1/project.proto @@ -68,6 +68,8 @@ message ProjectMember { google.protobuf.Timestamp updated_at = 5; // The ID of latest project version this member is associated with string latest_project_version_id = 6; + // Optional parent ID for nested project memberships + optional string parent_id = 7; } // ProjectServiceAddMemberRequest contains the information needed to add a user to a project diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/project.ts b/app/controlplane/api/gen/frontend/controlplane/v1/project.ts index 6d4f588e5..4c3b91745 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/project.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/project.ts @@ -49,6 +49,8 @@ export interface ProjectMember { updatedAt?: Date; /** The ID of latest project version this member is associated with */ latestProjectVersionId: string; + /** Optional parent ID for nested project memberships */ + parentId?: string | undefined; } /** ProjectServiceAddMemberRequest contains the information needed to add a user to a project */ @@ -304,6 +306,7 @@ function createBaseProjectMember(): ProjectMember { createdAt: undefined, updatedAt: undefined, latestProjectVersionId: "", + parentId: undefined, }; } @@ -327,6 +330,9 @@ export const ProjectMember = { if (message.latestProjectVersionId !== "") { writer.uint32(50).string(message.latestProjectVersionId); } + if (message.parentId !== undefined) { + writer.uint32(58).string(message.parentId); + } return writer; }, @@ -379,6 +385,13 @@ export const ProjectMember = { message.latestProjectVersionId = reader.string(); continue; + case 7: + if (tag !== 58) { + break; + } + + message.parentId = reader.string(); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -396,6 +409,7 @@ export const ProjectMember = { createdAt: isSet(object.createdAt) ? fromJsonTimestamp(object.createdAt) : undefined, updatedAt: isSet(object.updatedAt) ? fromJsonTimestamp(object.updatedAt) : undefined, latestProjectVersionId: isSet(object.latestProjectVersionId) ? String(object.latestProjectVersionId) : "", + parentId: isSet(object.parentId) ? String(object.parentId) : undefined, }; }, @@ -407,6 +421,7 @@ export const ProjectMember = { message.createdAt !== undefined && (obj.createdAt = message.createdAt.toISOString()); message.updatedAt !== undefined && (obj.updatedAt = message.updatedAt.toISOString()); message.latestProjectVersionId !== undefined && (obj.latestProjectVersionId = message.latestProjectVersionId); + message.parentId !== undefined && (obj.parentId = message.parentId); return obj; }, @@ -422,6 +437,7 @@ export const ProjectMember = { message.createdAt = object.createdAt ?? undefined; message.updatedAt = object.updatedAt ?? undefined; message.latestProjectVersionId = object.latestProjectVersionId ?? ""; + message.parentId = object.parentId ?? undefined; return message; }, }; diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.jsonschema.json index a0fda6b8e..e29c075c6 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.jsonschema.json @@ -12,6 +12,10 @@ "description": "The ID of latest project version this member is associated with", "type": "string" }, + "^(parent_id)$": { + "description": "Optional parent ID for nested project memberships", + "type": "string" + }, "^(updated_at)$": { "$ref": "google.protobuf.Timestamp.jsonschema.json", "description": "Timestamp when the project membership was last modified" @@ -30,6 +34,10 @@ "description": "The ID of latest project version this member is associated with", "type": "string" }, + "parentId": { + "description": "Optional parent ID for nested project memberships", + "type": "string" + }, "role": { "anyOf": [ { diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.schema.json index c8379f59c..335f6bdba 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.schema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.ProjectMember.schema.json @@ -12,6 +12,10 @@ "description": "The ID of latest project version this member is associated with", "type": "string" }, + "^(parentId)$": { + "description": "Optional parent ID for nested project memberships", + "type": "string" + }, "^(updatedAt)$": { "$ref": "google.protobuf.Timestamp.schema.json", "description": "Timestamp when the project membership was last modified" @@ -30,6 +34,10 @@ "description": "The ID of latest project version this member is associated with", "type": "string" }, + "parent_id": { + "description": "Optional parent ID for nested project memberships", + "type": "string" + }, "role": { "anyOf": [ { diff --git a/app/controlplane/internal/service/project.go b/app/controlplane/internal/service/project.go index 69e085687..61e621ce0 100644 --- a/app/controlplane/internal/service/project.go +++ b/app/controlplane/internal/service/project.go @@ -382,15 +382,16 @@ func mapProjectMemberRoleToAuthzRole(role pb.ProjectMemberRole) authz.Role { // bizProjectMembershipToPb converts a biz.ProjectMembership to a pb.ProjectMember func bizProjectMembershipToPb(m *biz.ProjectMembership) *pb.ProjectMember { - var role pb.ProjectMemberRole - - // Map the role string back to a protobuf enum - role = mapAuthzRoleToProjectMemberRole(m.Role) + role := mapAuthzRoleToProjectMemberRole(m.Role) pbMember := &pb.ProjectMember{ Role: role, } + if m.ParentID != nil { + pbMember.ParentId = biz.ToPtr(m.ParentID.String()) + } + if m.User != nil { pbMember.Subject = &pb.ProjectMember_User{ User: bizUserToPb(m.User), diff --git a/app/controlplane/pkg/biz/project.go b/app/controlplane/pkg/biz/project.go index 3500e67fb..eb35c3971 100644 --- a/app/controlplane/pkg/biz/project.go +++ b/app/controlplane/pkg/biz/project.go @@ -94,6 +94,8 @@ type ProjectMembership struct { CreatedAt *time.Time // UpdatedAt is the timestamp when the membership was last updated. UpdatedAt *time.Time + // ParentID is used for nested memberships, if applicable + ParentID *uuid.UUID } // GroupProjectInfo represents detailed information about a project that a group is a member of diff --git a/app/controlplane/pkg/data/project.go b/app/controlplane/pkg/data/project.go index ca5c38470..2031353a2 100644 --- a/app/controlplane/pkg/data/project.go +++ b/app/controlplane/pkg/data/project.go @@ -359,6 +359,7 @@ func entProjectMembershipToBiz(m *ent.Membership, u *ent.User, g *ent.Group) *bi Role: m.Role, CreatedAt: &m.CreatedAt, UpdatedAt: &m.UpdatedAt, + ParentID: m.ParentID, } if u != nil {