From 1f483e44deae25e6d5a82f7f5fb13e0c141b2d29 Mon Sep 17 00:00:00 2001 From: Peter Sanchez Date: Tue, 4 Jun 2024 18:55:28 -0600 Subject: [PATCH] GraphQL Mutation to remove a member. References: https://todo.code.netlandish.com/~netlandish/links/79 --- api/graph/generated.go | 136 ++++++++++++++++++++++++++++++++++ api/graph/schema.graphqls | 1 + api/graph/schema.resolvers.go | 82 ++++++++++++++++++++ 3 files changed, 219 insertions(+) diff --git a/api/graph/generated.go b/api/graph/generated.go index 240d977..44fa6e9 100644 --- a/api/graph/generated.go +++ b/api/graph/generated.go @@ -227,6 +227,7 @@ type ComplexityRoot struct { DeleteLinkShort func(childComplexity int, id int) int DeleteListing func(childComplexity int, id int) int DeleteListingLink func(childComplexity int, id int) int + DeleteMember func(childComplexity int, orgSlug string, email string) int DeleteQRCode func(childComplexity int, id int) int Follow func(childComplexity int, orgSlug string) int Register func(childComplexity int, input *model.RegisterInput) int @@ -438,6 +439,7 @@ type MutationResolver interface { DeleteLink(ctx context.Context, id int) (*model.DeletePayload, error) AddNote(ctx context.Context, input *model.NoteInput) (*models.OrgLink, error) AddMember(ctx context.Context, input *model.MemberInput) (*model.AddMemberPayload, error) + DeleteMember(ctx context.Context, orgSlug string, email string) (*model.AddMemberPayload, error) ConfirmMember(ctx context.Context, key string) (*model.AddMemberPayload, error) Register(ctx context.Context, input *model.RegisterInput) (*models.User, error) CompleteRegister(ctx context.Context, input *model.CompleteRegisterInput) (*models.User, error) @@ -1394,6 +1396,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.DeleteListingLink(childComplexity, args["id"].(int)), true + case "Mutation.deleteMember": + if e.complexity.Mutation.DeleteMember == nil { + break + } + + args, err := ec.field_Mutation_deleteMember_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.DeleteMember(childComplexity, args["orgSlug"].(string), args["email"].(string)), true + case "Mutation.deleteQRCode": if e.complexity.Mutation.DeleteQRCode == nil { break @@ -2957,6 +2971,30 @@ func (ec *executionContext) field_Mutation_deleteListing_args(ctx context.Contex return args, nil } +func (ec *executionContext) field_Mutation_deleteMember_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["orgSlug"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("orgSlug")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["orgSlug"] = arg0 + var arg1 string + if tmp, ok := rawArgs["email"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) + arg1, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["email"] = arg1 + return args, nil +} + func (ec *executionContext) field_Mutation_deleteQRCode_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -9441,6 +9479,95 @@ func (ec *executionContext) fieldContext_Mutation_addMember(ctx context.Context, return fc, nil } +func (ec *executionContext) _Mutation_deleteMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_deleteMember(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().DeleteMember(rctx, fc.Args["orgSlug"].(string), fc.Args["email"].(string)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + scope, err := ec.unmarshalNAccessScope2linksᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "ORGS") + if err != nil { + return nil, err + } + kind, err := ec.unmarshalNAccessKind2linksᚋapiᚋgraphᚋmodelᚐAccessKind(ctx, "RW") + if err != nil { + return nil, err + } + if ec.directives.Access == nil { + return nil, errors.New("directive access is not implemented") + } + return ec.directives.Access(ctx, nil, directive0, scope, kind) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*model.AddMemberPayload); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *links/api/graph/model.AddMemberPayload`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.AddMemberPayload) + fc.Result = res + return ec.marshalNAddMemberPayload2ᚖlinksᚋapiᚋgraphᚋmodelᚐAddMemberPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_deleteMember(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 "success": + return ec.fieldContext_AddMemberPayload_success(ctx, field) + case "message": + return ec.fieldContext_AddMemberPayload_message(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type AddMemberPayload", 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_deleteMember_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + func (ec *executionContext) _Mutation_confirmMember(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_confirmMember(ctx, field) if err != nil { @@ -24657,6 +24784,15 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return ec._Mutation_addMember(ctx, field) }) + if out.Values[i] == graphql.Null { + invalids++ + } + case "deleteMember": + + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_deleteMember(ctx, field) + }) + if out.Values[i] == graphql.Null { invalids++ } diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls index fad9ce0..4f73d5e 100644 --- a/api/graph/schema.graphqls +++ b/api/graph/schema.graphqls @@ -763,6 +763,7 @@ type Mutation { "Add organization members" addMember(input: MemberInput): AddMemberPayload! @access(scope: ORGS, kind: RW) + deleteMember(orgSlug: String!, email: String!): AddMemberPayload! @access(scope: ORGS, kind: RW) confirmMember(key: String!): AddMemberPayload! @access(scope: PROFILE, kind: RW) "Register an account" diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index 88a5868..7aa5704 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -896,6 +896,88 @@ func (r *mutationResolver) AddMember(ctx context.Context, input *model.MemberInp return addMemberPayload, nil } +// DeleteMember is the resolver for the deleteMember field. +func (r *mutationResolver) DeleteMember(ctx context.Context, orgSlug string, email string) (*model.AddMemberPayload, error) { + tokenUser := oauth2.ForContext(ctx) + if tokenUser == nil { + return nil, valid.ErrAuthorization + } + user := tokenUser.User.(*models.User) + c := server.EchoForContext(ctx) + lang := links.GetLangFromRequest(c.Request(), user) + lt := localizer.GetLocalizer(lang) + + emailRegex, err := regexp.Compile(links.EmailPattern) + if err != nil { + return nil, fmt.Errorf(lt.Translate("Error compiling url regex: %s", err)) + } + + validator := valid.New(ctx) + validator.Expect(orgSlug != "", lt.Translate("Org slug is required")). + WithField("org"). + WithCode(valid.ErrValidationCode) + validator.Expect(email != "", lt.Translate("User email is required")). + WithField("email"). + WithCode(valid.ErrValidationCode) + validator.Expect(len(email) < 255, lt.Translate("Email may not exceed 255 characters")). + WithField("email"). + WithCode(valid.ErrValidationCode) + validator.Expect(emailRegex.MatchString(email), lt.Translate("Invalid email format")). + WithField("email"). + WithCode(valid.ErrValidationCode) + + if !validator.Ok() { + return nil, nil + } + + org, err := user.GetOrgsSlug(ctx, models.OrgUserPermissionAdminWrite, orgSlug) + if err != nil { + return nil, err + } + if org == nil { + validator.Error(lt.Translate("Organization Not Found")). + WithCode(valid.ErrNotFoundCode) + return nil, nil + } + + opts := &database.FilterOptions{ + Filter: sq.Eq{"u.email": strings.ToLower(email)}, + Limit: 1, + } + + users, err := models.GetUsers(ctx, opts) + if err != nil { + return nil, err + } + if len(users) == 0 { + validator.Error(lt.Translate("User not found for given email")). + WithCode(valid.ErrNotFoundCode) + return nil, nil + } + duser := users[0] + + opts = &database.FilterOptions{ + Filter: sq.And{ + sq.Eq{"org_id": org.ID}, + sq.Eq{"user_id": duser.ID}, + }, + } + ousers, err := models.GetOrgUsers(ctx, opts) + if err != nil { + return nil, err + } + + addMemberPayload := &model.AddMemberPayload{} + if len(ousers) == 0 { + addMemberPayload.Success = false + addMemberPayload.Message = lt.Translate("The user for given email is not a member of given organization") + } else { + addMemberPayload.Success = true + addMemberPayload.Message = lt.Translate("The member was removed successfully") + } + return addMemberPayload, nil +} + // ConfirmMember is the resolver for the confirmMember field. func (r *mutationResolver) ConfirmMember(ctx context.Context, key string) (*model.AddMemberPayload, error) { lang := links.GetLangFromRequest(server.EchoForContext(ctx).Request(), nil) -- 2.45.2