Split connect profile schema into profile, membership, and invitation

Move OIDCProviderInfo type into base.graphql alongside its query field
in both connect and trust APIs, removing orphan oidc.graphql files.
Split connect profile.graphql into three domain files: profile (with
user mutations), membership, and invitation.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-04-15 10:03:50 +04:00
parent fe1b5dae4e
commit 0ce1d8039a
10 changed files with 250 additions and 205 deletions

View File

@@ -13,7 +13,6 @@ import (
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/iam"
"go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/server/api/authn"
"go.probo.inc/probo/pkg/server/api/authz"
"go.probo.inc/probo/pkg/server/api/connect/v1/schema"
"go.probo.inc/probo/pkg/server/api/connect/v1/types"
@@ -21,41 +20,6 @@ import (
"go.probo.inc/probo/pkg/server/gqlutils/types/cursor"
)
// Permission is the resolver for the permission field.
func (r *invitationResolver) Permission(ctx context.Context, obj *types.Invitation, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// LastSession is the resolver for the lastSession field.
func (r *membershipResolver) LastSession(ctx context.Context, obj *types.Membership) (*types.Session, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
session := authn.SessionFromContext(ctx)
if session == nil {
return nil, nil
}
childSession, err := r.iam.SessionService.GetActiveSessionForMembership(ctx, session.ID, obj.ID)
if err != nil {
var errSessionNotFound *iam.ErrSessionNotFound
if errors.As(err, &errSessionNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get active session for membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSession(childSession), nil
}
// Permission is the resolver for the permission field.
func (r *membershipResolver) Permission(ctx context.Context, obj *types.Membership, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// CreateUser is the resolver for the createUser field.
func (r *mutationResolver) CreateUser(ctx context.Context, input types.CreateUserInput) (*types.CreateUserPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionMembershipProfileCreate); err != nil {
@@ -91,40 +55,6 @@ func (r *mutationResolver) CreateUser(ctx context.Context, input types.CreateUse
}, nil
}
// InviteUser is the resolver for the inviteUser field.
func (r *mutationResolver) InviteUser(ctx context.Context, input types.InviteUserInput) (*types.InviteUserPayload, error) {
if err := r.authorize(ctx, input.ProfileID, iam.ActionInvitationCreate); err != nil {
return nil, err
}
invitation, err := r.iam.OrganizationService.InviteUser(
ctx,
&iam.CreateInvitationRequest{
OrganizationID: input.OrganizationID,
ProfileID: input.ProfileID,
},
)
if err != nil {
var errOrganizationNotFound *iam.ErrOrganizationNotFound
var errUserAlreadyExists *iam.ErrUserAlreadyExists
if errors.As(err, &errOrganizationNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if errors.As(err, &errUserAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot invite user", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.InviteUserPayload{
InvitationEdge: types.NewInvitationEdge(invitation, coredata.InvitationOrderFieldCreatedAt),
}, nil
}
// DeactivateUser is the resolver for the deactivateUser field.
func (r *mutationResolver) DeactivateUser(ctx context.Context, input types.DeactivateUserInput) (*types.DeactivateUserPayload, error) {
if err := r.authorize(ctx, input.ProfileID, iam.ActionMembershipProfileDeactivate); err != nil {
@@ -175,29 +105,6 @@ func (r *mutationResolver) UpdateUser(ctx context.Context, input types.UpdateUse
}, nil
}
// UpdateMembership is the resolver for the updateMembership field.
func (r *mutationResolver) UpdateMembership(ctx context.Context, input types.UpdateMembershipInput) (*types.UpdateMembershipPayload, error) {
if err := r.authorize(ctx, input.MembershipID, iam.ActionMembershipUpdate); err != nil {
return nil, err
}
if input.Role == coredata.MembershipRoleOwner {
if err := r.authorize(ctx, input.MembershipID, iam.ActionMembershipRoleSetOwner); err != nil {
return nil, err
}
}
membership, err := r.iam.OrganizationService.UpdateMempership(ctx, input.OrganizationID, input.MembershipID, input.Role)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateMembershipPayload{
Membership: types.NewMembership(membership),
}, nil
}
// RemoveUser is the resolver for the removeUser field.
func (r *mutationResolver) RemoveUser(ctx context.Context, input types.RemoveUserInput) (*types.RemoveUserPayload, error) {
if err := r.authorize(ctx, input.ProfileID, iam.ActionMembershipProfileDelete); err != nil {
@@ -339,12 +246,6 @@ func (r *profileConnectionResolver) TotalCount(ctx context.Context, obj *types.P
return nil, gqlutils.Internal(ctx)
}
// Invitation returns schema.InvitationResolver implementation.
func (r *Resolver) Invitation() schema.InvitationResolver { return &invitationResolver{r} }
// Membership returns schema.MembershipResolver implementation.
func (r *Resolver) Membership() schema.MembershipResolver { return &membershipResolver{r} }
// Profile returns schema.ProfileResolver implementation.
func (r *Resolver) Profile() schema.ProfileResolver { return &profileResolver{r} }
@@ -353,7 +254,5 @@ func (r *Resolver) ProfileConnection() schema.ProfileConnectionResolver {
return &profileConnectionResolver{r}
}
type invitationResolver struct{ *Resolver }
type membershipResolver struct{ *Resolver }
type profileResolver struct{ *Resolver }
type profileConnectionResolver struct{ *Resolver }