Rework console pages and implement profile update

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-02-06 19:50:42 +04:00
parent 091210dee5
commit 3a459f1992
35 changed files with 2197 additions and 108 deletions

View File

@@ -52,8 +52,9 @@ const (
ActionMembershipRoleSetOwner = "iam:membership-role:set-owner"
// Membership Profile actions
ActionMembershipProfileGet = "iam:membership-profile:get"
ActionMembershipProfileList = "iam:membership-profile:list"
ActionMembershipProfileGet = "iam:membership-profile:get"
ActionMembershipProfileList = "iam:membership-profile:list"
ActionMembershipProfileUpdate = "iam:membership-profile:update"
// Personal API Key actions
ActionPersonalAPIKeyCreate = "iam:personal-api-key:create"

View File

@@ -161,6 +161,7 @@ var IAMOwnerPolicy = policy.NewPolicy(
policy.Allow(
ActionMembershipProfileGet,
ActionMembershipProfileList,
ActionMembershipProfileUpdate,
).
WithSID("full-membership-profile-access").
When(policy.Equals("principal.organization_id", "resource.organization_id")),
@@ -239,6 +240,7 @@ var IAMAdminPolicy = policy.NewPolicy(
policy.Allow(
ActionMembershipProfileGet,
ActionMembershipProfileList,
ActionMembershipProfileUpdate,
).
WithSID("membership-profile-admin-access").
When(policy.Equals("principal.organization_id", "resource.organization_id")),

View File

@@ -97,6 +97,16 @@ type (
FullName string
Role coredata.MembershipRole
}
UpdateProfileRequest struct {
ID gid.GID
FullName string
AdditionalEmailAddresses mail.Addrs
Kind coredata.MembershipProfileKind
Position **string
ContractStartDate **time.Time
ContractEndDate **time.Time
}
)
var (
@@ -124,6 +134,8 @@ var (
const (
TokenTypeAPIKey = "api_key"
NameMaxLength = 100
TitleMaxLength = 1000
ContentMaxLength = 5000
DefaultAttributeEmail = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
@@ -180,6 +192,22 @@ func (req UpdateOrganizationRequest) Validate() error {
return v.Error()
}
func (upr *UpdateProfileRequest) Validate() error {
v := validator.New()
v.Check(upr.ID, "id", validator.Required(), validator.GID(coredata.MembershipProfileEntityType))
v.Check(upr.Kind, "kind", validator.OneOfSlice(coredata.PeopleKinds()))
v.Check(upr.FullName, "full_name", validator.SafeTextNoNewLine(NameMaxLength))
v.CheckEach(upr.AdditionalEmailAddresses, "additional_email_addresses", func(index int, item any) {
v.Check(item, fmt.Sprintf("additional_email_addresses[%d]", index), validator.Required(), validator.NotEmpty())
})
v.Check(upr.Position, "position", validator.SafeText(TitleMaxLength))
v.Check(upr.ContractStartDate, "contract_start_date", validator.Before(upr.ContractEndDate))
v.Check(upr.ContractEndDate, "contract_end_date", validator.After(upr.ContractStartDate))
return v.Error()
}
func NewOrganizationService(svc *Service) *OrganizationService {
return &OrganizationService{Service: svc}
}
@@ -946,6 +974,57 @@ func (s *OrganizationService) ListMembers(
return page.NewPage(memberships, cursor), nil
}
func (s *OrganizationService) UpdateProfile(ctx context.Context, req *UpdateProfileRequest) (*coredata.MembershipProfile, error) {
if err := req.Validate(); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
var (
scope = coredata.NewScopeFromObjectID(req.ID)
profile = &coredata.MembershipProfile{}
)
err := s.pg.WithConn(
ctx,
func(conn pg.Conn) error {
if err := profile.LoadByID(ctx, conn, scope, req.ID); err != nil {
return fmt.Errorf("cannot load profile: %w", err)
}
profile.FullName = req.FullName
profile.Kind = req.Kind
profile.AdditionalEmailAddresses = req.AdditionalEmailAddresses
profile.Position = *req.Position
if req.ContractStartDate != nil {
profile.ContractStartDate = *req.ContractStartDate
}
if req.ContractEndDate != nil {
profile.ContractEndDate = *req.ContractEndDate
}
if profile.ContractStartDate != nil && profile.ContractEndDate != nil {
if profile.ContractEndDate.Before(*profile.ContractStartDate) {
return fmt.Errorf("contract end date must be after or equal to start date")
}
}
profile.UpdatedAt = time.Now()
return profile.Update(ctx, conn, scope)
},
)
if err != nil {
return nil, err
}
return profile, nil
}
func (s *OrganizationService) GetProfile(ctx context.Context, profileID gid.GID) (*coredata.MembershipProfile, error) {
profile := &coredata.MembershipProfile{}