Files
probo/pkg/server/api/connect/v1/profile_resolvers.go
Émile Ré 0ce1d8039a 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>
2026-04-15 10:03:50 +04:00

259 lines
8.7 KiB
Go

package connect_v1
// This file will be automatically regenerated based on the schema, any resolver
// implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.87
import (
"context"
"errors"
"go.gearno.de/kit/log"
"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/authz"
"go.probo.inc/probo/pkg/server/api/connect/v1/schema"
"go.probo.inc/probo/pkg/server/api/connect/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
"go.probo.inc/probo/pkg/server/gqlutils/types/cursor"
)
// 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 {
return nil, err
}
profile, err := r.iam.OrganizationService.CreateUser(
ctx,
&iam.CreateUserRequest{
OrganizationID: input.OrganizationID,
EmailAddress: input.EmailAddress,
Role: input.Role,
FullName: input.FullName,
AdditionalEmailAddresses: input.AdditionalEmailAddresses,
Kind: input.Kind,
Position: input.Position,
ContractStartDate: gqlutils.UnwrapOmittable(input.ContractStartDate),
ContractEndDate: gqlutils.UnwrapOmittable(input.ContractEndDate),
},
)
if err != nil {
var errAlreadyExists *iam.ErrUserAlreadyExists
if errors.As(err, &errAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot create user", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateUserPayload{
ProfileEdge: types.NewProfileEdge(profile, coredata.MembershipProfileOrderFieldCreatedAt),
}, 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 {
return nil, err
}
_, err := r.iam.OrganizationService.UpdateUserState(
ctx,
input.ProfileID,
coredata.ProfileStateInactive,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot deactivate profile", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeactivateUserPayload{
Success: true,
}, nil
}
// UpdateUser is the resolver for the updateUser field.
func (r *mutationResolver) UpdateUser(ctx context.Context, input types.UpdateUserInput) (*types.UpdateUserPayload, error) {
if err := r.authorize(ctx, input.ID, iam.ActionMembershipProfileUpdate); err != nil {
return nil, err
}
profile, err := r.iam.OrganizationService.UpdateUser(
ctx,
&iam.UpdateUserRequest{
ID: input.ID,
FullName: input.FullName,
AdditionalEmailAddresses: input.AdditionalEmailAddresses,
Kind: input.Kind,
Position: input.Position,
ContractStartDate: gqlutils.UnwrapOmittable(input.ContractStartDate),
ContractEndDate: gqlutils.UnwrapOmittable(input.ContractEndDate),
},
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update profile", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateUserPayload{
Profile: types.NewProfile(profile),
}, 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 {
return nil, err
}
err := r.iam.OrganizationService.RemoveUser(ctx, input.OrganizationID, input.ProfileID)
if err != nil {
var errManagedBySCIM *iam.ErrUserManagedBySCIM
var errLastActiveOwner *iam.ErrLastActiveOwner
if errors.As(err, &errManagedBySCIM) {
return nil, gqlutils.Conflict(ctx, err)
}
if errors.As(err, &errLastActiveOwner) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot remove user from organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RemoveUserPayload{DeletedProfileID: input.ProfileID}, nil
}
// Identity is the resolver for the identity field.
func (r *profileResolver) Identity(ctx context.Context, obj *types.Profile) (*types.Identity, error) {
if err := r.authorize(
ctx,
obj.ID,
iam.ActionMembershipProfileGet,
authz.WithSkipAssumptionCheck(),
); err != nil {
return nil, err
}
identity, err := r.iam.AccountService.GetIdentity(ctx, obj.Identity.ID)
if err != nil {
var errNotFound *iam.ErrIdentityNotFound
if errors.As(err, &errNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get identity", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewIdentity(identity), nil
}
// Organization is the resolver for the organization field.
func (r *profileResolver) Organization(ctx context.Context, obj *types.Profile) (*types.Organization, error) {
if err := r.authorize(ctx, obj.Organization.ID, iam.ActionOrganizationGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
organization, err := r.iam.OrganizationService.GetOrganization(ctx, obj.Organization.ID)
if err != nil {
var errNotFound *iam.ErrOrganizationNotFound
if errors.As(err, &errNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Membership is the resolver for the membership field.
func (r *profileResolver) Membership(ctx context.Context, obj *types.Profile) (*types.Membership, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
membership, err := r.iam.AccountService.GetMembershipForOrganization(ctx, obj.Identity.ID, obj.Organization.ID)
if err != nil {
var errNotFound *iam.ErrMembershipNotFound
if errors.As(err, &errNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMembership(membership), nil
}
// PendingInvitations is the resolver for the pendingInvitations field.
func (r *profileResolver) PendingInvitations(ctx context.Context, obj *types.Profile, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.InvitationOrderBy) (*types.InvitationConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionInvitationList); err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.InvitationOrderField]{
Field: coredata.InvitationOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.AccountService.ListPendingInvitations(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list pending invitations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewInvitationConnection(page, r, obj.ID, nil), nil
}
// Permission is the resolver for the permission field.
func (r *profileResolver) Permission(ctx context.Context, obj *types.Profile, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *profileConnectionResolver) TotalCount(ctx context.Context, obj *types.ProfileConnection) (*int, error) {
switch obj.Resolver.(type) {
case *identityResolver:
count, err := r.iam.AccountService.CountProfiles(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count profiles", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
case *organizationResolver:
count, err := r.iam.OrganizationService.CountProfiles(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count profiles", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// Profile returns schema.ProfileResolver implementation.
func (r *Resolver) Profile() schema.ProfileResolver { return &profileResolver{r} }
// ProfileConnection returns schema.ProfileConnectionResolver implementation.
func (r *Resolver) ProfileConnection() schema.ProfileConnectionResolver {
return &profileConnectionResolver{r}
}
type profileResolver struct{ *Resolver }
type profileConnectionResolver struct{ *Resolver }