@@ -22,6 +22,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/mail"
|
||||
@@ -904,6 +905,11 @@ VALUES (
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
var pgErr *pgconn.PgError
|
||||
if errors.As(err, &pgErr) && pgErr.Code == "23505" {
|
||||
return ErrResourceAlreadyExists
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot insert profile: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ const (
|
||||
// Membership Profile actions
|
||||
ActionMembershipProfileGet = "iam:membership-profile:get"
|
||||
ActionMembershipProfileList = "iam:membership-profile:list"
|
||||
ActionMembershipProfileCreate = "iam:membership-profile:create"
|
||||
ActionMembershipProfileUpdate = "iam:membership-profile:update"
|
||||
|
||||
// Personal API Key actions
|
||||
|
||||
@@ -98,12 +98,24 @@ type (
|
||||
Role coredata.MembershipRole
|
||||
}
|
||||
|
||||
CreateUserRequest struct {
|
||||
OrganizationID gid.GID
|
||||
EmailAddress mail.Addr
|
||||
Role coredata.MembershipRole
|
||||
FullName string
|
||||
AdditionalEmailAddresses mail.Addrs
|
||||
Kind coredata.MembershipProfileKind
|
||||
Position *string
|
||||
ContractStartDate **time.Time
|
||||
ContractEndDate **time.Time
|
||||
}
|
||||
|
||||
UpdateProfileRequest struct {
|
||||
ID gid.GID
|
||||
FullName string
|
||||
AdditionalEmailAddresses mail.Addrs
|
||||
Kind coredata.MembershipProfileKind
|
||||
Position **string
|
||||
Position *string
|
||||
ContractStartDate **time.Time
|
||||
ContractEndDate **time.Time
|
||||
}
|
||||
@@ -192,6 +204,22 @@ func (req UpdateOrganizationRequest) Validate() error {
|
||||
return v.Error()
|
||||
}
|
||||
|
||||
func (cur *CreateUserRequest) Validate() error {
|
||||
v := validator.New()
|
||||
|
||||
v.Check(cur.OrganizationID, "id", validator.Required(), validator.GID(coredata.OrganizationEntityType))
|
||||
v.Check(cur.Kind, "kind", validator.OneOfSlice(coredata.MembershipProfileKinds()))
|
||||
v.Check(cur.FullName, "full_name", validator.SafeTextNoNewLine(NameMaxLength))
|
||||
v.CheckEach(cur.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(cur.Position, "position", validator.SafeText(TitleMaxLength))
|
||||
v.Check(cur.ContractStartDate, "contract_start_date", validator.Before(cur.ContractEndDate))
|
||||
v.Check(cur.ContractEndDate, "contract_end_date", validator.After(cur.ContractStartDate))
|
||||
|
||||
return v.Error()
|
||||
}
|
||||
|
||||
func (upr *UpdateProfileRequest) Validate() error {
|
||||
v := validator.New()
|
||||
|
||||
@@ -922,6 +950,90 @@ func (s *OrganizationService) DeleteOrganization(ctx context.Context, organizati
|
||||
)
|
||||
}
|
||||
|
||||
func (s *OrganizationService) CreateUser(ctx context.Context, req *CreateUserRequest) (*coredata.MembershipProfile, error) {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("invalid request: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
scope = coredata.NewScopeFromObjectID(req.OrganizationID)
|
||||
profile *coredata.MembershipProfile
|
||||
now = time.Now()
|
||||
)
|
||||
|
||||
err := s.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
identity := &coredata.Identity{}
|
||||
if err := identity.LoadByEmail(ctx, conn, req.EmailAddress); err != nil {
|
||||
if !errors.Is(err, coredata.ErrResourceNotFound) {
|
||||
return fmt.Errorf("cannot load identity: %w", err)
|
||||
}
|
||||
|
||||
identity = &coredata.Identity{
|
||||
ID: gid.New(gid.NilTenant, coredata.IdentityEntityType),
|
||||
EmailAddress: req.EmailAddress,
|
||||
FullName: req.FullName,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if err := identity.Insert(ctx, conn); err != nil {
|
||||
return fmt.Errorf("cannot insert identity: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
profile = &coredata.MembershipProfile{
|
||||
ID: gid.New(req.OrganizationID.TenantID(), coredata.MembershipProfileEntityType),
|
||||
FullName: req.FullName,
|
||||
Kind: req.Kind,
|
||||
AdditionalEmailAddresses: req.AdditionalEmailAddresses,
|
||||
Position: req.Position,
|
||||
// User is created inactive
|
||||
State: coredata.ProfileStateInactive,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if req.ContractStartDate != nil {
|
||||
profile.ContractStartDate = *req.ContractStartDate
|
||||
}
|
||||
|
||||
if req.ContractEndDate != nil {
|
||||
profile.ContractEndDate = *req.ContractEndDate
|
||||
}
|
||||
|
||||
if err := profile.Insert(ctx, conn); err != nil {
|
||||
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
|
||||
return NewUserAlreadyExistsError(identity.ID, req.OrganizationID)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot insert profile: %w", err)
|
||||
}
|
||||
|
||||
membership := &coredata.Membership{
|
||||
ID: gid.New(req.OrganizationID.TenantID(), coredata.MembershipEntityType),
|
||||
IdentityID: identity.ID,
|
||||
OrganizationID: req.OrganizationID,
|
||||
Role: req.Role,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if err := membership.Insert(ctx, conn, scope); err != nil {
|
||||
return fmt.Errorf("cannot insert membership: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return profile, 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)
|
||||
@@ -941,12 +1053,8 @@ func (s *OrganizationService) UpdateProfile(ctx context.Context, req *UpdateProf
|
||||
|
||||
profile.FullName = req.FullName
|
||||
profile.Kind = req.Kind
|
||||
|
||||
profile.AdditionalEmailAddresses = req.AdditionalEmailAddresses
|
||||
|
||||
if req.Position != nil {
|
||||
profile.Position = *req.Position
|
||||
}
|
||||
profile.Position = req.Position
|
||||
|
||||
if req.ContractStartDate != nil {
|
||||
profile.ContractStartDate = *req.ContractStartDate
|
||||
|
||||
@@ -94,6 +94,8 @@ type Mutation {
|
||||
input: DeleteOrganizationHorizontalLogoInput!
|
||||
): DeleteOrganizationHorizontalLogoPayload @session(required: PRESENT)
|
||||
|
||||
createUser(input: CreateUserInput!): CreateUserPayload
|
||||
@session(required: PRESENT)
|
||||
inviteMember(input: InviteMemberInput!): InviteMemberPayload
|
||||
@session(required: PRESENT)
|
||||
deleteInvitation(input: DeleteInvitationInput!): DeleteInvitationPayload
|
||||
@@ -727,6 +729,18 @@ input DeleteOrganizationHorizontalLogoInput {
|
||||
organizationId: ID!
|
||||
}
|
||||
|
||||
input CreateUserInput {
|
||||
organizationId: ID!
|
||||
fullName: String!
|
||||
emailAddress: EmailAddr!
|
||||
role: MembershipRole!
|
||||
additionalEmailAddresses: [EmailAddr!]
|
||||
kind: ProfileKind!
|
||||
position: String
|
||||
contractStartDate: Datetime @goField(omittable: true)
|
||||
contractEndDate: Datetime @goField(omittable: true)
|
||||
}
|
||||
|
||||
input InviteMemberInput {
|
||||
organizationId: ID!
|
||||
email: EmailAddr!
|
||||
@@ -739,7 +753,7 @@ input UpdateProfileInput {
|
||||
fullName: String!
|
||||
additionalEmailAddresses: [EmailAddr!]
|
||||
kind: ProfileKind!
|
||||
position: String @goField(omittable: true)
|
||||
position: String
|
||||
contractStartDate: Datetime @goField(omittable: true)
|
||||
contractEndDate: Datetime @goField(omittable: true)
|
||||
}
|
||||
@@ -890,6 +904,10 @@ type DeleteOrganizationHorizontalLogoPayload {
|
||||
organization: Organization!
|
||||
}
|
||||
|
||||
type CreateUserPayload {
|
||||
profileEdge: ProfileEdge!
|
||||
}
|
||||
|
||||
type InviteMemberPayload {
|
||||
invitationEdge: InvitationEdge!
|
||||
}
|
||||
|
||||
@@ -119,6 +119,10 @@ type ComplexityRoot struct {
|
||||
Token func(childComplexity int) int
|
||||
}
|
||||
|
||||
CreateUserPayload struct {
|
||||
ProfileEdge func(childComplexity int) int
|
||||
}
|
||||
|
||||
DeleteInvitationPayload struct {
|
||||
DeletedInvitationID func(childComplexity int) int
|
||||
}
|
||||
@@ -203,6 +207,7 @@ type ComplexityRoot struct {
|
||||
CreatePersonalAPIKey func(childComplexity int, input types.CreatePersonalAPIKeyInput) int
|
||||
CreateSAMLConfiguration func(childComplexity int, input types.CreateSAMLConfigurationInput) int
|
||||
CreateSCIMConfiguration func(childComplexity int, input types.CreateSCIMConfigurationInput) int
|
||||
CreateUser func(childComplexity int, input types.CreateUserInput) int
|
||||
DeleteInvitation func(childComplexity int, input types.DeleteInvitationInput) int
|
||||
DeleteOrganization func(childComplexity int, input types.DeleteOrganizationInput) int
|
||||
DeleteOrganizationHorizontalLogo func(childComplexity int, input types.DeleteOrganizationHorizontalLogoInput) int
|
||||
@@ -536,6 +541,7 @@ type MutationResolver interface {
|
||||
UpdateOrganization(ctx context.Context, input types.UpdateOrganizationInput) (*types.UpdateOrganizationPayload, error)
|
||||
DeleteOrganization(ctx context.Context, input types.DeleteOrganizationInput) (*types.DeleteOrganizationPayload, error)
|
||||
DeleteOrganizationHorizontalLogo(ctx context.Context, input types.DeleteOrganizationHorizontalLogoInput) (*types.DeleteOrganizationHorizontalLogoPayload, error)
|
||||
CreateUser(ctx context.Context, input types.CreateUserInput) (*types.CreateUserPayload, error)
|
||||
InviteMember(ctx context.Context, input types.InviteMemberInput) (*types.InviteMemberPayload, error)
|
||||
DeleteInvitation(ctx context.Context, input types.DeleteInvitationInput) (*types.DeleteInvitationPayload, error)
|
||||
UpdateProfile(ctx context.Context, input types.UpdateProfileInput) (*types.UpdateProfilePayload, error)
|
||||
@@ -760,6 +766,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
|
||||
return e.complexity.CreateSCIMConfigurationPayload.Token(childComplexity), true
|
||||
|
||||
case "CreateUserPayload.profileEdge":
|
||||
if e.complexity.CreateUserPayload.ProfileEdge == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.CreateUserPayload.ProfileEdge(childComplexity), true
|
||||
|
||||
case "DeleteInvitationPayload.deletedInvitationId":
|
||||
if e.complexity.DeleteInvitationPayload.DeletedInvitationID == nil {
|
||||
break
|
||||
@@ -1129,6 +1142,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.CreateSCIMConfiguration(childComplexity, args["input"].(types.CreateSCIMConfigurationInput)), true
|
||||
case "Mutation.createUser":
|
||||
if e.complexity.Mutation.CreateUser == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Mutation_createUser_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.CreateUser(childComplexity, args["input"].(types.CreateUserInput)), true
|
||||
case "Mutation.deleteInvitation":
|
||||
if e.complexity.Mutation.DeleteInvitation == nil {
|
||||
break
|
||||
@@ -2375,6 +2399,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
||||
ec.unmarshalInputCreatePersonalAPIKeyInput,
|
||||
ec.unmarshalInputCreateSAMLConfigurationInput,
|
||||
ec.unmarshalInputCreateSCIMConfigurationInput,
|
||||
ec.unmarshalInputCreateUserInput,
|
||||
ec.unmarshalInputDeleteInvitationInput,
|
||||
ec.unmarshalInputDeleteOrganizationHorizontalLogoInput,
|
||||
ec.unmarshalInputDeleteOrganizationInput,
|
||||
@@ -2594,6 +2619,8 @@ type Mutation {
|
||||
input: DeleteOrganizationHorizontalLogoInput!
|
||||
): DeleteOrganizationHorizontalLogoPayload @session(required: PRESENT)
|
||||
|
||||
createUser(input: CreateUserInput!): CreateUserPayload
|
||||
@session(required: PRESENT)
|
||||
inviteMember(input: InviteMemberInput!): InviteMemberPayload
|
||||
@session(required: PRESENT)
|
||||
deleteInvitation(input: DeleteInvitationInput!): DeleteInvitationPayload
|
||||
@@ -3227,6 +3254,18 @@ input DeleteOrganizationHorizontalLogoInput {
|
||||
organizationId: ID!
|
||||
}
|
||||
|
||||
input CreateUserInput {
|
||||
organizationId: ID!
|
||||
fullName: String!
|
||||
emailAddress: EmailAddr!
|
||||
role: MembershipRole!
|
||||
additionalEmailAddresses: [EmailAddr!]
|
||||
kind: ProfileKind!
|
||||
position: String
|
||||
contractStartDate: Datetime @goField(omittable: true)
|
||||
contractEndDate: Datetime @goField(omittable: true)
|
||||
}
|
||||
|
||||
input InviteMemberInput {
|
||||
organizationId: ID!
|
||||
email: EmailAddr!
|
||||
@@ -3239,7 +3278,7 @@ input UpdateProfileInput {
|
||||
fullName: String!
|
||||
additionalEmailAddresses: [EmailAddr!]
|
||||
kind: ProfileKind!
|
||||
position: String @goField(omittable: true)
|
||||
position: String
|
||||
contractStartDate: Datetime @goField(omittable: true)
|
||||
contractEndDate: Datetime @goField(omittable: true)
|
||||
}
|
||||
@@ -3390,6 +3429,10 @@ type DeleteOrganizationHorizontalLogoPayload {
|
||||
organization: Organization!
|
||||
}
|
||||
|
||||
type CreateUserPayload {
|
||||
profileEdge: ProfileEdge!
|
||||
}
|
||||
|
||||
type InviteMemberPayload {
|
||||
invitationEdge: InvitationEdge!
|
||||
}
|
||||
@@ -3775,6 +3818,17 @@ func (ec *executionContext) field_Mutation_createSCIMConfiguration_args(ctx cont
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_createUser_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
||||
var err error
|
||||
args := map[string]any{}
|
||||
arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNCreateUserInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐCreateUserInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args["input"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_deleteInvitation_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
||||
var err error
|
||||
args := map[string]any{}
|
||||
@@ -4985,6 +5039,41 @@ func (ec *executionContext) fieldContext_CreateSCIMConfigurationPayload_token(_
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _CreateUserPayload_profileEdge(ctx context.Context, field graphql.CollectedField, obj *types.CreateUserPayload) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_CreateUserPayload_profileEdge,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.ProfileEdge, nil
|
||||
},
|
||||
nil,
|
||||
ec.marshalNProfileEdge2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐProfileEdge,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_CreateUserPayload_profileEdge(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "CreateUserPayload",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "cursor":
|
||||
return ec.fieldContext_ProfileEdge_cursor(ctx, field)
|
||||
case "node":
|
||||
return ec.fieldContext_ProfileEdge_node(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type ProfileEdge", field.Name)
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _DeleteInvitationPayload_deletedInvitationId(ctx context.Context, field graphql.CollectedField, obj *types.DeleteInvitationPayload) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
@@ -7550,6 +7639,69 @@ func (ec *executionContext) fieldContext_Mutation_deleteOrganizationHorizontalLo
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_createUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_Mutation_createUser,
|
||||
func(ctx context.Context) (any, error) {
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Mutation().CreateUser(ctx, fc.Args["input"].(types.CreateUserInput))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "PRESENT")
|
||||
if err != nil {
|
||||
var zeroVal *types.CreateUserPayload
|
||||
return zeroVal, err
|
||||
}
|
||||
if ec.directives.Session == nil {
|
||||
var zeroVal *types.CreateUserPayload
|
||||
return zeroVal, errors.New("directive session is not implemented")
|
||||
}
|
||||
return ec.directives.Session(ctx, nil, directive0, required)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
ec.marshalOCreateUserPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐCreateUserPayload,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Mutation_createUser(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Mutation",
|
||||
Field: field,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "profileEdge":
|
||||
return ec.fieldContext_CreateUserPayload_profileEdge(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type CreateUserPayload", field.Name)
|
||||
},
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = ec.Recover(ctx, r)
|
||||
ec.Error(ctx, err)
|
||||
}
|
||||
}()
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
if fc.Args, err = ec.field_Mutation_createUser_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return fc, err
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_inviteMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
@@ -15572,6 +15724,89 @@ func (ec *executionContext) unmarshalInputCreateSCIMConfigurationInput(ctx conte
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputCreateUserInput(ctx context.Context, obj any) (types.CreateUserInput, error) {
|
||||
var it types.CreateUserInput
|
||||
asMap := map[string]any{}
|
||||
for k, v := range obj.(map[string]any) {
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"organizationId", "fullName", "emailAddress", "role", "additionalEmailAddresses", "kind", "position", "contractStartDate", "contractEndDate"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch k {
|
||||
case "organizationId":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("organizationId"))
|
||||
data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.OrganizationID = data
|
||||
case "fullName":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fullName"))
|
||||
data, err := ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.FullName = data
|
||||
case "emailAddress":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("emailAddress"))
|
||||
data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.EmailAddress = data
|
||||
case "role":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("role"))
|
||||
data, err := ec.unmarshalNMembershipRole2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐMembershipRole(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Role = data
|
||||
case "additionalEmailAddresses":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("additionalEmailAddresses"))
|
||||
data, err := ec.unmarshalOEmailAddr2ᚕgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddrᚄ(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.AdditionalEmailAddresses = data
|
||||
case "kind":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("kind"))
|
||||
data, err := ec.unmarshalNProfileKind2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐMembershipProfileKind(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Kind = data
|
||||
case "position":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("position"))
|
||||
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Position = data
|
||||
case "contractStartDate":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("contractStartDate"))
|
||||
data, err := ec.unmarshalODatetime2ᚖtimeᚐTime(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.ContractStartDate = graphql.OmittableOf(data)
|
||||
case "contractEndDate":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("contractEndDate"))
|
||||
data, err := ec.unmarshalODatetime2ᚖtimeᚐTime(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.ContractEndDate = graphql.OmittableOf(data)
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputDeleteInvitationInput(ctx context.Context, obj any) (types.DeleteInvitationInput, error) {
|
||||
var it types.DeleteInvitationInput
|
||||
asMap := map[string]any{}
|
||||
@@ -16431,7 +16666,7 @@ func (ec *executionContext) unmarshalInputUpdateProfileInput(ctx context.Context
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.Position = graphql.OmittableOf(data)
|
||||
it.Position = data
|
||||
case "contractStartDate":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("contractStartDate"))
|
||||
data, err := ec.unmarshalODatetime2ᚖtimeᚐTime(ctx, v)
|
||||
@@ -17156,6 +17391,45 @@ func (ec *executionContext) _CreateSCIMConfigurationPayload(ctx context.Context,
|
||||
return out
|
||||
}
|
||||
|
||||
var createUserPayloadImplementors = []string{"CreateUserPayload"}
|
||||
|
||||
func (ec *executionContext) _CreateUserPayload(ctx context.Context, sel ast.SelectionSet, obj *types.CreateUserPayload) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, createUserPayloadImplementors)
|
||||
|
||||
out := graphql.NewFieldSet(fields)
|
||||
deferred := make(map[string]*graphql.FieldSet)
|
||||
for i, field := range fields {
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("CreateUserPayload")
|
||||
case "profileEdge":
|
||||
out.Values[i] = ec._CreateUserPayload_profileEdge(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
}
|
||||
out.Dispatch(ctx)
|
||||
if out.Invalids > 0 {
|
||||
return graphql.Null
|
||||
}
|
||||
|
||||
atomic.AddInt32(&ec.deferred, int32(len(deferred)))
|
||||
|
||||
for label, dfs := range deferred {
|
||||
ec.processDeferredGroup(graphql.DeferredGroup{
|
||||
Label: label,
|
||||
Path: graphql.GetPath(ctx),
|
||||
FieldSet: dfs,
|
||||
Context: ctx,
|
||||
})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
var deleteInvitationPayloadImplementors = []string{"DeleteInvitationPayload"}
|
||||
|
||||
func (ec *executionContext) _DeleteInvitationPayload(ctx context.Context, sel ast.SelectionSet, obj *types.DeleteInvitationPayload) graphql.Marshaler {
|
||||
@@ -18167,6 +18441,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||
return ec._Mutation_deleteOrganizationHorizontalLogo(ctx, field)
|
||||
})
|
||||
case "createUser":
|
||||
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||
return ec._Mutation_createUser(ctx, field)
|
||||
})
|
||||
case "inviteMember":
|
||||
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||
return ec._Mutation_inviteMember(ctx, field)
|
||||
@@ -21688,6 +21966,11 @@ func (ec *executionContext) unmarshalNCreateSCIMConfigurationInput2goᚗproboᚗ
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNCreateUserInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐCreateUserInput(ctx context.Context, v any) (types.CreateUserInput, error) {
|
||||
res, err := ec.unmarshalInputCreateUserInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNCursorKey2goᚗproboᚗincᚋproboᚋpkgᚋpageᚐCursorKey(ctx context.Context, v any) (page.CursorKey, error) {
|
||||
res, err := cursor.UnmarshalCursorKeyScalar(v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
@@ -23175,6 +23458,13 @@ func (ec *executionContext) marshalOCreateSCIMConfigurationPayload2ᚖgoᚗprobo
|
||||
return ec._CreateSCIMConfigurationPayload(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOCreateUserPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐCreateUserPayload(ctx context.Context, sel ast.SelectionSet, v *types.CreateUserPayload) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._CreateUserPayload(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOCursorKey2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋpageᚐCursorKey(ctx context.Context, v any) (*page.CursorKey, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
|
||||
@@ -118,6 +118,22 @@ type CreateSCIMConfigurationPayload struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type CreateUserInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
FullName string `json:"fullName"`
|
||||
EmailAddress mail.Addr `json:"emailAddress"`
|
||||
Role coredata.MembershipRole `json:"role"`
|
||||
AdditionalEmailAddresses []mail.Addr `json:"additionalEmailAddresses,omitempty"`
|
||||
Kind coredata.MembershipProfileKind `json:"kind"`
|
||||
Position *string `json:"position,omitempty"`
|
||||
ContractStartDate graphql.Omittable[*time.Time] `json:"contractStartDate,omitempty"`
|
||||
ContractEndDate graphql.Omittable[*time.Time] `json:"contractEndDate,omitempty"`
|
||||
}
|
||||
|
||||
type CreateUserPayload struct {
|
||||
ProfileEdge *ProfileEdge `json:"profileEdge"`
|
||||
}
|
||||
|
||||
type DeleteInvitationInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
InvitationID gid.GID `json:"invitationId"`
|
||||
@@ -555,7 +571,7 @@ type UpdateProfileInput struct {
|
||||
FullName string `json:"fullName"`
|
||||
AdditionalEmailAddresses []mail.Addr `json:"additionalEmailAddresses,omitempty"`
|
||||
Kind coredata.MembershipProfileKind `json:"kind"`
|
||||
Position graphql.Omittable[*string] `json:"position,omitempty"`
|
||||
Position *string `json:"position,omitempty"`
|
||||
ContractStartDate graphql.Omittable[*time.Time] `json:"contractStartDate,omitempty"`
|
||||
ContractEndDate graphql.Omittable[*time.Time] `json:"contractEndDate,omitempty"`
|
||||
}
|
||||
|
||||
@@ -873,6 +873,40 @@ func (r *mutationResolver) DeleteOrganizationHorizontalLogo(ctx context.Context,
|
||||
panic(fmt.Errorf("not implemented: DeleteOrganizationHorizontalLogo - deleteOrganizationHorizontalLogo"))
|
||||
}
|
||||
|
||||
// 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.Conflictf(ctx, "user %q already exists", input.EmailAddress.String())
|
||||
}
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.CreateUserPayload{
|
||||
ProfileEdge: types.NewProfileEdge(profile, coredata.MembershipProfileOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InviteMember is the resolver for the inviteMember field.
|
||||
func (r *mutationResolver) InviteMember(ctx context.Context, input types.InviteMemberInput) (*types.InviteMemberPayload, error) {
|
||||
if err := r.authorize(ctx, input.OrganizationID, iam.ActionInvitationCreate); err != nil {
|
||||
@@ -948,7 +982,7 @@ func (r *mutationResolver) UpdateProfile(ctx context.Context, input types.Update
|
||||
FullName: input.FullName,
|
||||
AdditionalEmailAddresses: input.AdditionalEmailAddresses,
|
||||
Kind: input.Kind,
|
||||
Position: gqlutils.UnwrapOmittable(input.Position),
|
||||
Position: input.Position,
|
||||
ContractStartDate: gqlutils.UnwrapOmittable(input.ContractStartDate),
|
||||
ContractEndDate: gqlutils.UnwrapOmittable(input.ContractEndDate),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user