From 8f649f2a59dc4c03f08de96dba20dd942299923b Mon Sep 17 00:00:00 2001 From: Nimer Farahty Date: Mon, 9 Jun 2025 03:03:33 +0300 Subject: [PATCH] fix camelize generated fields --- generate.go | 3 +- generated/generated.go | 166 ++++++++++++++++++++++++++++++++ gql/category.gql | 3 + models/models_gen.go | 40 ++++---- resolvers/category.resolvers.go | 5 + services/auth/auth.go | 1 + services/category/category.go | 4 + utils/camelize.go | 37 +++++++ 8 files changed, 239 insertions(+), 20 deletions(-) create mode 100644 utils/camelize.go diff --git a/generate.go b/generate.go index c7ff56b..60f7b41 100644 --- a/generate.go +++ b/generate.go @@ -7,6 +7,7 @@ import ( "os" "strings" + "git.farahty.com/nimer/go-mongo/utils" "github.com/99designs/gqlgen/api" "github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/plugin/modelgen" @@ -29,7 +30,7 @@ func mutateHook(b *modelgen.ModelBuild) *modelgen.ModelBuild { field.Tag = strings.TrimSuffix(field.Tag, `"`) + `,omitempty" bson:"-"` } } else { - field.Tag = strings.TrimSuffix(field.Tag, `"`) + `" bson:"` + strings.ToLower(field.Name) + `,omitempty"` + field.Tag = strings.TrimSuffix(field.Tag, `"`) + `" bson:"` + utils.Camelize(field.Name) + `,omitempty"` } } } diff --git a/generated/generated.go b/generated/generated.go index 766e743..3490ce5 100644 --- a/generated/generated.go +++ b/generated/generated.go @@ -58,6 +58,7 @@ type DirectiveRoot struct { type ComplexityRoot struct { Category struct { Body func(childComplexity int) int + Children func(childComplexity int) int CreatedAt func(childComplexity int) int CreatedBy func(childComplexity int) int CreatedByID func(childComplexity int) int @@ -130,6 +131,8 @@ type ComplexityRoot struct { type CategoryResolver interface { Parent(ctx context.Context, obj *models.Category) (*models.Category, error) + Children(ctx context.Context, obj *models.Category) ([]*models.Category, error) + CreatedBy(ctx context.Context, obj *models.Category) (*models.User, error) UpdatedBy(ctx context.Context, obj *models.Category) (*models.User, error) @@ -189,6 +192,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Category.Body(childComplexity), true + case "Category.children": + if e.complexity.Category.Children == nil { + break + } + + return e.complexity.Category.Children(childComplexity), true + case "Category.createdAt": if e.complexity.Category.CreatedAt == nil { break @@ -752,6 +762,9 @@ input TranslatedInput { parent: Category @goField(forceResolver: true) parentId: ID + "#bson:ignore" + children: [Category] @goField(forceResolver: true) + createdAt: Time! updatedAt: Time! @@ -1389,6 +1402,8 @@ func (ec *executionContext) fieldContext_Category_parent(_ context.Context, fiel return ec.fieldContext_Category_parent(ctx, field) case "parentId": return ec.fieldContext_Category_parentId(ctx, field) + case "children": + return ec.fieldContext_Category_children(ctx, field) case "createdAt": return ec.fieldContext_Category_createdAt(ctx, field) case "updatedAt": @@ -1453,6 +1468,77 @@ func (ec *executionContext) fieldContext_Category_parentId(_ context.Context, fi return fc, nil } +func (ec *executionContext) _Category_children(ctx context.Context, field graphql.CollectedField, obj *models.Category) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Category_children(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) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Category().Children(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*models.Category) + fc.Result = res + return ec.marshalOCategory2ᚕᚖgitᚗfarahtyᚗcomᚋnimerᚋgoᚑmongoᚋmodelsᚐCategory(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Category_children(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Category", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Category_id(ctx, field) + case "title": + return ec.fieldContext_Category_title(ctx, field) + case "body": + return ec.fieldContext_Category_body(ctx, field) + case "parent": + return ec.fieldContext_Category_parent(ctx, field) + case "parentId": + return ec.fieldContext_Category_parentId(ctx, field) + case "children": + return ec.fieldContext_Category_children(ctx, field) + case "createdAt": + return ec.fieldContext_Category_createdAt(ctx, field) + case "updatedAt": + return ec.fieldContext_Category_updatedAt(ctx, field) + case "createdBy": + return ec.fieldContext_Category_createdBy(ctx, field) + case "createdById": + return ec.fieldContext_Category_createdById(ctx, field) + case "updatedBy": + return ec.fieldContext_Category_updatedBy(ctx, field) + case "updatedById": + return ec.fieldContext_Category_updatedById(ctx, field) + case "owner": + return ec.fieldContext_Category_owner(ctx, field) + case "ownerId": + return ec.fieldContext_Category_ownerId(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Category", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Category_createdAt(ctx context.Context, field graphql.CollectedField, obj *models.Category) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Category_createdAt(ctx, field) if err != nil { @@ -2102,6 +2188,8 @@ func (ec *executionContext) fieldContext_Mutation_createCategory(ctx context.Con return ec.fieldContext_Category_parent(ctx, field) case "parentId": return ec.fieldContext_Category_parentId(ctx, field) + case "children": + return ec.fieldContext_Category_children(ctx, field) case "createdAt": return ec.fieldContext_Category_createdAt(ctx, field) case "updatedAt": @@ -2327,6 +2415,8 @@ func (ec *executionContext) fieldContext_Query_categories(_ context.Context, fie return ec.fieldContext_Category_parent(ctx, field) case "parentId": return ec.fieldContext_Category_parentId(ctx, field) + case "children": + return ec.fieldContext_Category_children(ctx, field) case "createdAt": return ec.fieldContext_Category_createdAt(ctx, field) case "updatedAt": @@ -2399,6 +2489,8 @@ func (ec *executionContext) fieldContext_Query_category(ctx context.Context, fie return ec.fieldContext_Category_parent(ctx, field) case "parentId": return ec.fieldContext_Category_parentId(ctx, field) + case "children": + return ec.fieldContext_Category_children(ctx, field) case "createdAt": return ec.fieldContext_Category_createdAt(ctx, field) case "updatedAt": @@ -5979,6 +6071,39 @@ func (ec *executionContext) _Category(ctx context.Context, sel ast.SelectionSet, out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "parentId": out.Values[i] = ec._Category_parentId(ctx, field, obj) + case "children": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Category_children(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "createdAt": out.Values[i] = ec._Category_createdAt(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -7584,6 +7709,47 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast return res } +func (ec *executionContext) marshalOCategory2ᚕᚖgitᚗfarahtyᚗcomᚋnimerᚋgoᚑmongoᚋmodelsᚐCategory(ctx context.Context, sel ast.SelectionSet, v []*models.Category) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalOCategory2ᚖgitᚗfarahtyᚗcomᚋnimerᚋgoᚑmongoᚋmodelsᚐCategory(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + return ret +} + func (ec *executionContext) marshalOCategory2ᚖgitᚗfarahtyᚗcomᚋnimerᚋgoᚑmongoᚋmodelsᚐCategory(ctx context.Context, sel ast.SelectionSet, v *models.Category) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/gql/category.gql b/gql/category.gql index e773a9f..3a70a03 100644 --- a/gql/category.gql +++ b/gql/category.gql @@ -8,6 +8,9 @@ type Category implements Base { parent: Category @goField(forceResolver: true) parentId: ID + "#bson:ignore" + children: [Category] @goField(forceResolver: true) + createdAt: Time! updatedAt: Time! diff --git a/models/models_gen.go b/models/models_gen.go index 8ea9178..35fff68 100644 --- a/models/models_gen.go +++ b/models/models_gen.go @@ -27,19 +27,21 @@ type Category struct { Title []*Translated `json:"title" bson:"title,omitempty"` Body []*Translated `json:"body,omitempty" bson:"body,omitempty"` // #bson:ignore - Parent *Category `json:"parent,omitempty,omitempty" bson:"-"` - ParentID *primitive.ObjectID `json:"parentId,omitempty" bson:"parentid,omitempty"` - CreatedAt time.Time `json:"createdAt" bson:"createdat,omitempty"` - UpdatedAt time.Time `json:"updatedAt" bson:"updatedat,omitempty"` + Parent *Category `json:"parent,omitempty,omitempty" bson:"-"` + ParentID *primitive.ObjectID `json:"parentId,omitempty" bson:"parentId,omitempty"` + // #bson:ignore + Children []*Category `json:"children,omitempty,omitempty" bson:"-"` + CreatedAt time.Time `json:"createdAt" bson:"createdAt,omitempty"` + UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt,omitempty"` // #bson:ignore CreatedBy *User `json:"createdBy,omitempty" bson:"-"` - CreatedByID primitive.ObjectID `json:"createdById" bson:"createdbyid,omitempty"` + CreatedByID primitive.ObjectID `json:"createdById" bson:"createdById,omitempty"` // #bson:ignore UpdatedBy *User `json:"updatedBy,omitempty" bson:"-"` - UpdatedByID primitive.ObjectID `json:"updatedById" bson:"updatedbyid,omitempty"` + UpdatedByID primitive.ObjectID `json:"updatedById" bson:"updatedById,omitempty"` // #bson:ignore Owner *User `json:"owner,omitempty,omitempty" bson:"-"` - OwnerID primitive.ObjectID `json:"ownerId" bson:"ownerid,omitempty"` + OwnerID primitive.ObjectID `json:"ownerId" bson:"ownerId,omitempty"` } func (Category) IsBase() {} @@ -53,7 +55,7 @@ func (this Category) GetOwner() *User { return this.Owner } type CreateCategoryInput struct { Title []*TranslatedInput `json:"title" bson:"title,omitempty"` Body []*TranslatedInput `json:"body,omitempty" bson:"body,omitempty"` - ParentID *primitive.ObjectID `json:"parentId,omitempty" bson:"parentid,omitempty"` + ParentID *primitive.ObjectID `json:"parentId,omitempty" bson:"parentId,omitempty"` } type CreateTodoInput struct { @@ -75,8 +77,8 @@ type LoginInput struct { type LoginResponse struct { User *User `json:"user" bson:"user,omitempty"` - AccessToken string `json:"accessToken" bson:"accesstoken,omitempty"` - RefreshToken string `json:"refreshToken" bson:"refreshtoken,omitempty"` + AccessToken string `json:"accessToken" bson:"accessToken,omitempty"` + RefreshToken string `json:"refreshToken" bson:"refreshToken,omitempty"` } type Mutation struct { @@ -92,17 +94,17 @@ type Todo struct { ID primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"` Title *string `json:"title,omitempty" bson:"title,omitempty"` Completed *bool `json:"completed,omitempty" bson:"completed,omitempty"` - CreatedAt time.Time `json:"createdAt" bson:"createdat,omitempty"` - UpdatedAt time.Time `json:"updatedAt" bson:"updatedat,omitempty"` + CreatedAt time.Time `json:"createdAt" bson:"createdAt,omitempty"` + UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt,omitempty"` // #bson:ignore CreatedBy *User `json:"createdBy,omitempty" bson:"-"` - CreatedByID primitive.ObjectID `json:"createdById" bson:"createdbyid,omitempty"` + CreatedByID primitive.ObjectID `json:"createdById" bson:"createdById,omitempty"` // #bson:ignore UpdatedBy *User `json:"updatedBy,omitempty" bson:"-"` - UpdatedByID primitive.ObjectID `json:"updatedById" bson:"updatedbyid,omitempty"` + UpdatedByID primitive.ObjectID `json:"updatedById" bson:"updatedById,omitempty"` // #bson:ignore Owner *User `json:"owner,omitempty,omitempty" bson:"-"` - OwnerID primitive.ObjectID `json:"ownerId" bson:"ownerid,omitempty"` + OwnerID primitive.ObjectID `json:"ownerId" bson:"ownerId,omitempty"` } func (Todo) IsBase() {} @@ -115,13 +117,13 @@ func (this Todo) GetOwner() *User { return this.Owner } type Translated struct { Value string `json:"value" bson:"value,omitempty"` - IsPrimary bool `json:"isPrimary" bson:"isprimary,omitempty"` + IsPrimary bool `json:"isPrimary" bson:"isPrimary,omitempty"` Language string `json:"language" bson:"language,omitempty"` } type TranslatedInput struct { Value string `json:"value" bson:"value,omitempty"` - IsPrimary bool `json:"isPrimary" bson:"isprimary,omitempty"` + IsPrimary bool `json:"isPrimary" bson:"isPrimary,omitempty"` Language string `json:"language" bson:"language,omitempty"` } @@ -132,8 +134,8 @@ type User struct { Type *string `json:"type,omitempty" bson:"type,omitempty"` Status *string `json:"status,omitempty" bson:"status,omitempty"` Verified *bool `json:"verified,omitempty" bson:"verified,omitempty"` - Password *string `json:"-" bson:"password,omitempty"` - Token *string `json:"-" bson:"token,omitempty"` + Password *string `json:"-" bson:"Password,omitempty"` + Token *string `json:"-" bson:"Token,omitempty"` } type Role string diff --git a/resolvers/category.resolvers.go b/resolvers/category.resolvers.go index 4533f5b..065b512 100644 --- a/resolvers/category.resolvers.go +++ b/resolvers/category.resolvers.go @@ -22,6 +22,11 @@ func (r *categoryResolver) Parent(ctx context.Context, obj *models.Category) (*m return categoryService.FindByID(ctx, *obj.ParentID) } +// Children is the resolver for the children field. +func (r *categoryResolver) Children(ctx context.Context, obj *models.Category) ([]*models.Category, error) { + return categoryService.FindChildren(ctx, obj.ID) +} + // CreatedBy is the resolver for the createdBy field. func (r *categoryResolver) CreatedBy(ctx context.Context, obj *models.Category) (*models.User, error) { return userService.FindById(ctx, obj.CreatedByID) diff --git a/services/auth/auth.go b/services/auth/auth.go index 80b56a2..4b6d4ea 100644 --- a/services/auth/auth.go +++ b/services/auth/auth.go @@ -33,6 +33,7 @@ func Login(ctx context.Context, loginInput *models.LoginInput, redisClient redis user, err := app.FindOne[models.User](ctx, "users", filter) if err != nil { + logFailedLoginAttempt(ctx, loginInput.Identity, redisClient) // optional return nil, err } diff --git a/services/category/category.go b/services/category/category.go index a90a1bf..b55a8ed 100644 --- a/services/category/category.go +++ b/services/category/category.go @@ -16,6 +16,10 @@ func Find(ctx context.Context) ([]*models.Category, error) { return app.Find[models.Category](ctx, coll, bson.D{}) } +func FindChildren(ctx context.Context, id primitive.ObjectID) ([]*models.Category, error) { + return app.Find[models.Category](ctx, coll, bson.M{"parentId": id}) +} + func Create(ctx context.Context, input models.CreateCategoryInput) (*models.Category, error) { return app.InsertOne[models.Category](ctx, coll, input) } diff --git a/utils/camelize.go b/utils/camelize.go new file mode 100644 index 0000000..8085e0e --- /dev/null +++ b/utils/camelize.go @@ -0,0 +1,37 @@ +package utils + +import ( + "strings" + "unicode" +) + +func Camelize(s string) string { + // If input has no delimiters, assume it's already in camelCase or PascalCase + if !strings.ContainsAny(s, "_- ") { + return s[:1] + s[1:] // No change (optionally lowercase first letter: strings.ToLower(s[:1]) + s[1:]) + } + + // Otherwise, split and camelCase it + parts := strings.FieldsFunc(s, func(r rune) bool { + return r == '_' || r == '-' || unicode.IsSpace(r) + }) + + if len(parts) == 0 { + return "" + } + + var b strings.Builder + b.WriteString(strings.ToLower(parts[0])) + + for _, part := range parts[1:] { + if len(part) == 0 { + continue + } + b.WriteString(strings.ToUpper(part[:1])) + if len(part) > 1 { + b.WriteString(part[1:]) + } + } + + return b.String() +}