From d127340724df2a1e56aadaefe1957fa2ff9a3794 Mon Sep 17 00:00:00 2001 From: linty Date: Fri, 1 Nov 2024 23:36:26 +0800 Subject: [PATCH 01/17] feat: fix comment --- example/user/models/model.go | 6 +-- example/user/repo/repo.go | 4 +- example/user/resolver/operation_gen.go | 4 +- example/user/resolver/query.go | 56 +++++++++++++------------- example/user/schema/user.graphql | 1 + graphql/ast/funcs.go | 3 ++ graphql/parser/parsing.go | 40 +++++++++--------- 7 files changed, 59 insertions(+), 55 deletions(-) diff --git a/example/user/models/model.go b/example/user/models/model.go index fc40aba..7d0c345 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -7,7 +7,7 @@ import "github.com/light-speak/lighthouse/graphql/model" type User struct { model.Model Name string `json:"name" gorm:"index;type:varchar(255)" ` - Posts []Post `json:"posts" ` + Posts []Post `json:"posts" gorm:"comment:五二零" ` } func (*User) IsModel() bool { return true } @@ -18,11 +18,11 @@ func (*User) TypeName() string { return "user" } type Post struct { model.ModelSoftDelete - Title string `json:"title" gorm:"index;type:varchar(255)" ` + UserId int64 `json:"user_id" ` User User `json:"user" ` Enum TestEnum `json:"enum" ` + Title string `json:"title" gorm:"index;type:varchar(255)" ` Content string `json:"content" gorm:"type:varchar(255)" ` - UserId int64 `json:"user_id" ` } func (*Post) IsModel() bool { return true } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index cfd216e..f99d9a6 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,11 +2,11 @@ package repo import ( - "github.com/light-speak/lighthouse/graphql/model" - "sync" "github.com/light-speak/lighthouse/context" "user/models" "github.com/light-speak/lighthouse/graphql/ast" + "sync" + "github.com/light-speak/lighthouse/graphql/model" "gorm.io/gorm" ) diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index 9bb0ddf..7a7c579 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,12 +2,12 @@ package resolver import ( + "github.com/light-speak/lighthouse/graphql/model" + "fmt" "github.com/light-speak/lighthouse/graphql/excute" "github.com/light-speak/lighthouse/graphql" - "github.com/light-speak/lighthouse/graphql/model" "github.com/light-speak/lighthouse/context" "user/models" - "fmt" ) func init() { diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index c7231b2..7958e64 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,35 +2,14 @@ package resolver import ( - "github.com/light-speak/lighthouse/log" - "github.com/light-speak/lighthouse/context" "user/models" "fmt" "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/log" + "github.com/light-speak/lighthouse/context" ) -func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { - // Func:GetPost user code start. Do not remove this comment. - log.Debug().Msg("GetPostResolver") - db := model.GetDB() - post := &models.Post{} - db.Where("id = ?", fuck).First(post) - return post, nil - // Func:GetPost user code end. Do not remove this comment. -} -func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { - // Func:TestPostId user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) - return nil, nil - // Func:TestPostId user code end. Do not remove this comment. -} -func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { - // Func:TestPostInt user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) - return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. -} func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { // Func:GetPosts user code start. Do not remove this comment. posts := []*models.Post{} @@ -39,11 +18,11 @@ func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) return posts, nil // Func:GetPosts user code end. Do not remove this comment. } -func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { - // Func:TestPostInput user code start. Do not remove this comment. - res := fmt.Sprintf("input: %+v", input) - return res, nil - // Func:TestPostInput user code end. Do not remove this comment. +func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { + // Func:TestPostInt user code start. Do not remove this comment. + log.Debug().Msgf("id: %d", id) + return nil, nil + // Func:TestPostInt user code end. Do not remove this comment. } func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { // Func:GetPostIds user code start. Do not remove this comment. @@ -56,4 +35,25 @@ func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, e res := fmt.Sprintf("啥也不是!:%v", *enum == models.A) return res, nil // Func:TestPostEnum user code end. Do not remove this comment. +} +func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { + // Func:GetPost user code start. Do not remove this comment. + log.Debug().Msg("GetPostResolver") + db := model.GetDB() + post := &models.Post{} + db.Where("id = ?", fuck).First(post) + return post, nil + // Func:GetPost user code end. Do not remove this comment. +} +func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { + // Func:TestPostId user code start. Do not remove this comment. + log.Debug().Msgf("id: %d", id) + return nil, nil + // Func:TestPostId user code end. Do not remove this comment. +} +func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { + // Func:TestPostInput user code start. Do not remove this comment. + res := fmt.Sprintf("input: %+v", input) + return res, nil + // Func:TestPostInput user code end. Do not remove this comment. } \ No newline at end of file diff --git a/example/user/schema/user.graphql b/example/user/schema/user.graphql index fe61b61..93b5f53 100644 --- a/example/user/schema/user.graphql +++ b/example/user/schema/user.graphql @@ -1,6 +1,7 @@ "test user" type User implements HasName @key(fields: "id") @model { name: String! @index + "五二零" posts: [Post!]! @hasMany(relation: "post", foreignKey: "user_id") } diff --git a/graphql/ast/funcs.go b/graphql/ast/funcs.go index 931faa9..79e3790 100644 --- a/graphql/ast/funcs.go +++ b/graphql/ast/funcs.go @@ -104,6 +104,9 @@ func genTag(field *Field) string { if !hasType && field.Type.GetRealType().Name == "String" { tags["gorm"] = append(tags["gorm"], fmt.Sprintf("type:varchar(%s)", "255")) } + if field.Description != nil { + tags["gorm"] = append(tags["gorm"], fmt.Sprintf("comment:%s", *field.Description)) + } // Build the tag string using strings.Builder var builder strings.Builder diff --git a/graphql/parser/parsing.go b/graphql/parser/parsing.go index c7951d0..52ee857 100644 --- a/graphql/parser/parsing.go +++ b/graphql/parser/parsing.go @@ -27,7 +27,7 @@ func (p *Parser) parseObject() { object := &ast.ObjectNode{ BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, BaseNode: ast.BaseNode{ @@ -116,7 +116,7 @@ func (p *Parser) parseDirective() *ast.Directive { directive := &ast.Directive{ Name: p.expectAndGetValue(lexer.At), BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -139,7 +139,7 @@ func (p *Parser) parseDirective() *ast.Directive { // createdAt: DateTime func (p *Parser) parseField(isOperation bool, alias string) *ast.Field { // Handle comments - if p.currToken.Type == lexer.Comment { + if p.currToken.Type == lexer.Comment || p.currToken.Type == lexer.Message { p.nextToken() return nil } @@ -158,12 +158,12 @@ func (p *Parser) parseField(isOperation bool, alias string) *ast.Field { Type: &ast.TypeRef{ Name: p.currToken.Value, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, }, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -187,12 +187,12 @@ func (p *Parser) parseField(isOperation bool, alias string) *ast.Field { Type: &ast.TypeRef{ Name: p.currToken.Value, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, }, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -205,7 +205,7 @@ func (p *Parser) parseField(isOperation bool, alias string) *ast.Field { Alias: alias, Description: p.parseDescription(), BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -239,7 +239,7 @@ func (p *Parser) parseField(isOperation bool, alias string) *ast.Field { } // Skip any trailing comments - for p.currToken.Type == lexer.Comment { + for p.currToken.Type == lexer.Comment || p.currToken.Type == lexer.Message { p.nextToken() } @@ -301,7 +301,7 @@ func (p *Parser) parseTypeReferenceAndValue() (*ast.TypeRef, any) { Kind: ast.KindList, OfType: innerType, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -317,7 +317,7 @@ func (p *Parser) parseTypeReferenceAndValue() (*ast.TypeRef, any) { Kind: "", Name: p.currToken.Value, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -333,7 +333,7 @@ func (p *Parser) parseTypeReferenceAndValue() (*ast.TypeRef, any) { Kind: ast.KindNonNull, OfType: fieldType, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -388,7 +388,7 @@ func (p *Parser) parseArgument() *ast.Argument { IsVariable: !isReference && isVariable, IsReference: isReference, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -469,7 +469,7 @@ func (p *Parser) parseDirectiveDefinition() { Name: p.expectAndGetValue(lexer.At), Description: description, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -524,7 +524,7 @@ func (p *Parser) parseEnum() { Directives: p.parseDirectives(), }, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -558,7 +558,7 @@ func (p *Parser) parseEnumValue() *ast.EnumValue { Description: description, Directives: directives, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -596,7 +596,7 @@ func (p *Parser) parseInput() { Directives: p.parseDirectives(), }, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -640,7 +640,7 @@ func (p *Parser) parseInterface() { Name: p.expectAndGetValue(lexer.Interface), }, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -675,7 +675,7 @@ func (p *Parser) parseScalar() { Directives: p.parseDirectives(), }, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } @@ -698,7 +698,7 @@ func (p *Parser) parseUnion() { Directives: p.parseDirectives(), }, BaseLocation: ast.BaseLocation{ - Line: p.currToken.Line, + Line: p.currToken.Line, Column: p.currToken.LinePosition, }, } From 007b834f3131c41670284e54ba18a18efc46266d Mon Sep 17 00:00:00 2001 From: linty Date: Fri, 1 Nov 2024 23:44:21 +0800 Subject: [PATCH 02/17] feat: 1 --- example/user/models/enum.go | 36 +++++++++---------- example/user/models/model.go | 6 ++-- example/user/repo/repo.go | 6 ++-- example/user/resolver/operation_gen.go | 2 +- example/user/resolver/query.go | 48 +++++++++++++------------- graphql/model/generate/tpl/enum.tpl | 8 ++--- 6 files changed, 53 insertions(+), 53 deletions(-) diff --git a/example/user/models/enum.go b/example/user/models/enum.go index fce442e..e245c04 100644 --- a/example/user/models/enum.go +++ b/example/user/models/enum.go @@ -7,15 +7,15 @@ package models type SortOrder int8 const ( - ASC SortOrder = 1 - DESC SortOrder = -1 + SortOrderASC SortOrder = 1 + SortOrderDESC SortOrder = -1 ) func (e SortOrder) ToString() string { switch e { - case ASC: + case SortOrderASC: return "ASC" - case DESC: + case SortOrderDESC: return "DESC" default: return "unknown" @@ -23,21 +23,21 @@ func (e SortOrder) ToString() string { } var SortOrderMap = map[string]SortOrder{ - "ASC": ASC, - "DESC": DESC, + "ASC": SortOrderASC, + "DESC": SortOrderDESC, } type TestEnum int8 const ( - A TestEnum = 1 - B TestEnum = 2 + TestEnumA TestEnum = 1 + TestEnumB TestEnum = 2 ) func (e TestEnum) ToString() string { switch e { - case A: + case TestEnumA: return "A" - case B: + case TestEnumB: return "B" default: return "unknown" @@ -45,21 +45,21 @@ func (e TestEnum) ToString() string { } var TestEnumMap = map[string]TestEnum{ - "A": A, - "B": B, + "A": TestEnumA, + "B": TestEnumB, } type TestEnum2 int8 const ( - A2 TestEnum2 = iota - B2 + TestEnum2A2 = iota + TestEnum2B2 ) func (e TestEnum2) ToString() string { switch e { - case A2: + case TestEnum2A2: return "A2" - case B2: + case TestEnum2B2: return "B2" default: return "unknown" @@ -67,6 +67,6 @@ func (e TestEnum2) ToString() string { } var TestEnum2Map = map[string]TestEnum2{ - "A2": A2, - "B2": B2, + "A2": TestEnum2A2, + "B2": TestEnum2B2, } diff --git a/example/user/models/model.go b/example/user/models/model.go index 7d0c345..89f8b65 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -7,7 +7,7 @@ import "github.com/light-speak/lighthouse/graphql/model" type User struct { model.Model Name string `json:"name" gorm:"index;type:varchar(255)" ` - Posts []Post `json:"posts" gorm:"comment:五二零" ` + Posts []Post `gorm:"comment:五二零" json:"posts" ` } func (*User) IsModel() bool { return true } @@ -18,11 +18,11 @@ func (*User) TypeName() string { return "user" } type Post struct { model.ModelSoftDelete - UserId int64 `json:"user_id" ` + Content string `json:"content" gorm:"type:varchar(255)" ` User User `json:"user" ` Enum TestEnum `json:"enum" ` Title string `json:"title" gorm:"index;type:varchar(255)" ` - Content string `json:"content" gorm:"type:varchar(255)" ` + UserId int64 `json:"user_id" ` } func (*Post) IsModel() bool { return true } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index f99d9a6..44e8b29 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,12 +2,12 @@ package repo import ( + "gorm.io/gorm" + "sync" "github.com/light-speak/lighthouse/context" "user/models" - "github.com/light-speak/lighthouse/graphql/ast" - "sync" "github.com/light-speak/lighthouse/graphql/model" - "gorm.io/gorm" + "github.com/light-speak/lighthouse/graphql/ast" ) func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"name": {},"posts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"updated_at": {},}} diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index 7a7c579..728063e 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -3,11 +3,11 @@ package resolver import ( "github.com/light-speak/lighthouse/graphql/model" + "user/models" "fmt" "github.com/light-speak/lighthouse/graphql/excute" "github.com/light-speak/lighthouse/graphql" "github.com/light-speak/lighthouse/context" - "user/models" ) func init() { diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index 7958e64..9e400d3 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,39 +2,31 @@ package resolver import ( + "github.com/light-speak/lighthouse/log" + "github.com/light-speak/lighthouse/context" "user/models" "fmt" "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/log" - "github.com/light-speak/lighthouse/context" ) -func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { - // Func:GetPosts user code start. Do not remove this comment. - posts := []*models.Post{} - db := model.GetDB() - db.Find(&posts) - return posts, nil - // Func:GetPosts user code end. Do not remove this comment. -} -func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { - // Func:TestPostInt user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) - return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. +func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { + // Func:TestPostEnum user code start. Do not remove this comment. + log.Debug().Msgf("enum: %+v", enum) + res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) + return res, nil + // Func:TestPostEnum user code end. Do not remove this comment. } func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { // Func:GetPostIds user code start. Do not remove this comment. return []int64{1, 2, 3}, nil // Func:GetPostIds user code end. Do not remove this comment. } -func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { - // Func:TestPostEnum user code start. Do not remove this comment. - log.Debug().Msgf("enum: %+v", enum) - res := fmt.Sprintf("啥也不是!:%v", *enum == models.A) - return res, nil - // Func:TestPostEnum user code end. Do not remove this comment. +func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { + // Func:TestPostId user code start. Do not remove this comment. + log.Debug().Msgf("id: %d", id) + return nil, nil + // Func:TestPostId user code end. Do not remove this comment. } func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { // Func:GetPost user code start. Do not remove this comment. @@ -45,15 +37,23 @@ func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { return post, nil // Func:GetPost user code end. Do not remove this comment. } -func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { - // Func:TestPostId user code start. Do not remove this comment. +func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { + // Func:TestPostInt user code start. Do not remove this comment. log.Debug().Msgf("id: %d", id) return nil, nil - // Func:TestPostId user code end. Do not remove this comment. + // Func:TestPostInt user code end. Do not remove this comment. } func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { // Func:TestPostInput user code start. Do not remove this comment. res := fmt.Sprintf("input: %+v", input) return res, nil // Func:TestPostInput user code end. Do not remove this comment. +} +func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { + // Func:GetPosts user code start. Do not remove this comment. + posts := []*models.Post{} + db := model.GetDB() + db.Find(&posts) + return posts, nil + // Func:GetPosts user code end. Do not remove this comment. } \ No newline at end of file diff --git a/graphql/model/generate/tpl/enum.tpl b/graphql/model/generate/tpl/enum.tpl index 75160dd..61b76d4 100644 --- a/graphql/model/generate/tpl/enum.tpl +++ b/graphql/model/generate/tpl/enum.tpl @@ -6,9 +6,9 @@ const ( {{- $index := 0}} {{- range $vKey, $enumValue := $node.EnumValues }} {{- if $enumValue.Value }} - {{ $vKey }} {{ $key }} = {{ $enumValue.Value }} + {{ $key }}{{ $vKey }} {{ $key }} = {{ $enumValue.Value }} {{- else }} - {{ $vKey }}{{ if eq $index 0 }} {{ $key }} = iota{{ end }} + {{ $key }}{{ $vKey }}{{ if eq $index 0 }} = iota{{ end }} {{- $index = add $index 1 }} {{- end }} {{- end }} @@ -17,7 +17,7 @@ const ( func (e {{ $key }}) ToString() string { switch e { {{- range $vKey, $enumValue := $node.EnumValues }} - case {{ $vKey }}: + case {{ $key }}{{ $vKey }}: return "{{ $vKey }}" {{- end }} default: @@ -27,7 +27,7 @@ func (e {{ $key }}) ToString() string { var {{ $key }}Map = map[string]{{ $key }}{ {{- range $vKey, $enumValue := $node.EnumValues }} - "{{ $vKey }}": {{ $vKey }}, + "{{ $vKey }}": {{ $key }}{{ $vKey }}, {{- end }} } {{- end }} From aa768feec45345823772fec4effc0323d1c456aa Mon Sep 17 00:00:00 2001 From: linty Date: Fri, 1 Nov 2024 23:49:08 +0800 Subject: [PATCH 03/17] fix: type --- example/user/models/model.go | 6 ++-- example/user/models/response.go | 19 ++++++++++++ example/user/repo/repo.go | 4 +-- example/user/resolver/operation_gen.go | 4 +-- example/user/resolver/query.go | 42 +++++++++++++------------- example/user/schema/post.graphqls | 4 +++ graphql/ast/funcs.go | 7 ++++- graphql/genenrate.go | 6 ++++ 8 files changed, 63 insertions(+), 29 deletions(-) create mode 100644 example/user/models/response.go diff --git a/example/user/models/model.go b/example/user/models/model.go index 89f8b65..6ad1f55 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -7,7 +7,7 @@ import "github.com/light-speak/lighthouse/graphql/model" type User struct { model.Model Name string `json:"name" gorm:"index;type:varchar(255)" ` - Posts []Post `gorm:"comment:五二零" json:"posts" ` + Posts []Post `json:"posts" gorm:"comment:五二零" ` } func (*User) IsModel() bool { return true } @@ -18,11 +18,11 @@ func (*User) TypeName() string { return "user" } type Post struct { model.ModelSoftDelete + Title string `json:"title" gorm:"index;type:varchar(255)" ` Content string `json:"content" gorm:"type:varchar(255)" ` + UserId int64 `json:"user_id" ` User User `json:"user" ` Enum TestEnum `json:"enum" ` - Title string `json:"title" gorm:"index;type:varchar(255)" ` - UserId int64 `json:"user_id" ` } func (*Post) IsModel() bool { return true } diff --git a/example/user/models/response.go b/example/user/models/response.go new file mode 100644 index 0000000..8664823 --- /dev/null +++ b/example/user/models/response.go @@ -0,0 +1,19 @@ +// Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. +package models + +import "github.com/light-speak/lighthouse/graphql/model" + + +type UserPaginateResponse struct { + Data []any `json:"data" ` + Paginateinfo model.PaginateInfo `json:"paginateInfo" ` +} + +type Test struct { + Test string `json:"test" gorm:"type:varchar(255)" ` +} + +type PostPaginateResponse struct { + Data []any `json:"data" ` + Paginateinfo model.PaginateInfo `json:"paginateInfo" ` +} diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index 44e8b29..331888f 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,12 +2,12 @@ package repo import ( + "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/context" "gorm.io/gorm" "sync" - "github.com/light-speak/lighthouse/context" "user/models" "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/graphql/ast" ) func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"name": {},"posts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"updated_at": {},}} diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index 728063e..eedba3c 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -3,10 +3,10 @@ package resolver import ( "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/graphql" "user/models" - "fmt" "github.com/light-speak/lighthouse/graphql/excute" - "github.com/light-speak/lighthouse/graphql" + "fmt" "github.com/light-speak/lighthouse/context" ) diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index 9e400d3..6b6b0a0 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,11 +2,11 @@ package resolver import ( - "github.com/light-speak/lighthouse/log" - "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/model" "user/models" + "github.com/light-speak/lighthouse/log" "fmt" - "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/context" ) @@ -17,16 +17,19 @@ func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, e return res, nil // Func:TestPostEnum user code end. Do not remove this comment. } -func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { - // Func:GetPostIds user code start. Do not remove this comment. - return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. +func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { + // Func:GetPosts user code start. Do not remove this comment. + posts := []*models.Post{} + db := model.GetDB() + db.Find(&posts) + return posts, nil + // Func:GetPosts user code end. Do not remove this comment. } -func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { - // Func:TestPostId user code start. Do not remove this comment. +func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { + // Func:TestPostInt user code start. Do not remove this comment. log.Debug().Msgf("id: %d", id) return nil, nil - // Func:TestPostId user code end. Do not remove this comment. + // Func:TestPostInt user code end. Do not remove this comment. } func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { // Func:GetPost user code start. Do not remove this comment. @@ -37,23 +40,20 @@ func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { return post, nil // Func:GetPost user code end. Do not remove this comment. } -func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { - // Func:TestPostInt user code start. Do not remove this comment. +func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { + // Func:GetPostIds user code start. Do not remove this comment. + return []int64{1, 2, 3}, nil + // Func:GetPostIds user code end. Do not remove this comment. +} +func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { + // Func:TestPostId user code start. Do not remove this comment. log.Debug().Msgf("id: %d", id) return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. + // Func:TestPostId user code end. Do not remove this comment. } func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { // Func:TestPostInput user code start. Do not remove this comment. res := fmt.Sprintf("input: %+v", input) return res, nil // Func:TestPostInput user code end. Do not remove this comment. -} -func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { - // Func:GetPosts user code start. Do not remove this comment. - posts := []*models.Post{} - db := model.GetDB() - db.Find(&posts) - return posts, nil - // Func:GetPosts user code end. Do not remove this comment. } \ No newline at end of file diff --git a/example/user/schema/post.graphqls b/example/user/schema/post.graphqls index 659896a..13a6062 100644 --- a/example/user/schema/post.graphqls +++ b/example/user/schema/post.graphqls @@ -19,6 +19,10 @@ extend type Query { testPostInput(input: TestInput!): String! } +type Test { + test: String! +} + input TestInput { id: String e: Boolean diff --git a/graphql/ast/funcs.go b/graphql/ast/funcs.go index 79e3790..84ed92a 100644 --- a/graphql/ast/funcs.go +++ b/graphql/ast/funcs.go @@ -35,7 +35,12 @@ func Fields(fields map[string]*Field) string { if _, ok := excludeFieldName[field.Name]; ok { continue } - line := fmt.Sprintf(" %s %s %s", utils.UcFirst(utils.CamelCase(field.Name)), field.Type.GetGoType(false), genTag(field)) + goType := field.Type.GetGoType(false) + + if goType == "PaginateInfo" { + goType = "model.PaginateInfo" + } + line := fmt.Sprintf(" %s %s %s", utils.UcFirst(utils.CamelCase(field.Name)), goType, genTag(field)) lines = append(lines, line) } return strings.Join(lines, "\n") diff --git a/graphql/genenrate.go b/graphql/genenrate.go index 361379f..788d93b 100644 --- a/graphql/genenrate.go +++ b/graphql/genenrate.go @@ -23,6 +23,7 @@ func Generate() error { nodes := p.NodeStore.Nodes typeNodes := []*ast.ObjectNode{} + resNodes := []*ast.ObjectNode{} for _, node := range nodes { if isInternalType(node.GetName()) { @@ -33,6 +34,8 @@ func Generate() error { objectNode, _ := node.(*ast.ObjectNode) if objectNode.IsModel { typeNodes = append(typeNodes, objectNode) + } else { + resNodes = append(resNodes, objectNode) } } } @@ -52,6 +55,9 @@ func Generate() error { if err := generate.GenEnum(p.NodeStore.Enums, currentPath); err != nil { return err } + if err := generate.GenResponse(resNodes, currentPath); err != nil { + return err + } operationNodes := []*ast.ObjectNode{} From be6190c6c4cd6089adba49a7d418d8c33113224c Mon Sep 17 00:00:00 2001 From: zengdiv Date: Sat, 2 Nov 2024 16:35:00 +0800 Subject: [PATCH 04/17] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=20gorm=20bytes?= =?UTF-8?q?=20&=20bool=20=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- graphql/ast/directive/softdeletemodel.go | 2 +- graphql/excute/validate.go | 1 + graphql/scalar/bool.go | 6 ++++++ graphql/scalar/string.go | 9 ++++++++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/graphql/ast/directive/softdeletemodel.go b/graphql/ast/directive/softdeletemodel.go index a04be2a..e3d83cb 100644 --- a/graphql/ast/directive/softdeletemodel.go +++ b/graphql/ast/directive/softdeletemodel.go @@ -21,7 +21,7 @@ func handlerSoftDeleteModel(o *ast.ObjectNode, d *ast.Directive, store *ast.Node } o.Fields["deleted_at"] = &ast.Field{ Name: "deleted_at", - Type: &ast.TypeRef{Kind: ast.KindNonNull, OfType: &ast.TypeRef{Kind: ast.KindScalar, Name: "DateTime", TypeNode: store.Scalars["DateTime"]}}, + Type: &ast.TypeRef{Kind: ast.KindScalar, Name: "DateTime", TypeNode: store.Scalars["DateTime"]}, } if arg := d.GetArg("table"); arg != nil { o.Table = arg.Value.(string) diff --git a/graphql/excute/validate.go b/graphql/excute/validate.go index 7c4d945..fa8ae63 100644 --- a/graphql/excute/validate.go +++ b/graphql/excute/validate.go @@ -10,6 +10,7 @@ func ValidateValue(field *ast.Field, value interface{}, isVariable bool) (interf realType := field.Type.GetRealType() var v interface{} var err errors.GraphqlErrorInterface + switch realType.Kind { case ast.KindScalar: v, err = realType.TypeNode.(*ast.ScalarNode).ScalarType.Serialize(value, field.GetLocation()) diff --git a/graphql/scalar/bool.go b/graphql/scalar/bool.go index d4a7203..1386550 100644 --- a/graphql/scalar/bool.go +++ b/graphql/scalar/bool.go @@ -20,6 +20,8 @@ func (i *BooleanScalar) ParseValue(v interface{}, location *errors.GraphqlLocati } } return boolValue, nil + case int64: + return v != 0, nil case bool: return v, nil default: @@ -34,6 +36,8 @@ func (i *BooleanScalar) Serialize(v interface{}, location *errors.GraphqlLocatio switch v := v.(type) { case bool: return strconv.FormatBool(v), nil + case int64: + return strconv.FormatBool(v != 0), nil default: return "", &errors.GraphQLError{ Message: fmt.Sprintf("value is not a boolean: %v", v), @@ -46,6 +50,8 @@ func (i *BooleanScalar) ParseLiteral(v interface{}, location *errors.GraphqlLoca switch v := v.(type) { case bool: return v, nil + case int64: + return v != 0, nil } return nil, &errors.GraphQLError{ Message: fmt.Sprintf("invalid literal for Boolean: %v", v), diff --git a/graphql/scalar/string.go b/graphql/scalar/string.go index e585942..75ab84f 100644 --- a/graphql/scalar/string.go +++ b/graphql/scalar/string.go @@ -12,6 +12,8 @@ func (s *StringScalar) ParseValue(v interface{}, location *errors.GraphqlLocatio switch v := v.(type) { case string: return v, nil + case []byte: + return string(v), nil default: return nil, &errors.GraphQLError{ Message: fmt.Sprintf("invalid string value: %v", v), @@ -24,8 +26,10 @@ func (s *StringScalar) Serialize(v interface{}, location *errors.GraphqlLocation switch v := v.(type) { case string: return v, nil + case []byte: + return string(v), nil default: - return "", &errors.GraphQLError{ + return "", &errors.GraphQLError{ Message: fmt.Sprintf("value is not a string: %v", v), Locations: []*errors.GraphqlLocation{location}, } @@ -36,7 +40,10 @@ func (s *StringScalar) ParseLiteral(v interface{}, location *errors.GraphqlLocat switch v := v.(type) { case string: return v, nil + case []byte: + return string(v), nil } + return nil, &errors.GraphQLError{ Message: fmt.Sprintf("invalid literal for String: %v", v), Locations: []*errors.GraphqlLocation{location}, From c19b278d1d612d5bbc58d638b86ba5776284e0d3 Mon Sep 17 00:00:00 2001 From: linty Date: Sat, 2 Nov 2024 17:44:38 +0800 Subject: [PATCH 05/17] feat: paginate --- command/cli/generate/schema/schema.go | 2 +- command/cli/version/version.go | 17 ++--- config/lighthouse.go | 4 +- env/env_test.go | 1 - example/user/cmd/migrate/migrate.go | 1 + example/user/models/model.go | 32 +++++----- example/user/models/response.go | 14 ++-- example/user/repo/repo.go | 74 +++++++++++++--------- example/user/resolver/operation_gen.go | 6 +- example/user/resolver/query.go | 56 ++++++++-------- example/user/schema/post.graphqls | 4 +- example/user/service/service.go | 1 - graphql/ast/directive/belongsto.go | 4 +- graphql/ast/directive/hasmany.go | 7 +- graphql/ast/directive/paginate.go | 16 +++-- graphql/ast/funcs.go | 4 +- graphql/ast/node.go | 69 +++++++++++++++++++- graphql/excute/filter.go | 3 +- graphql/excute/quick.go | 4 +- graphql/excute/return.go | 88 ++++++++++++++++++-------- graphql/genenrate.go | 2 +- graphql/model/generate/tpl/repo.tpl | 6 ++ graphql/model/quick.go | 13 ++++ graphql/scalar/id.go | 4 +- graphql/scalar/int.go | 2 + graphql/scalar/string.go | 2 +- net/middleware/auth.go | 11 +++- plugins/file/file.go | 2 +- template/template.go | 2 - utils/str.go | 31 ++++----- utils/str_test.go | 23 ++++--- 31 files changed, 331 insertions(+), 174 deletions(-) diff --git a/command/cli/generate/schema/schema.go b/command/cli/generate/schema/schema.go index d638ba1..19c0d99 100644 --- a/command/cli/generate/schema/schema.go +++ b/command/cli/generate/schema/schema.go @@ -32,7 +32,7 @@ func (c *Schema) Action() func(flagValues map[string]interface{}) error { // Func:Action user code start. Do not remove this comment. err := graphql.Generate() if err != nil { - return err + return err } // Func:Action user code end. Do not remove this comment. return nil diff --git a/command/cli/version/version.go b/command/cli/version/version.go index 482a87e..47c0576 100644 --- a/command/cli/version/version.go +++ b/command/cli/version/version.go @@ -8,26 +8,25 @@ import ( "github.com/light-speak/lighthouse/version" ) - -type Version struct {} +type Version struct{} func (c *Version) Name() string { // Func:Name user code start. Do not remove this comment. return "app:version" - // Func:Name user code end. Do not remove this comment. + // Func:Name user code end. Do not remove this comment. } func (c *Version) Usage() string { // Func:Usage user code start. Do not remove this comment. return "Show the version of lighthouse" - // Func:Usage user code end. Do not remove this comment. + // Func:Usage user code end. Do not remove this comment. } func (c *Version) Args() []*command.CommandArg { return []*command.CommandArg{ // Func:Args user code start. Do not remove this comment. - - // Func:Args user code end. Do not remove this comment. + + // Func:Args user code end. Do not remove this comment. } } @@ -35,7 +34,7 @@ func (c *Version) Action() func(flagValues map[string]interface{}) error { return func(flagValues map[string]interface{}) error { // Func:Action user code start. Do not remove this comment. printVersion() - // Func:Action user code end. Do not remove this comment. + // Func:Action user code end. Do not remove this comment. return nil } } @@ -46,10 +45,8 @@ func (c *Version) OnExit() func() { // Section: user code section start. Do not remove this comment. - func printVersion() { fmt.Printf("Version: %s\n", version.Version) } - -// Section: user code section end. Do not remove this comment. +// Section: user code section end. Do not remove this comment. diff --git a/config/lighthouse.go b/config/lighthouse.go index c565b58..5192c85 100644 --- a/config/lighthouse.go +++ b/config/lighthouse.go @@ -14,8 +14,8 @@ type Config struct { } type SchemaConfig struct { - Ext []string `yaml:"ext"` - Path []string `yaml:"path"` + Ext []string `yaml:"ext"` + Path []string `yaml:"path"` } func ReadConfig(path string) (*Config, error) { diff --git a/env/env_test.go b/env/env_test.go index 19d78a8..0881799 100644 --- a/env/env_test.go +++ b/env/env_test.go @@ -48,4 +48,3 @@ func TestGetEnvInt(t *testing.T) { } } } - diff --git a/example/user/cmd/migrate/migrate.go b/example/user/cmd/migrate/migrate.go index 45e325a..125399b 100644 --- a/example/user/cmd/migrate/migrate.go +++ b/example/user/cmd/migrate/migrate.go @@ -43,5 +43,6 @@ func (c *Migrate) Action() func(flagValues map[string]interface{}) error { func (c *Migrate) OnExit() func() { return func() {} } + // Section: user code section start. Do not remove this comment. // Section: user code section end. Do not remove this comment. diff --git a/example/user/models/model.go b/example/user/models/model.go index 6ad1f55..ea05c3d 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -4,35 +4,37 @@ package models import "github.com/light-speak/lighthouse/graphql/model" -type User struct { - model.Model - Name string `json:"name" gorm:"index;type:varchar(255)" ` - Posts []Post `json:"posts" gorm:"comment:五二零" ` -} - -func (*User) IsModel() bool { return true } -func (*User) IsHasName() bool { return true } -func (this *User) GetName() string { return this.Name } -func (*User) TableName() string { return "users" } -func (*User) TypeName() string { return "user" } - type Post struct { model.ModelSoftDelete - Title string `json:"title" gorm:"index;type:varchar(255)" ` - Content string `json:"content" gorm:"type:varchar(255)" ` UserId int64 `json:"user_id" ` + Content string `json:"content" gorm:"type:varchar(255)" ` + TagId int64 `json:"tag_id" ` + BackId int64 `json:"back_id" ` User User `json:"user" ` Enum TestEnum `json:"enum" ` + Title string `json:"title" gorm:"index;type:varchar(255)" ` } func (*Post) IsModel() bool { return true } func (*Post) TableName() string { return "posts" } func (*Post) TypeName() string { return "post" } +type User struct { + model.Model + Posts []Post `json:"posts" gorm:"comment:五二零" ` + Name string `json:"name" gorm:"index;type:varchar(255)" ` +} + +func (*User) IsModel() bool { return true } +func (*User) IsHasName() bool { return true } +func (this *User) GetName() string { return this.Name } +func (*User) TableName() string { return "users" } +func (*User) TypeName() string { return "user" } + func Migrate() error { return model.GetDB().AutoMigrate( - &User{}, &Post{}, + &User{}, ) } \ No newline at end of file diff --git a/example/user/models/response.go b/example/user/models/response.go index 8664823..0682add 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -4,16 +4,16 @@ package models import "github.com/light-speak/lighthouse/graphql/model" -type UserPaginateResponse struct { - Data []any `json:"data" ` - Paginateinfo model.PaginateInfo `json:"paginateInfo" ` -} - type Test struct { Test string `json:"test" gorm:"type:varchar(255)" ` } type PostPaginateResponse struct { - Data []any `json:"data" ` - Paginateinfo model.PaginateInfo `json:"paginateInfo" ` + Data []*Post `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` +} + +type UserPaginateResponse struct { + Data []*User `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index 331888f..574c862 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -3,29 +3,29 @@ package repo import ( "github.com/light-speak/lighthouse/graphql/ast" - "github.com/light-speak/lighthouse/context" "gorm.io/gorm" "sync" "user/models" "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/context" ) -func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"name": {},"posts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"updated_at": {},}} -func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) +func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} +func Load__Post(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) } -func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) +func LoadList__Post(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) } -func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.User{}).Scopes(scopes...) +func Query__Post(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { + return model.GetDB().Model(&models.Post{}).Scopes(scopes...) } -func First__User(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { +func First__Post(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { var err error - selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__User()) + selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__Post()) if data == nil { data = make(map[string]interface{}) - err = Query__User().Scopes(scopes...).Select(selectColumns).First(data).Error + err = Query__Post().Scopes(scopes...).Select(selectColumns).First(data).Error if err != nil { return nil, err } @@ -54,12 +54,12 @@ func First__User(ctx *context.Context, columns map[string]interface{}, data map[ } return data, nil } -func List__User(ctx *context.Context, columns map[string]interface{},datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { +func List__Post(ctx *context.Context, columns map[string]interface{},datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { var err error - selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__User()) + selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__Post()) if datas == nil { datas = make([]map[string]interface{}, 0) - err = Query__User().Scopes(scopes...).Select(selectColumns).Find(&datas).Error + err = Query__Post().Scopes(scopes...).Select(selectColumns).Find(&datas).Error if err != nil { return nil, err } @@ -90,22 +90,27 @@ func List__User(ctx *context.Context, columns map[string]interface{},datas []map } return datas, nil } -func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"user_id": {},}} -func Load__Post(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) +func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { + var count int64 + err := Query__Post().Scopes(scopes...).Count(&count).Error + return count, err } -func LoadList__Post(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) +func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"name": {},"posts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"updated_at": {},}} +func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) } -func Query__Post(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Post{}).Scopes(scopes...) +func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) } -func First__Post(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { +func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { + return model.GetDB().Model(&models.User{}).Scopes(scopes...) +} +func First__User(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { var err error - selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__Post()) + selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__User()) if data == nil { data = make(map[string]interface{}) - err = Query__Post().Scopes(scopes...).Select(selectColumns).First(data).Error + err = Query__User().Scopes(scopes...).Select(selectColumns).First(data).Error if err != nil { return nil, err } @@ -134,12 +139,12 @@ func First__Post(ctx *context.Context, columns map[string]interface{}, data map[ } return data, nil } -func List__Post(ctx *context.Context, columns map[string]interface{},datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { +func List__User(ctx *context.Context, columns map[string]interface{},datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { var err error - selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__Post()) + selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__User()) if datas == nil { datas = make([]map[string]interface{}, 0) - err = Query__Post().Scopes(scopes...).Select(selectColumns).Find(&datas).Error + err = Query__User().Scopes(scopes...).Select(selectColumns).Find(&datas).Error if err != nil { return nil, err } @@ -170,15 +175,22 @@ func List__Post(ctx *context.Context, columns map[string]interface{},datas []map } return datas, nil } +func Count__User(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { + var count int64 + err := Query__User().Scopes(scopes...).Count(&count).Error + return count, err +} func init() { - model.AddQuickFirst("User", First__User) - model.AddQuickList("User", List__User) - model.AddQuickLoad("User", Load__User) - model.AddQuickLoadList("User", LoadList__User) model.AddQuickFirst("Post", First__Post) model.AddQuickList("Post", List__Post) model.AddQuickLoad("Post", Load__Post) model.AddQuickLoadList("Post", LoadList__Post) + model.AddQuickCount("Post", Count__Post) + model.AddQuickFirst("User", First__User) + model.AddQuickList("User", List__User) + model.AddQuickLoad("User", Load__User) + model.AddQuickLoadList("User", LoadList__User) + model.AddQuickCount("User", Count__User) } diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index eedba3c..67350a7 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,12 +2,12 @@ package resolver import ( - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/graphql" "user/models" - "github.com/light-speak/lighthouse/graphql/excute" "fmt" + "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/graphql" "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/excute" ) func init() { diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index 6b6b0a0..8003444 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,35 +2,14 @@ package resolver import ( - "github.com/light-speak/lighthouse/graphql/model" "user/models" - "github.com/light-speak/lighthouse/log" "fmt" + "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/log" "github.com/light-speak/lighthouse/context" ) -func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { - // Func:TestPostEnum user code start. Do not remove this comment. - log.Debug().Msgf("enum: %+v", enum) - res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) - return res, nil - // Func:TestPostEnum user code end. Do not remove this comment. -} -func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { - // Func:GetPosts user code start. Do not remove this comment. - posts := []*models.Post{} - db := model.GetDB() - db.Find(&posts) - return posts, nil - // Func:GetPosts user code end. Do not remove this comment. -} -func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { - // Func:TestPostInt user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) - return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. -} func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { // Func:GetPost user code start. Do not remove this comment. log.Debug().Msg("GetPostResolver") @@ -40,20 +19,41 @@ func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { return post, nil // Func:GetPost user code end. Do not remove this comment. } -func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { - // Func:GetPostIds user code start. Do not remove this comment. - return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. -} func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { // Func:TestPostId user code start. Do not remove this comment. log.Debug().Msgf("id: %d", id) return nil, nil // Func:TestPostId user code end. Do not remove this comment. } +func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { + // Func:TestPostEnum user code start. Do not remove this comment. + log.Debug().Msgf("enum: %+v", enum) + res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) + return res, nil + // Func:TestPostEnum user code end. Do not remove this comment. +} +func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { + // Func:GetPostIds user code start. Do not remove this comment. + return []int64{1, 2, 3}, nil + // Func:GetPostIds user code end. Do not remove this comment. +} +func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { + // Func:GetPosts user code start. Do not remove this comment. + posts := []*models.Post{} + db := model.GetDB() + db.Find(&posts) + return posts, nil + // Func:GetPosts user code end. Do not remove this comment. +} func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { // Func:TestPostInput user code start. Do not remove this comment. res := fmt.Sprintf("input: %+v", input) return res, nil // Func:TestPostInput user code end. Do not remove this comment. +} +func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { + // Func:TestPostInt user code start. Do not remove this comment. + log.Debug().Msgf("id: %d", id) + return nil, nil + // Func:TestPostInt user code end. Do not remove this comment. } \ No newline at end of file diff --git a/example/user/schema/post.graphqls b/example/user/schema/post.graphqls index 13a6062..ca207f7 100644 --- a/example/user/schema/post.graphqls +++ b/example/user/schema/post.graphqls @@ -1,7 +1,9 @@ type Post @key(fields: "id") @softDeleteModel { title: String! @index content: String! - user_id: ID! + userId: ID! + tagId: ID! + BackId: ID! user: User! @belongsTo enum: TestEnum! } diff --git a/example/user/service/service.go b/example/user/service/service.go index 0969998..dc99d81 100644 --- a/example/user/service/service.go +++ b/example/user/service/service.go @@ -6,7 +6,6 @@ import ( "github.com/light-speak/lighthouse/handler" ) - func StartService() { handler.StartService() } diff --git a/graphql/ast/directive/belongsto.go b/graphql/ast/directive/belongsto.go index 6bd56f4..464fc68 100644 --- a/graphql/ast/directive/belongsto.go +++ b/graphql/ast/directive/belongsto.go @@ -21,9 +21,9 @@ func handlerBelongsTo(f *ast.Field, d *ast.Directive, store *ast.NodeStore, pare relation.Reference = "id" } if foreignKey := d.GetArg("foreignKey"); foreignKey != nil { - relation.ForeignKey = foreignKey.Value.(string) + relation.ForeignKey = utils.SnakeCase(foreignKey.Value.(string)) } else { - relation.ForeignKey = utils.LcFirst(f.Name) + "_id" + relation.ForeignKey = utils.SnakeCase(utils.LcFirst(f.Name)) + "_id" } f.Relation = relation return nil diff --git a/graphql/ast/directive/hasmany.go b/graphql/ast/directive/hasmany.go index 64b7556..1d042d0 100644 --- a/graphql/ast/directive/hasmany.go +++ b/graphql/ast/directive/hasmany.go @@ -1,8 +1,9 @@ package directive import ( - "github.com/light-speak/lighthouse/errors" + "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/utils" ) func handlerHasMany(f *ast.Field, d *ast.Directive, store *ast.NodeStore, parent ast.Node) errors.GraphqlErrorInterface { @@ -19,7 +20,7 @@ func handlerHasMany(f *ast.Field, d *ast.Directive, store *ast.NodeStore, parent } } if foreignKey := d.GetArg("foreignKey"); foreignKey != nil { - relation.ForeignKey = foreignKey.Value.(string) + relation.ForeignKey = utils.SnakeCase(foreignKey.Value.(string)) } else { return &errors.GraphQLError{ Message: "foreign key is required for hasMany directive", @@ -27,7 +28,7 @@ func handlerHasMany(f *ast.Field, d *ast.Directive, store *ast.NodeStore, parent } } if reference := d.GetArg("reference"); reference != nil { - relation.Reference = reference.Value.(string) + relation.Reference = utils.SnakeCase(reference.Value.(string)) } else { relation.Reference = "id" } diff --git a/graphql/ast/directive/paginate.go b/graphql/ast/directive/paginate.go index f67eeab..dd8a16d 100644 --- a/graphql/ast/directive/paginate.go +++ b/graphql/ast/directive/paginate.go @@ -5,13 +5,12 @@ import ( "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" - ) func handlerPaginate(f *ast.Field, d *ast.Directive, store *ast.NodeStore, parent ast.Node) errors.GraphqlErrorInterface { if parent.GetName() != "Query" { return &errors.GraphQLError{ - Message: "paginate directive can only be used on Query type", + Message: "paginate directive can only be used on Query type", Locations: []*errors.GraphqlLocation{d.GetLocation()}, } } @@ -36,6 +35,9 @@ func addPaginationResponseType(f *ast.Field, store *ast.NodeStore) { return } description := fmt.Sprintf("The %sPaginateResponse type represents a paginated list of %s.", typeName, typeName) + if curType.TypeNode == nil { + f.Type.Validate(store) + } store.AddObject(responseName, &ast.ObjectNode{ BaseNode: ast.BaseNode{ Name: responseName, @@ -45,7 +47,13 @@ func addPaginationResponseType(f *ast.Field, store *ast.NodeStore) { Fields: map[string]*ast.Field{ "data": { Name: "data", - Type: f.Type, + Type: &ast.TypeRef{ + Kind: ast.KindNonNull, + OfType: &ast.TypeRef{ + Kind: ast.KindList, + OfType: curType, + }, + }, }, "paginateInfo": { Name: "paginateInfo", @@ -61,7 +69,7 @@ func addPaginationResponseType(f *ast.Field, store *ast.NodeStore) { }, }) f.Type = &ast.TypeRef{ - Kind: ast.KindNonNull, + Kind: ast.KindNonNull, OfType: &ast.TypeRef{ Kind: ast.KindObject, Name: responseName, diff --git a/graphql/ast/funcs.go b/graphql/ast/funcs.go index 84ed92a..333a762 100644 --- a/graphql/ast/funcs.go +++ b/graphql/ast/funcs.go @@ -35,8 +35,8 @@ func Fields(fields map[string]*Field) string { if _, ok := excludeFieldName[field.Name]; ok { continue } + goType := field.Type.GetGoType(false) - if goType == "PaginateInfo" { goType = "model.PaginateInfo" } @@ -91,7 +91,7 @@ var directiveFns = map[string]func(map[string][]string, *Directive) error{ func genTag(field *Field) string { tags := map[string][]string{ - "json": {field.Name}, + "json": {utils.SnakeCase(field.Name)}, } hasType := false diff --git a/graphql/ast/node.go b/graphql/ast/node.go index 6a8d91c..3cb1006 100644 --- a/graphql/ast/node.go +++ b/graphql/ast/node.go @@ -389,6 +389,18 @@ func (f *Field) Validate(store *NodeStore, objectFields map[string]*Field, objec // merge f.DefinitionDirectives = append(f.DefinitionDirectives, objectNode.GetFields()[f.Name].Directives...) f.DefinitionArgs = objectNode.GetFields()[f.Name].Args + for _, defArg := range f.DefinitionArgs { + if defArg.DefaultValue != nil && f.Args[defArg.Name] == nil { + if f.Args == nil { + f.Args = make(map[string]*Argument) + } + f.Args[defArg.Name] = &Argument{ + Name: defArg.Name, + Value: defArg.DefaultValue, + IsReference: defArg.IsReference, + } + } + } } else { // if the field is not found, it means the field is a fragment field // we need to validate the fragment field @@ -977,6 +989,61 @@ type Argument struct { IsReference bool `json:"-"` } +func (a *Argument) GetValue() (interface{}, errors.GraphqlErrorInterface) { + var err errors.GraphqlErrorInterface + if a.Value == nil { + return nil, nil + } + + // If type is non-null or list, get the inner type + t := a.Type + for t.Kind == KindNonNull || t.Kind == KindList { + t = t.OfType + } + + // For scalar and enum types, parse the value directly + if t.Kind == KindScalar { + val, err := t.TypeNode.(*ScalarNode).ScalarType.ParseValue(a.Value, a.GetLocation()) + if err != nil { + return nil, err + } + return val, nil + } + if t.Kind == KindEnum { + return a.Value.(string), nil + } + + // For object and input object types, recursively get values + if t.Kind == KindObject || t.Kind == KindInputObject { + if objValue, ok := a.Value.(map[string]interface{}); ok { + result := make(map[string]interface{}) + + var fields map[string]*Field + if t.Kind == KindObject { + fields = t.TypeNode.(*ObjectNode).Fields + } else { + fields = t.TypeNode.(*InputObjectNode).Fields + } + + for fieldName, fieldValue := range objValue { + if field, exists := fields[fieldName]; exists { + arg := &Argument{ + Type: field.Type, + Value: fieldValue, + } + result[fieldName], err = arg.GetValue() + if err != nil { + return nil, err + } + } + } + return result, nil + } + } + + return a.Value, nil +} + func (a *Argument) GetDefaultValue() *string { if a.DefaultValue == nil { return nil @@ -986,6 +1053,7 @@ func (a *Argument) GetDefaultValue() *string { } func (a *Argument) Validate(store *NodeStore, args map[string]*Argument, field *Field) errors.GraphqlErrorInterface { + location := LocationArgumentDefinition if a.IsVariable { location = LocationVariableDefinition @@ -1042,7 +1110,6 @@ func (a *Argument) Validate(store *NodeStore, args map[string]*Argument, field * return err } } - err := ValidateDirectives(a.Name, a.Directives, store, location) if err != nil { return err diff --git a/graphql/excute/filter.go b/graphql/excute/filter.go index a869ff9..cbdf0d9 100644 --- a/graphql/excute/filter.go +++ b/graphql/excute/filter.go @@ -3,11 +3,10 @@ package excute import ( "fmt" + "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" - "github.com/light-speak/lighthouse/context" "gorm.io/gorm" - ) func executeFilter(ctx *context.Context, arg *ast.Argument, value interface{}) (func(db *gorm.DB) *gorm.DB, errors.GraphqlErrorInterface) { diff --git a/graphql/excute/quick.go b/graphql/excute/quick.go index 242f7ab..e0a00a2 100644 --- a/graphql/excute/quick.go +++ b/graphql/excute/quick.go @@ -6,6 +6,7 @@ import ( "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/utils" "gorm.io/gorm" ) @@ -43,7 +44,8 @@ func QuickExecute(ctx *context.Context, field *ast.Field) (interface{}, bool, er return nil, false, nil } func mergeData(field *ast.Field, datas map[string]interface{}) (interface{}, errors.GraphqlErrorInterface) { - v, ok := datas[field.Name] + fieldName := utils.SnakeCase(field.Name) + v, ok := datas[fieldName] if !ok { return nil, &errors.GraphQLError{ Message: fmt.Sprintf("field %s not found", field.Name), diff --git a/graphql/excute/return.go b/graphql/excute/return.go index db6c038..4988477 100644 --- a/graphql/excute/return.go +++ b/graphql/excute/return.go @@ -2,11 +2,12 @@ package excute import ( "fmt" + "math" + "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/context" "gorm.io/gorm" ) @@ -44,44 +45,75 @@ func executeFirst(ctx *context.Context, field *ast.Field, scopes ...func(db *gor } func executePaginate(ctx *context.Context, field *ast.Field, scopes ...func(db *gorm.DB) *gorm.DB) (interface{}, errors.GraphqlErrorInterface) { - res := make(map[string]interface{}) - info := &model.PaginateInfo{} - res["paginateInfo"] = info - columns, err := getColumns(field) + pageArg := field.Args["page"] + sizeArg := field.Args["size"] + sortArg := field.Args["sort"] + page, err := pageArg.GetValue() if err != nil { return nil, err } - fn := model.GetQuickList(field.Type.GetGoName()) - if fn == nil { - return nil, &errors.GraphQLError{ - Message: fmt.Sprintf("field %s not found", field.Name), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } + size, err := sizeArg.GetValue() + if err != nil { + return nil, err } - datas, e := fn(ctx, columns, nil, scopes...) - if e != nil { - return nil, &errors.GraphQLError{ - Message: e.Error(), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } + sort, err := sortArg.GetValue() + if err != nil { + return nil, err } - values := make([]map[string]interface{}, 0) - for _, data := range datas { - d := make(map[string]interface{}, 0) - for _, child := range field.Children { - v, err := mergeData(child, data) - d[child.Name] = v - if err != nil { - return nil, err + scope := func(db *gorm.DB) *gorm.DB { + return db.Offset((int(page.(int64)) - 1) * int(size.(int64))).Limit(int(size.(int64))).Order(fmt.Sprintf("%s %s", "id", sort.(string))) + } + data, err := executeFind(ctx, field.Children["data"], append(scopes, scope)...) + if err != nil { + return nil, err + } + res := make(map[string]interface{}) + res["data"] = data + if field.Children["paginateInfo"] != nil { + count := int64(0) + var e error + if field.Children["paginateInfo"].Children["totalPage"] != nil || + field.Children["paginateInfo"].Children["hasNextPage"] != nil || + field.Children["paginateInfo"].Children["totalCount"] != nil { + countFn := model.GetQuickCount(field.Children["data"].Type.GetRealType().GetGoName()) + if countFn == nil { + return nil, &errors.GraphQLError{ + Message: fmt.Sprintf("quick count function %s not found", field.Type.GetGoName()), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } } + count, e = countFn(scopes...) + if e != nil { + return nil, &errors.GraphQLError{ + Message: e.Error(), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } + } + } + paginateInfo := make(map[string]interface{}) + for _, child := range field.Children["paginateInfo"].Children { + paginateInfo[child.Name] = mergePaginateInfo(child, count, page.(int64), size.(int64)) } - values = append(values, d) + res["paginateInfo"] = paginateInfo } - res["data"] = values - return res, nil } +func mergePaginateInfo(field *ast.Field, count int64, page int64, size int64) interface{} { + switch field.Name { + case "totalCount": + return count + case "currentPage": + return page + case "hasNextPage": + totalPage := int64(math.Ceil(float64(count) / float64(size))) + return page < totalPage + case "totalPage": + return int64(math.Ceil(float64(count) / float64(size))) + } + return nil +} + func executeFind(ctx *context.Context, field *ast.Field, scopes ...func(db *gorm.DB) *gorm.DB) (interface{}, errors.GraphqlErrorInterface) { columns, err := getColumns(field) if err != nil { diff --git a/graphql/genenrate.go b/graphql/genenrate.go index 788d93b..913ff12 100644 --- a/graphql/genenrate.go +++ b/graphql/genenrate.go @@ -73,7 +73,7 @@ func Generate() error { return err } } - + if err := generate.GenOperationResolverGen(operationNodes, currentPath); err != nil { return err } diff --git a/graphql/model/generate/tpl/repo.tpl b/graphql/model/generate/tpl/repo.tpl index 08426ba..2e3d356 100644 --- a/graphql/model/generate/tpl/repo.tpl +++ b/graphql/model/generate/tpl/repo.tpl @@ -84,6 +84,11 @@ func List__{{ $name | ucFirst }}(ctx *context.Context, columns map[string]interf } return datas, nil } +func Count__{{ $name | ucFirst }}(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { + var count int64 + err := Query__{{ $name | ucFirst }}().Scopes(scopes...).Count(&count).Error + return count, err +} {{ end }} func init() { @@ -92,5 +97,6 @@ func init() { model.AddQuickList("{{ .Name | ucFirst }}", List__{{ .Name | ucFirst }}) model.AddQuickLoad("{{ .Name | ucFirst }}", Load__{{ .Name | ucFirst }}) model.AddQuickLoadList("{{ .Name | ucFirst }}", LoadList__{{ .Name | ucFirst }}) + model.AddQuickCount("{{ .Name | ucFirst }}", Count__{{ .Name | ucFirst }}) {{- end }} } diff --git a/graphql/model/quick.go b/graphql/model/quick.go index e03c920..6568d33 100644 --- a/graphql/model/quick.go +++ b/graphql/model/quick.go @@ -24,6 +24,8 @@ var quickLoadMap = make(map[string]func(ctx *context.Context, key int64, field s var quickLoadListMap = make(map[string]func(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error)) +var quickCountMap = make(map[string]func(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error)) + func AddQuickList(name string, fn func(ctx *context.Context, columns map[string]interface{}, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error)) { quickListMap[name] = fn } @@ -36,6 +38,9 @@ func AddQuickLoad(name string, fn func(ctx *context.Context, key int64, field st func AddQuickLoadList(name string, fn func(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error)) { quickLoadListMap[name] = fn } +func AddQuickCount(name string, fn func(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error)) { + quickCountMap[name] = fn +} func GetQuickFirst(name string) func(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { fn, ok := quickFirstMap[name] @@ -69,6 +74,14 @@ func GetQuickLoadList(name string) func(ctx *context.Context, key int64, field s return fn } +func GetQuickCount(name string) func(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { + fn, ok := quickCountMap[name] + if !ok { + return nil + } + return fn +} + func GetSelectInfo(columns map[string]interface{}, provide map[string]*ast.Relation) (selectColumns []string, selectRelations map[string]*SelectRelation) { selectColumns = make([]string, 0) selectRelations = make(map[string]*SelectRelation, 0) diff --git a/graphql/scalar/id.go b/graphql/scalar/id.go index 8850200..fb3a193 100644 --- a/graphql/scalar/id.go +++ b/graphql/scalar/id.go @@ -24,6 +24,8 @@ func (i *IDScalar) ParseValue(v interface{}, location *errors.GraphqlLocation) ( return v, nil case float64: return int64(v), nil + case int: + return int64(v), nil default: return nil, &errors.GraphQLError{ Message: fmt.Sprintf("invalid integer value: %v got %T", v, v), @@ -39,7 +41,7 @@ func (i *IDScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (s case int: return strconv.FormatInt(int64(v), 10), nil case float64: - return strconv.FormatInt(int64(v), 10), nil + return strconv.FormatInt(int64(v), 10), nil default: return "", &errors.GraphQLError{ Message: fmt.Sprintf("value is not an integer: %v, got %T", v, v), diff --git a/graphql/scalar/int.go b/graphql/scalar/int.go index 598167a..406ce9d 100644 --- a/graphql/scalar/int.go +++ b/graphql/scalar/int.go @@ -24,6 +24,8 @@ func (i *IntScalar) ParseValue(v interface{}, location *errors.GraphqlLocation) return v, nil case float64: return int64(v), nil + case int: + return int64(v), nil default: return nil, &errors.GraphQLError{ Message: fmt.Sprintf("invalid integer value: %v, got %T", v, v), diff --git a/graphql/scalar/string.go b/graphql/scalar/string.go index e585942..cd0c770 100644 --- a/graphql/scalar/string.go +++ b/graphql/scalar/string.go @@ -25,7 +25,7 @@ func (s *StringScalar) Serialize(v interface{}, location *errors.GraphqlLocation case string: return v, nil default: - return "", &errors.GraphQLError{ + return "", &errors.GraphQLError{ Message: fmt.Sprintf("value is not a string: %v", v), Locations: []*errors.GraphqlLocation{location}, } diff --git a/net/middleware/auth.go b/net/middleware/auth.go index 261770a..b2e3185 100644 --- a/net/middleware/auth.go +++ b/net/middleware/auth.go @@ -1,9 +1,18 @@ package middleware -import "net/http" +import ( + "net/http" + + "github.com/light-speak/lighthouse/env" +) func AuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if env.LighthouseConfig.App.Mode == env.Single { + //TODO: 本地验证 + } else { + //TODO: 从router获取 + } next.ServeHTTP(w, r) }) } diff --git a/plugins/file/file.go b/plugins/file/file.go index e560535..0cbd922 100644 --- a/plugins/file/file.go +++ b/plugins/file/file.go @@ -18,4 +18,4 @@ func NewFileOutput(path string) (*FileOutput, error) { func (fo *FileOutput) Write(p []byte) (n int, err error) { return fo.file.Write(p) -} \ No newline at end of file +} diff --git a/template/template.go b/template/template.go index 1ab82f9..70629bb 100644 --- a/template/template.go +++ b/template/template.go @@ -262,8 +262,6 @@ func init() { return } - log.Debug().Msgf("goModel: %s", goModel) - packages := []string{ "cmd", "schema", diff --git a/utils/str.go b/utils/str.go index 8830956..9e1deb2 100644 --- a/utils/str.go +++ b/utils/str.go @@ -30,15 +30,12 @@ func SnakeCase(str string) string { for i, char := range str { isUpper := unicode.IsUpper(char) - // 处理连续的大写字母 if isUpper { if !lastIsUpper && i > 0 { - // 如果前一个不是大写,当前是大写,添加下划线 result = append(result, string(currentWord)) currentWord = []rune{} } else if lastIsUpper && i+1 < len(str) { nextChar := rune(str[i+1]) - // 只有当下一个字符是小写字母时才添加下划线 if !unicode.IsUpper(nextChar) && !unicode.IsNumber(nextChar) { result = append(result, string(currentWord)) currentWord = []rune{} @@ -59,14 +56,18 @@ func SnakeCase(str string) string { // CamelCase 驼峰命名 func CamelCase(str string) string { - // 处理前导下划线 + // 如果已经是驼峰命名,直接返回 + if !strings.Contains(str, "_") { + // 确保首字母小写 + return LcFirst(str) + } + str = strings.TrimLeft(str, "_") - // 处理尾部下划线 str = strings.TrimRight(str, "_") - + parts := strings.Split(str, "_") var result []string - + for i, part := range parts { if part == "" { continue @@ -77,7 +78,7 @@ func CamelCase(str string) string { result = append(result, UcFirst(strings.ToLower(part))) } } - + return strings.Join(result, "") } @@ -87,12 +88,12 @@ func StrPtr(str string) *string { // Irregular plural forms var irregulars = map[string]string{ - "man": "men", - "woman": "women", - "child": "children", - "tooth": "teeth", - "foot": "feet", - "mouse": "mice", + "man": "men", + "woman": "women", + "child": "children", + "tooth": "teeth", + "foot": "feet", + "mouse": "mice", "person": "people", } @@ -122,4 +123,4 @@ func isVowel(c rune) bool { func IsInternalType(name string) bool { return len(name) >= 2 && name[:2] == "__" -} \ No newline at end of file +} diff --git a/utils/str_test.go b/utils/str_test.go index 58f0827..be72df5 100644 --- a/utils/str_test.go +++ b/utils/str_test.go @@ -4,7 +4,7 @@ import "testing" func TestUcFirst(t *testing.T) { tests := []struct { - input string + input string expected string }{ {"hello", "Hello"}, @@ -27,7 +27,7 @@ func TestUcFirst(t *testing.T) { func TestLcFirst(t *testing.T) { tests := []struct { - input string + input string expected string }{ {"Hello", "hello"}, @@ -50,7 +50,7 @@ func TestLcFirst(t *testing.T) { func TestSnakeCase(t *testing.T) { tests := []struct { - input string + input string expected string }{ {"helloWorld", "hello_world"}, @@ -79,7 +79,7 @@ func TestSnakeCase(t *testing.T) { func TestCamelCase(t *testing.T) { tests := []struct { - input string + input string expected string }{ {"hello_world", "helloWorld"}, @@ -94,6 +94,11 @@ func TestCamelCase(t *testing.T) { {"hello__world", "helloWorld"}, {"_hello_world", "helloWorld"}, {"hello_world_", "helloWorld"}, + {"userId", "userId"}, + {"tagId", "tagId"}, + {"BackId", "backId"}, + {"back_id", "backId"}, + {"Back_Id", "backId"}, } for _, test := range tests { @@ -139,20 +144,20 @@ func TestPluralize(t *testing.T) { {"foot", "feet"}, {"mouse", "mice"}, {"person", "people"}, - + // Words ending in s, x, z, ch, sh {"bus", "buses"}, {"box", "boxes"}, {"buzz", "buzzes"}, {"church", "churches"}, {"dish", "dishes"}, - + // Words ending in y - {"baby", "babies"}, // consonant + y - {"boy", "boys"}, // vowel + y + {"baby", "babies"}, // consonant + y + {"boy", "boys"}, // vowel + y {"day", "days"}, {"key", "keys"}, - + // Regular words {"book", "books"}, {"pen", "pens"}, From 1524a43437b1157c9042ab8bcd7d06dec5eab263 Mon Sep 17 00:00:00 2001 From: linty Date: Sat, 2 Nov 2024 20:06:36 +0800 Subject: [PATCH 06/17] feat: mergedata --- auth/claims.go | 17 ++ auth/utils.go | 37 ++++ example/user/go.mod | 1 + example/user/go.sum | 2 + example/user/models/model.go | 8 +- example/user/models/response.go | 7 +- example/user/repo/repo.go | 6 +- example/user/resolver/mutation.go | 33 ++++ example/user/resolver/operation_gen.go | 32 ++- example/user/resolver/query.go | 56 +++--- example/user/schema/post.graphqls | 4 + example/user/schema/user.graphql | 9 + go.mod | 1 + go.sum | 2 + graphql/ast/node.go | 6 +- graphql/excute/merge.go | 90 +++++++++ graphql/excute/quick.go | 68 ------- graphql/excute/resolver.go | 194 +++++++------------ graphql/excute/return.go | 4 +- graphql/model/generate/tpl/operation_gen.tpl | 6 +- graphql/model/quick.go | 68 ++++++- template/import.go | 3 + 22 files changed, 412 insertions(+), 242 deletions(-) create mode 100644 auth/claims.go create mode 100644 auth/utils.go create mode 100644 example/user/resolver/mutation.go create mode 100644 graphql/excute/merge.go diff --git a/auth/claims.go b/auth/claims.go new file mode 100644 index 0000000..fc1f8cf --- /dev/null +++ b/auth/claims.go @@ -0,0 +1,17 @@ +package auth + +import ( + "github.com/golang-jwt/jwt/v5" + "github.com/light-speak/lighthouse/env" +) + +type claim struct { + UserId int64 `json:"user_id"` + jwt.RegisteredClaims +} + +var key []byte + +func init() { + key = []byte(env.GetEnv("JWT_SECRET", "IWY@*3JUI#d309HhefzX2WpLtPKtD!hn")) +} diff --git a/auth/utils.go b/auth/utils.go new file mode 100644 index 0000000..78c8b82 --- /dev/null +++ b/auth/utils.go @@ -0,0 +1,37 @@ +package auth + +import ( + "errors" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +func GetToken(userId int64) (string, error) { + return jwt.NewWithClaims(jwt.SigningMethodHS256, buildClaims(userId)).SignedString(key) +} + +func buildClaims(userId int64) *claim { + now := time.Now() + return &claim{ + UserId: userId, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(now.Add(time.Hour * 24 * 30)), + IssuedAt: jwt.NewNumericDate(now), + Issuer: "lighthouse", + }, + } +} + +func GetUserId(token string) (int64, error) { + t, err := jwt.ParseWithClaims(token, &claim{}, func(token *jwt.Token) (interface{}, error) { + return key, nil + }) + if err != nil { + return 0, err + } + if claims, ok := t.Claims.(*claim); ok && t.Valid { + return claims.UserId, nil + } + return 0, errors.New("invalid token") +} diff --git a/example/user/go.mod b/example/user/go.mod index 9647660..2dd1d32 100644 --- a/example/user/go.mod +++ b/example/user/go.mod @@ -17,6 +17,7 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/joho/godotenv v1.5.1 // indirect diff --git a/example/user/go.sum b/example/user/go.sum index d93acd4..a611f17 100644 --- a/example/user/go.sum +++ b/example/user/go.sum @@ -15,6 +15,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= diff --git a/example/user/models/model.go b/example/user/models/model.go index ea05c3d..456ef7c 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -6,13 +6,13 @@ import "github.com/light-speak/lighthouse/graphql/model" type Post struct { model.ModelSoftDelete - UserId int64 `json:"user_id" ` - Content string `json:"content" gorm:"type:varchar(255)" ` TagId int64 `json:"tag_id" ` BackId int64 `json:"back_id" ` User User `json:"user" ` Enum TestEnum `json:"enum" ` - Title string `json:"title" gorm:"index;type:varchar(255)" ` + Title string `gorm:"index;type:varchar(255)" json:"title" ` + Content string `json:"content" gorm:"type:varchar(255)" ` + UserId int64 `json:"user_id" ` } func (*Post) IsModel() bool { return true } @@ -21,8 +21,8 @@ func (*Post) TypeName() string { return "post" } type User struct { model.Model - Posts []Post `json:"posts" gorm:"comment:五二零" ` Name string `json:"name" gorm:"index;type:varchar(255)" ` + Posts []Post `json:"posts" gorm:"comment:五二零" ` } func (*User) IsModel() bool { return true } diff --git a/example/user/models/response.go b/example/user/models/response.go index 0682add..9bf3e63 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -5,7 +5,7 @@ import "github.com/light-speak/lighthouse/graphql/model" type Test struct { - Test string `json:"test" gorm:"type:varchar(255)" ` + Test string `gorm:"type:varchar(255)" json:"test" ` } type PostPaginateResponse struct { @@ -17,3 +17,8 @@ type UserPaginateResponse struct { Data []*User `json:"data" ` PaginateInfo model.PaginateInfo `json:"paginate_info" ` } + +type LoginResponse struct { + User User `json:"user" ` + Token string `json:"token" gorm:"type:varchar(255)" ` +} diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index 574c862..8abe50e 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,12 +2,12 @@ package repo import ( - "github.com/light-speak/lighthouse/graphql/ast" "gorm.io/gorm" + "github.com/light-speak/lighthouse/context" "sync" - "user/models" "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/context" + "user/models" + "github.com/light-speak/lighthouse/graphql/ast" ) func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} diff --git a/example/user/resolver/mutation.go b/example/user/resolver/mutation.go new file mode 100644 index 0000000..ec24634 --- /dev/null +++ b/example/user/resolver/mutation.go @@ -0,0 +1,33 @@ +// Code generated by github.com/light-speak/lighthouse, YOU CAN FUCKING EDIT BY YOURSELF. +package resolver + +import ( + "github.com/light-speak/lighthouse/graphql/model" + "user/models" + "github.com/light-speak/lighthouse/auth" + "github.com/light-speak/lighthouse/context" +) + + +func LoginResolver(ctx *context.Context,name string) (*models.LoginResponse, error) { + // Func:Login user code start. Do not remove this comment. + user := &models.User{} + db := model.GetDB() + if err := db.Model(&models.User{}).First(user).Error; err != nil { + return nil, err + } + token, err := auth.GetToken(user.Id) + if err != nil { + return nil, err + } + return &models.LoginResponse{ + User: *user, + Token: token, + }, nil + // Func:Login user code end. Do not remove this comment. +} +func CreatePostResolver(ctx *context.Context,input *models.TestInput) (*models.Post, error) { + // Func:CreatePost user code start. Do not remove this comment. + panic("not implement") + // Func:CreatePost user code end. Do not remove this comment. +} \ No newline at end of file diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index 67350a7..0a950c3 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,12 +2,12 @@ package resolver import ( - "user/models" - "fmt" - "github.com/light-speak/lighthouse/graphql/model" "github.com/light-speak/lighthouse/graphql" + "fmt" "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/graphql/excute" + "github.com/light-speak/lighthouse/graphql/model" + "user/models" ) func init() { @@ -103,4 +103,30 @@ func init() { } return model.StructToMap(res) }) + excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { + input, err := models.MapToTestInput(args["input"].(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) + } + res, err := CreatePostResolver(ctx, input) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("login", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) + if e != nil { + return nil, e + } + name, ok := pv.(string) + if !ok { + return nil, fmt.Errorf("argument: 'name' is not a string, got %T", args["name"]) + } + res, err := LoginResolver(ctx, name) + if res == nil { + return nil, err + } + return model.TypeToMap(res) + }) } diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index 8003444..5cfcd76 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,22 +2,19 @@ package resolver import ( - "user/models" - "fmt" - "github.com/light-speak/lighthouse/graphql/model" "github.com/light-speak/lighthouse/log" + "fmt" "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/model" + "user/models" ) -func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { - // Func:GetPost user code start. Do not remove this comment. - log.Debug().Msg("GetPostResolver") - db := model.GetDB() - post := &models.Post{} - db.Where("id = ?", fuck).First(post) - return post, nil - // Func:GetPost user code end. Do not remove this comment. +func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { + // Func:TestPostInt user code start. Do not remove this comment. + log.Debug().Msgf("id: %d", id) + return nil, nil + // Func:TestPostInt user code end. Do not remove this comment. } func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { // Func:TestPostId user code start. Do not remove this comment. @@ -25,18 +22,26 @@ func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { return nil, nil // Func:TestPostId user code end. Do not remove this comment. } -func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { - // Func:TestPostEnum user code start. Do not remove this comment. - log.Debug().Msgf("enum: %+v", enum) - res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) - return res, nil - // Func:TestPostEnum user code end. Do not remove this comment. -} func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { // Func:GetPostIds user code start. Do not remove this comment. return []int64{1, 2, 3}, nil // Func:GetPostIds user code end. Do not remove this comment. } +func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { + // Func:TestPostInput user code start. Do not remove this comment. + res := fmt.Sprintf("input: %+v", input) + return res, nil + // Func:TestPostInput user code end. Do not remove this comment. +} +func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { + // Func:GetPost user code start. Do not remove this comment. + log.Debug().Msg("GetPostResolver") + db := model.GetDB() + post := &models.Post{} + db.Where("id = ?", fuck).First(post) + return post, nil + // Func:GetPost user code end. Do not remove this comment. +} func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { // Func:GetPosts user code start. Do not remove this comment. posts := []*models.Post{} @@ -45,15 +50,10 @@ func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) return posts, nil // Func:GetPosts user code end. Do not remove this comment. } -func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { - // Func:TestPostInput user code start. Do not remove this comment. - res := fmt.Sprintf("input: %+v", input) +func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { + // Func:TestPostEnum user code start. Do not remove this comment. + log.Debug().Msgf("enum: %+v", enum) + res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) return res, nil - // Func:TestPostInput user code end. Do not remove this comment. -} -func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { - // Func:TestPostInt user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) - return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. + // Func:TestPostEnum user code end. Do not remove this comment. } \ No newline at end of file diff --git a/example/user/schema/post.graphqls b/example/user/schema/post.graphqls index ca207f7..5804159 100644 --- a/example/user/schema/post.graphqls +++ b/example/user/schema/post.graphqls @@ -21,6 +21,10 @@ extend type Query { testPostInput(input: TestInput!): String! } +extend type Mutation { + createPost(input: TestInput!): Post! +} + type Test { test: String! } diff --git a/example/user/schema/user.graphql b/example/user/schema/user.graphql index 93b5f53..b04b511 100644 --- a/example/user/schema/user.graphql +++ b/example/user/schema/user.graphql @@ -10,6 +10,15 @@ extend type Query { user(id: ID!): User! @first } +type LoginResponse { + user: User! + token: String! +} + +extend type Mutation { + login(name: String!): LoginResponse! +} + interface Userable { userId: ID! user: User! diff --git a/go.mod b/go.mod index 586a089..f2e83d2 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.23.1 require ( github.com/elastic/go-elasticsearch/v8 v8.15.0 github.com/go-chi/chi/v5 v5.1.0 + github.com/golang-jwt/jwt/v5 v5.2.1 github.com/joho/godotenv v1.5.1 github.com/rs/zerolog v1.33.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index d93acd4..a611f17 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= diff --git a/graphql/ast/node.go b/graphql/ast/node.go index 3cb1006..a4c6429 100644 --- a/graphql/ast/node.go +++ b/graphql/ast/node.go @@ -386,9 +386,11 @@ func (f *Field) Validate(store *NodeStore, objectFields map[string]*Field, objec Locations: []*errors.GraphqlLocation{f.GetLocation()}, } } + field := objectNode.GetFields()[f.Name] // merge - f.DefinitionDirectives = append(f.DefinitionDirectives, objectNode.GetFields()[f.Name].Directives...) - f.DefinitionArgs = objectNode.GetFields()[f.Name].Args + f.DefinitionDirectives = append(f.DefinitionDirectives, field.Directives...) + f.Relation = field.Relation + f.DefinitionArgs = field.Args for _, defArg := range f.DefinitionArgs { if defArg.DefaultValue != nil && f.Args[defArg.Name] == nil { if f.Args == nil { diff --git a/graphql/excute/merge.go b/graphql/excute/merge.go new file mode 100644 index 0000000..2f2a4a3 --- /dev/null +++ b/graphql/excute/merge.go @@ -0,0 +1,90 @@ +package excute + +import ( + "fmt" + + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/errors" + "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/utils" +) + +func mergeData(ctx *context.Context, field *ast.Field, datas map[string]interface{}) (interface{}, errors.GraphqlErrorInterface) { + fieldName := utils.SnakeCase(field.Name) + + v, ok := datas[fieldName] + if !ok { + return nil, &errors.GraphQLError{ + Message: fmt.Sprintf("field %s not found", field.Name), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } + } + if v == nil { + if field.Relation != nil { + cData, err := model.FetchRelation(ctx, datas, &model.SelectRelation{Relation: field.Relation}) + if err != nil { + return nil, &errors.GraphQLError{ + Message: err.Error(), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } + } + v = cData + } + } + if v == nil { + return nil, nil + } + + typeRef := field.Type + if typeRef.Kind == ast.KindNonNull { + typeRef = typeRef.OfType + } + + if typeRef.Kind == ast.KindList { + vList, ok := v.([]map[string]interface{}) + if !ok { + return nil, &errors.GraphQLError{ + Message: fmt.Sprintf("expected list but got %T", v), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } + } + + result := make([]interface{}, len(vList)) + for i, item := range vList { + listField := &ast.Field{ + Name: field.Name, + Children: field.Children, + Type: typeRef.OfType, + } + + m := make(map[string]interface{}) + m[field.Name] = item + merged, err := mergeData(ctx, listField, m) + if err != nil { + return nil, err + } + result[i] = merged + } + return result, nil + } + + if field.Children != nil { + cv := make(map[string]interface{}) + vMap := v.(map[string]interface{}) + for _, child := range field.Children { + c, err := mergeData(ctx, child, vMap) + if err != nil { + return nil, err + } + cv[child.Name] = c + } + return cv, nil + } + + v, err := ValidateValue(field, v, false) + if err != nil { + return nil, err + } + return v, nil +} diff --git a/graphql/excute/quick.go b/graphql/excute/quick.go index e0a00a2..5bd355c 100644 --- a/graphql/excute/quick.go +++ b/graphql/excute/quick.go @@ -1,12 +1,9 @@ package excute import ( - "fmt" - "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" - "github.com/light-speak/lighthouse/utils" "gorm.io/gorm" ) @@ -43,71 +40,6 @@ func QuickExecute(ctx *context.Context, field *ast.Field) (interface{}, bool, er return nil, false, nil } -func mergeData(field *ast.Field, datas map[string]interface{}) (interface{}, errors.GraphqlErrorInterface) { - fieldName := utils.SnakeCase(field.Name) - v, ok := datas[fieldName] - if !ok { - return nil, &errors.GraphQLError{ - Message: fmt.Sprintf("field %s not found", field.Name), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } - } - if v == nil { - return nil, nil - } - - typeRef := field.Type - if typeRef.Kind == ast.KindNonNull { - typeRef = typeRef.OfType - } - - if typeRef.Kind == ast.KindList { - vList, ok := v.([]map[string]interface{}) - if !ok { - return nil, &errors.GraphQLError{ - Message: fmt.Sprintf("expected list but got %T", v), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } - } - - result := make([]interface{}, len(vList)) - for i, item := range vList { - listField := &ast.Field{ - Name: field.Name, - Children: field.Children, - Type: typeRef.OfType, - } - - m := make(map[string]interface{}) - m[field.Name] = item - merged, err := mergeData(listField, m) - if err != nil { - return nil, err - } - result[i] = merged - } - return result, nil - } - - if field.Children != nil { - cv := make(map[string]interface{}) - vMap := v.(map[string]interface{}) - for _, child := range field.Children { - c, err := mergeData(child, vMap) - if err != nil { - return nil, err - } - cv[child.Name] = c - } - return cv, nil - } - - v, err := ValidateValue(field, v, false) - if err != nil { - return nil, err - } - return v, nil -} func getColumns(field *ast.Field) (map[string]interface{}, errors.GraphqlErrorInterface) { res := make(map[string]interface{}) diff --git a/graphql/excute/resolver.go b/graphql/excute/resolver.go index 0cde65c..e6ab22e 100644 --- a/graphql/excute/resolver.go +++ b/graphql/excute/resolver.go @@ -5,6 +5,7 @@ import ( "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/log" ) func executeResolver(ctx *context.Context, field *ast.Field) (interface{}, bool, errors.GraphqlErrorInterface) { @@ -33,136 +34,83 @@ func executeResolver(ctx *context.Context, field *ast.Field) (interface{}, bool, if field.Type.IsScalar() { return r, true, nil - } else { - columns, e := getColumns(field) - if e != nil { - return nil, true, &errors.GraphQLError{ - Message: e.Error(), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } - } - realType := field.Type.GetRealType() - if field.Type.IsList() { - r, err := model.GetQuickList(realType.Name)(ctx, columns, r.([]map[string]interface{})) - if err != nil { - return nil, true, &errors.GraphQLError{ - Message: err.Error(), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } - } - data := make([]map[string]interface{}, 0) - for _, ri := range r { - riData := make(map[string]interface{}) - for _, child := range field.Children { - v, err := mergeData(child, ri) - riData[child.Name] = v - if err != nil { - return nil, true, err - } - } - data = append(data, riData) - } - return data, true, nil - } else { - r, err := model.GetQuickFirst(realType.Name)(ctx, columns, r.(map[string]interface{})) - if err != nil { - return nil, true, &errors.GraphQLError{ - Message: err.Error(), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } - } - data := make(map[string]interface{}) - for _, child := range field.Children { - v, err := mergeData(child, r) - data[child.Name] = v - if err != nil { - return nil, true, err - } - } - return data, true, nil - } } + return processObjectResult(ctx, field, r) } return nil, false, nil } -// if resolverFunc, ok := resolverMap[field.Name]; ok { -// args := make(map[string]any) -// for _, arg := range field.Args { -// args[arg.Name] = arg.Value -// } -// r, e := resolverFunc(ctx, args) -// if e != nil { -// ctx.Errors = append(ctx.Errors, &errors.GraphQLError{ -// Message: e.Error(), -// Locations: []*errors.GraphqlLocation{field.GetLocation()}, -// }) -// continue -// } +func processObjectResult(ctx *context.Context, field *ast.Field, r interface{}) (interface{}, bool, errors.GraphqlErrorInterface) { + columns, e := getColumns(field) + if e != nil { + return nil, true, &errors.GraphQLError{ + Message: e.Error(), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } + } + + realType := field.Type.GetRealType() + if realType.TypeNode.GetKind() == ast.KindObject && realType.TypeNode.(*ast.ObjectNode).IsModel { + if field.Type.IsList() { + return processListResult(ctx, field, realType, columns, r) + } + return processSingleResult(ctx, field, realType, columns, r) + } + + data := make(map[string]interface{}) + for _, child := range field.Children { + v, err := mergeData(ctx, child, r.(map[string]interface{})) + if err != nil { + return nil, true, err + } + data[child.Name] = v + } -// if r == nil { -// if field.Type.Kind == ast.KindNonNull { -// ctx.Errors = append(ctx.Errors, &errors.GraphQLError{ -// Message: "field is not nullable", -// Locations: []*errors.GraphqlLocation{field.GetLocation()}, -// }) -// continue -// } -// res[field.Name] = nil -// continue -// } + log.Info().Msgf("processObjectResult: %v", data) + return data, true, nil +} -// if modelData, ok := r.(model.ModelInterface); ok { -// columns, e := getColumns(field) -// if e != nil { -// ctx.Errors = append(ctx.Errors, &errors.GraphQLError{ -// Message: e.Error(), -// Locations: []*errors.GraphqlLocation{field.GetLocation()}, -// }) -// continue -// } -// modelMap, err := model.StructToMap(modelData) -// if err != nil { -// ctx.Errors = append(ctx.Errors, &errors.GraphQLError{ -// Message: err.Error(), -// Locations: []*errors.GraphqlLocation{field.GetLocation()}, -// }) -// continue -// } -// isList := false +func processListResult(ctx *context.Context, field *ast.Field, realType *ast.TypeRef, columns map[string]interface{}, r interface{}) (interface{}, bool, errors.GraphqlErrorInterface) { + result, err := model.GetQuickList(realType.Name)(ctx, columns, r.([]map[string]interface{})) + if err != nil { + return nil, true, &errors.GraphQLError{ + Message: err.Error(), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } + } -// returnType := field.Type -// if field.Type.Kind == ast.KindNonNull { -// returnType = field.Type.OfType -// } -// if returnType.Kind == ast.KindList { -// isList = true -// returnType = returnType.OfType -// } + data := make([]map[string]interface{}, 0) + for _, ri := range result { + riData := make(map[string]interface{}) + for _, child := range field.Children { + v, err := mergeData(ctx, child, ri) + if err != nil { + return nil, true, err + } + riData[child.Name] = v + } + data = append(data, riData) + } + return data, true, nil +} + +func processSingleResult(ctx *context.Context, field *ast.Field, realType *ast.TypeRef, columns map[string]interface{}, r interface{}) (interface{}, bool, errors.GraphqlErrorInterface) { + result, err := model.GetQuickFirst(realType.Name)(ctx, columns, r.(map[string]interface{})) + if err != nil { + return nil, true, &errors.GraphQLError{ + Message: err.Error(), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } + } -// if returnType.Kind == ast.KindObject { -// if isList { -// model.GetQuickLoadList(returnType.Name)(ctx, modelMap) -// } else { -// model.GetQuickFirst(returnType.Name)(ctx, columns, modelMap) -// } -// } -// data := make(map[string]interface{}) -// for _, child := range field.Children { -// d, err := mergeData(child, modelMap) -// if err != nil { -// ctx.Errors = append(ctx.Errors, &errors.GraphQLError{ -// Message: err.Error(), -// Locations: []*errors.GraphqlLocation{child.GetLocation()}, -// }) -// continue -// } -// data[child.Name] = d -// } -// res[field.Name] = data -// continue -// } -// res[field.Name] = r -// continue -// } + data := make(map[string]interface{}) + for _, child := range field.Children { + v, err := mergeData(ctx, child, result) + if err != nil { + return nil, true, err + } + data[child.Name] = v + } + return data, true, nil +} diff --git a/graphql/excute/return.go b/graphql/excute/return.go index 4988477..80ad19c 100644 --- a/graphql/excute/return.go +++ b/graphql/excute/return.go @@ -35,7 +35,7 @@ func executeFirst(ctx *context.Context, field *ast.Field, scopes ...func(db *gor } data := make(map[string]interface{}) for _, child := range field.Children { - v, err := mergeData(child, d) + v, err := mergeData(ctx, child, d) data[child.Name] = v if err != nil { return nil, err @@ -137,7 +137,7 @@ func executeFind(ctx *context.Context, field *ast.Field, scopes ...func(db *gorm for _, item := range datas { d := make(map[string]interface{}) for _, child := range field.Children { - v, err := mergeData(child, item) + v, err := mergeData(ctx, child, item) d[child.Name] = v if err != nil { return nil, err diff --git a/graphql/model/generate/tpl/operation_gen.tpl b/graphql/model/generate/tpl/operation_gen.tpl index 35f5f8b..d21687f 100644 --- a/graphql/model/generate/tpl/operation_gen.tpl +++ b/graphql/model/generate/tpl/operation_gen.tpl @@ -1,6 +1,5 @@ func init() { {{- range .Nodes }} -{{- if eq .Name "Query" }} {{- range .Fields }} {{- if not (isInternalType .Name) }} {{- if eq (len .Directives) 0 }} @@ -68,8 +67,12 @@ func init() { if res == nil { return nil, err } + {{- if .Type.GetRealType.TypeNode.IsModel }} return model.StructToMap(res) {{- else }} + return model.TypeToMap(res) + {{- end }} + {{- else }} res, err := {{ .Name | ucFirst }}Resolver(ctx{{ range $index, $arg := $args }}, {{ $arg.Name | lcFirst }}{{ end }}) return res, err {{- end }} @@ -78,5 +81,4 @@ func init() { {{- end }} {{- end }} {{- end }} -{{- end }} } diff --git a/graphql/model/quick.go b/graphql/model/quick.go index 6568d33..5e47dea 100644 --- a/graphql/model/quick.go +++ b/graphql/model/quick.go @@ -3,6 +3,7 @@ package model import ( "encoding/json" "fmt" + "strconv" "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/errors" @@ -103,6 +104,16 @@ func GetSelectInfo(columns map[string]interface{}, provide map[string]*ast.Relat return selectColumns, selectRelations } +func GetRelation(relation *ast.Relation) *SelectRelation { + if relation == nil { + return nil + } + return &SelectRelation{ + Relation: relation, + SelectColumns: make(map[string]interface{}), + } +} + func StructToMap(m ModelInterface) (map[string]interface{}, error) { jsonData, err := json.Marshal(m) if err != nil { @@ -127,6 +138,29 @@ func StructToMap(m ModelInterface) (map[string]interface{}, error) { return result, nil } +func TypeToMap(m interface{}) (map[string]interface{}, error) { + jsonData, err := json.Marshal(m) + if err != nil { + return nil, err + } + + var result map[string]interface{} + err = json.Unmarshal(jsonData, &result) + if err != nil { + return nil, err + } + for key, value := range result { + switch v := value.(type) { + case float64: + if v == float64(int64(v)) { + result[key] = int64(v) + } + } + } + + return result, nil +} + func MapToStruct[T any](data map[string]interface{}) (T, error) { var m T jsonData, err := json.Marshal(data) @@ -173,9 +207,20 @@ func FetchRelation(ctx *context.Context, data map[string]interface{}, relation * } func fetchHasMany(ctx *context.Context, relation *SelectRelation, fieldValue interface{}) ([]map[string]interface{}, error) { - key, ok := fieldValue.(int64) - if !ok { - return nil, fmt.Errorf("relation %s field %s value is not int64", relation.Relation.Name, relation.Relation.ForeignKey) + var err error + var key int64 + switch v := fieldValue.(type) { + case int64: + key = v + case string: + key, err = strconv.ParseInt(v, 10, 64) + if err != nil { + return nil, err + } + case float64: + key = int64(v) + default: + return nil, fmt.Errorf("relation %s field %s value is not int64, got %v, type %T", relation.Relation.Name, relation.Relation.ForeignKey, fieldValue, fieldValue) } datas, err := GetQuickLoadList(utils.UcFirst(relation.Relation.Name))(ctx, key, relation.Relation.ForeignKey) if err != nil { @@ -189,9 +234,20 @@ func fetchHasMany(ctx *context.Context, relation *SelectRelation, fieldValue int } func fetchBelongsTo(ctx *context.Context, relation *SelectRelation, fieldValue interface{}) (map[string]interface{}, error) { - key, ok := fieldValue.(int64) - if !ok { - return nil, fmt.Errorf("relation %s field %s value is not int64", relation.Relation.Name, relation.Relation.ForeignKey) + var err error + var key int64 + switch v := fieldValue.(type) { + case int64: + key = v + case string: + key, err = strconv.ParseInt(v, 10, 64) + if err != nil { + return nil, err + } + case float64: + key = int64(v) + default: + return nil, fmt.Errorf("relation %s field %s value is not int64, got %v, type %T", relation.Relation.Name, relation.Relation.ForeignKey, fieldValue, fieldValue) } data, err := GetQuickLoad(utils.UcFirst(relation.Relation.Name))(ctx, key, relation.Relation.Reference) if err != nil { diff --git a/template/import.go b/template/import.go index 877612a..089597f 100644 --- a/template/import.go +++ b/template/import.go @@ -78,6 +78,9 @@ var importRegexMap = map[string]Import{ `graphql\.`: { Path: "github.com/light-speak/lighthouse/graphql", }, + `auth\.`: { + Path: "github.com/light-speak/lighthouse/auth", + }, } // AddImportRegex add a new import regex and path to the importRegexMap From 9192e04f37b4611261ddf1a1f72efe688d815ea3 Mon Sep 17 00:00:00 2001 From: linty Date: Sat, 2 Nov 2024 21:48:33 +0800 Subject: [PATCH 07/17] feat: merge data --- example/user/models/model.go | 8 +- example/user/models/response.go | 14 +-- example/user/repo/repo.go | 117 ++---------------- example/user/resolver/mutation.go | 22 ++-- example/user/resolver/operation_gen.go | 2 +- example/user/resolver/query.go | 40 +++--- graphql/ast/node.go | 2 +- graphql/excute/merge.go | 138 +++++++++++++++------ graphql/excute/resolver.go | 22 +--- graphql/excute/return.go | 162 +++++++++++++++++-------- graphql/model/generate/tpl/repo.tpl | 50 +------- graphql/model/quick.go | 71 +++-------- 12 files changed, 298 insertions(+), 350 deletions(-) diff --git a/example/user/models/model.go b/example/user/models/model.go index 456ef7c..b4c27eb 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -6,13 +6,13 @@ import "github.com/light-speak/lighthouse/graphql/model" type Post struct { model.ModelSoftDelete - TagId int64 `json:"tag_id" ` - BackId int64 `json:"back_id" ` User User `json:"user" ` Enum TestEnum `json:"enum" ` - Title string `gorm:"index;type:varchar(255)" json:"title" ` Content string `json:"content" gorm:"type:varchar(255)" ` UserId int64 `json:"user_id" ` + TagId int64 `json:"tag_id" ` + BackId int64 `json:"back_id" ` + Title string `json:"title" gorm:"index;type:varchar(255)" ` } func (*Post) IsModel() bool { return true } @@ -22,7 +22,7 @@ func (*Post) TypeName() string { return "post" } type User struct { model.Model Name string `json:"name" gorm:"index;type:varchar(255)" ` - Posts []Post `json:"posts" gorm:"comment:五二零" ` + Posts []Post `gorm:"comment:五二零" json:"posts" ` } func (*User) IsModel() bool { return true } diff --git a/example/user/models/response.go b/example/user/models/response.go index 9bf3e63..1941d6e 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -4,21 +4,21 @@ package models import "github.com/light-speak/lighthouse/graphql/model" -type Test struct { - Test string `gorm:"type:varchar(255)" json:"test" ` -} - type PostPaginateResponse struct { Data []*Post `json:"data" ` PaginateInfo model.PaginateInfo `json:"paginate_info" ` } +type LoginResponse struct { + User User `json:"user" ` + Token string `json:"token" gorm:"type:varchar(255)" ` +} + type UserPaginateResponse struct { Data []*User `json:"data" ` PaginateInfo model.PaginateInfo `json:"paginate_info" ` } -type LoginResponse struct { - User User `json:"user" ` - Token string `json:"token" gorm:"type:varchar(255)" ` +type Test struct { + Test string `json:"test" gorm:"type:varchar(255)" ` } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index 8abe50e..43d2832 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,12 +2,11 @@ package repo import ( - "gorm.io/gorm" "github.com/light-speak/lighthouse/context" - "sync" "github.com/light-speak/lighthouse/graphql/model" - "user/models" + "gorm.io/gorm" "github.com/light-speak/lighthouse/graphql/ast" + "user/models" ) func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} @@ -20,74 +19,26 @@ func LoadList__Post(ctx *context.Context, key int64, field string) ([]map[string func Query__Post(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { return model.GetDB().Model(&models.Post{}).Scopes(scopes...) } -func First__Post(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { +func First__Post(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { var err error - selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__Post()) if data == nil { data = make(map[string]interface{}) - err = Query__Post().Scopes(scopes...).Select(selectColumns).First(data).Error + err = Query__Post().Scopes(scopes...).First(data).Error if err != nil { return nil, err } } - var wg sync.WaitGroup - errChan := make(chan error, len(selectRelations)) - var mu sync.Mutex - - for key, relation := range selectRelations { - wg.Add(1) - go func(data map[string]interface{}, relation *model.SelectRelation) { - defer wg.Done() - cData, err := model.FetchRelation(ctx, data, relation) - if err != nil { - errChan <- err - } - mu.Lock() - defer mu.Unlock() - data[key] = cData - }(data, relation) - } - wg.Wait() - close(errChan) - for err := range errChan { - return nil, err - } return data, nil } -func List__Post(ctx *context.Context, columns map[string]interface{},datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { +func List__Post(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { var err error - selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__Post()) if datas == nil { datas = make([]map[string]interface{}, 0) - err = Query__Post().Scopes(scopes...).Select(selectColumns).Find(&datas).Error + err = Query__Post().Scopes(scopes...).Find(&datas).Error if err != nil { return nil, err } } - var wg sync.WaitGroup - errChan := make(chan error, len(datas)*len(selectRelations)) - var mu sync.Mutex - - for _, data := range datas { - for key, relation := range selectRelations { - wg.Add(1) - go func(data map[string]interface{}, relation *model.SelectRelation) { - defer wg.Done() - cData, err := model.FetchRelation(ctx, data, relation) - if err != nil { - errChan <- err - } - mu.Lock() - defer mu.Unlock() - data[key] = cData - }(data, relation) - } - } - wg.Wait() - close(errChan) - for err := range errChan { - return nil, err - } return datas, nil } func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { @@ -105,74 +56,26 @@ func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { return model.GetDB().Model(&models.User{}).Scopes(scopes...) } -func First__User(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { +func First__User(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { var err error - selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__User()) if data == nil { data = make(map[string]interface{}) - err = Query__User().Scopes(scopes...).Select(selectColumns).First(data).Error + err = Query__User().Scopes(scopes...).First(data).Error if err != nil { return nil, err } } - var wg sync.WaitGroup - errChan := make(chan error, len(selectRelations)) - var mu sync.Mutex - - for key, relation := range selectRelations { - wg.Add(1) - go func(data map[string]interface{}, relation *model.SelectRelation) { - defer wg.Done() - cData, err := model.FetchRelation(ctx, data, relation) - if err != nil { - errChan <- err - } - mu.Lock() - defer mu.Unlock() - data[key] = cData - }(data, relation) - } - wg.Wait() - close(errChan) - for err := range errChan { - return nil, err - } return data, nil } -func List__User(ctx *context.Context, columns map[string]interface{},datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { +func List__User(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { var err error - selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__User()) if datas == nil { datas = make([]map[string]interface{}, 0) - err = Query__User().Scopes(scopes...).Select(selectColumns).Find(&datas).Error + err = Query__User().Scopes(scopes...).Find(&datas).Error if err != nil { return nil, err } } - var wg sync.WaitGroup - errChan := make(chan error, len(datas)*len(selectRelations)) - var mu sync.Mutex - - for _, data := range datas { - for key, relation := range selectRelations { - wg.Add(1) - go func(data map[string]interface{}, relation *model.SelectRelation) { - defer wg.Done() - cData, err := model.FetchRelation(ctx, data, relation) - if err != nil { - errChan <- err - } - mu.Lock() - defer mu.Unlock() - data[key] = cData - }(data, relation) - } - } - wg.Wait() - close(errChan) - for err := range errChan { - return nil, err - } return datas, nil } func Count__User(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { diff --git a/example/user/resolver/mutation.go b/example/user/resolver/mutation.go index ec24634..519582d 100644 --- a/example/user/resolver/mutation.go +++ b/example/user/resolver/mutation.go @@ -2,18 +2,18 @@ package resolver import ( - "github.com/light-speak/lighthouse/graphql/model" - "user/models" - "github.com/light-speak/lighthouse/auth" - "github.com/light-speak/lighthouse/context" -) + "user/models" + "github.com/light-speak/lighthouse/auth" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/model" +) -func LoginResolver(ctx *context.Context,name string) (*models.LoginResponse, error) { +func LoginResolver(ctx *context.Context, name string) (*models.LoginResponse, error) { // Func:Login user code start. Do not remove this comment. user := &models.User{} db := model.GetDB() - if err := db.Model(&models.User{}).First(user).Error; err != nil { + if err := db.Model(&models.User{}).Where("id = ?", 2).First(user).Error; err != nil { return nil, err } token, err := auth.GetToken(user.Id) @@ -24,10 +24,10 @@ func LoginResolver(ctx *context.Context,name string) (*models.LoginResponse, err User: *user, Token: token, }, nil - // Func:Login user code end. Do not remove this comment. + // Func:Login user code end. Do not remove this comment. } -func CreatePostResolver(ctx *context.Context,input *models.TestInput) (*models.Post, error) { +func CreatePostResolver(ctx *context.Context, input *models.TestInput) (*models.Post, error) { // Func:CreatePost user code start. Do not remove this comment. panic("not implement") - // Func:CreatePost user code end. Do not remove this comment. -} \ No newline at end of file + // Func:CreatePost user code end. Do not remove this comment. +} diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index 0a950c3..326aeb0 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,8 +2,8 @@ package resolver import ( - "github.com/light-speak/lighthouse/graphql" "fmt" + "github.com/light-speak/lighthouse/graphql" "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/graphql/excute" "github.com/light-speak/lighthouse/graphql/model" diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index 5cfcd76..495d007 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,8 +2,8 @@ package resolver import ( - "github.com/light-speak/lighthouse/log" "fmt" + "github.com/light-speak/lighthouse/log" "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/graphql/model" "user/models" @@ -16,23 +16,26 @@ func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { return nil, nil // Func:TestPostInt user code end. Do not remove this comment. } -func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { - // Func:TestPostId user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) - return nil, nil - // Func:TestPostId user code end. Do not remove this comment. -} -func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { - // Func:GetPostIds user code start. Do not remove this comment. - return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. -} func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { // Func:TestPostInput user code start. Do not remove this comment. res := fmt.Sprintf("input: %+v", input) return res, nil // Func:TestPostInput user code end. Do not remove this comment. } +func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { + // Func:GetPosts user code start. Do not remove this comment. + posts := []*models.Post{} + db := model.GetDB() + db.Find(&posts) + return posts, nil + // Func:GetPosts user code end. Do not remove this comment. +} +func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { + // Func:TestPostId user code start. Do not remove this comment. + log.Debug().Msgf("id: %d", id) + return nil, nil + // Func:TestPostId user code end. Do not remove this comment. +} func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { // Func:GetPost user code start. Do not remove this comment. log.Debug().Msg("GetPostResolver") @@ -42,18 +45,15 @@ func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { return post, nil // Func:GetPost user code end. Do not remove this comment. } -func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { - // Func:GetPosts user code start. Do not remove this comment. - posts := []*models.Post{} - db := model.GetDB() - db.Find(&posts) - return posts, nil - // Func:GetPosts user code end. Do not remove this comment. -} func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { // Func:TestPostEnum user code start. Do not remove this comment. log.Debug().Msgf("enum: %+v", enum) res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) return res, nil // Func:TestPostEnum user code end. Do not remove this comment. +} +func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { + // Func:GetPostIds user code start. Do not remove this comment. + return []int64{1, 2, 3}, nil + // Func:GetPostIds user code end. Do not remove this comment. } \ No newline at end of file diff --git a/graphql/ast/node.go b/graphql/ast/node.go index a4c6429..3419888 100644 --- a/graphql/ast/node.go +++ b/graphql/ast/node.go @@ -414,7 +414,7 @@ func (f *Field) Validate(store *NodeStore, objectFields map[string]*Field, objec } } else { return &errors.GraphQLError{ - Message: fmt.Sprintf("field %s not found", f.Name), + Message: fmt.Sprintf("field %s not found in function %s", f.Name, "Validate"), Locations: []*errors.GraphqlLocation{f.GetLocation()}, } } diff --git a/graphql/excute/merge.go b/graphql/excute/merge.go index 2f2a4a3..1024706 100644 --- a/graphql/excute/merge.go +++ b/graphql/excute/merge.go @@ -2,6 +2,7 @@ package excute import ( "fmt" + "sync" "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/errors" @@ -10,81 +11,148 @@ import ( "github.com/light-speak/lighthouse/utils" ) +// mergeData merges the field data with the given data map based on GraphQL field definition func mergeData(ctx *context.Context, field *ast.Field, datas map[string]interface{}) (interface{}, errors.GraphqlErrorInterface) { + // Convert field name to snake case for database column mapping fieldName := utils.SnakeCase(field.Name) + var v interface{} - v, ok := datas[fieldName] - if !ok { - return nil, &errors.GraphQLError{ - Message: fmt.Sprintf("field %s not found", field.Name), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } + // Get value from data map + ev, exists := datas[fieldName] + if exists && ev != nil { + v = ev + } else { + v = nil } - if v == nil { - if field.Relation != nil { - cData, err := model.FetchRelation(ctx, datas, &model.SelectRelation{Relation: field.Relation}) - if err != nil { - return nil, &errors.GraphQLError{ - Message: err.Error(), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } + + // Handle relation fields + if v == nil && field.Relation != nil { + cData, err := model.FetchRelation(ctx, datas, field.Relation) + if err != nil { + return nil, &errors.GraphQLError{ + Message: fmt.Sprintf("Failed to fetch relation: %v", err), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, } - v = cData } + v = cData } + + // Return nil if value is nil if v == nil { return nil, nil } + // Get real type by unwrapping NonNull typeRef := field.Type if typeRef.Kind == ast.KindNonNull { typeRef = typeRef.OfType } + // Handle list type if typeRef.Kind == ast.KindList { vList, ok := v.([]map[string]interface{}) if !ok { return nil, &errors.GraphQLError{ - Message: fmt.Sprintf("expected list but got %T", v), + Message: fmt.Sprintf("Expected list type but got %T", v), Locations: []*errors.GraphqlLocation{field.GetLocation()}, } } + // Process each item in list result := make([]interface{}, len(vList)) + var wg sync.WaitGroup + errChan := make(chan errors.GraphqlErrorInterface, len(vList)) + for i, item := range vList { - listField := &ast.Field{ - Name: field.Name, - Children: field.Children, - Type: typeRef.OfType, - } + wg.Add(1) + go func(index int, itemData map[string]interface{}) { + defer wg.Done() + listField := &ast.Field{ + Name: field.Name, + Children: field.Children, + Type: typeRef.OfType, + } - m := make(map[string]interface{}) - m[field.Name] = item - merged, err := mergeData(ctx, listField, m) - if err != nil { - return nil, err - } - result[i] = merged + m := make(map[string]interface{}) + m[field.Name] = itemData + merged, err := mergeData(ctx, listField, m) + if err != nil { + errChan <- err + return + } + result[index] = merged + }(i, item) + } + + wg.Wait() + close(errChan) + + if len(errChan) > 0 { + return nil, <-errChan } return result, nil } + // Handle object type with children if field.Children != nil { cv := make(map[string]interface{}) - vMap := v.(map[string]interface{}) - for _, child := range field.Children { - c, err := mergeData(ctx, child, vMap) - if err != nil { - return nil, err + vMap, ok := v.(map[string]interface{}) + if !ok { + return nil, &errors.GraphQLError{ + Message: fmt.Sprintf("Expected map type but got %T", v), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, } - cv[child.Name] = c } + + // Process each child field + var wg sync.WaitGroup + errChan := make(chan errors.GraphqlErrorInterface, len(field.Children)) + resultChan := make(chan struct { + key string + value interface{} + }, len(field.Children)) + + for _, child := range field.Children { + + wg.Add(1) + go func(childField *ast.Field) { + defer wg.Done() + c, err := mergeData(ctx, childField, vMap) + if err != nil { + errChan <- err + return + } + resultChan <- struct { + key string + value interface{} + }{childField.Name, c} + }(child) + } + + go func() { + wg.Wait() + close(errChan) + close(resultChan) + }() + + if err := <-errChan; err != nil { + return nil, err + } + + for result := range resultChan { + cv[result.key] = result.value + } + return cv, nil } + // Validate scalar value v, err := ValidateValue(field, v, false) if err != nil { - return nil, err + return nil, &errors.GraphQLError{ + Message: fmt.Sprintf("Value validation failed: %v", err), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } } return v, nil } diff --git a/graphql/excute/resolver.go b/graphql/excute/resolver.go index e6ab22e..507fc91 100644 --- a/graphql/excute/resolver.go +++ b/graphql/excute/resolver.go @@ -5,7 +5,6 @@ import ( "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/log" ) func executeResolver(ctx *context.Context, field *ast.Field) (interface{}, bool, errors.GraphqlErrorInterface) { @@ -42,20 +41,12 @@ func executeResolver(ctx *context.Context, field *ast.Field) (interface{}, bool, } func processObjectResult(ctx *context.Context, field *ast.Field, r interface{}) (interface{}, bool, errors.GraphqlErrorInterface) { - columns, e := getColumns(field) - if e != nil { - return nil, true, &errors.GraphQLError{ - Message: e.Error(), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } - } - realType := field.Type.GetRealType() if realType.TypeNode.GetKind() == ast.KindObject && realType.TypeNode.(*ast.ObjectNode).IsModel { if field.Type.IsList() { - return processListResult(ctx, field, realType, columns, r) + return processListResult(ctx, field, realType, r) } - return processSingleResult(ctx, field, realType, columns, r) + return processSingleResult(ctx, field, realType, r) } data := make(map[string]interface{}) @@ -67,12 +58,11 @@ func processObjectResult(ctx *context.Context, field *ast.Field, r interface{}) data[child.Name] = v } - log.Info().Msgf("processObjectResult: %v", data) return data, true, nil } -func processListResult(ctx *context.Context, field *ast.Field, realType *ast.TypeRef, columns map[string]interface{}, r interface{}) (interface{}, bool, errors.GraphqlErrorInterface) { - result, err := model.GetQuickList(realType.Name)(ctx, columns, r.([]map[string]interface{})) +func processListResult(ctx *context.Context, field *ast.Field, realType *ast.TypeRef, r interface{}) (interface{}, bool, errors.GraphqlErrorInterface) { + result, err := model.GetQuickList(realType.Name)(ctx, r.([]map[string]interface{})) if err != nil { return nil, true, &errors.GraphQLError{ Message: err.Error(), @@ -95,8 +85,8 @@ func processListResult(ctx *context.Context, field *ast.Field, realType *ast.Typ return data, true, nil } -func processSingleResult(ctx *context.Context, field *ast.Field, realType *ast.TypeRef, columns map[string]interface{}, r interface{}) (interface{}, bool, errors.GraphqlErrorInterface) { - result, err := model.GetQuickFirst(realType.Name)(ctx, columns, r.(map[string]interface{})) +func processSingleResult(ctx *context.Context, field *ast.Field, realType *ast.TypeRef, r interface{}) (interface{}, bool, errors.GraphqlErrorInterface) { + result, err := model.GetQuickFirst(realType.Name)(ctx, r.(map[string]interface{})) if err != nil { return nil, true, &errors.GraphQLError{ Message: err.Error(), diff --git a/graphql/excute/return.go b/graphql/excute/return.go index 80ad19c..c41856f 100644 --- a/graphql/excute/return.go +++ b/graphql/excute/return.go @@ -3,6 +3,7 @@ package excute import ( "fmt" "math" + "sync" "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/errors" @@ -15,15 +16,11 @@ func executeFirst(ctx *context.Context, field *ast.Field, scopes ...func(db *gor fn := model.GetQuickFirst(field.Type.GetGoName()) if fn == nil { return nil, &errors.GraphQLError{ - Message: fmt.Sprintf("field %s not found", field.Type.GetGoName()), + Message: fmt.Sprintf("field %s not found in function %s", field.Type.GetGoName(), "executeFirst"), Locations: []*errors.GraphqlLocation{field.GetLocation()}, } } - columns, err := getColumns(field) - if err != nil { - return nil, err - } - d, e := fn(ctx, columns, nil, scopes...) + d, e := fn(ctx, nil, scopes...) if e != nil { return nil, &errors.GraphQLError{ Message: e.Error(), @@ -34,12 +31,30 @@ func executeFirst(ctx *context.Context, field *ast.Field, scopes ...func(db *gor return nil, nil } data := make(map[string]interface{}) + var wg sync.WaitGroup + var mu sync.Mutex + var lastErr errors.GraphqlErrorInterface + for _, child := range field.Children { - v, err := mergeData(ctx, child, d) - data[child.Name] = v - if err != nil { - return nil, err - } + wg.Add(1) + go func(child *ast.Field) { + defer wg.Done() + v, err := mergeData(ctx, child, d) + if err != nil { + mu.Lock() + lastErr = err + mu.Unlock() + return + } + mu.Lock() + data[child.Name] = v + mu.Unlock() + }(child) + } + wg.Wait() + + if lastErr != nil { + return nil, lastErr } return data, nil } @@ -63,37 +78,71 @@ func executePaginate(ctx *context.Context, field *ast.Field, scopes ...func(db * scope := func(db *gorm.DB) *gorm.DB { return db.Offset((int(page.(int64)) - 1) * int(size.(int64))).Limit(int(size.(int64))).Order(fmt.Sprintf("%s %s", "id", sort.(string))) } - data, err := executeFind(ctx, field.Children["data"], append(scopes, scope)...) - if err != nil { - return nil, err - } - res := make(map[string]interface{}) - res["data"] = data + + var wg sync.WaitGroup + var data interface{} + var dataErr errors.GraphqlErrorInterface + var count int64 + var countErr error + + wg.Add(1) + go func() { + defer wg.Done() + data, dataErr = executeFind(ctx, field.Children["data"], append(scopes, scope)...) + }() + if field.Children["paginateInfo"] != nil { - count := int64(0) - var e error if field.Children["paginateInfo"].Children["totalPage"] != nil || field.Children["paginateInfo"].Children["hasNextPage"] != nil || field.Children["paginateInfo"].Children["totalCount"] != nil { - countFn := model.GetQuickCount(field.Children["data"].Type.GetRealType().GetGoName()) - if countFn == nil { - return nil, &errors.GraphQLError{ - Message: fmt.Sprintf("quick count function %s not found", field.Type.GetGoName()), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, - } - } - count, e = countFn(scopes...) - if e != nil { - return nil, &errors.GraphQLError{ - Message: e.Error(), - Locations: []*errors.GraphqlLocation{field.GetLocation()}, + + wg.Add(1) + go func() { + defer wg.Done() + countFn := model.GetQuickCount(field.Children["data"].Type.GetRealType().GetGoName()) + if countFn == nil { + dataErr = &errors.GraphQLError{ + Message: fmt.Sprintf("quick count function %s not found", field.Type.GetGoName()), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } + return } - } + count, countErr = countFn(scopes...) + }() } + } + + wg.Wait() + + if dataErr != nil { + return nil, dataErr + } + if countErr != nil { + return nil, &errors.GraphQLError{ + Message: countErr.Error(), + Locations: []*errors.GraphqlLocation{field.GetLocation()}, + } + } + + res := make(map[string]interface{}) + res["data"] = data + + if field.Children["paginateInfo"] != nil { paginateInfo := make(map[string]interface{}) + var wg sync.WaitGroup + var mu sync.Mutex + for _, child := range field.Children["paginateInfo"].Children { - paginateInfo[child.Name] = mergePaginateInfo(child, count, page.(int64), size.(int64)) + wg.Add(1) + go func(child *ast.Field) { + defer wg.Done() + value := mergePaginateInfo(child, count, page.(int64), size.(int64)) + mu.Lock() + paginateInfo[child.Name] = value + mu.Unlock() + }(child) } + wg.Wait() res["paginateInfo"] = paginateInfo } return res, nil @@ -115,35 +164,50 @@ func mergePaginateInfo(field *ast.Field, count int64, page int64, size int64) in } func executeFind(ctx *context.Context, field *ast.Field, scopes ...func(db *gorm.DB) *gorm.DB) (interface{}, errors.GraphqlErrorInterface) { - columns, err := getColumns(field) - if err != nil { - return nil, err - } fn := model.GetQuickList(field.Type.GetGoName()) if fn == nil { return nil, &errors.GraphQLError{ - Message: fmt.Sprintf("field %s not found", field.Name), + Message: fmt.Sprintf("field %s not found in function %s", field.Name, "executeFind"), Locations: []*errors.GraphqlLocation{field.GetLocation()}, } } - datas, e := fn(ctx, columns, nil, scopes...) + datas, e := fn(ctx, nil, scopes...) if e != nil { return nil, &errors.GraphQLError{ Message: e.Error(), Locations: []*errors.GraphqlLocation{field.GetLocation()}, } } - data := make([]interface{}, 0) - for _, item := range datas { - d := make(map[string]interface{}) - for _, child := range field.Children { - v, err := mergeData(ctx, child, item) - d[child.Name] = v - if err != nil { - return nil, err + + data := make([]interface{}, len(datas)) + var wg sync.WaitGroup + var mu sync.Mutex + var lastErr errors.GraphqlErrorInterface + + for i, item := range datas { + wg.Add(1) + go func(i int, item interface{}) { + defer wg.Done() + d := make(map[string]interface{}) + for _, child := range field.Children { + v, err := mergeData(ctx, child, item.(map[string]interface{})) + if err != nil { + mu.Lock() + lastErr = err + mu.Unlock() + return + } + d[child.Name] = v } - } - data = append(data, d) + mu.Lock() + data[i] = d + mu.Unlock() + }(i, item) + } + wg.Wait() + + if lastErr != nil { + return nil, lastErr } return data, nil } diff --git a/graphql/model/generate/tpl/repo.tpl b/graphql/model/generate/tpl/repo.tpl index 2e3d356..0022ed2 100644 --- a/graphql/model/generate/tpl/repo.tpl +++ b/graphql/model/generate/tpl/repo.tpl @@ -16,7 +16,7 @@ func Query__{{ $name | ucFirst }}(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB } func First__{{ $name | ucFirst }}(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { var err error - selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__{{ $name | ucFirst }}()) + selectColumns := model.GetSelectInfo(columns, Provide__{{ $name | ucFirst }}()) if data == nil { data = make(map[string]interface{}) err = Query__{{ $name | ucFirst }}().Scopes(scopes...).Select(selectColumns).First(data).Error @@ -24,33 +24,11 @@ func First__{{ $name | ucFirst }}(ctx *context.Context, columns map[string]inter return nil, err } } - var wg sync.WaitGroup - errChan := make(chan error, len(selectRelations)) - var mu sync.Mutex - - for key, relation := range selectRelations { - wg.Add(1) - go func(data map[string]interface{}, relation *model.SelectRelation) { - defer wg.Done() - cData, err := model.FetchRelation(ctx, data, relation) - if err != nil { - errChan <- err - } - mu.Lock() - defer mu.Unlock() - data[key] = cData - }(data, relation) - } - wg.Wait() - close(errChan) - for err := range errChan { - return nil, err - } return data, nil } func List__{{ $name | ucFirst }}(ctx *context.Context, columns map[string]interface{},datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { var err error - selectColumns, selectRelations := model.GetSelectInfo(columns, Provide__{{ $name | ucFirst }}()) + selectColumns := model.GetSelectInfo(columns, Provide__{{ $name | ucFirst }}()) if datas == nil { datas = make([]map[string]interface{}, 0) err = Query__{{ $name | ucFirst }}().Scopes(scopes...).Select(selectColumns).Find(&datas).Error @@ -58,30 +36,6 @@ func List__{{ $name | ucFirst }}(ctx *context.Context, columns map[string]interf return nil, err } } - var wg sync.WaitGroup - errChan := make(chan error, len(datas)*len(selectRelations)) - var mu sync.Mutex - - for _, data := range datas { - for key, relation := range selectRelations { - wg.Add(1) - go func(data map[string]interface{}, relation *model.SelectRelation) { - defer wg.Done() - cData, err := model.FetchRelation(ctx, data, relation) - if err != nil { - errChan <- err - } - mu.Lock() - defer mu.Unlock() - data[key] = cData - }(data, relation) - } - } - wg.Wait() - close(errChan) - for err := range errChan { - return nil, err - } return datas, nil } func Count__{{ $name | ucFirst }}(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { diff --git a/graphql/model/quick.go b/graphql/model/quick.go index 5e47dea..11ff8f0 100644 --- a/graphql/model/quick.go +++ b/graphql/model/quick.go @@ -17,9 +17,9 @@ type SelectRelation struct { SelectColumns map[string]interface{} } -var quickListMap = make(map[string]func(ctx *context.Context, columns map[string]interface{}, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error)) +var quickListMap = make(map[string]func(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error)) -var quickFirstMap = make(map[string]func(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error)) +var quickFirstMap = make(map[string]func(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error)) var quickLoadMap = make(map[string]func(ctx *context.Context, key int64, field string) (map[string]interface{}, error)) @@ -27,10 +27,10 @@ var quickLoadListMap = make(map[string]func(ctx *context.Context, key int64, fie var quickCountMap = make(map[string]func(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error)) -func AddQuickList(name string, fn func(ctx *context.Context, columns map[string]interface{}, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error)) { +func AddQuickList(name string, fn func(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error)) { quickListMap[name] = fn } -func AddQuickFirst(name string, fn func(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error)) { +func AddQuickFirst(name string, fn func(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error)) { quickFirstMap[name] = fn } func AddQuickLoad(name string, fn func(ctx *context.Context, key int64, field string) (map[string]interface{}, error)) { @@ -43,7 +43,7 @@ func AddQuickCount(name string, fn func(scopes ...func(db *gorm.DB) *gorm.DB) (i quickCountMap[name] = fn } -func GetQuickFirst(name string) func(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { +func GetQuickFirst(name string) func(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { fn, ok := quickFirstMap[name] if !ok { return nil @@ -51,7 +51,7 @@ func GetQuickFirst(name string) func(ctx *context.Context, columns map[string]in return fn } -func GetQuickList(name string) func(ctx *context.Context, columns map[string]interface{}, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { +func GetQuickList(name string) func(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { fn, ok := quickListMap[name] if !ok { return nil @@ -83,37 +83,6 @@ func GetQuickCount(name string) func(scopes ...func(db *gorm.DB) *gorm.DB) (int6 return fn } -func GetSelectInfo(columns map[string]interface{}, provide map[string]*ast.Relation) (selectColumns []string, selectRelations map[string]*SelectRelation) { - selectColumns = make([]string, 0) - selectRelations = make(map[string]*SelectRelation, 0) - - for key, value := range columns { - if value != nil && len(value.(map[string]interface{})) > 0 { - relation := provide[key] - selectRelations[key] = &SelectRelation{Relation: relation, SelectColumns: value.(map[string]interface{})} - switch relation.RelationType { - case ast.RelationTypeBelongsTo: - selectColumns = append(selectColumns, relation.ForeignKey) - case ast.RelationTypeHasMany: - selectColumns = append(selectColumns, relation.Reference) - } - } else { - selectColumns = append(selectColumns, key) - } - } - return selectColumns, selectRelations -} - -func GetRelation(relation *ast.Relation) *SelectRelation { - if relation == nil { - return nil - } - return &SelectRelation{ - Relation: relation, - SelectColumns: make(map[string]interface{}), - } -} - func StructToMap(m ModelInterface) (map[string]interface{}, error) { jsonData, err := json.Marshal(m) if err != nil { @@ -174,13 +143,13 @@ func MapToStruct[T any](data map[string]interface{}) (T, error) { return m, nil } -func FetchRelation(ctx *context.Context, data map[string]interface{}, relation *SelectRelation) (interface{}, error) { - switch relation.Relation.RelationType { +func FetchRelation(ctx *context.Context, data map[string]interface{}, relation *ast.Relation) (interface{}, error) { + switch relation.RelationType { case ast.RelationTypeBelongsTo: - fieldValue, ok := data[relation.Relation.ForeignKey] + fieldValue, ok := data[relation.ForeignKey] if !ok { return nil, &errors.GraphQLError{ - Message: fmt.Sprintf("field %s not found in function %s", relation.Relation.ForeignKey, "FetchRelation"), + Message: fmt.Sprintf("field %s not found in function %s", relation.ForeignKey, "FetchRelation"), Locations: []*errors.GraphqlLocation{}, } } @@ -190,10 +159,10 @@ func FetchRelation(ctx *context.Context, data map[string]interface{}, relation * } return data, nil case ast.RelationTypeHasMany: - fieldValue, ok := data[relation.Relation.Reference] + fieldValue, ok := data[relation.Reference] if !ok { return nil, &errors.GraphQLError{ - Message: fmt.Sprintf("field %s not found in function %s", relation.Relation.Reference, "FetchRelation"), + Message: fmt.Sprintf("field %s not found in function %s", relation.Reference, "FetchRelation"), Locations: []*errors.GraphqlLocation{}, } } @@ -206,7 +175,7 @@ func FetchRelation(ctx *context.Context, data map[string]interface{}, relation * return nil, nil } -func fetchHasMany(ctx *context.Context, relation *SelectRelation, fieldValue interface{}) ([]map[string]interface{}, error) { +func fetchHasMany(ctx *context.Context, relation *ast.Relation, fieldValue interface{}) ([]map[string]interface{}, error) { var err error var key int64 switch v := fieldValue.(type) { @@ -220,20 +189,20 @@ func fetchHasMany(ctx *context.Context, relation *SelectRelation, fieldValue int case float64: key = int64(v) default: - return nil, fmt.Errorf("relation %s field %s value is not int64, got %v, type %T", relation.Relation.Name, relation.Relation.ForeignKey, fieldValue, fieldValue) + return nil, fmt.Errorf("relation %s field %s value is not int64, got %v, type %T", relation.Name, relation.ForeignKey, fieldValue, fieldValue) } - datas, err := GetQuickLoadList(utils.UcFirst(relation.Relation.Name))(ctx, key, relation.Relation.ForeignKey) + datas, err := GetQuickLoadList(utils.UcFirst(relation.Name))(ctx, key, relation.ForeignKey) if err != nil { return nil, err } - datas, err = GetQuickList(utils.UcFirst(relation.Relation.Name))(ctx, relation.SelectColumns, datas) + datas, err = GetQuickList(utils.UcFirst(relation.Name))(ctx, datas) if err != nil { return nil, err } return datas, nil } -func fetchBelongsTo(ctx *context.Context, relation *SelectRelation, fieldValue interface{}) (map[string]interface{}, error) { +func fetchBelongsTo(ctx *context.Context, relation *ast.Relation, fieldValue interface{}) (map[string]interface{}, error) { var err error var key int64 switch v := fieldValue.(type) { @@ -247,13 +216,13 @@ func fetchBelongsTo(ctx *context.Context, relation *SelectRelation, fieldValue i case float64: key = int64(v) default: - return nil, fmt.Errorf("relation %s field %s value is not int64, got %v, type %T", relation.Relation.Name, relation.Relation.ForeignKey, fieldValue, fieldValue) + return nil, fmt.Errorf("relation %s field %s value is not int64, got %v, type %T", relation.Name, relation.ForeignKey, fieldValue, fieldValue) } - data, err := GetQuickLoad(utils.UcFirst(relation.Relation.Name))(ctx, key, relation.Relation.Reference) + data, err := GetQuickLoad(utils.UcFirst(relation.Name))(ctx, key, relation.Reference) if err != nil { return nil, err } - data, err = GetQuickFirst(utils.UcFirst(relation.Relation.Name))(ctx, relation.SelectColumns, data) + data, err = GetQuickFirst(utils.UcFirst(relation.Name))(ctx, data) if err != nil { return nil, err } From 50d182cfa415825ca869fcac3b1d8235abd2c692 Mon Sep 17 00:00:00 2001 From: linty Date: Sat, 2 Nov 2024 23:16:59 +0800 Subject: [PATCH 08/17] fix: fix field name --- example/user/models/model.go | 8 ++--- example/user/models/response.go | 14 ++++---- example/user/repo/repo.go | 6 ++-- example/user/resolver/operation_gen.go | 4 +-- example/user/resolver/query.go | 44 +++++++++++++------------- example/user/schema/user.graphql | 2 +- graphql/excute/merge.go | 8 +++-- graphql/model/generate/tpl/repo.tpl | 10 +++--- 8 files changed, 49 insertions(+), 47 deletions(-) diff --git a/example/user/models/model.go b/example/user/models/model.go index b4c27eb..490804e 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -6,13 +6,13 @@ import "github.com/light-speak/lighthouse/graphql/model" type Post struct { model.ModelSoftDelete + BackId int64 `json:"back_id" ` User User `json:"user" ` Enum TestEnum `json:"enum" ` - Content string `json:"content" gorm:"type:varchar(255)" ` + Title string `json:"title" gorm:"index;type:varchar(255)" ` UserId int64 `json:"user_id" ` + Content string `json:"content" gorm:"type:varchar(255)" ` TagId int64 `json:"tag_id" ` - BackId int64 `json:"back_id" ` - Title string `json:"title" gorm:"index;type:varchar(255)" ` } func (*Post) IsModel() bool { return true } @@ -22,7 +22,7 @@ func (*Post) TypeName() string { return "post" } type User struct { model.Model Name string `json:"name" gorm:"index;type:varchar(255)" ` - Posts []Post `gorm:"comment:五二零" json:"posts" ` + MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` } func (*User) IsModel() bool { return true } diff --git a/example/user/models/response.go b/example/user/models/response.go index 1941d6e..f9bfc55 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -4,6 +4,11 @@ package models import "github.com/light-speak/lighthouse/graphql/model" +type UserPaginateResponse struct { + Data []*User `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` +} + type PostPaginateResponse struct { Data []*Post `json:"data" ` PaginateInfo model.PaginateInfo `json:"paginate_info" ` @@ -11,14 +16,9 @@ type PostPaginateResponse struct { type LoginResponse struct { User User `json:"user" ` - Token string `json:"token" gorm:"type:varchar(255)" ` -} - -type UserPaginateResponse struct { - Data []*User `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` + Token string `gorm:"type:varchar(255)" json:"token" ` } type Test struct { - Test string `json:"test" gorm:"type:varchar(255)" ` + Test string `gorm:"type:varchar(255)" json:"test" ` } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index 43d2832..3a7a087 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,11 +2,11 @@ package repo import ( - "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/graphql/model" - "gorm.io/gorm" "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/context" "user/models" + "gorm.io/gorm" ) func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} @@ -46,7 +46,7 @@ func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { err := Query__Post().Scopes(scopes...).Count(&count).Error return count, err } -func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"name": {},"posts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"updated_at": {},}} +func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"name": {},"updated_at": {},}} func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) } diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index 326aeb0..630908a 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,11 +2,11 @@ package resolver import ( + "github.com/light-speak/lighthouse/graphql/excute" + "github.com/light-speak/lighthouse/graphql/model" "fmt" "github.com/light-speak/lighthouse/graphql" "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/excute" - "github.com/light-speak/lighthouse/graphql/model" "user/models" ) diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index 495d007..861e2ba 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,20 +2,40 @@ package resolver import ( - "fmt" "github.com/light-speak/lighthouse/log" - "github.com/light-speak/lighthouse/context" + "fmt" "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/context" "user/models" ) +func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { + // Func:GetPostIds user code start. Do not remove this comment. + return []int64{1, 2, 3}, nil + // Func:GetPostIds user code end. Do not remove this comment. +} func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { // Func:TestPostInt user code start. Do not remove this comment. log.Debug().Msgf("id: %d", id) return nil, nil // Func:TestPostInt user code end. Do not remove this comment. } +func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { + // Func:GetPost user code start. Do not remove this comment. + log.Debug().Msg("GetPostResolver") + db := model.GetDB() + post := &models.Post{} + db.Where("id = ?", fuck).First(post) + return post, nil + // Func:GetPost user code end. Do not remove this comment. +} +func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { + // Func:TestPostId user code start. Do not remove this comment. + log.Debug().Msgf("id: %d", id) + return nil, nil + // Func:TestPostId user code end. Do not remove this comment. +} func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { // Func:TestPostInput user code start. Do not remove this comment. res := fmt.Sprintf("input: %+v", input) @@ -30,30 +50,10 @@ func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) return posts, nil // Func:GetPosts user code end. Do not remove this comment. } -func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { - // Func:TestPostId user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) - return nil, nil - // Func:TestPostId user code end. Do not remove this comment. -} -func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { - // Func:GetPost user code start. Do not remove this comment. - log.Debug().Msg("GetPostResolver") - db := model.GetDB() - post := &models.Post{} - db.Where("id = ?", fuck).First(post) - return post, nil - // Func:GetPost user code end. Do not remove this comment. -} func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { // Func:TestPostEnum user code start. Do not remove this comment. log.Debug().Msgf("enum: %+v", enum) res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) return res, nil // Func:TestPostEnum user code end. Do not remove this comment. -} -func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { - // Func:GetPostIds user code start. Do not remove this comment. - return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. } \ No newline at end of file diff --git a/example/user/schema/user.graphql b/example/user/schema/user.graphql index b04b511..c8be435 100644 --- a/example/user/schema/user.graphql +++ b/example/user/schema/user.graphql @@ -2,7 +2,7 @@ type User implements HasName @key(fields: "id") @model { name: String! @index "五二零" - posts: [Post!]! @hasMany(relation: "post", foreignKey: "user_id") + myPosts: [Post!]! @hasMany(relation: "post", foreignKey: "user_id") } extend type Query { diff --git a/graphql/excute/merge.go b/graphql/excute/merge.go index 1024706..aa1532d 100644 --- a/graphql/excute/merge.go +++ b/graphql/excute/merge.go @@ -8,12 +8,14 @@ import ( "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/log" "github.com/light-speak/lighthouse/utils" ) // mergeData merges the field data with the given data map based on GraphQL field definition func mergeData(ctx *context.Context, field *ast.Field, datas map[string]interface{}) (interface{}, errors.GraphqlErrorInterface) { // Convert field name to snake case for database column mapping + fieldName := utils.SnakeCase(field.Name) var v interface{} @@ -25,6 +27,8 @@ func mergeData(ctx *context.Context, field *ast.Field, datas map[string]interfac v = nil } + log.Info().Msgf("mergeData field: %v, datas: %v", field.Name, datas[fieldName]) + // Handle relation fields if v == nil && field.Relation != nil { cData, err := model.FetchRelation(ctx, datas, field.Relation) @@ -68,13 +72,13 @@ func mergeData(ctx *context.Context, field *ast.Field, datas map[string]interfac go func(index int, itemData map[string]interface{}) { defer wg.Done() listField := &ast.Field{ - Name: field.Name, + Name: fieldName, Children: field.Children, Type: typeRef.OfType, } m := make(map[string]interface{}) - m[field.Name] = itemData + m[fieldName] = itemData merged, err := mergeData(ctx, listField, m) if err != nil { errChan <- err diff --git a/graphql/model/generate/tpl/repo.tpl b/graphql/model/generate/tpl/repo.tpl index 0022ed2..3170fec 100644 --- a/graphql/model/generate/tpl/repo.tpl +++ b/graphql/model/generate/tpl/repo.tpl @@ -14,24 +14,22 @@ func LoadList__{{ $name | ucFirst }}(ctx *context.Context, key int64, field stri func Query__{{ $name | ucFirst }}(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { return model.GetDB().Model(&models.{{ $name | ucFirst }}{}).Scopes(scopes...) } -func First__{{ $name | ucFirst }}(ctx *context.Context, columns map[string]interface{}, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { +func First__{{ $name | ucFirst }}(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { var err error - selectColumns := model.GetSelectInfo(columns, Provide__{{ $name | ucFirst }}()) if data == nil { data = make(map[string]interface{}) - err = Query__{{ $name | ucFirst }}().Scopes(scopes...).Select(selectColumns).First(data).Error + err = Query__{{ $name | ucFirst }}().Scopes(scopes...).First(data).Error if err != nil { return nil, err } } return data, nil } -func List__{{ $name | ucFirst }}(ctx *context.Context, columns map[string]interface{},datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { +func List__{{ $name | ucFirst }}(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { var err error - selectColumns := model.GetSelectInfo(columns, Provide__{{ $name | ucFirst }}()) if datas == nil { datas = make([]map[string]interface{}, 0) - err = Query__{{ $name | ucFirst }}().Scopes(scopes...).Select(selectColumns).Find(&datas).Error + err = Query__{{ $name | ucFirst }}().Scopes(scopes...).Find(&datas).Error if err != nil { return nil, err } From 69bb582040372b93cf7a62d62468863331603d2b Mon Sep 17 00:00:00 2001 From: linty Date: Sun, 3 Nov 2024 00:18:24 +0800 Subject: [PATCH 09/17] feat: scalar serialize --- example/user/models/model.go | 35 +++++++------- example/user/models/response.go | 12 ++--- example/user/repo/repo.go | 64 +++++++++++++------------- example/user/resolver/mutation.go | 20 ++++---- example/user/resolver/operation_gen.go | 8 ++-- example/user/resolver/query.go | 46 +++++++++--------- example/user/schema/post.graphqls | 1 + graphql/ast/node.go | 2 +- graphql/excute/merge.go | 3 -- graphql/excute/quick.go | 31 ------------- graphql/scalar/bool.go | 8 ++-- graphql/scalar/datetime.go | 6 +-- graphql/scalar/float.go | 17 +++++-- graphql/scalar/id.go | 24 ++++++---- graphql/scalar/int.go | 19 ++++++-- graphql/scalar/string.go | 9 +++- 16 files changed, 153 insertions(+), 152 deletions(-) diff --git a/example/user/models/model.go b/example/user/models/model.go index 490804e..3b83aa8 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -4,25 +4,10 @@ package models import "github.com/light-speak/lighthouse/graphql/model" -type Post struct { - model.ModelSoftDelete - BackId int64 `json:"back_id" ` - User User `json:"user" ` - Enum TestEnum `json:"enum" ` - Title string `json:"title" gorm:"index;type:varchar(255)" ` - UserId int64 `json:"user_id" ` - Content string `json:"content" gorm:"type:varchar(255)" ` - TagId int64 `json:"tag_id" ` -} - -func (*Post) IsModel() bool { return true } -func (*Post) TableName() string { return "posts" } -func (*Post) TypeName() string { return "post" } - type User struct { model.Model - Name string `json:"name" gorm:"index;type:varchar(255)" ` MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` + Name string `json:"name" gorm:"index;type:varchar(255)" ` } func (*User) IsModel() bool { return true } @@ -31,10 +16,26 @@ func (this *User) GetName() string { return this.Name } func (*User) TableName() string { return "users" } func (*User) TypeName() string { return "user" } +type Post struct { + model.ModelSoftDelete + UserId int64 `json:"user_id" ` + TagId int64 `json:"tag_id" ` + Enum TestEnum `json:"enum" ` + Content string `json:"content" gorm:"type:varchar(255)" ` + BackId int64 `json:"back_id" ` + IsBool bool `json:"is_bool" gorm:"default:false" ` + User User `json:"user" ` + Title string `json:"title" gorm:"index;type:varchar(255)" ` +} + +func (*Post) IsModel() bool { return true } +func (*Post) TableName() string { return "posts" } +func (*Post) TypeName() string { return "post" } + func Migrate() error { return model.GetDB().AutoMigrate( - &Post{}, &User{}, + &Post{}, ) } \ No newline at end of file diff --git a/example/user/models/response.go b/example/user/models/response.go index f9bfc55..336eb5f 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -4,9 +4,8 @@ package models import "github.com/light-speak/lighthouse/graphql/model" -type UserPaginateResponse struct { - Data []*User `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` +type Test struct { + Test string `gorm:"type:varchar(255)" json:"test" ` } type PostPaginateResponse struct { @@ -16,9 +15,10 @@ type PostPaginateResponse struct { type LoginResponse struct { User User `json:"user" ` - Token string `gorm:"type:varchar(255)" json:"token" ` + Token string `json:"token" gorm:"type:varchar(255)" ` } -type Test struct { - Test string `gorm:"type:varchar(255)" json:"test" ` +type UserPaginateResponse struct { + Data []*User `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index 3a7a087..1bb0061 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -3,97 +3,97 @@ package repo import ( "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/graphql/ast" "github.com/light-speak/lighthouse/context" "user/models" "gorm.io/gorm" + "github.com/light-speak/lighthouse/graphql/ast" ) -func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} -func Load__Post(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) +func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"name": {},"updated_at": {},}} +func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) } -func LoadList__Post(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) +func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) } -func Query__Post(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Post{}).Scopes(scopes...) +func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { + return model.GetDB().Model(&models.User{}).Scopes(scopes...) } -func First__Post(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { +func First__User(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { var err error if data == nil { data = make(map[string]interface{}) - err = Query__Post().Scopes(scopes...).First(data).Error + err = Query__User().Scopes(scopes...).First(data).Error if err != nil { return nil, err } } return data, nil } -func List__Post(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { +func List__User(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { var err error if datas == nil { datas = make([]map[string]interface{}, 0) - err = Query__Post().Scopes(scopes...).Find(&datas).Error + err = Query__User().Scopes(scopes...).Find(&datas).Error if err != nil { return nil, err } } return datas, nil } -func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { +func Count__User(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { var count int64 - err := Query__Post().Scopes(scopes...).Count(&count).Error + err := Query__User().Scopes(scopes...).Count(&count).Error return count, err } -func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"name": {},"updated_at": {},}} -func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) +func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"IsBool": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} +func Load__Post(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) } -func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) +func LoadList__Post(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) } -func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.User{}).Scopes(scopes...) +func Query__Post(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { + return model.GetDB().Model(&models.Post{}).Scopes(scopes...) } -func First__User(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { +func First__Post(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { var err error if data == nil { data = make(map[string]interface{}) - err = Query__User().Scopes(scopes...).First(data).Error + err = Query__Post().Scopes(scopes...).First(data).Error if err != nil { return nil, err } } return data, nil } -func List__User(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { +func List__Post(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { var err error if datas == nil { datas = make([]map[string]interface{}, 0) - err = Query__User().Scopes(scopes...).Find(&datas).Error + err = Query__Post().Scopes(scopes...).Find(&datas).Error if err != nil { return nil, err } } return datas, nil } -func Count__User(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { +func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { var count int64 - err := Query__User().Scopes(scopes...).Count(&count).Error + err := Query__Post().Scopes(scopes...).Count(&count).Error return count, err } func init() { - model.AddQuickFirst("Post", First__Post) - model.AddQuickList("Post", List__Post) - model.AddQuickLoad("Post", Load__Post) - model.AddQuickLoadList("Post", LoadList__Post) - model.AddQuickCount("Post", Count__Post) model.AddQuickFirst("User", First__User) model.AddQuickList("User", List__User) model.AddQuickLoad("User", Load__User) model.AddQuickLoadList("User", LoadList__User) model.AddQuickCount("User", Count__User) + model.AddQuickFirst("Post", First__Post) + model.AddQuickList("Post", List__Post) + model.AddQuickLoad("Post", Load__Post) + model.AddQuickLoadList("Post", LoadList__Post) + model.AddQuickCount("Post", Count__Post) } diff --git a/example/user/resolver/mutation.go b/example/user/resolver/mutation.go index 519582d..2f0bdb7 100644 --- a/example/user/resolver/mutation.go +++ b/example/user/resolver/mutation.go @@ -2,14 +2,14 @@ package resolver import ( - "user/models" - - "github.com/light-speak/lighthouse/auth" - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/context" + "user/models" + "github.com/light-speak/lighthouse/auth" ) -func LoginResolver(ctx *context.Context, name string) (*models.LoginResponse, error) { + +func LoginResolver(ctx *context.Context,name string) (*models.LoginResponse, error) { // Func:Login user code start. Do not remove this comment. user := &models.User{} db := model.GetDB() @@ -24,10 +24,10 @@ func LoginResolver(ctx *context.Context, name string) (*models.LoginResponse, er User: *user, Token: token, }, nil - // Func:Login user code end. Do not remove this comment. + // Func:Login user code end. Do not remove this comment. } -func CreatePostResolver(ctx *context.Context, input *models.TestInput) (*models.Post, error) { +func CreatePostResolver(ctx *context.Context,input *models.TestInput) (*models.Post, error) { // Func:CreatePost user code start. Do not remove this comment. panic("not implement") - // Func:CreatePost user code end. Do not remove this comment. -} + // Func:CreatePost user code end. Do not remove this comment. +} \ No newline at end of file diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index 630908a..b3542e3 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,12 +2,12 @@ package resolver import ( - "github.com/light-speak/lighthouse/graphql/excute" + "user/models" "github.com/light-speak/lighthouse/graphql/model" - "fmt" - "github.com/light-speak/lighthouse/graphql" "github.com/light-speak/lighthouse/context" - "user/models" + "github.com/light-speak/lighthouse/graphql/excute" + "github.com/light-speak/lighthouse/graphql" + "fmt" ) func init() { diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index 861e2ba..af3754f 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -10,17 +10,6 @@ import ( ) -func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { - // Func:GetPostIds user code start. Do not remove this comment. - return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. -} -func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { - // Func:TestPostInt user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) - return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. -} func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { // Func:GetPost user code start. Do not remove this comment. log.Debug().Msg("GetPostResolver") @@ -30,18 +19,6 @@ func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { return post, nil // Func:GetPost user code end. Do not remove this comment. } -func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { - // Func:TestPostId user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) - return nil, nil - // Func:TestPostId user code end. Do not remove this comment. -} -func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { - // Func:TestPostInput user code start. Do not remove this comment. - res := fmt.Sprintf("input: %+v", input) - return res, nil - // Func:TestPostInput user code end. Do not remove this comment. -} func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { // Func:GetPosts user code start. Do not remove this comment. posts := []*models.Post{} @@ -50,10 +27,33 @@ func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) return posts, nil // Func:GetPosts user code end. Do not remove this comment. } +func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { + // Func:TestPostInt user code start. Do not remove this comment. + log.Debug().Msgf("id: %d", id) + return nil, nil + // Func:TestPostInt user code end. Do not remove this comment. +} +func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { + // Func:GetPostIds user code start. Do not remove this comment. + return []int64{1, 2, 3}, nil + // Func:GetPostIds user code end. Do not remove this comment. +} func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { // Func:TestPostEnum user code start. Do not remove this comment. log.Debug().Msgf("enum: %+v", enum) res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) return res, nil // Func:TestPostEnum user code end. Do not remove this comment. +} +func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { + // Func:TestPostId user code start. Do not remove this comment. + log.Debug().Msgf("id: %d", id) + return nil, nil + // Func:TestPostId user code end. Do not remove this comment. +} +func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { + // Func:TestPostInput user code start. Do not remove this comment. + res := fmt.Sprintf("input: %+v", input) + return res, nil + // Func:TestPostInput user code end. Do not remove this comment. } \ No newline at end of file diff --git a/example/user/schema/post.graphqls b/example/user/schema/post.graphqls index 5804159..2ef7c9e 100644 --- a/example/user/schema/post.graphqls +++ b/example/user/schema/post.graphqls @@ -4,6 +4,7 @@ type Post @key(fields: "id") @softDeleteModel { userId: ID! tagId: ID! BackId: ID! + IsBool: Boolean! @default(value: "false") user: User! @belongsTo enum: TestEnum! } diff --git a/graphql/ast/node.go b/graphql/ast/node.go index 3419888..3a8abfd 100644 --- a/graphql/ast/node.go +++ b/graphql/ast/node.go @@ -291,7 +291,7 @@ func (s *ScalarNode) Validate(store *NodeStore) errors.GraphqlErrorInterface { type ScalarType interface { ParseValue(v interface{}, location *errors.GraphqlLocation) (interface{}, errors.GraphqlErrorInterface) - Serialize(v interface{}, location *errors.GraphqlLocation) (string, errors.GraphqlErrorInterface) + Serialize(v interface{}, location *errors.GraphqlLocation) (interface{}, errors.GraphqlErrorInterface) ParseLiteral(v interface{}, location *errors.GraphqlLocation) (interface{}, errors.GraphqlErrorInterface) GoType() string } diff --git a/graphql/excute/merge.go b/graphql/excute/merge.go index aa1532d..6e61086 100644 --- a/graphql/excute/merge.go +++ b/graphql/excute/merge.go @@ -8,7 +8,6 @@ import ( "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/log" "github.com/light-speak/lighthouse/utils" ) @@ -27,8 +26,6 @@ func mergeData(ctx *context.Context, field *ast.Field, datas map[string]interfac v = nil } - log.Info().Msgf("mergeData field: %v, datas: %v", field.Name, datas[fieldName]) - // Handle relation fields if v == nil && field.Relation != nil { cData, err := model.FetchRelation(ctx, datas, field.Relation) diff --git a/graphql/excute/quick.go b/graphql/excute/quick.go index 5bd355c..b89687d 100644 --- a/graphql/excute/quick.go +++ b/graphql/excute/quick.go @@ -40,34 +40,3 @@ func QuickExecute(ctx *context.Context, field *ast.Field) (interface{}, bool, er return nil, false, nil } - -func getColumns(field *ast.Field) (map[string]interface{}, errors.GraphqlErrorInterface) { - res := make(map[string]interface{}) - if len(field.Children) == 0 { - return nil, nil - } - for _, child := range field.Children { - if child.IsFragment || child.IsUnion { - for _, c := range child.Children { - column, err := getColumns(c) - if err != nil { - return nil, err - } - res[c.Name] = column - } - } else if child.Children != nil { - cRes := make(map[string]interface{}) - for _, c := range child.Children { - column, err := getColumns(c) - if err != nil { - return nil, err - } - cRes[c.Name] = column - } - res[child.Name] = cRes - } else { - res[child.Name] = nil - } - } - return res, nil -} diff --git a/graphql/scalar/bool.go b/graphql/scalar/bool.go index 1386550..e55cce9 100644 --- a/graphql/scalar/bool.go +++ b/graphql/scalar/bool.go @@ -32,14 +32,14 @@ func (i *BooleanScalar) ParseValue(v interface{}, location *errors.GraphqlLocati } } -func (i *BooleanScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (string, errors.GraphqlErrorInterface) { +func (i *BooleanScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (interface{}, errors.GraphqlErrorInterface) { switch v := v.(type) { case bool: - return strconv.FormatBool(v), nil + return v, nil case int64: - return strconv.FormatBool(v != 0), nil + return v != 0, nil default: - return "", &errors.GraphQLError{ + return nil, &errors.GraphQLError{ Message: fmt.Sprintf("value is not a boolean: %v", v), Locations: []*errors.GraphqlLocation{location}, } diff --git a/graphql/scalar/datetime.go b/graphql/scalar/datetime.go index 215978d..281e4aa 100644 --- a/graphql/scalar/datetime.go +++ b/graphql/scalar/datetime.go @@ -30,21 +30,21 @@ func (d *DateTimeScalar) ParseValue(v interface{}, location *errors.GraphqlLocat } } -func (d *DateTimeScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (string, errors.GraphqlErrorInterface) { +func (d *DateTimeScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (interface{}, errors.GraphqlErrorInterface) { switch t := v.(type) { case time.Time: return t.Format("2006-01-02 15:04:05"), nil case string: parsedTime, err := time.Parse("2006-01-02T15:04:05Z07:00", t) if err != nil { - return "", &errors.GraphQLError{ + return nil, &errors.GraphQLError{ Message: fmt.Sprintf("invalid datetime string: %s", t), Locations: []*errors.GraphqlLocation{location}, } } return parsedTime.Format("2006-01-02 15:04:05"), nil default: - return "", &errors.GraphQLError{ + return nil, &errors.GraphQLError{ Message: fmt.Sprintf("unsupported value type: %v, type is %T", v, v), Locations: []*errors.GraphqlLocation{location}, } diff --git a/graphql/scalar/float.go b/graphql/scalar/float.go index 9e7e19f..3ee7d3f 100644 --- a/graphql/scalar/float.go +++ b/graphql/scalar/float.go @@ -30,12 +30,23 @@ func (f *FloatScalar) ParseValue(v interface{}, location *errors.GraphqlLocation } } -func (f *FloatScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (string, errors.GraphqlErrorInterface) { +func (f *FloatScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (interface{}, errors.GraphqlErrorInterface) { switch v := v.(type) { case float64: - return strconv.FormatFloat(v, 'f', -1, 64), nil + return v, nil + case int64: + return float64(v), nil + case string: + floatValue, err := strconv.ParseFloat(v, 64) + if err != nil { + return nil, &errors.GraphQLError{ + Message: fmt.Sprintf("invalid float value: %s", v), + Locations: []*errors.GraphqlLocation{location}, + } + } + return floatValue, nil default: - return "", &errors.GraphQLError{ + return nil, &errors.GraphQLError{ Message: fmt.Sprintf("value is not a float: %v", v), Locations: []*errors.GraphqlLocation{location}, } diff --git a/graphql/scalar/id.go b/graphql/scalar/id.go index fb3a193..6dd612b 100644 --- a/graphql/scalar/id.go +++ b/graphql/scalar/id.go @@ -34,19 +34,27 @@ func (i *IDScalar) ParseValue(v interface{}, location *errors.GraphqlLocation) ( } } -func (i *IDScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (string, errors.GraphqlErrorInterface) { +func (i *IDScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (interface{}, errors.GraphqlErrorInterface) { switch v := v.(type) { case int64: - return strconv.FormatInt(v, 10), nil + return v, nil case int: - return strconv.FormatInt(int64(v), 10), nil + return int64(v), nil case float64: - return strconv.FormatInt(int64(v), 10), nil - default: - return "", &errors.GraphQLError{ - Message: fmt.Sprintf("value is not an integer: %v, got %T", v, v), - Locations: []*errors.GraphqlLocation{location}, + return int64(v), nil + case string: + intValue, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return nil, &errors.GraphQLError{ + Message: fmt.Sprintf("value is not an integer: %v, got %T", v, v), + Locations: []*errors.GraphqlLocation{location}, + } } + return intValue, nil + } + return nil, &errors.GraphQLError{ + Message: fmt.Sprintf("value is not an integer: %v, got %T", v, v), + Locations: []*errors.GraphqlLocation{location}, } } diff --git a/graphql/scalar/int.go b/graphql/scalar/int.go index 406ce9d..3483e93 100644 --- a/graphql/scalar/int.go +++ b/graphql/scalar/int.go @@ -34,16 +34,25 @@ func (i *IntScalar) ParseValue(v interface{}, location *errors.GraphqlLocation) } } -func (i *IntScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (string, errors.GraphqlErrorInterface) { +func (i *IntScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (interface{}, errors.GraphqlErrorInterface) { switch v := v.(type) { case int64: - return strconv.FormatInt(v, 10), nil + return v, nil case int: - return strconv.FormatInt(int64(v), 10), nil + return int64(v), nil case float64: - return strconv.FormatInt(int64(v), 10), nil + return int64(v), nil + case string: + intValue, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return nil, &errors.GraphQLError{ + Message: fmt.Sprintf("invalid integer value: %s, got %T", v, v), + Locations: []*errors.GraphqlLocation{location}, + } + } + return intValue, nil default: - return "", &errors.GraphQLError{ + return nil, &errors.GraphQLError{ Message: fmt.Sprintf("value is not an integer: %v, got %T", v, v), Locations: []*errors.GraphqlLocation{location}, } diff --git a/graphql/scalar/string.go b/graphql/scalar/string.go index 75ab84f..30cf98b 100644 --- a/graphql/scalar/string.go +++ b/graphql/scalar/string.go @@ -2,6 +2,7 @@ package scalar import ( "fmt" + "strconv" "github.com/light-speak/lighthouse/errors" ) @@ -22,14 +23,18 @@ func (s *StringScalar) ParseValue(v interface{}, location *errors.GraphqlLocatio } } -func (s *StringScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (string, errors.GraphqlErrorInterface) { +func (s *StringScalar) Serialize(v interface{}, location *errors.GraphqlLocation) (interface{}, errors.GraphqlErrorInterface) { switch v := v.(type) { case string: return v, nil case []byte: return string(v), nil + case int64: + return strconv.FormatInt(v, 10), nil + case float64: + return strconv.FormatFloat(v, 'f', -1, 64), nil default: - return "", &errors.GraphQLError{ + return nil, &errors.GraphQLError{ Message: fmt.Sprintf("value is not a string: %v", v), Locations: []*errors.GraphqlLocation{location}, } From 29db89fd7e93b0cb989772dc06066185162a1fad Mon Sep 17 00:00:00 2001 From: linty Date: Sun, 3 Nov 2024 03:34:38 +0800 Subject: [PATCH 10/17] feat: fragment --- example/user/models/enum.go | 77 ++++---- example/user/models/input.go | 49 +++-- example/user/models/interface.go | 13 +- example/user/models/model.go | 48 +++-- example/user/models/response.go | 17 +- example/user/repo/repo.go | 135 +++++++------- example/user/resolver/mutation.go | 19 +- example/user/resolver/operation_gen.go | 248 ++++++++++++------------- example/user/resolver/query.go | 41 ++-- graphql/excute/merge.go | 75 +++++--- 10 files changed, 374 insertions(+), 348 deletions(-) diff --git a/example/user/models/enum.go b/example/user/models/enum.go index e245c04..96731c0 100644 --- a/example/user/models/enum.go +++ b/example/user/models/enum.go @@ -1,72 +1,71 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models - - - type SortOrder int8 const ( - SortOrderASC SortOrder = 1 - SortOrderDESC SortOrder = -1 + SortOrderASC SortOrder = 1 + SortOrderDESC SortOrder = -1 ) func (e SortOrder) ToString() string { - switch e { - case SortOrderASC: - return "ASC" - case SortOrderDESC: - return "DESC" - default: - return "unknown" - } + switch e { + case SortOrderASC: + return "ASC" + case SortOrderDESC: + return "DESC" + default: + return "unknown" + } } var SortOrderMap = map[string]SortOrder{ - "ASC": SortOrderASC, - "DESC": SortOrderDESC, + "ASC": SortOrderASC, + "DESC": SortOrderDESC, } + type TestEnum int8 const ( - TestEnumA TestEnum = 1 - TestEnumB TestEnum = 2 + TestEnumA TestEnum = 1 + TestEnumB TestEnum = 2 ) func (e TestEnum) ToString() string { - switch e { - case TestEnumA: - return "A" - case TestEnumB: - return "B" - default: - return "unknown" - } + switch e { + case TestEnumA: + return "A" + case TestEnumB: + return "B" + default: + return "unknown" + } } var TestEnumMap = map[string]TestEnum{ - "A": TestEnumA, - "B": TestEnumB, + "A": TestEnumA, + "B": TestEnumB, } + type TestEnum2 int8 const ( - TestEnum2A2 = iota - TestEnum2B2 + TestEnum2A2 = iota + TestEnum2B2 ) func (e TestEnum2) ToString() string { - switch e { - case TestEnum2A2: - return "A2" - case TestEnum2B2: - return "B2" - default: - return "unknown" - } + switch e { + case TestEnum2A2: + return "A2" + case TestEnum2B2: + return "B2" + default: + return "unknown" + } } var TestEnum2Map = map[string]TestEnum2{ - "A2": TestEnum2A2, - "B2": TestEnum2B2, + "A2": TestEnum2A2, + "B2": TestEnum2B2, } diff --git a/example/user/models/input.go b/example/user/models/input.go index c551035..1e300c0 100644 --- a/example/user/models/input.go +++ b/example/user/models/input.go @@ -1,34 +1,33 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "fmt" - +import "fmt" type TestInput struct { - E bool `json:"E"` - Enum TestEnum `json:"Enum"` - Id string `json:"Id"` + E bool `json:"E"` + Enum TestEnum `json:"Enum"` + Id string `json:"Id"` } func MapToTestInput(data map[string]interface{}) (*TestInput, error) { - result := &TestInput{} - - e, ok := data["e"].(bool) - if !ok { - return nil, fmt.Errorf("invalid value for field 'e', got %T", data["e"]) - } - result.E = e - - enum, ok := TestEnumMap[data["enum"].(string)] - if !ok { - return nil, fmt.Errorf("invalid value for field 'enum', got %T", data["enum"]) - } - result.Enum = enum - - id, ok := data["id"].(string) - if !ok { - return nil, fmt.Errorf("invalid value for field 'id', got %T", data["id"]) - } - result.Id = id - return result, nil + result := &TestInput{} + + e, ok := data["e"].(bool) + if !ok { + return nil, fmt.Errorf("invalid value for field 'e', got %T", data["e"]) + } + result.E = e + + enum, ok := TestEnumMap[data["enum"].(string)] + if !ok { + return nil, fmt.Errorf("invalid value for field 'enum', got %T", data["enum"]) + } + result.Enum = enum + + id, ok := data["id"].(string) + if !ok { + return nil, fmt.Errorf("invalid value for field 'id', got %T", data["id"]) + } + result.Id = id + return result, nil } diff --git a/example/user/models/interface.go b/example/user/models/interface.go index 6f191c4..de5901b 100644 --- a/example/user/models/interface.go +++ b/example/user/models/interface.go @@ -1,16 +1,13 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models - - - type HasName interface { - IsHasName() - GetName() string + IsHasName() + GetName() string } type Userable interface { - IsUserable() - GetUser() User - GetUserId() int64 + IsUserable() + GetUser() User + GetUserId() int64 } diff --git a/example/user/models/model.go b/example/user/models/model.go index 3b83aa8..84cd470 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -1,41 +1,39 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "github.com/light-speak/lighthouse/graphql/model" - +import "github.com/light-speak/lighthouse/graphql/model" type User struct { - model.Model - MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` - Name string `json:"name" gorm:"index;type:varchar(255)" ` + model.Model + MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` + Name string `json:"name" gorm:"index;type:varchar(255)" ` } -func (*User) IsModel() bool { return true } -func (*User) IsHasName() bool { return true } +func (*User) IsModel() bool { return true } +func (*User) IsHasName() bool { return true } func (this *User) GetName() string { return this.Name } -func (*User) TableName() string { return "users" } -func (*User) TypeName() string { return "user" } +func (*User) TableName() string { return "users" } +func (*User) TypeName() string { return "user" } type Post struct { - model.ModelSoftDelete - UserId int64 `json:"user_id" ` - TagId int64 `json:"tag_id" ` - Enum TestEnum `json:"enum" ` - Content string `json:"content" gorm:"type:varchar(255)" ` - BackId int64 `json:"back_id" ` - IsBool bool `json:"is_bool" gorm:"default:false" ` - User User `json:"user" ` - Title string `json:"title" gorm:"index;type:varchar(255)" ` + model.ModelSoftDelete + UserId int64 `json:"user_id" ` + TagId int64 `json:"tag_id" ` + Enum TestEnum `json:"enum" ` + Content string `json:"content" gorm:"type:varchar(255)" ` + BackId int64 `json:"back_id" ` + IsBool bool `json:"is_bool" gorm:"default:false" ` + User User `json:"user" ` + Title string `json:"title" gorm:"index;type:varchar(255)" ` } -func (*Post) IsModel() bool { return true } +func (*Post) IsModel() bool { return true } func (*Post) TableName() string { return "posts" } -func (*Post) TypeName() string { return "post" } - +func (*Post) TypeName() string { return "post" } func Migrate() error { return model.GetDB().AutoMigrate( - &User{}, - &Post{}, - ) -} \ No newline at end of file + &User{}, + &Post{}, + ) +} diff --git a/example/user/models/response.go b/example/user/models/response.go index 336eb5f..94d820c 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -1,24 +1,23 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "github.com/light-speak/lighthouse/graphql/model" - +import "github.com/light-speak/lighthouse/graphql/model" type Test struct { - Test string `gorm:"type:varchar(255)" json:"test" ` + Test string `gorm:"type:varchar(255)" json:"test" ` } type PostPaginateResponse struct { - Data []*Post `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` + Data []*Post `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` } type LoginResponse struct { - User User `json:"user" ` - Token string `json:"token" gorm:"type:varchar(255)" ` + User User `json:"user" ` + Token string `json:"token" gorm:"type:varchar(255)" ` } type UserPaginateResponse struct { - Data []*User `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` + Data []*User `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index 1bb0061..9d4bddb 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,98 +2,101 @@ package repo import ( - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/context" - "user/models" - "gorm.io/gorm" - "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/graphql/model" + "gorm.io/gorm" + "user/models" ) -func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"name": {},"updated_at": {},}} +func Provide__User() map[string]*ast.Relation { + return map[string]*ast.Relation{"created_at": {}, "id": {}, "myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"}, "name": {}, "updated_at": {}} +} func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) + return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) } func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) + return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) } func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.User{}).Scopes(scopes...) + return model.GetDB().Model(&models.User{}).Scopes(scopes...) } func First__User(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__User().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__User().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } func List__User(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__User().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__User().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil } func Count__User(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__User().Scopes(scopes...).Count(&count).Error - return count, err + var count int64 + err := Query__User().Scopes(scopes...).Count(&count).Error + return count, err +} +func Provide__Post() map[string]*ast.Relation { + return map[string]*ast.Relation{"BackId": {}, "IsBool": {}, "content": {}, "created_at": {}, "deleted_at": {}, "enum": {}, "id": {}, "tagId": {}, "title": {}, "updated_at": {}, "user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"}, "userId": {}} } -func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"IsBool": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} func Load__Post(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) + return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) } func LoadList__Post(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) + return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) } func Query__Post(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Post{}).Scopes(scopes...) + return model.GetDB().Model(&models.Post{}).Scopes(scopes...) } func First__Post(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__Post().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__Post().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } func List__Post(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__Post().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__Post().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil } func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__Post().Scopes(scopes...).Count(&count).Error - return count, err + var count int64 + err := Query__Post().Scopes(scopes...).Count(&count).Error + return count, err } - func init() { - model.AddQuickFirst("User", First__User) - model.AddQuickList("User", List__User) - model.AddQuickLoad("User", Load__User) - model.AddQuickLoadList("User", LoadList__User) - model.AddQuickCount("User", Count__User) - model.AddQuickFirst("Post", First__Post) - model.AddQuickList("Post", List__Post) - model.AddQuickLoad("Post", Load__Post) - model.AddQuickLoadList("Post", LoadList__Post) - model.AddQuickCount("Post", Count__Post) + model.AddQuickFirst("User", First__User) + model.AddQuickList("User", List__User) + model.AddQuickLoad("User", Load__User) + model.AddQuickLoadList("User", LoadList__User) + model.AddQuickCount("User", Count__User) + model.AddQuickFirst("Post", First__Post) + model.AddQuickList("Post", List__Post) + model.AddQuickLoad("Post", Load__Post) + model.AddQuickLoadList("Post", LoadList__Post) + model.AddQuickCount("Post", Count__Post) } diff --git a/example/user/resolver/mutation.go b/example/user/resolver/mutation.go index 2f0bdb7..02093a9 100644 --- a/example/user/resolver/mutation.go +++ b/example/user/resolver/mutation.go @@ -2,14 +2,13 @@ package resolver import ( - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/context" - "user/models" - "github.com/light-speak/lighthouse/auth" + "github.com/light-speak/lighthouse/auth" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/model" + "user/models" ) - -func LoginResolver(ctx *context.Context,name string) (*models.LoginResponse, error) { +func LoginResolver(ctx *context.Context, name string) (*models.LoginResponse, error) { // Func:Login user code start. Do not remove this comment. user := &models.User{} db := model.GetDB() @@ -24,10 +23,10 @@ func LoginResolver(ctx *context.Context,name string) (*models.LoginResponse, err User: *user, Token: token, }, nil - // Func:Login user code end. Do not remove this comment. + // Func:Login user code end. Do not remove this comment. } -func CreatePostResolver(ctx *context.Context,input *models.TestInput) (*models.Post, error) { +func CreatePostResolver(ctx *context.Context, input *models.TestInput) (*models.Post, error) { // Func:CreatePost user code start. Do not remove this comment. panic("not implement") - // Func:CreatePost user code end. Do not remove this comment. -} \ No newline at end of file + // Func:CreatePost user code end. Do not remove this comment. +} diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index b3542e3..6796439 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,131 +2,131 @@ package resolver import ( - "user/models" - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/excute" - "github.com/light-speak/lighthouse/graphql" - "fmt" + "fmt" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql" + "github.com/light-speak/lighthouse/graphql/excute" + "github.com/light-speak/lighthouse/graphql/model" + "user/models" ) func init() { - excute.AddResolver("getPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) - if e != nil { - return nil, e - } - fuck, ok := pv.(string) - if !ok { - return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) - } - res, err := GetPostResolver(ctx, fuck) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("getPostIds", func(ctx *context.Context, args map[string]any) (interface{}, error) { - res, err := GetPostIdsResolver(ctx) - if res == nil { - return nil, err - } - return res, nil - }) - excute.AddResolver("getPosts", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) - if e != nil { - return nil, e - } - fuck, ok := pv.(string) - if !ok { - return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) - } - list, err := GetPostsResolver(ctx, fuck) - if list == nil { - return nil, err - } - res := []map[string]interface{}{} - for _, item := range list { - itemMap, err := model.StructToMap(item) - if err != nil { - return nil, err - } - res = append(res, itemMap) - } - return res, nil - }) - excute.AddResolver("testPostEnum", func(ctx *context.Context, args map[string]any) (interface{}, error) { - enumValue, ok := models.TestEnumMap[args["enum"].(string)] - if !ok { - return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) - } - enum := &enumValue - res, err := TestPostEnumResolver(ctx, enum) - return res, err - }) - excute.AddResolver("testPostId", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["ID"].ScalarType.ParseValue(args["id"], nil) - if e != nil { - return nil, e - } - id, ok := pv.(int64) - if !ok { - return nil, fmt.Errorf("argument: 'id' is not a int64, got %T", args["id"]) - } - res, err := TestPostIdResolver(ctx, id) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("testPostInput", func(ctx *context.Context, args map[string]any) (interface{}, error) { - input, err := models.MapToTestInput(args["input"].(map[string]interface{})) - if err != nil { - return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) - } - res, err := TestPostInputResolver(ctx, input) - return res, err - }) - excute.AddResolver("testPostInt", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["Boolean"].ScalarType.ParseValue(args["id"], nil) - if e != nil { - return nil, e - } - id, ok := pv.(bool) - if !ok { - return nil, fmt.Errorf("argument: 'id' is not a bool, got %T", args["id"]) - } - res, err := TestPostIntResolver(ctx, id) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { - input, err := models.MapToTestInput(args["input"].(map[string]interface{})) - if err != nil { - return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) - } - res, err := CreatePostResolver(ctx, input) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("login", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) - if e != nil { - return nil, e - } - name, ok := pv.(string) - if !ok { - return nil, fmt.Errorf("argument: 'name' is not a string, got %T", args["name"]) - } - res, err := LoginResolver(ctx, name) - if res == nil { - return nil, err - } - return model.TypeToMap(res) - }) + excute.AddResolver("getPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) + if e != nil { + return nil, e + } + fuck, ok := pv.(string) + if !ok { + return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) + } + res, err := GetPostResolver(ctx, fuck) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("getPostIds", func(ctx *context.Context, args map[string]any) (interface{}, error) { + res, err := GetPostIdsResolver(ctx) + if res == nil { + return nil, err + } + return res, nil + }) + excute.AddResolver("getPosts", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) + if e != nil { + return nil, e + } + fuck, ok := pv.(string) + if !ok { + return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) + } + list, err := GetPostsResolver(ctx, fuck) + if list == nil { + return nil, err + } + res := []map[string]interface{}{} + for _, item := range list { + itemMap, err := model.StructToMap(item) + if err != nil { + return nil, err + } + res = append(res, itemMap) + } + return res, nil + }) + excute.AddResolver("testPostEnum", func(ctx *context.Context, args map[string]any) (interface{}, error) { + enumValue, ok := models.TestEnumMap[args["enum"].(string)] + if !ok { + return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) + } + enum := &enumValue + res, err := TestPostEnumResolver(ctx, enum) + return res, err + }) + excute.AddResolver("testPostId", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["ID"].ScalarType.ParseValue(args["id"], nil) + if e != nil { + return nil, e + } + id, ok := pv.(int64) + if !ok { + return nil, fmt.Errorf("argument: 'id' is not a int64, got %T", args["id"]) + } + res, err := TestPostIdResolver(ctx, id) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("testPostInput", func(ctx *context.Context, args map[string]any) (interface{}, error) { + input, err := models.MapToTestInput(args["input"].(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) + } + res, err := TestPostInputResolver(ctx, input) + return res, err + }) + excute.AddResolver("testPostInt", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["Boolean"].ScalarType.ParseValue(args["id"], nil) + if e != nil { + return nil, e + } + id, ok := pv.(bool) + if !ok { + return nil, fmt.Errorf("argument: 'id' is not a bool, got %T", args["id"]) + } + res, err := TestPostIntResolver(ctx, id) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { + input, err := models.MapToTestInput(args["input"].(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) + } + res, err := CreatePostResolver(ctx, input) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("login", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) + if e != nil { + return nil, e + } + name, ok := pv.(string) + if !ok { + return nil, fmt.Errorf("argument: 'name' is not a string, got %T", args["name"]) + } + res, err := LoginResolver(ctx, name) + if res == nil { + return nil, err + } + return model.TypeToMap(res) + }) } diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index af3754f..bb37f4e 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,58 +2,57 @@ package resolver import ( - "github.com/light-speak/lighthouse/log" - "fmt" - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/context" - "user/models" -) + "fmt" + "user/models" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/log" +) -func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { +func GetPostResolver(ctx *context.Context, fuck string) (*models.Post, error) { // Func:GetPost user code start. Do not remove this comment. log.Debug().Msg("GetPostResolver") db := model.GetDB() post := &models.Post{} db.Where("id = ?", fuck).First(post) return post, nil - // Func:GetPost user code end. Do not remove this comment. + // Func:GetPost user code end. Do not remove this comment. } -func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { +func GetPostsResolver(ctx *context.Context, fuck string) ([]*models.Post, error) { // Func:GetPosts user code start. Do not remove this comment. posts := []*models.Post{} db := model.GetDB() db.Find(&posts) return posts, nil - // Func:GetPosts user code end. Do not remove this comment. + // Func:GetPosts user code end. Do not remove this comment. } -func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { +func TestPostIntResolver(ctx *context.Context, id bool) (*models.Post, error) { // Func:TestPostInt user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. + // Func:TestPostInt user code end. Do not remove this comment. } func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { // Func:GetPostIds user code start. Do not remove this comment. return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. + // Func:GetPostIds user code end. Do not remove this comment. } -func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { +func TestPostEnumResolver(ctx *context.Context, enum *models.TestEnum) (string, error) { // Func:TestPostEnum user code start. Do not remove this comment. log.Debug().Msgf("enum: %+v", enum) res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) return res, nil - // Func:TestPostEnum user code end. Do not remove this comment. + // Func:TestPostEnum user code end. Do not remove this comment. } -func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { +func TestPostIdResolver(ctx *context.Context, id int64) (*models.Post, error) { // Func:TestPostId user code start. Do not remove this comment. log.Debug().Msgf("id: %d", id) return nil, nil - // Func:TestPostId user code end. Do not remove this comment. + // Func:TestPostId user code end. Do not remove this comment. } -func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { +func TestPostInputResolver(ctx *context.Context, input *models.TestInput) (string, error) { // Func:TestPostInput user code start. Do not remove this comment. res := fmt.Sprintf("input: %+v", input) return res, nil - // Func:TestPostInput user code end. Do not remove this comment. -} \ No newline at end of file + // Func:TestPostInput user code end. Do not remove this comment. +} diff --git a/graphql/excute/merge.go b/graphql/excute/merge.go index 6e61086..79b56b9 100644 --- a/graphql/excute/merge.go +++ b/graphql/excute/merge.go @@ -11,10 +11,55 @@ import ( "github.com/light-speak/lighthouse/utils" ) +// countTotalFields recursively counts total fields including nested fragments +func countTotalFields(fields map[string]*ast.Field) int { + total := 0 + for _, field := range fields { + if field.IsFragment { + total += countTotalFields(field.Children) + } else { + total++ + } + } + return total +} + +// processFields handles field processing and sends results to channels +func processFields( + ctx *context.Context, + fields map[string]*ast.Field, + data map[string]interface{}, + wg *sync.WaitGroup, + errChan chan<- errors.GraphqlErrorInterface, + resultChan chan<- struct { + key string + value interface{} + }, +) { + for _, field := range fields { + if field.IsFragment { + // Recursively process fragment fields + processFields(ctx, field.Children, data, wg, errChan, resultChan) + } else { + wg.Add(1) + go func(f *ast.Field) { + defer wg.Done() + c, err := mergeData(ctx, f, data) + if err != nil { + errChan <- err + return + } + resultChan <- struct { + key string + value interface{} + }{f.Name, c} + }(field) + } + } +} + // mergeData merges the field data with the given data map based on GraphQL field definition func mergeData(ctx *context.Context, field *ast.Field, datas map[string]interface{}) (interface{}, errors.GraphqlErrorInterface) { - // Convert field name to snake case for database column mapping - fieldName := utils.SnakeCase(field.Name) var v interface{} @@ -105,30 +150,18 @@ func mergeData(ctx *context.Context, field *ast.Field, datas map[string]interfac } } - // Process each child field + // Calculate total fields including nested fragments + totalFields := countTotalFields(field.Children) + var wg sync.WaitGroup - errChan := make(chan errors.GraphqlErrorInterface, len(field.Children)) + errChan := make(chan errors.GraphqlErrorInterface, totalFields) resultChan := make(chan struct { key string value interface{} - }, len(field.Children)) + }, totalFields) - for _, child := range field.Children { - - wg.Add(1) - go func(childField *ast.Field) { - defer wg.Done() - c, err := mergeData(ctx, childField, vMap) - if err != nil { - errChan <- err - return - } - resultChan <- struct { - key string - value interface{} - }{childField.Name, c} - }(child) - } + // Process all fields including nested fragments + processFields(ctx, field.Children, vMap, &wg, errChan, resultChan) go func() { wg.Wait() From ebcc1816b0f3dfd3d91c329a8731aec95ab2ea88 Mon Sep 17 00:00:00 2001 From: linty Date: Sun, 3 Nov 2024 18:33:31 +0800 Subject: [PATCH 11/17] fix: fix enummap --- example/user/models/enum.go | 77 +++--- example/user/models/input.go | 49 ++-- example/user/models/interface.go | 13 +- example/user/models/model.go | 56 ++-- example/user/models/response.go | 25 +- example/user/repo/repo.go | 157 ++++++----- example/user/resolver/mutation.go | 19 +- example/user/resolver/operation_gen.go | 257 ++++++++++--------- example/user/resolver/query.go | 73 +++--- example/user/schema/article.graphql | 3 + example/user/schema/post.graphqls | 1 + graphql/model/generate/tpl/operation_gen.tpl | 4 +- 12 files changed, 379 insertions(+), 355 deletions(-) create mode 100644 example/user/schema/article.graphql diff --git a/example/user/models/enum.go b/example/user/models/enum.go index 96731c0..e245c04 100644 --- a/example/user/models/enum.go +++ b/example/user/models/enum.go @@ -1,71 +1,72 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models + + + type SortOrder int8 const ( - SortOrderASC SortOrder = 1 - SortOrderDESC SortOrder = -1 + SortOrderASC SortOrder = 1 + SortOrderDESC SortOrder = -1 ) func (e SortOrder) ToString() string { - switch e { - case SortOrderASC: - return "ASC" - case SortOrderDESC: - return "DESC" - default: - return "unknown" - } + switch e { + case SortOrderASC: + return "ASC" + case SortOrderDESC: + return "DESC" + default: + return "unknown" + } } var SortOrderMap = map[string]SortOrder{ - "ASC": SortOrderASC, - "DESC": SortOrderDESC, + "ASC": SortOrderASC, + "DESC": SortOrderDESC, } - type TestEnum int8 const ( - TestEnumA TestEnum = 1 - TestEnumB TestEnum = 2 + TestEnumA TestEnum = 1 + TestEnumB TestEnum = 2 ) func (e TestEnum) ToString() string { - switch e { - case TestEnumA: - return "A" - case TestEnumB: - return "B" - default: - return "unknown" - } + switch e { + case TestEnumA: + return "A" + case TestEnumB: + return "B" + default: + return "unknown" + } } var TestEnumMap = map[string]TestEnum{ - "A": TestEnumA, - "B": TestEnumB, + "A": TestEnumA, + "B": TestEnumB, } - type TestEnum2 int8 const ( - TestEnum2A2 = iota - TestEnum2B2 + TestEnum2A2 = iota + TestEnum2B2 ) func (e TestEnum2) ToString() string { - switch e { - case TestEnum2A2: - return "A2" - case TestEnum2B2: - return "B2" - default: - return "unknown" - } + switch e { + case TestEnum2A2: + return "A2" + case TestEnum2B2: + return "B2" + default: + return "unknown" + } } var TestEnum2Map = map[string]TestEnum2{ - "A2": TestEnum2A2, - "B2": TestEnum2B2, + "A2": TestEnum2A2, + "B2": TestEnum2B2, } diff --git a/example/user/models/input.go b/example/user/models/input.go index 1e300c0..c551035 100644 --- a/example/user/models/input.go +++ b/example/user/models/input.go @@ -1,33 +1,34 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "fmt" +import "fmt" + type TestInput struct { - E bool `json:"E"` - Enum TestEnum `json:"Enum"` - Id string `json:"Id"` + E bool `json:"E"` + Enum TestEnum `json:"Enum"` + Id string `json:"Id"` } func MapToTestInput(data map[string]interface{}) (*TestInput, error) { - result := &TestInput{} - - e, ok := data["e"].(bool) - if !ok { - return nil, fmt.Errorf("invalid value for field 'e', got %T", data["e"]) - } - result.E = e - - enum, ok := TestEnumMap[data["enum"].(string)] - if !ok { - return nil, fmt.Errorf("invalid value for field 'enum', got %T", data["enum"]) - } - result.Enum = enum - - id, ok := data["id"].(string) - if !ok { - return nil, fmt.Errorf("invalid value for field 'id', got %T", data["id"]) - } - result.Id = id - return result, nil + result := &TestInput{} + + e, ok := data["e"].(bool) + if !ok { + return nil, fmt.Errorf("invalid value for field 'e', got %T", data["e"]) + } + result.E = e + + enum, ok := TestEnumMap[data["enum"].(string)] + if !ok { + return nil, fmt.Errorf("invalid value for field 'enum', got %T", data["enum"]) + } + result.Enum = enum + + id, ok := data["id"].(string) + if !ok { + return nil, fmt.Errorf("invalid value for field 'id', got %T", data["id"]) + } + result.Id = id + return result, nil } diff --git a/example/user/models/interface.go b/example/user/models/interface.go index de5901b..6f191c4 100644 --- a/example/user/models/interface.go +++ b/example/user/models/interface.go @@ -1,13 +1,16 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models + + + type HasName interface { - IsHasName() - GetName() string + IsHasName() + GetName() string } type Userable interface { - IsUserable() - GetUser() User - GetUserId() int64 + IsUserable() + GetUser() User + GetUserId() int64 } diff --git a/example/user/models/model.go b/example/user/models/model.go index 84cd470..f82b9b0 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -1,39 +1,41 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "github.com/light-speak/lighthouse/graphql/model" +import "github.com/light-speak/lighthouse/graphql/model" -type User struct { - model.Model - MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` - Name string `json:"name" gorm:"index;type:varchar(255)" ` -} - -func (*User) IsModel() bool { return true } -func (*User) IsHasName() bool { return true } -func (this *User) GetName() string { return this.Name } -func (*User) TableName() string { return "users" } -func (*User) TypeName() string { return "user" } type Post struct { - model.ModelSoftDelete - UserId int64 `json:"user_id" ` - TagId int64 `json:"tag_id" ` - Enum TestEnum `json:"enum" ` - Content string `json:"content" gorm:"type:varchar(255)" ` - BackId int64 `json:"back_id" ` - IsBool bool `json:"is_bool" gorm:"default:false" ` - User User `json:"user" ` - Title string `json:"title" gorm:"index;type:varchar(255)" ` + model.ModelSoftDelete + TagId int64 `json:"tag_id" ` + User User `json:"user" ` + Enum TestEnum `json:"enum" ` + Content string `json:"content" gorm:"type:varchar(255)" ` + UserId int64 `json:"user_id" ` + BackId int64 `json:"back_id" ` + IsBool bool `json:"is_bool" gorm:"default:false" ` + Title string `gorm:"index;type:varchar(255)" json:"title" ` } -func (*Post) IsModel() bool { return true } +func (*Post) IsModel() bool { return true } func (*Post) TableName() string { return "posts" } -func (*Post) TypeName() string { return "post" } +func (*Post) TypeName() string { return "post" } + +type User struct { + model.Model + Name string `json:"name" gorm:"index;type:varchar(255)" ` + MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` +} + +func (*User) IsModel() bool { return true } +func (*User) IsHasName() bool { return true } +func (this *User) GetName() string { return this.Name } +func (*User) TableName() string { return "users" } +func (*User) TypeName() string { return "user" } + func Migrate() error { return model.GetDB().AutoMigrate( - &User{}, - &Post{}, - ) -} + &Post{}, + &User{}, + ) +} \ No newline at end of file diff --git a/example/user/models/response.go b/example/user/models/response.go index 94d820c..2a48ae9 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -1,23 +1,24 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "github.com/light-speak/lighthouse/graphql/model" +import "github.com/light-speak/lighthouse/graphql/model" -type Test struct { - Test string `gorm:"type:varchar(255)" json:"test" ` + +type LoginResponse struct { + User User `json:"user" ` + Token string `json:"token" gorm:"type:varchar(255)" ` } -type PostPaginateResponse struct { - Data []*Post `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` +type UserPaginateResponse struct { + Data []*User `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` } -type LoginResponse struct { - User User `json:"user" ` - Token string `json:"token" gorm:"type:varchar(255)" ` +type Test struct { + Test string `json:"test" gorm:"type:varchar(255)" ` } -type UserPaginateResponse struct { - Data []*User `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` +type PostPaginateResponse struct { + Data []*Post `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index 9d4bddb..f1a5d17 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,101 +2,98 @@ package repo import ( - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/ast" - "github.com/light-speak/lighthouse/graphql/model" - "gorm.io/gorm" - "user/models" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/ast" + "user/models" + "github.com/light-speak/lighthouse/graphql/model" + "gorm.io/gorm" ) -func Provide__User() map[string]*ast.Relation { - return map[string]*ast.Relation{"created_at": {}, "id": {}, "myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"}, "name": {}, "updated_at": {}} +func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"IsBool": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} +func Load__Post(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) +} +func LoadList__Post(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) +} +func Query__Post(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { + return model.GetDB().Model(&models.Post{}).Scopes(scopes...) +} +func First__Post(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__Post().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } +func List__Post(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__Post().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil +} +func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { + var count int64 + err := Query__Post().Scopes(scopes...).Count(&count).Error + return count, err +} +func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"name": {},"updated_at": {},}} func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) + return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) } func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) + return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) } func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.User{}).Scopes(scopes...) + return model.GetDB().Model(&models.User{}).Scopes(scopes...) } func First__User(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__User().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__User().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } func List__User(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__User().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__User().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil } func Count__User(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__User().Scopes(scopes...).Count(&count).Error - return count, err -} -func Provide__Post() map[string]*ast.Relation { - return map[string]*ast.Relation{"BackId": {}, "IsBool": {}, "content": {}, "created_at": {}, "deleted_at": {}, "enum": {}, "id": {}, "tagId": {}, "title": {}, "updated_at": {}, "user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"}, "userId": {}} -} -func Load__Post(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) -} -func LoadList__Post(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) -} -func Query__Post(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Post{}).Scopes(scopes...) -} -func First__Post(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__Post().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil -} -func List__Post(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__Post().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil -} -func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__Post().Scopes(scopes...).Count(&count).Error - return count, err + var count int64 + err := Query__User().Scopes(scopes...).Count(&count).Error + return count, err } + func init() { - model.AddQuickFirst("User", First__User) - model.AddQuickList("User", List__User) - model.AddQuickLoad("User", Load__User) - model.AddQuickLoadList("User", LoadList__User) - model.AddQuickCount("User", Count__User) - model.AddQuickFirst("Post", First__Post) - model.AddQuickList("Post", List__Post) - model.AddQuickLoad("Post", Load__Post) - model.AddQuickLoadList("Post", LoadList__Post) - model.AddQuickCount("Post", Count__Post) + model.AddQuickFirst("Post", First__Post) + model.AddQuickList("Post", List__Post) + model.AddQuickLoad("Post", Load__Post) + model.AddQuickLoadList("Post", LoadList__Post) + model.AddQuickCount("Post", Count__Post) + model.AddQuickFirst("User", First__User) + model.AddQuickList("User", List__User) + model.AddQuickLoad("User", Load__User) + model.AddQuickLoadList("User", LoadList__User) + model.AddQuickCount("User", Count__User) } diff --git a/example/user/resolver/mutation.go b/example/user/resolver/mutation.go index 02093a9..c70f516 100644 --- a/example/user/resolver/mutation.go +++ b/example/user/resolver/mutation.go @@ -2,13 +2,14 @@ package resolver import ( - "github.com/light-speak/lighthouse/auth" - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/model" - "user/models" + "user/models" + "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/auth" + "github.com/light-speak/lighthouse/context" ) -func LoginResolver(ctx *context.Context, name string) (*models.LoginResponse, error) { + +func LoginResolver(ctx *context.Context,name string) (*models.LoginResponse, error) { // Func:Login user code start. Do not remove this comment. user := &models.User{} db := model.GetDB() @@ -23,10 +24,10 @@ func LoginResolver(ctx *context.Context, name string) (*models.LoginResponse, er User: *user, Token: token, }, nil - // Func:Login user code end. Do not remove this comment. + // Func:Login user code end. Do not remove this comment. } -func CreatePostResolver(ctx *context.Context, input *models.TestInput) (*models.Post, error) { +func CreatePostResolver(ctx *context.Context,input *models.TestInput) (*models.Post, error) { // Func:CreatePost user code start. Do not remove this comment. panic("not implement") - // Func:CreatePost user code end. Do not remove this comment. -} + // Func:CreatePost user code end. Do not remove this comment. +} \ No newline at end of file diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index 6796439..fb6e6f8 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,131 +2,140 @@ package resolver import ( - "fmt" - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql" - "github.com/light-speak/lighthouse/graphql/excute" - "github.com/light-speak/lighthouse/graphql/model" - "user/models" + "github.com/light-speak/lighthouse/graphql/excute" + "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/graphql" + "github.com/light-speak/lighthouse/context" + "user/models" + "fmt" ) func init() { - excute.AddResolver("getPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) - if e != nil { - return nil, e - } - fuck, ok := pv.(string) - if !ok { - return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) - } - res, err := GetPostResolver(ctx, fuck) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("getPostIds", func(ctx *context.Context, args map[string]any) (interface{}, error) { - res, err := GetPostIdsResolver(ctx) - if res == nil { - return nil, err - } - return res, nil - }) - excute.AddResolver("getPosts", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) - if e != nil { - return nil, e - } - fuck, ok := pv.(string) - if !ok { - return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) - } - list, err := GetPostsResolver(ctx, fuck) - if list == nil { - return nil, err - } - res := []map[string]interface{}{} - for _, item := range list { - itemMap, err := model.StructToMap(item) - if err != nil { - return nil, err - } - res = append(res, itemMap) - } - return res, nil - }) - excute.AddResolver("testPostEnum", func(ctx *context.Context, args map[string]any) (interface{}, error) { - enumValue, ok := models.TestEnumMap[args["enum"].(string)] - if !ok { - return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) - } - enum := &enumValue - res, err := TestPostEnumResolver(ctx, enum) - return res, err - }) - excute.AddResolver("testPostId", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["ID"].ScalarType.ParseValue(args["id"], nil) - if e != nil { - return nil, e - } - id, ok := pv.(int64) - if !ok { - return nil, fmt.Errorf("argument: 'id' is not a int64, got %T", args["id"]) - } - res, err := TestPostIdResolver(ctx, id) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("testPostInput", func(ctx *context.Context, args map[string]any) (interface{}, error) { - input, err := models.MapToTestInput(args["input"].(map[string]interface{})) - if err != nil { - return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) - } - res, err := TestPostInputResolver(ctx, input) - return res, err - }) - excute.AddResolver("testPostInt", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["Boolean"].ScalarType.ParseValue(args["id"], nil) - if e != nil { - return nil, e - } - id, ok := pv.(bool) - if !ok { - return nil, fmt.Errorf("argument: 'id' is not a bool, got %T", args["id"]) - } - res, err := TestPostIntResolver(ctx, id) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { - input, err := models.MapToTestInput(args["input"].(map[string]interface{})) - if err != nil { - return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) - } - res, err := CreatePostResolver(ctx, input) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("login", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) - if e != nil { - return nil, e - } - name, ok := pv.(string) - if !ok { - return nil, fmt.Errorf("argument: 'name' is not a string, got %T", args["name"]) - } - res, err := LoginResolver(ctx, name) - if res == nil { - return nil, err - } - return model.TypeToMap(res) - }) + excute.AddResolver("getPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) + if e != nil { + return nil, e + } + fuck, ok := pv.(string) + if !ok { + return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) + } + res, err := GetPostResolver(ctx, fuck) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("getPostIds", func(ctx *context.Context, args map[string]any) (interface{}, error) { + res, err := GetPostIdsResolver(ctx) + if res == nil { + return nil, err + } + return res, nil + }) + excute.AddResolver("getPosts", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) + if e != nil { + return nil, e + } + fuck, ok := pv.(string) + if !ok { + return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) + } + list, err := GetPostsResolver(ctx, fuck) + if list == nil { + return nil, err + } + res := []map[string]interface{}{} + for _, item := range list { + itemMap, err := model.StructToMap(item) + if err != nil { + return nil, err + } + res = append(res, itemMap) + } + return res, nil + }) + excute.AddResolver("testNullableEnum", func(ctx *context.Context, args map[string]any) (interface{}, error) { + enumValue, ok := models.TestEnumMap[args["enum"].(string)] + if !ok { + return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) + } + enum := &enumValue + res, err := TestNullableEnumResolver(ctx, enum) + return res, err + }) + excute.AddResolver("testPostEnum", func(ctx *context.Context, args map[string]any) (interface{}, error) { + enumValue, ok := models.TestEnumMap[args["enum"].(string)] + if !ok { + return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) + } + enum := &enumValue + res, err := TestPostEnumResolver(ctx, enum) + return res, err + }) + excute.AddResolver("testPostId", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["ID"].ScalarType.ParseValue(args["id"], nil) + if e != nil { + return nil, e + } + id, ok := pv.(int64) + if !ok { + return nil, fmt.Errorf("argument: 'id' is not a int64, got %T", args["id"]) + } + res, err := TestPostIdResolver(ctx, id) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("testPostInput", func(ctx *context.Context, args map[string]any) (interface{}, error) { + input, err := models.MapToTestInput(args["input"].(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) + } + res, err := TestPostInputResolver(ctx, input) + return res, err + }) + excute.AddResolver("testPostInt", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["Boolean"].ScalarType.ParseValue(args["id"], nil) + if e != nil { + return nil, e + } + id, ok := pv.(bool) + if !ok { + return nil, fmt.Errorf("argument: 'id' is not a bool, got %T", args["id"]) + } + res, err := TestPostIntResolver(ctx, id) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { + input, err := models.MapToTestInput(args["input"].(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) + } + res, err := CreatePostResolver(ctx, input) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("login", func(ctx *context.Context, args map[string]any) (interface{}, error) { + pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) + if e != nil { + return nil, e + } + name, ok := pv.(string) + if !ok { + return nil, fmt.Errorf("argument: 'name' is not a string, got %T", args["name"]) + } + res, err := LoginResolver(ctx, name) + if res == nil { + return nil, err + } + return model.TypeToMap(res) + }) } diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index bb37f4e..dfb5d7f 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,57 +2,62 @@ package resolver import ( - "fmt" - "user/models" - - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/log" + "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/context" + "user/models" + "fmt" + "github.com/light-speak/lighthouse/log" ) -func GetPostResolver(ctx *context.Context, fuck string) (*models.Post, error) { - // Func:GetPost user code start. Do not remove this comment. - log.Debug().Msg("GetPostResolver") - db := model.GetDB() - post := &models.Post{} - db.Where("id = ?", fuck).First(post) - return post, nil - // Func:GetPost user code end. Do not remove this comment. -} -func GetPostsResolver(ctx *context.Context, fuck string) ([]*models.Post, error) { + +func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { // Func:GetPosts user code start. Do not remove this comment. posts := []*models.Post{} db := model.GetDB() db.Find(&posts) return posts, nil - // Func:GetPosts user code end. Do not remove this comment. -} -func TestPostIntResolver(ctx *context.Context, id bool) (*models.Post, error) { - // Func:TestPostInt user code start. Do not remove this comment. - return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. + // Func:GetPosts user code end. Do not remove this comment. } func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { // Func:GetPostIds user code start. Do not remove this comment. return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. + // Func:GetPostIds user code end. Do not remove this comment. } -func TestPostEnumResolver(ctx *context.Context, enum *models.TestEnum) (string, error) { - // Func:TestPostEnum user code start. Do not remove this comment. - log.Debug().Msgf("enum: %+v", enum) - res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) +func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { + // Func:TestPostInput user code start. Do not remove this comment. + res := fmt.Sprintf("input: %+v", input) return res, nil - // Func:TestPostEnum user code end. Do not remove this comment. + // Func:TestPostInput user code end. Do not remove this comment. +} +func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { + // Func:GetPost user code start. Do not remove this comment. + log.Debug().Msg("GetPostResolver") + db := model.GetDB() + post := &models.Post{} + db.Where("id = ?", fuck).First(post) + return post, nil + // Func:GetPost user code end. Do not remove this comment. } -func TestPostIdResolver(ctx *context.Context, id int64) (*models.Post, error) { +func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { // Func:TestPostId user code start. Do not remove this comment. log.Debug().Msgf("id: %d", id) return nil, nil - // Func:TestPostId user code end. Do not remove this comment. + // Func:TestPostId user code end. Do not remove this comment. } -func TestPostInputResolver(ctx *context.Context, input *models.TestInput) (string, error) { - // Func:TestPostInput user code start. Do not remove this comment. - res := fmt.Sprintf("input: %+v", input) +func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { + // Func:TestPostEnum user code start. Do not remove this comment. + log.Debug().Msgf("enum: %+v", enum) + res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) return res, nil - // Func:TestPostInput user code end. Do not remove this comment. + // Func:TestPostEnum user code end. Do not remove this comment. +} +func TestNullableEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { + // Func:TestNullableEnum user code start. Do not remove this comment. + panic("not implement") + // Func:TestNullableEnum user code end. Do not remove this comment. } +func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { + // Func:TestPostInt user code start. Do not remove this comment. + return nil, nil + // Func:TestPostInt user code end. Do not remove this comment. +} \ No newline at end of file diff --git a/example/user/schema/article.graphql b/example/user/schema/article.graphql new file mode 100644 index 0000000..9323197 --- /dev/null +++ b/example/user/schema/article.graphql @@ -0,0 +1,3 @@ +# type Article @model { + +# } \ No newline at end of file diff --git a/example/user/schema/post.graphqls b/example/user/schema/post.graphqls index 2ef7c9e..ded2c15 100644 --- a/example/user/schema/post.graphqls +++ b/example/user/schema/post.graphqls @@ -20,6 +20,7 @@ extend type Query { testPostInt(id: Boolean!): Post testPostEnum(enum: TestEnum!): String! testPostInput(input: TestInput!): String! + testNullableEnum(enum: TestEnum): String! } extend type Mutation { diff --git a/graphql/model/generate/tpl/operation_gen.tpl b/graphql/model/generate/tpl/operation_gen.tpl index d21687f..4c3f819 100644 --- a/graphql/model/generate/tpl/operation_gen.tpl +++ b/graphql/model/generate/tpl/operation_gen.tpl @@ -18,9 +18,9 @@ func init() { } {{- else if eq $arg.Type.GetRealType.Kind "ENUM" }} - {{ $arg.Name | lcFirst }}Value, ok := models.{{ false | $arg.Type.GetGoType }}Map[args["{{ $arg.Name | lcFirst }}"].(string)] + {{ $arg.Name | lcFirst }}Value, ok := models.{{ $arg.Type.GetRealType.Name }}Map[args["{{ $arg.Name | lcFirst }}"].(string)] if !ok { - return nil, fmt.Errorf("argument: '{{ $arg.Name }}' is not a models.{{ false | $arg.Type.GetGoType }}, got %T", args["{{ $index }}"]) + return nil, fmt.Errorf("argument: '{{ $arg.Name }}' is not a models.{{ $arg.Type.GetRealType.Name }}, got %T", args["{{ $index }}"]) } {{ $arg.Name | lcFirst }} := &{{ $arg.Name | lcFirst }}Value {{- else if eq $arg.Type.GetRealType.Kind "OBJECT" }} From 06f7c1ac0a2c3910658ca5c9034e555f36dce6b3 Mon Sep 17 00:00:00 2001 From: linty Date: Mon, 4 Nov 2024 15:53:33 +0800 Subject: [PATCH 12/17] fix: fix --- command/run.go | 22 ++-- example/user/models/enum.go | 22 ++++ example/user/models/interface.go | 4 + example/user/models/model.go | 52 +++++--- example/user/models/response.go | 16 +-- example/user/repo/repo.go | 124 ++++++++++++++++--- example/user/resolver/mutation.go | 4 +- example/user/resolver/operation_gen.go | 6 +- example/user/resolver/query.go | 62 +++++----- example/user/resolver/resolver.go | 8 ++ example/user/schema/article.graphql | 7 +- example/user/schema/comment.graphql | 18 +++ example/user/schema/post.graphqls | 4 +- example/user/service/service.go | 1 + graphql/ast/funcs.go | 5 + graphql/ast/node.go | 11 +- graphql/excute/merge.go | 1 - graphql/genenrate.go | 10 +- graphql/model/generate/generate.go | 2 +- graphql/model/generate/tpl/operation_gen.tpl | 10 +- 20 files changed, 280 insertions(+), 109 deletions(-) create mode 100644 example/user/resolver/resolver.go create mode 100644 example/user/schema/comment.graphql diff --git a/command/run.go b/command/run.go index b254b66..6eaaf78 100644 --- a/command/run.go +++ b/command/run.go @@ -162,18 +162,14 @@ func validateRequiredFlags(cmd Command, flagValues map[string]interface{}) error } return nil } - func printLogo() { - fmt.Print("\033[33m") - fmt.Printf(` - _ _ _ _ _ - | ) | | | - | _ __ |__ _ |_ |__ _ _ _ ___ __ - | | ' \ \ | \ \ | | __| _ \ - |___ | | | | | |_ | | | | |_ | __ \ '__ - | | ' - | | | / | | '- / / / | - | - \__ / %s by @light-speak - `, version.Version) - fmt.Print("\033[0m\n") + fmt.Printf("\033[38;5;196m _ _ _ _ _\n") + fmt.Printf("\033[38;5;202m | ) | | |\n") + fmt.Printf("\033[38;5;208m | _ __ |__ _ |_ |__ _ _ _ ___ __\n") + fmt.Printf("\033[38;5;214m | | ' \\ \\ | \\ \\ | | __| _ \\\n") + fmt.Printf("\033[38;5;220m |___ | | | | | |_ | | | | |_ | __ \\ '__\n") + fmt.Printf("\033[38;5;226m | | ' - | | | / | | '- / / / |\n") + fmt.Printf("\033[38;5;190m |\n") + fmt.Printf("\033[38;5;154m \\__ / %s by @light-speak\n\n", version.Version) + fmt.Print("\033[0m") } diff --git a/example/user/models/enum.go b/example/user/models/enum.go index e245c04..05b52eb 100644 --- a/example/user/models/enum.go +++ b/example/user/models/enum.go @@ -4,6 +4,28 @@ package models +type CommentableType int8 + +const ( + CommentableTypeARTICLE = iota + CommentableTypePOST +) + +func (e CommentableType) ToString() string { + switch e { + case CommentableTypeARTICLE: + return "ARTICLE" + case CommentableTypePOST: + return "POST" + default: + return "unknown" + } +} + +var CommentableTypeMap = map[string]CommentableType{ + "ARTICLE": CommentableTypeARTICLE, + "POST": CommentableTypePOST, +} type SortOrder int8 const ( diff --git a/example/user/models/interface.go b/example/user/models/interface.go index 6f191c4..69dcbaf 100644 --- a/example/user/models/interface.go +++ b/example/user/models/interface.go @@ -14,3 +14,7 @@ type Userable interface { GetUser() User GetUserId() int64 } + +type Commentable interface { + IsCommentable() +} diff --git a/example/user/models/model.go b/example/user/models/model.go index f82b9b0..beb24a8 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -4,38 +4,62 @@ package models import "github.com/light-speak/lighthouse/graphql/model" +type User struct { + model.Model + Name string `json:"name" gorm:"index;type:varchar(255)" ` + MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` +} + +func (*User) IsModel() bool { return true } +func (*User) IsHasName() bool { return true } +func (this *User) GetName() string { return this.Name } +func (*User) TableName() string { return "users" } +func (*User) TypeName() string { return "user" } + type Post struct { model.ModelSoftDelete + UserId int64 `json:"user_id" gorm:"index" ` TagId int64 `json:"tag_id" ` - User User `json:"user" ` - Enum TestEnum `json:"enum" ` - Content string `json:"content" gorm:"type:varchar(255)" ` - UserId int64 `json:"user_id" ` BackId int64 `json:"back_id" ` IsBool bool `json:"is_bool" gorm:"default:false" ` - Title string `gorm:"index;type:varchar(255)" json:"title" ` + Enum TestEnum `json:"enum" ` + Title string `json:"title" gorm:"index;type:varchar(255)" ` + Content string `json:"content" gorm:"type:varchar(255)" ` + User User `json:"user" ` } func (*Post) IsModel() bool { return true } func (*Post) TableName() string { return "posts" } func (*Post) TypeName() string { return "post" } -type User struct { +type Article struct { model.Model - Name string `json:"name" gorm:"index;type:varchar(255)" ` - MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` + Name string `gorm:"type:varchar(255)" json:"name" ` + Content string `json:"content" gorm:"type:varchar(255)" ` } -func (*User) IsModel() bool { return true } -func (*User) IsHasName() bool { return true } -func (this *User) GetName() string { return this.Name } -func (*User) TableName() string { return "users" } -func (*User) TypeName() string { return "user" } +func (*Article) IsModel() bool { return true } +func (*Article) TableName() string { return "articles" } +func (*Article) TypeName() string { return "article" } + +type Comment struct { + model.Model + Content string `json:"content" gorm:"type:varchar(255)" ` + CommentableId int64 `gorm:"index:commentable" json:"commentable_id" ` + CommentableType CommentableType `json:"commentable_type" gorm:"index:commentable" ` + Commentable interface{} `json:"commentable" gorm:"-" ` +} + +func (*Comment) IsModel() bool { return true } +func (*Comment) TableName() string { return "comments" } +func (*Comment) TypeName() string { return "comment" } func Migrate() error { return model.GetDB().AutoMigrate( - &Post{}, &User{}, + &Post{}, + &Article{}, + &Comment{}, ) } \ No newline at end of file diff --git a/example/user/models/response.go b/example/user/models/response.go index 2a48ae9..9bede54 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -4,9 +4,9 @@ package models import "github.com/light-speak/lighthouse/graphql/model" -type LoginResponse struct { - User User `json:"user" ` - Token string `json:"token" gorm:"type:varchar(255)" ` +type PostPaginateResponse struct { + Data []*Post `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` } type UserPaginateResponse struct { @@ -14,11 +14,11 @@ type UserPaginateResponse struct { PaginateInfo model.PaginateInfo `json:"paginate_info" ` } -type Test struct { - Test string `json:"test" gorm:"type:varchar(255)" ` +type LoginResponse struct { + User User `json:"user" ` + Token string `gorm:"type:varchar(255)" json:"token" ` } -type PostPaginateResponse struct { - Data []*Post `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` +type Test struct { + Test string `gorm:"type:varchar(255)" json:"test" ` } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index f1a5d17..c5a118b 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,13 +2,50 @@ package repo import ( - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/ast" "user/models" + "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/graphql/model" "gorm.io/gorm" ) +func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"name": {},"updated_at": {},}} +func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) +} +func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) +} +func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { + return model.GetDB().Model(&models.User{}).Scopes(scopes...) +} +func First__User(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__User().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil +} +func List__User(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__User().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil +} +func Count__User(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { + var count int64 + err := Query__User().Scopes(scopes...).Count(&count).Error + return count, err +} func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"IsBool": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} func Load__Post(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) @@ -46,54 +83,101 @@ func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { err := Query__Post().Scopes(scopes...).Count(&count).Error return count, err } -func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"name": {},"updated_at": {},}} -func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) +func Provide__Article() map[string]*ast.Relation { return map[string]*ast.Relation{"content": {},"created_at": {},"id": {},"name": {},"updated_at": {},}} +func Load__Article(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "articles", field).Load(key) } -func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) +func LoadList__Article(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "articles", field).LoadList(key) } -func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.User{}).Scopes(scopes...) +func Query__Article(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { + return model.GetDB().Model(&models.Article{}).Scopes(scopes...) } -func First__User(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { +func First__Article(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { var err error if data == nil { data = make(map[string]interface{}) - err = Query__User().Scopes(scopes...).First(data).Error + err = Query__Article().Scopes(scopes...).First(data).Error if err != nil { return nil, err } } return data, nil } -func List__User(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { +func List__Article(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { var err error if datas == nil { datas = make([]map[string]interface{}, 0) - err = Query__User().Scopes(scopes...).Find(&datas).Error + err = Query__Article().Scopes(scopes...).Find(&datas).Error if err != nil { return nil, err } } return datas, nil } -func Count__User(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { +func Count__Article(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { var count int64 - err := Query__User().Scopes(scopes...).Count(&count).Error + err := Query__Article().Scopes(scopes...).Count(&count).Error + return count, err +} +func Provide__Comment() map[string]*ast.Relation { return map[string]*ast.Relation{"commentable": {},"commentableId": {},"commentableType": {},"content": {},"created_at": {},"id": {},"updated_at": {},}} +func Load__Comment(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "comments", field).Load(key) +} +func LoadList__Comment(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "comments", field).LoadList(key) +} +func Query__Comment(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { + return model.GetDB().Model(&models.Comment{}).Scopes(scopes...) +} +func First__Comment(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__Comment().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil +} +func List__Comment(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__Comment().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil +} +func Count__Comment(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { + var count int64 + err := Query__Comment().Scopes(scopes...).Count(&count).Error return count, err } func init() { - model.AddQuickFirst("Post", First__Post) - model.AddQuickList("Post", List__Post) - model.AddQuickLoad("Post", Load__Post) - model.AddQuickLoadList("Post", LoadList__Post) - model.AddQuickCount("Post", Count__Post) model.AddQuickFirst("User", First__User) model.AddQuickList("User", List__User) model.AddQuickLoad("User", Load__User) model.AddQuickLoadList("User", LoadList__User) model.AddQuickCount("User", Count__User) + model.AddQuickFirst("Post", First__Post) + model.AddQuickList("Post", List__Post) + model.AddQuickLoad("Post", Load__Post) + model.AddQuickLoadList("Post", LoadList__Post) + model.AddQuickCount("Post", Count__Post) + model.AddQuickFirst("Article", First__Article) + model.AddQuickList("Article", List__Article) + model.AddQuickLoad("Article", Load__Article) + model.AddQuickLoadList("Article", LoadList__Article) + model.AddQuickCount("Article", Count__Article) + model.AddQuickFirst("Comment", First__Comment) + model.AddQuickList("Comment", List__Comment) + model.AddQuickLoad("Comment", Load__Comment) + model.AddQuickLoadList("Comment", LoadList__Comment) + model.AddQuickCount("Comment", Count__Comment) } diff --git a/example/user/resolver/mutation.go b/example/user/resolver/mutation.go index c70f516..3940d8b 100644 --- a/example/user/resolver/mutation.go +++ b/example/user/resolver/mutation.go @@ -2,10 +2,10 @@ package resolver import ( - "user/models" + "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/graphql/model" "github.com/light-speak/lighthouse/auth" - "github.com/light-speak/lighthouse/context" + "user/models" ) diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index fb6e6f8..21a7718 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -3,11 +3,11 @@ package resolver import ( "github.com/light-speak/lighthouse/graphql/excute" - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/graphql" - "github.com/light-speak/lighthouse/context" "user/models" "fmt" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql" + "github.com/light-speak/lighthouse/graphql/model" ) func init() { diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index dfb5d7f..5fe0da8 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,33 +2,14 @@ package resolver import ( - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/context" "user/models" - "fmt" "github.com/light-speak/lighthouse/log" + "github.com/light-speak/lighthouse/context" + "fmt" + "github.com/light-speak/lighthouse/graphql/model" ) -func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { - // Func:GetPosts user code start. Do not remove this comment. - posts := []*models.Post{} - db := model.GetDB() - db.Find(&posts) - return posts, nil - // Func:GetPosts user code end. Do not remove this comment. -} -func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { - // Func:GetPostIds user code start. Do not remove this comment. - return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. -} -func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { - // Func:TestPostInput user code start. Do not remove this comment. - res := fmt.Sprintf("input: %+v", input) - return res, nil - // Func:TestPostInput user code end. Do not remove this comment. -} func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { // Func:GetPost user code start. Do not remove this comment. log.Debug().Msg("GetPostResolver") @@ -38,11 +19,10 @@ func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { return post, nil // Func:GetPost user code end. Do not remove this comment. } -func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { - // Func:TestPostId user code start. Do not remove this comment. - log.Debug().Msgf("id: %d", id) +func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { + // Func:TestPostInt user code start. Do not remove this comment. return nil, nil - // Func:TestPostId user code end. Do not remove this comment. + // Func:TestPostInt user code end. Do not remove this comment. } func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { // Func:TestPostEnum user code start. Do not remove this comment. @@ -51,13 +31,33 @@ func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, e return res, nil // Func:TestPostEnum user code end. Do not remove this comment. } +func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { + // Func:TestPostId user code start. Do not remove this comment. + log.Debug().Msgf("id: %d", id) + return nil, nil + // Func:TestPostId user code end. Do not remove this comment. +} +func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { + // Func:GetPostIds user code start. Do not remove this comment. + return []int64{1, 2, 3}, nil + // Func:GetPostIds user code end. Do not remove this comment. +} +func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { + // Func:TestPostInput user code start. Do not remove this comment. + res := fmt.Sprintf("input: %+v", input) + return res, nil + // Func:TestPostInput user code end. Do not remove this comment. +} +func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { + // Func:GetPosts user code start. Do not remove this comment. + posts := []*models.Post{} + db := model.GetDB() + db.Find(&posts) + return posts, nil + // Func:GetPosts user code end. Do not remove this comment. +} func TestNullableEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { // Func:TestNullableEnum user code start. Do not remove this comment. panic("not implement") // Func:TestNullableEnum user code end. Do not remove this comment. -} -func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { - // Func:TestPostInt user code start. Do not remove this comment. - return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. } \ No newline at end of file diff --git a/example/user/resolver/resolver.go b/example/user/resolver/resolver.go new file mode 100644 index 0000000..9aca284 --- /dev/null +++ b/example/user/resolver/resolver.go @@ -0,0 +1,8 @@ +package resolver + +type Resolver struct { +} + +func (r *Resolver) IsResolver() bool { + return true +} diff --git a/example/user/schema/article.graphql b/example/user/schema/article.graphql index 9323197..39df19d 100644 --- a/example/user/schema/article.graphql +++ b/example/user/schema/article.graphql @@ -1,3 +1,4 @@ -# type Article @model { - -# } \ No newline at end of file +type Article @model { + name: String! + content: String! +} diff --git a/example/user/schema/comment.graphql b/example/user/schema/comment.graphql new file mode 100644 index 0000000..430d8ba --- /dev/null +++ b/example/user/schema/comment.graphql @@ -0,0 +1,18 @@ +type Comment @model { + content: String! + commentableId: ID! @index(name: "commentable") + commentableType: CommentableType! @index(name: "commentable") + commentable: Commentable! +} + +enum CommentableType { + ARTICLE + POST +} + +union Commentable = Article | Post + +extend type Query { + comment(id: ID! @eq): Comment @first + comments: [Comment!]! @find +} diff --git a/example/user/schema/post.graphqls b/example/user/schema/post.graphqls index ded2c15..063f0e1 100644 --- a/example/user/schema/post.graphqls +++ b/example/user/schema/post.graphqls @@ -1,8 +1,8 @@ type Post @key(fields: "id") @softDeleteModel { title: String! @index content: String! - userId: ID! - tagId: ID! + userId: ID! @index + tagId: ID! BackId: ID! IsBool: Boolean! @default(value: "false") user: User! @belongsTo diff --git a/example/user/service/service.go b/example/user/service/service.go index dc99d81..355c84b 100644 --- a/example/user/service/service.go +++ b/example/user/service/service.go @@ -7,5 +7,6 @@ import ( ) func StartService() { + handler.StartService() } diff --git a/graphql/ast/funcs.go b/graphql/ast/funcs.go index 333a762..9209509 100644 --- a/graphql/ast/funcs.go +++ b/graphql/ast/funcs.go @@ -40,6 +40,7 @@ func Fields(fields map[string]*Field) string { if goType == "PaginateInfo" { goType = "model.PaginateInfo" } + line := fmt.Sprintf(" %s %s %s", utils.UcFirst(utils.CamelCase(field.Name)), goType, genTag(field)) lines = append(lines, line) } @@ -95,6 +96,10 @@ func genTag(field *Field) string { } hasType := false + if field.Type.GetRealType().Kind == KindUnion { + tags["gorm"] = append(tags["gorm"], "-") + } + for _, directive := range field.Directives { if fn, ok := directiveFns[directive.Name]; ok { if directive.Name == "type" { diff --git a/graphql/ast/node.go b/graphql/ast/node.go index 3a8abfd..11c245b 100644 --- a/graphql/ast/node.go +++ b/graphql/ast/node.go @@ -377,6 +377,13 @@ func (f *Field) Validate(store *NodeStore, objectFields map[string]*Field, objec Locations: []*errors.GraphqlLocation{f.GetLocation()}, } } else { + if f.Name == "__typename" { + f.Type = &TypeRef{ + Kind: KindScalar, + Name: "String", + } + return nil + } if objectNode.GetFields()[f.Name] != nil { f.Type = objectNode.GetFields()[f.Name].Type realType := f.Type.GetRealType() @@ -551,7 +558,7 @@ func (t *TypeRef) GetGoType(NonNull bool) string { case KindNonNull: return t.OfType.GetGoType(true) } - return "any" + return "interface{}" } func (t *TypeRef) Validate(store *NodeStore) errors.GraphqlErrorInterface { @@ -614,7 +621,7 @@ func (t *TypeRef) ValidateValue(v interface{}, isVariable bool) errors.GraphqlEr case KindNonNull: if v == nil { return &errors.GraphQLError{ - Message: "non-null type cannot be null", + Message: "non-null type cannot be null, f", Locations: []*errors.GraphqlLocation{t.GetLocation()}, } } diff --git a/graphql/excute/merge.go b/graphql/excute/merge.go index 79b56b9..0559855 100644 --- a/graphql/excute/merge.go +++ b/graphql/excute/merge.go @@ -70,7 +70,6 @@ func mergeData(ctx *context.Context, field *ast.Field, datas map[string]interfac } else { v = nil } - // Handle relation fields if v == nil && field.Relation != nil { cData, err := model.FetchRelation(ctx, datas, field.Relation) diff --git a/graphql/genenrate.go b/graphql/genenrate.go index 913ff12..29c579f 100644 --- a/graphql/genenrate.go +++ b/graphql/genenrate.go @@ -40,10 +40,18 @@ func Generate() error { } } + interfaces := []ast.Node{} + for _, interfaceNode := range p.NodeStore.Interfaces { + interfaces = append(interfaces, interfaceNode) + } + for _, union := range p.NodeStore.Unions { + interfaces = append(interfaces, union) + } + if err := generate.GenObject(typeNodes, currentPath); err != nil { return err } - if err := generate.GenInterface(p.NodeStore.Interfaces, currentPath); err != nil { + if err := generate.GenInterface(interfaces, currentPath); err != nil { return err } if err := generate.GenInput(p.NodeStore.Inputs, currentPath); err != nil { diff --git a/graphql/model/generate/generate.go b/graphql/model/generate/generate.go index 0bc4833..9c324cc 100644 --- a/graphql/model/generate/generate.go +++ b/graphql/model/generate/generate.go @@ -19,7 +19,7 @@ var excludeType = map[string]struct{}{ "Subscription": {}, } -func GenInterface(nodes map[string]*ast.InterfaceNode, path string) error { +func GenInterface(nodes []ast.Node, path string) error { interfaceTemplate, err := modelFs.ReadFile("tpl/interface.tpl") if err != nil { return err diff --git a/graphql/model/generate/tpl/operation_gen.tpl b/graphql/model/generate/tpl/operation_gen.tpl index 4c3f819..ccfe361 100644 --- a/graphql/model/generate/tpl/operation_gen.tpl +++ b/graphql/model/generate/tpl/operation_gen.tpl @@ -16,22 +16,16 @@ func init() { if !ok { return nil, fmt.Errorf("argument: '{{ $arg.Name }}' is not a {{ false | $arg.Type.GetGoType }}, got %T", args["{{ $index }}"]) } - {{- else if eq $arg.Type.GetRealType.Kind "ENUM" }} {{ $arg.Name | lcFirst }}Value, ok := models.{{ $arg.Type.GetRealType.Name }}Map[args["{{ $arg.Name | lcFirst }}"].(string)] if !ok { return nil, fmt.Errorf("argument: '{{ $arg.Name }}' is not a models.{{ $arg.Type.GetRealType.Name }}, got %T", args["{{ $index }}"]) } {{ $arg.Name | lcFirst }} := &{{ $arg.Name | lcFirst }}Value - {{- else if eq $arg.Type.GetRealType.Kind "OBJECT" }} - {{ $arg.Name | lcFirst }}, ok := args["{{ $index }}"].(models.{{ false | $arg.Type.GetGoType }}) - if !ok { - return nil, fmt.Errorf("argument: '{{ $arg.Name }}' is not a models.{{ false | $arg.Type.GetGoType }}, got %T", args["{{ $index }}"]) - } {{- else if eq $arg.Type.GetRealType.Kind "INPUT_OBJECT" }} - {{ $arg.Name | lcFirst }}, err := models.MapTo{{ false | $arg.Type.GetGoType }}(args["{{ $index }}"].(map[string]interface{})) + {{ $arg.Name | lcFirst }}, err := models.MapTo{{ $arg.Type.GetRealType.Name }}(args["{{ $index }}"].(map[string]interface{})) if err != nil { - return nil, fmt.Errorf("argument: '{{ $arg.Name }}' is not a models.{{ false | $arg.Type.GetGoType }}, got %T", args["{{ $index }}"]) + return nil, fmt.Errorf("argument: '{{ $arg.Name }}' can not convert to models.{{ $arg.Type.GetRealType.Name }}, got %T", args["{{ $index }}"]) } {{- else }} {{ $arg.Name | lcFirst }}, ok := args["{{ $index }}"].(models.{{ false | $arg.Type.GetGoType }}) From a9c52af176d560126a42bd7193f8e4cff5ac39f8 Mon Sep 17 00:00:00 2001 From: linty Date: Mon, 4 Nov 2024 15:55:53 +0800 Subject: [PATCH 13/17] fix: fix --- graphql/model/generate/tpl/operation_gen.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphql/model/generate/tpl/operation_gen.tpl b/graphql/model/generate/tpl/operation_gen.tpl index ccfe361..1e02d35 100644 --- a/graphql/model/generate/tpl/operation_gen.tpl +++ b/graphql/model/generate/tpl/operation_gen.tpl @@ -8,11 +8,11 @@ func init() { {{- range $index, $arg := $args }} {{- if eq $arg.Type.GetRealType.Kind "SCALAR" }} - pv, e := graphql.Parser.NodeStore.Scalars["{{ $arg.Type.GetRealType.Name }}"].ScalarType.ParseValue(args["{{ $index }}"], nil) + p{{ $arg.Name | lcFirst }}, e := graphql.Parser.NodeStore.Scalars["{{ $arg.Type.GetRealType.Name }}"].ScalarType.ParseValue(args["{{ $index }}"], nil) if e != nil { return nil, e } - {{ $arg.Name | lcFirst }}, ok := pv.({{ false | $arg.Type.GetGoType }}) + {{ $arg.Name | lcFirst }}, ok := p{{ $arg.Name | lcFirst }}.({{ false | $arg.Type.GetGoType }}) if !ok { return nil, fmt.Errorf("argument: '{{ $arg.Name }}' is not a {{ false | $arg.Type.GetGoType }}, got %T", args["{{ $index }}"]) } From e0d96b92e636d574f0b3e470411d3aa37a779ddf Mon Sep 17 00:00:00 2001 From: linty Date: Mon, 4 Nov 2024 18:55:49 +0800 Subject: [PATCH 14/17] feat: can make resolver your own --- command/cli/generate/initialize/one/one.go | 18 ++++ .../generate/initialize/one/tpl/resolver.tpl | 6 ++ example/user/models/model.go | 30 +++---- example/user/models/response.go | 23 ++--- example/user/repo/repo.go | 86 +++++++++---------- example/user/resolver/mutation.go | 25 +++--- example/user/resolver/operation_gen.go | 81 +++++++++-------- example/user/resolver/query.go | 66 +++++++------- example/user/resolver/resolver.go | 1 + example/user/schema/user.graphql | 1 + example/user/service/service.go | 7 +- graphql/excute/excute.go | 5 +- graphql/excute/resolver.go | 3 +- graphql/model/generate/tpl/operation.tpl | 4 +- graphql/model/generate/tpl/operation_gen.tpl | 11 +-- handler/service.go | 4 +- resolve/resolve.go | 7 ++ template/import.go | 3 + 18 files changed, 219 insertions(+), 162 deletions(-) create mode 100644 command/cli/generate/initialize/one/tpl/resolver.tpl create mode 100644 resolve/resolve.go diff --git a/command/cli/generate/initialize/one/one.go b/command/cli/generate/initialize/one/one.go index 3040124..99b7a01 100644 --- a/command/cli/generate/initialize/one/one.go +++ b/command/cli/generate/initialize/one/one.go @@ -32,6 +32,7 @@ func Run(module string) error { InitMod, InitIdeHelper, InitLighthouseYml, + InitResolver, } for _, initFunc := range initFunctions { @@ -167,3 +168,20 @@ func InitLighthouseYml() error { } return template.Render(options) } + +func InitResolver() error { + resolverTemplate, err := oneFs.ReadFile("tpl/resolver.tpl") + if err != nil { + return err + } + options := &template.Options{ + Path: filepath.Join(projectName, "resolver"), + Template: string(resolverTemplate), + FileName: "resolver", + FileExt: "go", + Package: "resolver", + Editable: true, + SkipIfExists: true, + } + return template.Render(options) +} diff --git a/command/cli/generate/initialize/one/tpl/resolver.tpl b/command/cli/generate/initialize/one/tpl/resolver.tpl new file mode 100644 index 0000000..5b48e94 --- /dev/null +++ b/command/cli/generate/initialize/one/tpl/resolver.tpl @@ -0,0 +1,6 @@ +type Resolver struct { +} + +func (r *Resolver) IsResolver() bool { + return true +} diff --git a/example/user/models/model.go b/example/user/models/model.go index beb24a8..856402e 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -4,6 +4,16 @@ package models import "github.com/light-speak/lighthouse/graphql/model" +type Article struct { + model.Model + Name string `gorm:"type:varchar(255)" json:"name" ` + Content string `json:"content" gorm:"type:varchar(255)" ` +} + +func (*Article) IsModel() bool { return true } +func (*Article) TableName() string { return "articles" } +func (*Article) TypeName() string { return "article" } + type User struct { model.Model Name string `json:"name" gorm:"index;type:varchar(255)" ` @@ -18,13 +28,13 @@ func (*User) TypeName() string { return "user" } type Post struct { model.ModelSoftDelete - UserId int64 `json:"user_id" gorm:"index" ` TagId int64 `json:"tag_id" ` - BackId int64 `json:"back_id" ` - IsBool bool `json:"is_bool" gorm:"default:false" ` Enum TestEnum `json:"enum" ` Title string `json:"title" gorm:"index;type:varchar(255)" ` Content string `json:"content" gorm:"type:varchar(255)" ` + UserId int64 `json:"user_id" gorm:"index" ` + BackId int64 `json:"back_id" ` + IsBool bool `json:"is_bool" gorm:"default:false" ` User User `json:"user" ` } @@ -32,20 +42,10 @@ func (*Post) IsModel() bool { return true } func (*Post) TableName() string { return "posts" } func (*Post) TypeName() string { return "post" } -type Article struct { - model.Model - Name string `gorm:"type:varchar(255)" json:"name" ` - Content string `json:"content" gorm:"type:varchar(255)" ` -} - -func (*Article) IsModel() bool { return true } -func (*Article) TableName() string { return "articles" } -func (*Article) TypeName() string { return "article" } - type Comment struct { model.Model Content string `json:"content" gorm:"type:varchar(255)" ` - CommentableId int64 `gorm:"index:commentable" json:"commentable_id" ` + CommentableId int64 `json:"commentable_id" gorm:"index:commentable" ` CommentableType CommentableType `json:"commentable_type" gorm:"index:commentable" ` Commentable interface{} `json:"commentable" gorm:"-" ` } @@ -57,9 +57,9 @@ func (*Comment) TypeName() string { return "comment" } func Migrate() error { return model.GetDB().AutoMigrate( + &Article{}, &User{}, &Post{}, - &Article{}, &Comment{}, ) } \ No newline at end of file diff --git a/example/user/models/response.go b/example/user/models/response.go index 9bede54..c08085d 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -4,21 +4,22 @@ package models import "github.com/light-speak/lighthouse/graphql/model" -type PostPaginateResponse struct { - Data []*Post `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` -} - -type UserPaginateResponse struct { - Data []*User `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` +type Test struct { + Test string `json:"test" gorm:"type:varchar(255)" ` } type LoginResponse struct { User User `json:"user" ` - Token string `gorm:"type:varchar(255)" json:"token" ` + Token string `json:"token" gorm:"type:varchar(255)" ` + Authorization string `json:"authorization" gorm:"type:varchar(255)" ` } -type Test struct { - Test string `gorm:"type:varchar(255)" json:"test" ` +type UserPaginateResponse struct { + PaginateInfo model.PaginateInfo `json:"paginate_info" ` + Data []*User `json:"data" ` +} + +type PostPaginateResponse struct { + Data []*Post `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index c5a118b..f794af1 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,13 +2,50 @@ package repo import ( + "gorm.io/gorm" "user/models" "github.com/light-speak/lighthouse/graphql/ast" "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/graphql/model" - "gorm.io/gorm" ) +func Provide__Article() map[string]*ast.Relation { return map[string]*ast.Relation{"content": {},"created_at": {},"id": {},"name": {},"updated_at": {},}} +func Load__Article(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "articles", field).Load(key) +} +func LoadList__Article(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "articles", field).LoadList(key) +} +func Query__Article(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { + return model.GetDB().Model(&models.Article{}).Scopes(scopes...) +} +func First__Article(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__Article().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil +} +func List__Article(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__Article().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil +} +func Count__Article(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { + var count int64 + err := Query__Article().Scopes(scopes...).Count(&count).Error + return count, err +} func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"name": {},"updated_at": {},}} func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) @@ -83,43 +120,6 @@ func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { err := Query__Post().Scopes(scopes...).Count(&count).Error return count, err } -func Provide__Article() map[string]*ast.Relation { return map[string]*ast.Relation{"content": {},"created_at": {},"id": {},"name": {},"updated_at": {},}} -func Load__Article(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "articles", field).Load(key) -} -func LoadList__Article(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "articles", field).LoadList(key) -} -func Query__Article(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Article{}).Scopes(scopes...) -} -func First__Article(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__Article().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil -} -func List__Article(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__Article().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil -} -func Count__Article(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__Article().Scopes(scopes...).Count(&count).Error - return count, err -} func Provide__Comment() map[string]*ast.Relation { return map[string]*ast.Relation{"commentable": {},"commentableId": {},"commentableType": {},"content": {},"created_at": {},"id": {},"updated_at": {},}} func Load__Comment(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { return model.GetLoader[int64](model.GetDB(), "comments", field).Load(key) @@ -160,6 +160,11 @@ func Count__Comment(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { func init() { + model.AddQuickFirst("Article", First__Article) + model.AddQuickList("Article", List__Article) + model.AddQuickLoad("Article", Load__Article) + model.AddQuickLoadList("Article", LoadList__Article) + model.AddQuickCount("Article", Count__Article) model.AddQuickFirst("User", First__User) model.AddQuickList("User", List__User) model.AddQuickLoad("User", Load__User) @@ -170,11 +175,6 @@ func init() { model.AddQuickLoad("Post", Load__Post) model.AddQuickLoadList("Post", LoadList__Post) model.AddQuickCount("Post", Count__Post) - model.AddQuickFirst("Article", First__Article) - model.AddQuickList("Article", List__Article) - model.AddQuickLoad("Article", Load__Article) - model.AddQuickLoadList("Article", LoadList__Article) - model.AddQuickCount("Article", Count__Article) model.AddQuickFirst("Comment", First__Comment) model.AddQuickList("Comment", List__Comment) model.AddQuickLoad("Comment", Load__Comment) diff --git a/example/user/resolver/mutation.go b/example/user/resolver/mutation.go index 3940d8b..5b0c00a 100644 --- a/example/user/resolver/mutation.go +++ b/example/user/resolver/mutation.go @@ -2,14 +2,14 @@ package resolver import ( - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/auth" - "user/models" -) + "user/models" + "github.com/light-speak/lighthouse/auth" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/model" +) -func LoginResolver(ctx *context.Context,name string) (*models.LoginResponse, error) { +func (r *Resolver) LoginResolver(ctx *context.Context, name string) (*models.LoginResponse, error) { // Func:Login user code start. Do not remove this comment. user := &models.User{} db := model.GetDB() @@ -21,13 +21,14 @@ func LoginResolver(ctx *context.Context,name string) (*models.LoginResponse, err return nil, err } return &models.LoginResponse{ - User: *user, - Token: token, + User: *user, + Token: token, + Authorization: r.Www, }, nil - // Func:Login user code end. Do not remove this comment. + // Func:Login user code end. Do not remove this comment. } -func CreatePostResolver(ctx *context.Context,input *models.TestInput) (*models.Post, error) { +func (r *Resolver) CreatePostResolver(ctx *context.Context, input *models.TestInput) (*models.Post, error) { // Func:CreatePost user code start. Do not remove this comment. panic("not implement") - // Func:CreatePost user code end. Do not remove this comment. -} \ No newline at end of file + // Func:CreatePost user code end. Do not remove this comment. +} diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index 21a7718..d0137b9 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,47 +2,51 @@ package resolver import ( + "github.com/light-speak/lighthouse/graphql" + "fmt" + "github.com/light-speak/lighthouse/graphql/model" "github.com/light-speak/lighthouse/graphql/excute" "user/models" - "fmt" + "github.com/light-speak/lighthouse/resolve" "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql" - "github.com/light-speak/lighthouse/graphql/model" ) func init() { - excute.AddResolver("getPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) + excute.AddResolver("getPost", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pfuck, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) if e != nil { return nil, e } - fuck, ok := pv.(string) + fuck, ok := pfuck.(string) if !ok { return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) } - res, err := GetPostResolver(ctx, fuck) + res, err := r.GetPostResolver(ctx, fuck) if res == nil { return nil, err } return model.StructToMap(res) }) - excute.AddResolver("getPostIds", func(ctx *context.Context, args map[string]any) (interface{}, error) { - res, err := GetPostIdsResolver(ctx) + excute.AddResolver("getPostIds", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + res, err := r.GetPostIdsResolver(ctx) if res == nil { return nil, err } return res, nil }) - excute.AddResolver("getPosts", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) + excute.AddResolver("getPosts", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pfuck, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) if e != nil { return nil, e } - fuck, ok := pv.(string) + fuck, ok := pfuck.(string) if !ok { return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) } - list, err := GetPostsResolver(ctx, fuck) + list, err := r.GetPostsResolver(ctx, fuck) if list == nil { return nil, err } @@ -56,83 +60,90 @@ func init() { } return res, nil }) - excute.AddResolver("testNullableEnum", func(ctx *context.Context, args map[string]any) (interface{}, error) { + excute.AddResolver("testNullableEnum", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) enumValue, ok := models.TestEnumMap[args["enum"].(string)] if !ok { return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) } enum := &enumValue - res, err := TestNullableEnumResolver(ctx, enum) + res, err := r.TestNullableEnumResolver(ctx, enum) return res, err }) - excute.AddResolver("testPostEnum", func(ctx *context.Context, args map[string]any) (interface{}, error) { + excute.AddResolver("testPostEnum", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) enumValue, ok := models.TestEnumMap[args["enum"].(string)] if !ok { return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) } enum := &enumValue - res, err := TestPostEnumResolver(ctx, enum) + res, err := r.TestPostEnumResolver(ctx, enum) return res, err }) - excute.AddResolver("testPostId", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["ID"].ScalarType.ParseValue(args["id"], nil) + excute.AddResolver("testPostId", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pid, e := graphql.Parser.NodeStore.Scalars["ID"].ScalarType.ParseValue(args["id"], nil) if e != nil { return nil, e } - id, ok := pv.(int64) + id, ok := pid.(int64) if !ok { return nil, fmt.Errorf("argument: 'id' is not a int64, got %T", args["id"]) } - res, err := TestPostIdResolver(ctx, id) + res, err := r.TestPostIdResolver(ctx, id) if res == nil { return nil, err } return model.StructToMap(res) }) - excute.AddResolver("testPostInput", func(ctx *context.Context, args map[string]any) (interface{}, error) { + excute.AddResolver("testPostInput", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) input, err := models.MapToTestInput(args["input"].(map[string]interface{})) if err != nil { - return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) + return nil, fmt.Errorf("argument: 'input' can not convert to models.TestInput, got %T", args["input"]) } - res, err := TestPostInputResolver(ctx, input) + res, err := r.TestPostInputResolver(ctx, input) return res, err }) - excute.AddResolver("testPostInt", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["Boolean"].ScalarType.ParseValue(args["id"], nil) + excute.AddResolver("testPostInt", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pid, e := graphql.Parser.NodeStore.Scalars["Boolean"].ScalarType.ParseValue(args["id"], nil) if e != nil { return nil, e } - id, ok := pv.(bool) + id, ok := pid.(bool) if !ok { return nil, fmt.Errorf("argument: 'id' is not a bool, got %T", args["id"]) } - res, err := TestPostIntResolver(ctx, id) + res, err := r.TestPostIntResolver(ctx, id) if res == nil { return nil, err } return model.StructToMap(res) }) - excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any) (interface{}, error) { + excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) input, err := models.MapToTestInput(args["input"].(map[string]interface{})) if err != nil { - return nil, fmt.Errorf("argument: 'input' is not a models.TestInput, got %T", args["input"]) + return nil, fmt.Errorf("argument: 'input' can not convert to models.TestInput, got %T", args["input"]) } - res, err := CreatePostResolver(ctx, input) + res, err := r.CreatePostResolver(ctx, input) if res == nil { return nil, err } return model.StructToMap(res) }) - excute.AddResolver("login", func(ctx *context.Context, args map[string]any) (interface{}, error) { - pv, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) + excute.AddResolver("login", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pname, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) if e != nil { return nil, e } - name, ok := pv.(string) + name, ok := pname.(string) if !ok { return nil, fmt.Errorf("argument: 'name' is not a string, got %T", args["name"]) } - res, err := LoginResolver(ctx, name) + res, err := r.LoginResolver(ctx, name) if res == nil { return nil, err } diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index 5fe0da8..8c50739 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,62 +2,62 @@ package resolver import ( - "user/models" - "github.com/light-speak/lighthouse/log" "github.com/light-speak/lighthouse/context" "fmt" "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/log" + "user/models" ) -func GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { - // Func:GetPost user code start. Do not remove this comment. - log.Debug().Msg("GetPostResolver") +func (r *Resolver) GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { + // Func:GetPosts user code start. Do not remove this comment. + posts := []*models.Post{} db := model.GetDB() - post := &models.Post{} - db.Where("id = ?", fuck).First(post) - return post, nil - // Func:GetPost user code end. Do not remove this comment. + db.Find(&posts) + return posts, nil + // Func:GetPosts user code end. Do not remove this comment. } -func TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { +func (r *Resolver) TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { // Func:TestPostInt user code start. Do not remove this comment. return nil, nil // Func:TestPostInt user code end. Do not remove this comment. } -func TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { - // Func:TestPostEnum user code start. Do not remove this comment. - log.Debug().Msgf("enum: %+v", enum) - res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) - return res, nil - // Func:TestPostEnum user code end. Do not remove this comment. -} -func TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { +func (r *Resolver) TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { // Func:TestPostId user code start. Do not remove this comment. log.Debug().Msgf("id: %d", id) return nil, nil // Func:TestPostId user code end. Do not remove this comment. } -func GetPostIdsResolver(ctx *context.Context) ([]int64, error) { - // Func:GetPostIds user code start. Do not remove this comment. - return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. -} -func TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { +func (r *Resolver) TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { // Func:TestPostInput user code start. Do not remove this comment. res := fmt.Sprintf("input: %+v", input) return res, nil // Func:TestPostInput user code end. Do not remove this comment. } -func GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { - // Func:GetPosts user code start. Do not remove this comment. - posts := []*models.Post{} - db := model.GetDB() - db.Find(&posts) - return posts, nil - // Func:GetPosts user code end. Do not remove this comment. -} -func TestNullableEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { +func (r *Resolver) TestNullableEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { // Func:TestNullableEnum user code start. Do not remove this comment. panic("not implement") // Func:TestNullableEnum user code end. Do not remove this comment. +} +func (r *Resolver) TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { + // Func:TestPostEnum user code start. Do not remove this comment. + log.Debug().Msgf("enum: %+v", enum) + res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) + return res, nil + // Func:TestPostEnum user code end. Do not remove this comment. +} +func (r *Resolver) GetPostIdsResolver(ctx *context.Context) ([]int64, error) { + // Func:GetPostIds user code start. Do not remove this comment. + return []int64{1, 2, 3}, nil + // Func:GetPostIds user code end. Do not remove this comment. +} +func (r *Resolver) GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { + // Func:GetPost user code start. Do not remove this comment. + log.Debug().Msg("GetPostResolver") + db := model.GetDB() + post := &models.Post{} + db.Where("id = ?", fuck).First(post) + return post, nil + // Func:GetPost user code end. Do not remove this comment. } \ No newline at end of file diff --git a/example/user/resolver/resolver.go b/example/user/resolver/resolver.go index 9aca284..ab502fc 100644 --- a/example/user/resolver/resolver.go +++ b/example/user/resolver/resolver.go @@ -1,6 +1,7 @@ package resolver type Resolver struct { + Www string } func (r *Resolver) IsResolver() bool { diff --git a/example/user/schema/user.graphql b/example/user/schema/user.graphql index c8be435..3f0e6ff 100644 --- a/example/user/schema/user.graphql +++ b/example/user/schema/user.graphql @@ -13,6 +13,7 @@ extend type Query { type LoginResponse { user: User! token: String! + authorization: String! } extend type Mutation { diff --git a/example/user/service/service.go b/example/user/service/service.go index 355c84b..34d62cf 100644 --- a/example/user/service/service.go +++ b/example/user/service/service.go @@ -2,11 +2,14 @@ package service import ( _ "user/repo" + "user/resolver" "github.com/light-speak/lighthouse/handler" ) func StartService() { - - handler.StartService() + resolver := &resolver.Resolver{ + Www: "fuck", + } + handler.StartService(resolver) } diff --git a/graphql/excute/excute.go b/graphql/excute/excute.go index 013ff5c..3769fe5 100644 --- a/graphql/excute/excute.go +++ b/graphql/excute/excute.go @@ -9,6 +9,7 @@ import ( "github.com/light-speak/lighthouse/graphql/ast" "github.com/light-speak/lighthouse/graphql/parser" "github.com/light-speak/lighthouse/graphql/parser/lexer" + "github.com/light-speak/lighthouse/resolve" ) func ExecuteQuery(ctx *context.Context, query string, variables map[string]any) interface{} { @@ -135,8 +136,8 @@ func AddSubscription(name string, fn func(qp *parser.QueryParser, field *ast.Fie subscriptionMap[name] = fn } -var resolverMap = make(map[string]func(ctx *context.Context, args map[string]any) (interface{}, error)) +var resolverMap = make(map[string]func(ctx *context.Context, args map[string]any, r resolve.Resolve) (interface{}, error)) -func AddResolver(name string, fn func(ctx *context.Context, args map[string]any) (interface{}, error)) { +func AddResolver(name string, fn func(ctx *context.Context, args map[string]any, r resolve.Resolve) (interface{}, error)) { resolverMap[name] = fn } diff --git a/graphql/excute/resolver.go b/graphql/excute/resolver.go index 507fc91..989de38 100644 --- a/graphql/excute/resolver.go +++ b/graphql/excute/resolver.go @@ -5,6 +5,7 @@ import ( "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/resolve" ) func executeResolver(ctx *context.Context, field *ast.Field) (interface{}, bool, errors.GraphqlErrorInterface) { @@ -13,7 +14,7 @@ func executeResolver(ctx *context.Context, field *ast.Field) (interface{}, bool, for _, arg := range field.Args { args[arg.Name] = arg.Value } - r, e := resolverFunc(ctx, args) + r, e := resolverFunc(ctx, args, resolve.R) if e != nil { return nil, true, &errors.GraphQLError{ Message: e.Error(), diff --git a/graphql/model/generate/tpl/operation.tpl b/graphql/model/generate/tpl/operation.tpl index e232ec1..4120bd5 100644 --- a/graphql/model/generate/tpl/operation.tpl +++ b/graphql/model/generate/tpl/operation.tpl @@ -1,5 +1,5 @@ {{- range $key, $field := .Fields }} -func {{ $field.Name | ucFirst }}Resolver(ctx *context.Context +func (r *Resolver) {{ $field.Name | ucFirst }}Resolver(ctx *context.Context {{- range $index, $arg := $field.Args -}}, {{- $arg.Name -}} {{- " " -}} @@ -8,7 +8,7 @@ func {{ $field.Name | ucFirst }}Resolver(ctx *context.Context {{- else -}} {{- (false | $arg.Type.GetGoType) | prefixModels -}} {{- end -}} - {{- end -}} ) ( + {{- end -}}) ( {{- if eq $field.Type.GetRealType.Kind "SCALAR" -}} {{- false | $field.Type.GetGoType -}} {{- else -}} diff --git a/graphql/model/generate/tpl/operation_gen.tpl b/graphql/model/generate/tpl/operation_gen.tpl index 1e02d35..0da3ffd 100644 --- a/graphql/model/generate/tpl/operation_gen.tpl +++ b/graphql/model/generate/tpl/operation_gen.tpl @@ -4,7 +4,8 @@ func init() { {{- if not (isInternalType .Name) }} {{- if eq (len .Directives) 0 }} {{- $args := .Args }} - excute.AddResolver("{{ .Name }}", func(ctx *context.Context, args map[string]any) (interface{}, error) { + excute.AddResolver("{{ .Name }}", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) {{- range $index, $arg := $args }} {{- if eq $arg.Type.GetRealType.Kind "SCALAR" }} @@ -36,7 +37,7 @@ func init() { {{- end }} {{- if .Type.IsList }} {{- if .Type.IsObject }} - list, err := {{ .Name | ucFirst }}Resolver(ctx{{ range $index, $arg := $args }}, {{ $arg.Name | lcFirst }}{{ end }}) + list, err := r.{{ .Name | ucFirst }}Resolver(ctx{{ range $index, $arg := $args }}, {{ $arg.Name | lcFirst }}{{ end }}) if list == nil { return nil, err } @@ -50,14 +51,14 @@ func init() { } return res, nil {{- else }} - res, err := {{ .Name | ucFirst }}Resolver(ctx{{ range $index, $arg := $args }}, {{ $arg.Name | lcFirst }}{{ end }}) + res, err := r.{{ .Name | ucFirst }}Resolver(ctx{{ range $index, $arg := $args }}, {{ $arg.Name | lcFirst }}{{ end }}) if res == nil { return nil, err } return res, nil {{- end }} {{- else if .Type.IsObject }} - res, err := {{ .Name | ucFirst }}Resolver(ctx{{ range $index, $arg := $args }}, {{ $arg.Name | lcFirst }}{{ end }}) + res, err := r.{{ .Name | ucFirst }}Resolver(ctx{{ range $index, $arg := $args }}, {{ $arg.Name | lcFirst }}{{ end }}) if res == nil { return nil, err } @@ -67,7 +68,7 @@ func init() { return model.TypeToMap(res) {{- end }} {{- else }} - res, err := {{ .Name | ucFirst }}Resolver(ctx{{ range $index, $arg := $args }}, {{ $arg.Name | lcFirst }}{{ end }}) + res, err := r.{{ .Name | ucFirst }}Resolver(ctx{{ range $index, $arg := $args }}, {{ $arg.Name | lcFirst }}{{ end }}) return res, err {{- end }} }) diff --git a/handler/service.go b/handler/service.go index 7c61dd1..7484dfc 100644 --- a/handler/service.go +++ b/handler/service.go @@ -8,9 +8,11 @@ import ( "github.com/light-speak/lighthouse/graphql" "github.com/light-speak/lighthouse/log" "github.com/light-speak/lighthouse/net" + "github.com/light-speak/lighthouse/resolve" ) -func StartService() { +func StartService(resolver resolve.Resolve) { + resolve.R = resolver err := graphql.LoadSchema() if err != nil { log.Error().Msgf("Failed to load schema: %v", err) diff --git a/resolve/resolve.go b/resolve/resolve.go new file mode 100644 index 0000000..5e0009f --- /dev/null +++ b/resolve/resolve.go @@ -0,0 +1,7 @@ +package resolve + +var R Resolve + +type Resolve interface { + IsResolver() bool +} diff --git a/template/import.go b/template/import.go index 089597f..e8173e9 100644 --- a/template/import.go +++ b/template/import.go @@ -81,6 +81,9 @@ var importRegexMap = map[string]Import{ `auth\.`: { Path: "github.com/light-speak/lighthouse/auth", }, + `resolve\.`: { + Path: "github.com/light-speak/lighthouse/resolve", + }, } // AddImportRegex add a new import regex and path to the importRegexMap From 7a1ab605ee707a4e45709eea064fd9b812cd2cdc Mon Sep 17 00:00:00 2001 From: linty Date: Mon, 4 Nov 2024 19:40:29 +0800 Subject: [PATCH 15/17] feat: jwt auth --- example/user/resolver/mutation.go | 5 ++++- graphql/ast/node.go | 2 +- net/graphql.go | 2 +- net/http.go | 1 + net/middleware/auth.go | 18 +++++++++++++++++- net/middleware/context.go | 15 +++++++++++++++ 6 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 net/middleware/context.go diff --git a/example/user/resolver/mutation.go b/example/user/resolver/mutation.go index 5b0c00a..783f4aa 100644 --- a/example/user/resolver/mutation.go +++ b/example/user/resolver/mutation.go @@ -2,11 +2,13 @@ package resolver import ( + "fmt" "user/models" "github.com/light-speak/lighthouse/auth" "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/log" ) func (r *Resolver) LoginResolver(ctx *context.Context, name string) (*models.LoginResponse, error) { @@ -20,10 +22,11 @@ func (r *Resolver) LoginResolver(ctx *context.Context, name string) (*models.Log if err != nil { return nil, err } + log.Info().Msgf("currentUser: %v", ctx.UserId) return &models.LoginResponse{ User: *user, Token: token, - Authorization: r.Www, + Authorization: fmt.Sprintf("Bearer %s", token), }, nil // Func:Login user code end. Do not remove this comment. } diff --git a/graphql/ast/node.go b/graphql/ast/node.go index 11c245b..039255b 100644 --- a/graphql/ast/node.go +++ b/graphql/ast/node.go @@ -621,7 +621,7 @@ func (t *TypeRef) ValidateValue(v interface{}, isVariable bool) errors.GraphqlEr case KindNonNull: if v == nil { return &errors.GraphQLError{ - Message: "non-null type cannot be null, f", + Message: "non-null type cannot be null", Locations: []*errors.GraphqlLocation{t.GetLocation()}, } } diff --git a/net/graphql.go b/net/graphql.go index 0e38497..664d2ac 100644 --- a/net/graphql.go +++ b/net/graphql.go @@ -51,7 +51,7 @@ func graphQLHandler(w http.ResponseWriter, r *http.Request) { sendErrorResponse(w, "Method not allowed", http.StatusMethodNotAllowed) return } - ctx := context.NewContext(r.Context()) + ctx := r.Context().(*context.Context) data := excute.ExecuteQuery(ctx, request.Query, request.Variables) response := GraphQLResponse{ Data: data, diff --git a/net/http.go b/net/http.go index 3dcc5b2..04d5e2e 100644 --- a/net/http.go +++ b/net/http.go @@ -28,6 +28,7 @@ func setMiddlewares(r *chi.Mux) { r.Use(chiMiddleware.Compress(5)) r.Use(chiMiddleware.Timeout(60 * time.Second)) r.Use(chiMiddleware.Throttle(env.LighthouseConfig.Server.Throttle)) + r.Use(middleware.ContextMiddleware) r.Use(middleware.AuthMiddleware) } diff --git a/net/middleware/auth.go b/net/middleware/auth.go index b2e3185..e052f45 100644 --- a/net/middleware/auth.go +++ b/net/middleware/auth.go @@ -2,14 +2,30 @@ package middleware import ( "net/http" + "strings" + "github.com/light-speak/lighthouse/auth" + "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/env" ) func AuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + token := r.Header.Get("Authorization") + if token == "" { + next.ServeHTTP(w, r) + return + } + token = strings.TrimPrefix(token, "Bearer ") + if env.LighthouseConfig.App.Mode == env.Single { - //TODO: 本地验证 + userId, err := auth.GetUserId(token) + if err != nil { + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } + ctx := r.Context().(*context.Context) + ctx.UserId = userId } else { //TODO: 从router获取 } diff --git a/net/middleware/context.go b/net/middleware/context.go new file mode 100644 index 0000000..2a2f2bf --- /dev/null +++ b/net/middleware/context.go @@ -0,0 +1,15 @@ +package middleware + +import ( + "net/http" + + "github.com/light-speak/lighthouse/context" +) + +func ContextMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := context.NewContext(r.Context()) + r = r.WithContext(ctx) + next.ServeHTTP(w, r) + }) +} From 7aa817343b3b19931faa26074572b6104665abbf Mon Sep 17 00:00:00 2001 From: linty Date: Mon, 4 Nov 2024 19:41:04 +0800 Subject: [PATCH 16/17] feat: format --- example/user/models/enum.go | 102 ++++----- example/user/models/input.go | 49 +++-- example/user/models/interface.go | 15 +- example/user/models/model.go | 76 ++++--- example/user/models/response.go | 19 +- example/user/repo/repo.go | 259 +++++++++++----------- example/user/resolver/operation_gen.go | 288 ++++++++++++------------- example/user/resolver/query.go | 43 ++-- 8 files changed, 425 insertions(+), 426 deletions(-) diff --git a/example/user/models/enum.go b/example/user/models/enum.go index 05b52eb..c1060ee 100644 --- a/example/user/models/enum.go +++ b/example/user/models/enum.go @@ -1,94 +1,94 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models - - - type CommentableType int8 const ( - CommentableTypeARTICLE = iota - CommentableTypePOST + CommentableTypeARTICLE = iota + CommentableTypePOST ) func (e CommentableType) ToString() string { - switch e { - case CommentableTypeARTICLE: - return "ARTICLE" - case CommentableTypePOST: - return "POST" - default: - return "unknown" - } + switch e { + case CommentableTypeARTICLE: + return "ARTICLE" + case CommentableTypePOST: + return "POST" + default: + return "unknown" + } } var CommentableTypeMap = map[string]CommentableType{ - "ARTICLE": CommentableTypeARTICLE, - "POST": CommentableTypePOST, + "ARTICLE": CommentableTypeARTICLE, + "POST": CommentableTypePOST, } + type SortOrder int8 const ( - SortOrderASC SortOrder = 1 - SortOrderDESC SortOrder = -1 + SortOrderASC SortOrder = 1 + SortOrderDESC SortOrder = -1 ) func (e SortOrder) ToString() string { - switch e { - case SortOrderASC: - return "ASC" - case SortOrderDESC: - return "DESC" - default: - return "unknown" - } + switch e { + case SortOrderASC: + return "ASC" + case SortOrderDESC: + return "DESC" + default: + return "unknown" + } } var SortOrderMap = map[string]SortOrder{ - "ASC": SortOrderASC, - "DESC": SortOrderDESC, + "ASC": SortOrderASC, + "DESC": SortOrderDESC, } + type TestEnum int8 const ( - TestEnumA TestEnum = 1 - TestEnumB TestEnum = 2 + TestEnumA TestEnum = 1 + TestEnumB TestEnum = 2 ) func (e TestEnum) ToString() string { - switch e { - case TestEnumA: - return "A" - case TestEnumB: - return "B" - default: - return "unknown" - } + switch e { + case TestEnumA: + return "A" + case TestEnumB: + return "B" + default: + return "unknown" + } } var TestEnumMap = map[string]TestEnum{ - "A": TestEnumA, - "B": TestEnumB, + "A": TestEnumA, + "B": TestEnumB, } + type TestEnum2 int8 const ( - TestEnum2A2 = iota - TestEnum2B2 + TestEnum2A2 = iota + TestEnum2B2 ) func (e TestEnum2) ToString() string { - switch e { - case TestEnum2A2: - return "A2" - case TestEnum2B2: - return "B2" - default: - return "unknown" - } + switch e { + case TestEnum2A2: + return "A2" + case TestEnum2B2: + return "B2" + default: + return "unknown" + } } var TestEnum2Map = map[string]TestEnum2{ - "A2": TestEnum2A2, - "B2": TestEnum2B2, + "A2": TestEnum2A2, + "B2": TestEnum2B2, } diff --git a/example/user/models/input.go b/example/user/models/input.go index c551035..1e300c0 100644 --- a/example/user/models/input.go +++ b/example/user/models/input.go @@ -1,34 +1,33 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "fmt" - +import "fmt" type TestInput struct { - E bool `json:"E"` - Enum TestEnum `json:"Enum"` - Id string `json:"Id"` + E bool `json:"E"` + Enum TestEnum `json:"Enum"` + Id string `json:"Id"` } func MapToTestInput(data map[string]interface{}) (*TestInput, error) { - result := &TestInput{} - - e, ok := data["e"].(bool) - if !ok { - return nil, fmt.Errorf("invalid value for field 'e', got %T", data["e"]) - } - result.E = e - - enum, ok := TestEnumMap[data["enum"].(string)] - if !ok { - return nil, fmt.Errorf("invalid value for field 'enum', got %T", data["enum"]) - } - result.Enum = enum - - id, ok := data["id"].(string) - if !ok { - return nil, fmt.Errorf("invalid value for field 'id', got %T", data["id"]) - } - result.Id = id - return result, nil + result := &TestInput{} + + e, ok := data["e"].(bool) + if !ok { + return nil, fmt.Errorf("invalid value for field 'e', got %T", data["e"]) + } + result.E = e + + enum, ok := TestEnumMap[data["enum"].(string)] + if !ok { + return nil, fmt.Errorf("invalid value for field 'enum', got %T", data["enum"]) + } + result.Enum = enum + + id, ok := data["id"].(string) + if !ok { + return nil, fmt.Errorf("invalid value for field 'id', got %T", data["id"]) + } + result.Id = id + return result, nil } diff --git a/example/user/models/interface.go b/example/user/models/interface.go index 69dcbaf..8a3bdef 100644 --- a/example/user/models/interface.go +++ b/example/user/models/interface.go @@ -1,20 +1,17 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models - - - type HasName interface { - IsHasName() - GetName() string + IsHasName() + GetName() string } type Userable interface { - IsUserable() - GetUser() User - GetUserId() int64 + IsUserable() + GetUser() User + GetUserId() int64 } type Commentable interface { - IsCommentable() + IsCommentable() } diff --git a/example/user/models/model.go b/example/user/models/model.go index 856402e..a04998e 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -1,65 +1,63 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "github.com/light-speak/lighthouse/graphql/model" - +import "github.com/light-speak/lighthouse/graphql/model" type Article struct { - model.Model - Name string `gorm:"type:varchar(255)" json:"name" ` - Content string `json:"content" gorm:"type:varchar(255)" ` + model.Model + Name string `gorm:"type:varchar(255)" json:"name" ` + Content string `json:"content" gorm:"type:varchar(255)" ` } -func (*Article) IsModel() bool { return true } +func (*Article) IsModel() bool { return true } func (*Article) TableName() string { return "articles" } -func (*Article) TypeName() string { return "article" } +func (*Article) TypeName() string { return "article" } type User struct { - model.Model - Name string `json:"name" gorm:"index;type:varchar(255)" ` - MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` + model.Model + Name string `json:"name" gorm:"index;type:varchar(255)" ` + MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` } -func (*User) IsModel() bool { return true } -func (*User) IsHasName() bool { return true } +func (*User) IsModel() bool { return true } +func (*User) IsHasName() bool { return true } func (this *User) GetName() string { return this.Name } -func (*User) TableName() string { return "users" } -func (*User) TypeName() string { return "user" } +func (*User) TableName() string { return "users" } +func (*User) TypeName() string { return "user" } type Post struct { - model.ModelSoftDelete - TagId int64 `json:"tag_id" ` - Enum TestEnum `json:"enum" ` - Title string `json:"title" gorm:"index;type:varchar(255)" ` - Content string `json:"content" gorm:"type:varchar(255)" ` - UserId int64 `json:"user_id" gorm:"index" ` - BackId int64 `json:"back_id" ` - IsBool bool `json:"is_bool" gorm:"default:false" ` - User User `json:"user" ` + model.ModelSoftDelete + TagId int64 `json:"tag_id" ` + Enum TestEnum `json:"enum" ` + Title string `json:"title" gorm:"index;type:varchar(255)" ` + Content string `json:"content" gorm:"type:varchar(255)" ` + UserId int64 `json:"user_id" gorm:"index" ` + BackId int64 `json:"back_id" ` + IsBool bool `json:"is_bool" gorm:"default:false" ` + User User `json:"user" ` } -func (*Post) IsModel() bool { return true } +func (*Post) IsModel() bool { return true } func (*Post) TableName() string { return "posts" } -func (*Post) TypeName() string { return "post" } +func (*Post) TypeName() string { return "post" } type Comment struct { - model.Model - Content string `json:"content" gorm:"type:varchar(255)" ` - CommentableId int64 `json:"commentable_id" gorm:"index:commentable" ` - CommentableType CommentableType `json:"commentable_type" gorm:"index:commentable" ` - Commentable interface{} `json:"commentable" gorm:"-" ` + model.Model + Content string `json:"content" gorm:"type:varchar(255)" ` + CommentableId int64 `json:"commentable_id" gorm:"index:commentable" ` + CommentableType CommentableType `json:"commentable_type" gorm:"index:commentable" ` + Commentable interface{} `json:"commentable" gorm:"-" ` } -func (*Comment) IsModel() bool { return true } +func (*Comment) IsModel() bool { return true } func (*Comment) TableName() string { return "comments" } -func (*Comment) TypeName() string { return "comment" } - +func (*Comment) TypeName() string { return "comment" } func Migrate() error { return model.GetDB().AutoMigrate( - &Article{}, - &User{}, - &Post{}, - &Comment{}, - ) -} \ No newline at end of file + &Article{}, + &User{}, + &Post{}, + &Comment{}, + ) +} diff --git a/example/user/models/response.go b/example/user/models/response.go index c08085d..0f7c377 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -1,25 +1,24 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "github.com/light-speak/lighthouse/graphql/model" - +import "github.com/light-speak/lighthouse/graphql/model" type Test struct { - Test string `json:"test" gorm:"type:varchar(255)" ` + Test string `json:"test" gorm:"type:varchar(255)" ` } type LoginResponse struct { - User User `json:"user" ` - Token string `json:"token" gorm:"type:varchar(255)" ` - Authorization string `json:"authorization" gorm:"type:varchar(255)" ` + User User `json:"user" ` + Token string `json:"token" gorm:"type:varchar(255)" ` + Authorization string `json:"authorization" gorm:"type:varchar(255)" ` } type UserPaginateResponse struct { - PaginateInfo model.PaginateInfo `json:"paginate_info" ` - Data []*User `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` + Data []*User `json:"data" ` } type PostPaginateResponse struct { - Data []*Post `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` + Data []*Post `json:"data" ` + PaginateInfo model.PaginateInfo `json:"paginate_info" ` } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index f794af1..5a47514 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,182 +2,189 @@ package repo import ( - "gorm.io/gorm" - "user/models" - "github.com/light-speak/lighthouse/graphql/ast" - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/graphql/model" + "gorm.io/gorm" + "user/models" ) -func Provide__Article() map[string]*ast.Relation { return map[string]*ast.Relation{"content": {},"created_at": {},"id": {},"name": {},"updated_at": {},}} +func Provide__Article() map[string]*ast.Relation { + return map[string]*ast.Relation{"content": {}, "created_at": {}, "id": {}, "name": {}, "updated_at": {}} +} func Load__Article(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "articles", field).Load(key) + return model.GetLoader[int64](model.GetDB(), "articles", field).Load(key) } func LoadList__Article(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "articles", field).LoadList(key) + return model.GetLoader[int64](model.GetDB(), "articles", field).LoadList(key) } func Query__Article(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Article{}).Scopes(scopes...) + return model.GetDB().Model(&models.Article{}).Scopes(scopes...) } func First__Article(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__Article().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__Article().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } func List__Article(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__Article().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__Article().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil } func Count__Article(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__Article().Scopes(scopes...).Count(&count).Error - return count, err + var count int64 + err := Query__Article().Scopes(scopes...).Count(&count).Error + return count, err +} +func Provide__User() map[string]*ast.Relation { + return map[string]*ast.Relation{"created_at": {}, "id": {}, "myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"}, "name": {}, "updated_at": {}} } -func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"name": {},"updated_at": {},}} func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) + return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) } func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) + return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) } func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.User{}).Scopes(scopes...) + return model.GetDB().Model(&models.User{}).Scopes(scopes...) } func First__User(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__User().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__User().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } func List__User(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__User().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__User().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil } func Count__User(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__User().Scopes(scopes...).Count(&count).Error - return count, err + var count int64 + err := Query__User().Scopes(scopes...).Count(&count).Error + return count, err +} +func Provide__Post() map[string]*ast.Relation { + return map[string]*ast.Relation{"BackId": {}, "IsBool": {}, "content": {}, "created_at": {}, "deleted_at": {}, "enum": {}, "id": {}, "tagId": {}, "title": {}, "updated_at": {}, "user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"}, "userId": {}} } -func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"IsBool": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} func Load__Post(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) + return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) } func LoadList__Post(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) + return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) } func Query__Post(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Post{}).Scopes(scopes...) + return model.GetDB().Model(&models.Post{}).Scopes(scopes...) } func First__Post(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__Post().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__Post().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } func List__Post(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__Post().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__Post().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil } func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__Post().Scopes(scopes...).Count(&count).Error - return count, err + var count int64 + err := Query__Post().Scopes(scopes...).Count(&count).Error + return count, err +} +func Provide__Comment() map[string]*ast.Relation { + return map[string]*ast.Relation{"commentable": {}, "commentableId": {}, "commentableType": {}, "content": {}, "created_at": {}, "id": {}, "updated_at": {}} } -func Provide__Comment() map[string]*ast.Relation { return map[string]*ast.Relation{"commentable": {},"commentableId": {},"commentableType": {},"content": {},"created_at": {},"id": {},"updated_at": {},}} func Load__Comment(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "comments", field).Load(key) + return model.GetLoader[int64](model.GetDB(), "comments", field).Load(key) } func LoadList__Comment(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "comments", field).LoadList(key) + return model.GetLoader[int64](model.GetDB(), "comments", field).LoadList(key) } func Query__Comment(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Comment{}).Scopes(scopes...) + return model.GetDB().Model(&models.Comment{}).Scopes(scopes...) } func First__Comment(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__Comment().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__Comment().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } func List__Comment(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__Comment().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__Comment().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil } func Count__Comment(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__Comment().Scopes(scopes...).Count(&count).Error - return count, err + var count int64 + err := Query__Comment().Scopes(scopes...).Count(&count).Error + return count, err } - func init() { - model.AddQuickFirst("Article", First__Article) - model.AddQuickList("Article", List__Article) - model.AddQuickLoad("Article", Load__Article) - model.AddQuickLoadList("Article", LoadList__Article) - model.AddQuickCount("Article", Count__Article) - model.AddQuickFirst("User", First__User) - model.AddQuickList("User", List__User) - model.AddQuickLoad("User", Load__User) - model.AddQuickLoadList("User", LoadList__User) - model.AddQuickCount("User", Count__User) - model.AddQuickFirst("Post", First__Post) - model.AddQuickList("Post", List__Post) - model.AddQuickLoad("Post", Load__Post) - model.AddQuickLoadList("Post", LoadList__Post) - model.AddQuickCount("Post", Count__Post) - model.AddQuickFirst("Comment", First__Comment) - model.AddQuickList("Comment", List__Comment) - model.AddQuickLoad("Comment", Load__Comment) - model.AddQuickLoadList("Comment", LoadList__Comment) - model.AddQuickCount("Comment", Count__Comment) + model.AddQuickFirst("Article", First__Article) + model.AddQuickList("Article", List__Article) + model.AddQuickLoad("Article", Load__Article) + model.AddQuickLoadList("Article", LoadList__Article) + model.AddQuickCount("Article", Count__Article) + model.AddQuickFirst("User", First__User) + model.AddQuickList("User", List__User) + model.AddQuickLoad("User", Load__User) + model.AddQuickLoadList("User", LoadList__User) + model.AddQuickCount("User", Count__User) + model.AddQuickFirst("Post", First__Post) + model.AddQuickList("Post", List__Post) + model.AddQuickLoad("Post", Load__Post) + model.AddQuickLoadList("Post", LoadList__Post) + model.AddQuickCount("Post", Count__Post) + model.AddQuickFirst("Comment", First__Comment) + model.AddQuickList("Comment", List__Comment) + model.AddQuickLoad("Comment", Load__Comment) + model.AddQuickLoadList("Comment", LoadList__Comment) + model.AddQuickCount("Comment", Count__Comment) } diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index d0137b9..70516ab 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,151 +2,151 @@ package resolver import ( - "github.com/light-speak/lighthouse/graphql" - "fmt" - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/graphql/excute" - "user/models" - "github.com/light-speak/lighthouse/resolve" - "github.com/light-speak/lighthouse/context" + "fmt" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql" + "github.com/light-speak/lighthouse/graphql/excute" + "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/resolve" + "user/models" ) func init() { - excute.AddResolver("getPost", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - pfuck, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) - if e != nil { - return nil, e - } - fuck, ok := pfuck.(string) - if !ok { - return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) - } - res, err := r.GetPostResolver(ctx, fuck) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("getPostIds", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - res, err := r.GetPostIdsResolver(ctx) - if res == nil { - return nil, err - } - return res, nil - }) - excute.AddResolver("getPosts", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - pfuck, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) - if e != nil { - return nil, e - } - fuck, ok := pfuck.(string) - if !ok { - return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) - } - list, err := r.GetPostsResolver(ctx, fuck) - if list == nil { - return nil, err - } - res := []map[string]interface{}{} - for _, item := range list { - itemMap, err := model.StructToMap(item) - if err != nil { - return nil, err - } - res = append(res, itemMap) - } - return res, nil - }) - excute.AddResolver("testNullableEnum", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - enumValue, ok := models.TestEnumMap[args["enum"].(string)] - if !ok { - return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) - } - enum := &enumValue - res, err := r.TestNullableEnumResolver(ctx, enum) - return res, err - }) - excute.AddResolver("testPostEnum", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - enumValue, ok := models.TestEnumMap[args["enum"].(string)] - if !ok { - return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) - } - enum := &enumValue - res, err := r.TestPostEnumResolver(ctx, enum) - return res, err - }) - excute.AddResolver("testPostId", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - pid, e := graphql.Parser.NodeStore.Scalars["ID"].ScalarType.ParseValue(args["id"], nil) - if e != nil { - return nil, e - } - id, ok := pid.(int64) - if !ok { - return nil, fmt.Errorf("argument: 'id' is not a int64, got %T", args["id"]) - } - res, err := r.TestPostIdResolver(ctx, id) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("testPostInput", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - input, err := models.MapToTestInput(args["input"].(map[string]interface{})) - if err != nil { - return nil, fmt.Errorf("argument: 'input' can not convert to models.TestInput, got %T", args["input"]) - } - res, err := r.TestPostInputResolver(ctx, input) - return res, err - }) - excute.AddResolver("testPostInt", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - pid, e := graphql.Parser.NodeStore.Scalars["Boolean"].ScalarType.ParseValue(args["id"], nil) - if e != nil { - return nil, e - } - id, ok := pid.(bool) - if !ok { - return nil, fmt.Errorf("argument: 'id' is not a bool, got %T", args["id"]) - } - res, err := r.TestPostIntResolver(ctx, id) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - input, err := models.MapToTestInput(args["input"].(map[string]interface{})) - if err != nil { - return nil, fmt.Errorf("argument: 'input' can not convert to models.TestInput, got %T", args["input"]) - } - res, err := r.CreatePostResolver(ctx, input) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("login", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - pname, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) - if e != nil { - return nil, e - } - name, ok := pname.(string) - if !ok { - return nil, fmt.Errorf("argument: 'name' is not a string, got %T", args["name"]) - } - res, err := r.LoginResolver(ctx, name) - if res == nil { - return nil, err - } - return model.TypeToMap(res) - }) + excute.AddResolver("getPost", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pfuck, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) + if e != nil { + return nil, e + } + fuck, ok := pfuck.(string) + if !ok { + return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) + } + res, err := r.GetPostResolver(ctx, fuck) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("getPostIds", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + res, err := r.GetPostIdsResolver(ctx) + if res == nil { + return nil, err + } + return res, nil + }) + excute.AddResolver("getPosts", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pfuck, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) + if e != nil { + return nil, e + } + fuck, ok := pfuck.(string) + if !ok { + return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) + } + list, err := r.GetPostsResolver(ctx, fuck) + if list == nil { + return nil, err + } + res := []map[string]interface{}{} + for _, item := range list { + itemMap, err := model.StructToMap(item) + if err != nil { + return nil, err + } + res = append(res, itemMap) + } + return res, nil + }) + excute.AddResolver("testNullableEnum", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + enumValue, ok := models.TestEnumMap[args["enum"].(string)] + if !ok { + return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) + } + enum := &enumValue + res, err := r.TestNullableEnumResolver(ctx, enum) + return res, err + }) + excute.AddResolver("testPostEnum", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + enumValue, ok := models.TestEnumMap[args["enum"].(string)] + if !ok { + return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) + } + enum := &enumValue + res, err := r.TestPostEnumResolver(ctx, enum) + return res, err + }) + excute.AddResolver("testPostId", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pid, e := graphql.Parser.NodeStore.Scalars["ID"].ScalarType.ParseValue(args["id"], nil) + if e != nil { + return nil, e + } + id, ok := pid.(int64) + if !ok { + return nil, fmt.Errorf("argument: 'id' is not a int64, got %T", args["id"]) + } + res, err := r.TestPostIdResolver(ctx, id) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("testPostInput", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + input, err := models.MapToTestInput(args["input"].(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("argument: 'input' can not convert to models.TestInput, got %T", args["input"]) + } + res, err := r.TestPostInputResolver(ctx, input) + return res, err + }) + excute.AddResolver("testPostInt", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pid, e := graphql.Parser.NodeStore.Scalars["Boolean"].ScalarType.ParseValue(args["id"], nil) + if e != nil { + return nil, e + } + id, ok := pid.(bool) + if !ok { + return nil, fmt.Errorf("argument: 'id' is not a bool, got %T", args["id"]) + } + res, err := r.TestPostIntResolver(ctx, id) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + input, err := models.MapToTestInput(args["input"].(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("argument: 'input' can not convert to models.TestInput, got %T", args["input"]) + } + res, err := r.CreatePostResolver(ctx, input) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("login", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pname, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) + if e != nil { + return nil, e + } + name, ok := pname.(string) + if !ok { + return nil, fmt.Errorf("argument: 'name' is not a string, got %T", args["name"]) + } + res, err := r.LoginResolver(ctx, name) + if res == nil { + return nil, err + } + return model.TypeToMap(res) + }) } diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index 8c50739..7e527b1 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,62 +2,61 @@ package resolver import ( - "github.com/light-speak/lighthouse/context" - "fmt" - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/log" - "user/models" + "fmt" + "github.com/light-speak/lighthouse/context" + "github.com/light-speak/lighthouse/graphql/model" + "github.com/light-speak/lighthouse/log" + "user/models" ) - -func (r *Resolver) GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { +func (r *Resolver) GetPostsResolver(ctx *context.Context, fuck string) ([]*models.Post, error) { // Func:GetPosts user code start. Do not remove this comment. posts := []*models.Post{} db := model.GetDB() db.Find(&posts) return posts, nil - // Func:GetPosts user code end. Do not remove this comment. + // Func:GetPosts user code end. Do not remove this comment. } -func (r *Resolver) TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { +func (r *Resolver) TestPostIntResolver(ctx *context.Context, id bool) (*models.Post, error) { // Func:TestPostInt user code start. Do not remove this comment. return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. + // Func:TestPostInt user code end. Do not remove this comment. } -func (r *Resolver) TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { +func (r *Resolver) TestPostIdResolver(ctx *context.Context, id int64) (*models.Post, error) { // Func:TestPostId user code start. Do not remove this comment. log.Debug().Msgf("id: %d", id) return nil, nil - // Func:TestPostId user code end. Do not remove this comment. + // Func:TestPostId user code end. Do not remove this comment. } -func (r *Resolver) TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { +func (r *Resolver) TestPostInputResolver(ctx *context.Context, input *models.TestInput) (string, error) { // Func:TestPostInput user code start. Do not remove this comment. res := fmt.Sprintf("input: %+v", input) return res, nil - // Func:TestPostInput user code end. Do not remove this comment. + // Func:TestPostInput user code end. Do not remove this comment. } -func (r *Resolver) TestNullableEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { +func (r *Resolver) TestNullableEnumResolver(ctx *context.Context, enum *models.TestEnum) (string, error) { // Func:TestNullableEnum user code start. Do not remove this comment. panic("not implement") - // Func:TestNullableEnum user code end. Do not remove this comment. + // Func:TestNullableEnum user code end. Do not remove this comment. } -func (r *Resolver) TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { +func (r *Resolver) TestPostEnumResolver(ctx *context.Context, enum *models.TestEnum) (string, error) { // Func:TestPostEnum user code start. Do not remove this comment. log.Debug().Msgf("enum: %+v", enum) res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) return res, nil - // Func:TestPostEnum user code end. Do not remove this comment. + // Func:TestPostEnum user code end. Do not remove this comment. } func (r *Resolver) GetPostIdsResolver(ctx *context.Context) ([]int64, error) { // Func:GetPostIds user code start. Do not remove this comment. return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. + // Func:GetPostIds user code end. Do not remove this comment. } -func (r *Resolver) GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { +func (r *Resolver) GetPostResolver(ctx *context.Context, fuck string) (*models.Post, error) { // Func:GetPost user code start. Do not remove this comment. log.Debug().Msg("GetPostResolver") db := model.GetDB() post := &models.Post{} db.Where("id = ?", fuck).First(post) return post, nil - // Func:GetPost user code end. Do not remove this comment. -} \ No newline at end of file + // Func:GetPost user code end. Do not remove this comment. +} From 90c5c517430360007b31ff6816ef5c59b7e05638 Mon Sep 17 00:00:00 2001 From: linty Date: Mon, 4 Nov 2024 22:44:50 +0800 Subject: [PATCH 17/17] fix: fix resolver waitgroup, fix model relation to ptr --- .../initialize/one/tpl/ide-helper.tpl | 5 - example/user/ide-helper.graphql | 6 - example/user/models/enum.go | 102 +++---- example/user/models/input.go | 49 +-- example/user/models/interface.go | 15 +- example/user/models/model.go | 84 ++--- example/user/models/response.go | 23 +- example/user/repo/repo.go | 283 +++++++++-------- example/user/resolver/mutation.go | 2 +- example/user/resolver/operation_gen.go | 288 +++++++++--------- example/user/resolver/query.go | 83 ++--- example/user/schema/comment.graphql | 2 +- example/user/schema/post.graphqls | 2 +- example/user/schema/user.graphql | 2 +- graphql/ast/directive/morphto.go | 36 +++ graphql/ast/funcs.go | 4 + graphql/ast/node.go | 13 +- graphql/excute/resolver.go | 117 +++++-- graphql/parser/directives.go | 75 ++--- 19 files changed, 652 insertions(+), 539 deletions(-) create mode 100644 graphql/ast/directive/morphto.go diff --git a/command/cli/generate/initialize/one/tpl/ide-helper.tpl b/command/cli/generate/initialize/one/tpl/ide-helper.tpl index 4df6015..c7e06a4 100644 --- a/command/cli/generate/initialize/one/tpl/ide-helper.tpl +++ b/command/cli/generate/initialize/one/tpl/ide-helper.tpl @@ -2,11 +2,6 @@ type Query type Mutation type Subscription -directive @external repeatable on FIELD_DEFINITION -directive @requires(fields: [String!]!) repeatable on FIELD_DEFINITION -directive @provides(fields: [String!]!) repeatable on FIELD_DEFINITION -directive @key(fields: [String!]!) repeatable on OBJECT | INTERFACE -directive @extends repeatable on OBJECT directive @paginate(scopes: [String!]) on FIELD_DEFINITION directive @skip(if: Boolean!) on FIELD_DEFINITION diff --git a/example/user/ide-helper.graphql b/example/user/ide-helper.graphql index f95e7b9..3d66e7f 100644 --- a/example/user/ide-helper.graphql +++ b/example/user/ide-helper.graphql @@ -3,12 +3,6 @@ type Query type Mutation type Subscription -directive @external repeatable on FIELD_DEFINITION -directive @requires(fields: [String!]!) repeatable on FIELD_DEFINITION -directive @provides(fields: [String!]!) repeatable on FIELD_DEFINITION -directive @key(fields: [String!]!) repeatable on OBJECT | INTERFACE -directive @extends repeatable on OBJECT - directive @skip(if: Boolean!) on FIELD_DEFINITION directive @include(if: Boolean!) on FIELD_DEFINITION diff --git a/example/user/models/enum.go b/example/user/models/enum.go index c1060ee..05b52eb 100644 --- a/example/user/models/enum.go +++ b/example/user/models/enum.go @@ -1,94 +1,94 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models + + + type CommentableType int8 const ( - CommentableTypeARTICLE = iota - CommentableTypePOST + CommentableTypeARTICLE = iota + CommentableTypePOST ) func (e CommentableType) ToString() string { - switch e { - case CommentableTypeARTICLE: - return "ARTICLE" - case CommentableTypePOST: - return "POST" - default: - return "unknown" - } + switch e { + case CommentableTypeARTICLE: + return "ARTICLE" + case CommentableTypePOST: + return "POST" + default: + return "unknown" + } } var CommentableTypeMap = map[string]CommentableType{ - "ARTICLE": CommentableTypeARTICLE, - "POST": CommentableTypePOST, + "ARTICLE": CommentableTypeARTICLE, + "POST": CommentableTypePOST, } - type SortOrder int8 const ( - SortOrderASC SortOrder = 1 - SortOrderDESC SortOrder = -1 + SortOrderASC SortOrder = 1 + SortOrderDESC SortOrder = -1 ) func (e SortOrder) ToString() string { - switch e { - case SortOrderASC: - return "ASC" - case SortOrderDESC: - return "DESC" - default: - return "unknown" - } + switch e { + case SortOrderASC: + return "ASC" + case SortOrderDESC: + return "DESC" + default: + return "unknown" + } } var SortOrderMap = map[string]SortOrder{ - "ASC": SortOrderASC, - "DESC": SortOrderDESC, + "ASC": SortOrderASC, + "DESC": SortOrderDESC, } - type TestEnum int8 const ( - TestEnumA TestEnum = 1 - TestEnumB TestEnum = 2 + TestEnumA TestEnum = 1 + TestEnumB TestEnum = 2 ) func (e TestEnum) ToString() string { - switch e { - case TestEnumA: - return "A" - case TestEnumB: - return "B" - default: - return "unknown" - } + switch e { + case TestEnumA: + return "A" + case TestEnumB: + return "B" + default: + return "unknown" + } } var TestEnumMap = map[string]TestEnum{ - "A": TestEnumA, - "B": TestEnumB, + "A": TestEnumA, + "B": TestEnumB, } - type TestEnum2 int8 const ( - TestEnum2A2 = iota - TestEnum2B2 + TestEnum2A2 = iota + TestEnum2B2 ) func (e TestEnum2) ToString() string { - switch e { - case TestEnum2A2: - return "A2" - case TestEnum2B2: - return "B2" - default: - return "unknown" - } + switch e { + case TestEnum2A2: + return "A2" + case TestEnum2B2: + return "B2" + default: + return "unknown" + } } var TestEnum2Map = map[string]TestEnum2{ - "A2": TestEnum2A2, - "B2": TestEnum2B2, + "A2": TestEnum2A2, + "B2": TestEnum2B2, } diff --git a/example/user/models/input.go b/example/user/models/input.go index 1e300c0..c551035 100644 --- a/example/user/models/input.go +++ b/example/user/models/input.go @@ -1,33 +1,34 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "fmt" +import "fmt" + type TestInput struct { - E bool `json:"E"` - Enum TestEnum `json:"Enum"` - Id string `json:"Id"` + E bool `json:"E"` + Enum TestEnum `json:"Enum"` + Id string `json:"Id"` } func MapToTestInput(data map[string]interface{}) (*TestInput, error) { - result := &TestInput{} - - e, ok := data["e"].(bool) - if !ok { - return nil, fmt.Errorf("invalid value for field 'e', got %T", data["e"]) - } - result.E = e - - enum, ok := TestEnumMap[data["enum"].(string)] - if !ok { - return nil, fmt.Errorf("invalid value for field 'enum', got %T", data["enum"]) - } - result.Enum = enum - - id, ok := data["id"].(string) - if !ok { - return nil, fmt.Errorf("invalid value for field 'id', got %T", data["id"]) - } - result.Id = id - return result, nil + result := &TestInput{} + + e, ok := data["e"].(bool) + if !ok { + return nil, fmt.Errorf("invalid value for field 'e', got %T", data["e"]) + } + result.E = e + + enum, ok := TestEnumMap[data["enum"].(string)] + if !ok { + return nil, fmt.Errorf("invalid value for field 'enum', got %T", data["enum"]) + } + result.Enum = enum + + id, ok := data["id"].(string) + if !ok { + return nil, fmt.Errorf("invalid value for field 'id', got %T", data["id"]) + } + result.Id = id + return result, nil } diff --git a/example/user/models/interface.go b/example/user/models/interface.go index 8a3bdef..69dcbaf 100644 --- a/example/user/models/interface.go +++ b/example/user/models/interface.go @@ -1,17 +1,20 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models + + + type HasName interface { - IsHasName() - GetName() string + IsHasName() + GetName() string } type Userable interface { - IsUserable() - GetUser() User - GetUserId() int64 + IsUserable() + GetUser() User + GetUserId() int64 } type Commentable interface { - IsCommentable() + IsCommentable() } diff --git a/example/user/models/model.go b/example/user/models/model.go index a04998e..371959c 100644 --- a/example/user/models/model.go +++ b/example/user/models/model.go @@ -1,63 +1,65 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "github.com/light-speak/lighthouse/graphql/model" +import "github.com/light-speak/lighthouse/graphql/model" -type Article struct { - model.Model - Name string `gorm:"type:varchar(255)" json:"name" ` - Content string `json:"content" gorm:"type:varchar(255)" ` -} - -func (*Article) IsModel() bool { return true } -func (*Article) TableName() string { return "articles" } -func (*Article) TypeName() string { return "article" } type User struct { - model.Model - Name string `json:"name" gorm:"index;type:varchar(255)" ` - MyPosts []Post `json:"my_posts" gorm:"comment:五二零" ` + model.Model + Name string `json:"name" gorm:"index;type:varchar(255)" ` + MyPosts *[]Post `json:"my_posts" gorm:"comment:五二零" ` } -func (*User) IsModel() bool { return true } -func (*User) IsHasName() bool { return true } +func (*User) IsModel() bool { return true } +func (*User) IsHasName() bool { return true } func (this *User) GetName() string { return this.Name } -func (*User) TableName() string { return "users" } -func (*User) TypeName() string { return "user" } +func (*User) TableName() string { return "users" } +func (*User) TypeName() string { return "user" } type Post struct { - model.ModelSoftDelete - TagId int64 `json:"tag_id" ` - Enum TestEnum `json:"enum" ` - Title string `json:"title" gorm:"index;type:varchar(255)" ` - Content string `json:"content" gorm:"type:varchar(255)" ` - UserId int64 `json:"user_id" gorm:"index" ` - BackId int64 `json:"back_id" ` - IsBool bool `json:"is_bool" gorm:"default:false" ` - User User `json:"user" ` + model.ModelSoftDelete + Title string `json:"title" gorm:"index;type:varchar(255)" ` + Content string `json:"content" gorm:"type:varchar(255)" ` + TagId int64 `json:"tag_id" ` + BackId int64 `json:"back_id" ` + Enum TestEnum `json:"enum" ` + UserId int64 `json:"user_id" gorm:"index" ` + IsBool bool `json:"is_bool" gorm:"default:false" ` + User *User `json:"user" ` } -func (*Post) IsModel() bool { return true } +func (*Post) IsModel() bool { return true } func (*Post) TableName() string { return "posts" } -func (*Post) TypeName() string { return "post" } +func (*Post) TypeName() string { return "post" } type Comment struct { - model.Model - Content string `json:"content" gorm:"type:varchar(255)" ` - CommentableId int64 `json:"commentable_id" gorm:"index:commentable" ` - CommentableType CommentableType `json:"commentable_type" gorm:"index:commentable" ` - Commentable interface{} `json:"commentable" gorm:"-" ` + model.Model + Content string `json:"content" gorm:"type:varchar(255)" ` + CommentableId int64 `json:"commentable_id" gorm:"index:commentable" ` + CommentableType CommentableType `gorm:"index:commentable" json:"commentable_type" ` + Commentable interface{} `gorm:"-" json:"commentable" ` } -func (*Comment) IsModel() bool { return true } +func (*Comment) IsModel() bool { return true } func (*Comment) TableName() string { return "comments" } -func (*Comment) TypeName() string { return "comment" } +func (*Comment) TypeName() string { return "comment" } + +type Article struct { + model.Model + Name string `json:"name" gorm:"type:varchar(255)" ` + Content string `json:"content" gorm:"type:varchar(255)" ` +} + +func (*Article) IsModel() bool { return true } +func (*Article) TableName() string { return "articles" } +func (*Article) TypeName() string { return "article" } + func Migrate() error { return model.GetDB().AutoMigrate( - &Article{}, - &User{}, - &Post{}, - &Comment{}, - ) -} + &User{}, + &Post{}, + &Comment{}, + &Article{}, + ) +} \ No newline at end of file diff --git a/example/user/models/response.go b/example/user/models/response.go index 0f7c377..ffa2547 100644 --- a/example/user/models/response.go +++ b/example/user/models/response.go @@ -1,24 +1,25 @@ // Code generated by github.com/light-speak/lighthouse, DO NOT EDIT. package models -import "github.com/light-speak/lighthouse/graphql/model" +import "github.com/light-speak/lighthouse/graphql/model" -type Test struct { - Test string `json:"test" gorm:"type:varchar(255)" ` + +type UserPaginateResponse struct { + Data *[]*User `json:"data" ` + PaginateInfo *model.PaginateInfo `json:"paginate_info" ` } type LoginResponse struct { - User User `json:"user" ` - Token string `json:"token" gorm:"type:varchar(255)" ` - Authorization string `json:"authorization" gorm:"type:varchar(255)" ` + Token string `json:"token" gorm:"type:varchar(255)" ` + Authorization string `json:"authorization" gorm:"type:varchar(255)" ` + User *User `json:"user" ` } -type UserPaginateResponse struct { - PaginateInfo model.PaginateInfo `json:"paginate_info" ` - Data []*User `json:"data" ` +type Test struct { + Test string `json:"test" gorm:"type:varchar(255)" ` } type PostPaginateResponse struct { - Data []*Post `json:"data" ` - PaginateInfo model.PaginateInfo `json:"paginate_info" ` + Data *[]*Post `json:"data" ` + PaginateInfo *model.PaginateInfo `json:"paginate_info" ` } diff --git a/example/user/repo/repo.go b/example/user/repo/repo.go index 5a47514..493604f 100644 --- a/example/user/repo/repo.go +++ b/example/user/repo/repo.go @@ -2,189 +2,182 @@ package repo import ( - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/ast" - "github.com/light-speak/lighthouse/graphql/model" - "gorm.io/gorm" - "user/models" + "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/context" + "gorm.io/gorm" + "user/models" + "github.com/light-speak/lighthouse/graphql/model" ) -func Provide__Article() map[string]*ast.Relation { - return map[string]*ast.Relation{"content": {}, "created_at": {}, "id": {}, "name": {}, "updated_at": {}} -} -func Load__Article(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "articles", field).Load(key) -} -func LoadList__Article(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "articles", field).LoadList(key) -} -func Query__Article(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Article{}).Scopes(scopes...) -} -func First__Article(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__Article().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil -} -func List__Article(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__Article().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil -} -func Count__Article(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__Article().Scopes(scopes...).Count(&count).Error - return count, err -} -func Provide__User() map[string]*ast.Relation { - return map[string]*ast.Relation{"created_at": {}, "id": {}, "myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"}, "name": {}, "updated_at": {}} -} +func Provide__User() map[string]*ast.Relation { return map[string]*ast.Relation{"created_at": {},"id": {},"myPosts": {Name: "post", RelationType: ast.RelationTypeHasMany, ForeignKey: "user_id", Reference: "id"},"name": {},"updated_at": {},}} func Load__User(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) + return model.GetLoader[int64](model.GetDB(), "users", field).Load(key) } func LoadList__User(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) + return model.GetLoader[int64](model.GetDB(), "users", field).LoadList(key) } func Query__User(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.User{}).Scopes(scopes...) + return model.GetDB().Model(&models.User{}).Scopes(scopes...) } func First__User(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__User().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__User().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } func List__User(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__User().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__User().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil } func Count__User(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__User().Scopes(scopes...).Count(&count).Error - return count, err -} -func Provide__Post() map[string]*ast.Relation { - return map[string]*ast.Relation{"BackId": {}, "IsBool": {}, "content": {}, "created_at": {}, "deleted_at": {}, "enum": {}, "id": {}, "tagId": {}, "title": {}, "updated_at": {}, "user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"}, "userId": {}} + var count int64 + err := Query__User().Scopes(scopes...).Count(&count).Error + return count, err } +func Provide__Post() map[string]*ast.Relation { return map[string]*ast.Relation{"BackId": {},"IsBool": {},"content": {},"created_at": {},"deleted_at": {},"enum": {},"id": {},"tagId": {},"title": {},"updated_at": {},"user": {Name: "user", RelationType: ast.RelationTypeBelongsTo, ForeignKey: "user_id", Reference: "id"},"userId": {},}} func Load__Post(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) + return model.GetLoader[int64](model.GetDB(), "posts", field).Load(key) } func LoadList__Post(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) + return model.GetLoader[int64](model.GetDB(), "posts", field).LoadList(key) } func Query__Post(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Post{}).Scopes(scopes...) + return model.GetDB().Model(&models.Post{}).Scopes(scopes...) } func First__Post(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__Post().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__Post().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } func List__Post(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__Post().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__Post().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil } func Count__Post(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__Post().Scopes(scopes...).Count(&count).Error - return count, err -} -func Provide__Comment() map[string]*ast.Relation { - return map[string]*ast.Relation{"commentable": {}, "commentableId": {}, "commentableType": {}, "content": {}, "created_at": {}, "id": {}, "updated_at": {}} + var count int64 + err := Query__Post().Scopes(scopes...).Count(&count).Error + return count, err } +func Provide__Comment() map[string]*ast.Relation { return map[string]*ast.Relation{"commentable": {Name: "", RelationType: ast.RelationTypeMorphTo, ForeignKey: "", Reference: "id"},"commentableId": {},"commentableType": {},"content": {},"created_at": {},"id": {},"updated_at": {},}} func Load__Comment(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "comments", field).Load(key) + return model.GetLoader[int64](model.GetDB(), "comments", field).Load(key) } func LoadList__Comment(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { - return model.GetLoader[int64](model.GetDB(), "comments", field).LoadList(key) + return model.GetLoader[int64](model.GetDB(), "comments", field).LoadList(key) } func Query__Comment(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { - return model.GetDB().Model(&models.Comment{}).Scopes(scopes...) + return model.GetDB().Model(&models.Comment{}).Scopes(scopes...) } func First__Comment(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { - var err error - if data == nil { - data = make(map[string]interface{}) - err = Query__Comment().Scopes(scopes...).First(data).Error - if err != nil { - return nil, err - } - } - return data, nil + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__Comment().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil } func List__Comment(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { - var err error - if datas == nil { - datas = make([]map[string]interface{}, 0) - err = Query__Comment().Scopes(scopes...).Find(&datas).Error - if err != nil { - return nil, err - } - } - return datas, nil + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__Comment().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil } func Count__Comment(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { - var count int64 - err := Query__Comment().Scopes(scopes...).Count(&count).Error - return count, err + var count int64 + err := Query__Comment().Scopes(scopes...).Count(&count).Error + return count, err +} +func Provide__Article() map[string]*ast.Relation { return map[string]*ast.Relation{"content": {},"created_at": {},"id": {},"name": {},"updated_at": {},}} +func Load__Article(ctx *context.Context, key int64, field string) (map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "articles", field).Load(key) +} +func LoadList__Article(ctx *context.Context, key int64, field string) ([]map[string]interface{}, error) { + return model.GetLoader[int64](model.GetDB(), "articles", field).LoadList(key) +} +func Query__Article(scopes ...func(db *gorm.DB) *gorm.DB) *gorm.DB { + return model.GetDB().Model(&models.Article{}).Scopes(scopes...) } +func First__Article(ctx *context.Context, data map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) (map[string]interface{}, error) { + var err error + if data == nil { + data = make(map[string]interface{}) + err = Query__Article().Scopes(scopes...).First(data).Error + if err != nil { + return nil, err + } + } + return data, nil +} +func List__Article(ctx *context.Context, datas []map[string]interface{}, scopes ...func(db *gorm.DB) *gorm.DB) ([]map[string]interface{}, error) { + var err error + if datas == nil { + datas = make([]map[string]interface{}, 0) + err = Query__Article().Scopes(scopes...).Find(&datas).Error + if err != nil { + return nil, err + } + } + return datas, nil +} +func Count__Article(scopes ...func(db *gorm.DB) *gorm.DB) (int64, error) { + var count int64 + err := Query__Article().Scopes(scopes...).Count(&count).Error + return count, err +} + func init() { - model.AddQuickFirst("Article", First__Article) - model.AddQuickList("Article", List__Article) - model.AddQuickLoad("Article", Load__Article) - model.AddQuickLoadList("Article", LoadList__Article) - model.AddQuickCount("Article", Count__Article) - model.AddQuickFirst("User", First__User) - model.AddQuickList("User", List__User) - model.AddQuickLoad("User", Load__User) - model.AddQuickLoadList("User", LoadList__User) - model.AddQuickCount("User", Count__User) - model.AddQuickFirst("Post", First__Post) - model.AddQuickList("Post", List__Post) - model.AddQuickLoad("Post", Load__Post) - model.AddQuickLoadList("Post", LoadList__Post) - model.AddQuickCount("Post", Count__Post) - model.AddQuickFirst("Comment", First__Comment) - model.AddQuickList("Comment", List__Comment) - model.AddQuickLoad("Comment", Load__Comment) - model.AddQuickLoadList("Comment", LoadList__Comment) - model.AddQuickCount("Comment", Count__Comment) + model.AddQuickFirst("User", First__User) + model.AddQuickList("User", List__User) + model.AddQuickLoad("User", Load__User) + model.AddQuickLoadList("User", LoadList__User) + model.AddQuickCount("User", Count__User) + model.AddQuickFirst("Post", First__Post) + model.AddQuickList("Post", List__Post) + model.AddQuickLoad("Post", Load__Post) + model.AddQuickLoadList("Post", LoadList__Post) + model.AddQuickCount("Post", Count__Post) + model.AddQuickFirst("Comment", First__Comment) + model.AddQuickList("Comment", List__Comment) + model.AddQuickLoad("Comment", Load__Comment) + model.AddQuickLoadList("Comment", LoadList__Comment) + model.AddQuickCount("Comment", Count__Comment) + model.AddQuickFirst("Article", First__Article) + model.AddQuickList("Article", List__Article) + model.AddQuickLoad("Article", Load__Article) + model.AddQuickLoadList("Article", LoadList__Article) + model.AddQuickCount("Article", Count__Article) } diff --git a/example/user/resolver/mutation.go b/example/user/resolver/mutation.go index 783f4aa..8aebd28 100644 --- a/example/user/resolver/mutation.go +++ b/example/user/resolver/mutation.go @@ -24,7 +24,7 @@ func (r *Resolver) LoginResolver(ctx *context.Context, name string) (*models.Log } log.Info().Msgf("currentUser: %v", ctx.UserId) return &models.LoginResponse{ - User: *user, + User: user, Token: token, Authorization: fmt.Sprintf("Bearer %s", token), }, nil diff --git a/example/user/resolver/operation_gen.go b/example/user/resolver/operation_gen.go index 70516ab..bcee27d 100644 --- a/example/user/resolver/operation_gen.go +++ b/example/user/resolver/operation_gen.go @@ -2,151 +2,151 @@ package resolver import ( - "fmt" - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql" - "github.com/light-speak/lighthouse/graphql/excute" - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/resolve" - "user/models" + "github.com/light-speak/lighthouse/context" + "user/models" + "github.com/light-speak/lighthouse/graphql/model" + "fmt" + "github.com/light-speak/lighthouse/graphql" + "github.com/light-speak/lighthouse/resolve" + "github.com/light-speak/lighthouse/graphql/excute" ) func init() { - excute.AddResolver("getPost", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - pfuck, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) - if e != nil { - return nil, e - } - fuck, ok := pfuck.(string) - if !ok { - return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) - } - res, err := r.GetPostResolver(ctx, fuck) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("getPostIds", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - res, err := r.GetPostIdsResolver(ctx) - if res == nil { - return nil, err - } - return res, nil - }) - excute.AddResolver("getPosts", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - pfuck, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) - if e != nil { - return nil, e - } - fuck, ok := pfuck.(string) - if !ok { - return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) - } - list, err := r.GetPostsResolver(ctx, fuck) - if list == nil { - return nil, err - } - res := []map[string]interface{}{} - for _, item := range list { - itemMap, err := model.StructToMap(item) - if err != nil { - return nil, err - } - res = append(res, itemMap) - } - return res, nil - }) - excute.AddResolver("testNullableEnum", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - enumValue, ok := models.TestEnumMap[args["enum"].(string)] - if !ok { - return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) - } - enum := &enumValue - res, err := r.TestNullableEnumResolver(ctx, enum) - return res, err - }) - excute.AddResolver("testPostEnum", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - enumValue, ok := models.TestEnumMap[args["enum"].(string)] - if !ok { - return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) - } - enum := &enumValue - res, err := r.TestPostEnumResolver(ctx, enum) - return res, err - }) - excute.AddResolver("testPostId", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - pid, e := graphql.Parser.NodeStore.Scalars["ID"].ScalarType.ParseValue(args["id"], nil) - if e != nil { - return nil, e - } - id, ok := pid.(int64) - if !ok { - return nil, fmt.Errorf("argument: 'id' is not a int64, got %T", args["id"]) - } - res, err := r.TestPostIdResolver(ctx, id) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("testPostInput", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - input, err := models.MapToTestInput(args["input"].(map[string]interface{})) - if err != nil { - return nil, fmt.Errorf("argument: 'input' can not convert to models.TestInput, got %T", args["input"]) - } - res, err := r.TestPostInputResolver(ctx, input) - return res, err - }) - excute.AddResolver("testPostInt", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - pid, e := graphql.Parser.NodeStore.Scalars["Boolean"].ScalarType.ParseValue(args["id"], nil) - if e != nil { - return nil, e - } - id, ok := pid.(bool) - if !ok { - return nil, fmt.Errorf("argument: 'id' is not a bool, got %T", args["id"]) - } - res, err := r.TestPostIntResolver(ctx, id) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - input, err := models.MapToTestInput(args["input"].(map[string]interface{})) - if err != nil { - return nil, fmt.Errorf("argument: 'input' can not convert to models.TestInput, got %T", args["input"]) - } - res, err := r.CreatePostResolver(ctx, input) - if res == nil { - return nil, err - } - return model.StructToMap(res) - }) - excute.AddResolver("login", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { - r := resolve.(*Resolver) - pname, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) - if e != nil { - return nil, e - } - name, ok := pname.(string) - if !ok { - return nil, fmt.Errorf("argument: 'name' is not a string, got %T", args["name"]) - } - res, err := r.LoginResolver(ctx, name) - if res == nil { - return nil, err - } - return model.TypeToMap(res) - }) + excute.AddResolver("getPost", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pfuck, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) + if e != nil { + return nil, e + } + fuck, ok := pfuck.(string) + if !ok { + return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) + } + res, err := r.GetPostResolver(ctx, fuck) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("getPostIds", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + res, err := r.GetPostIdsResolver(ctx) + if res == nil { + return nil, err + } + return res, nil + }) + excute.AddResolver("getPosts", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pfuck, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["fuck"], nil) + if e != nil { + return nil, e + } + fuck, ok := pfuck.(string) + if !ok { + return nil, fmt.Errorf("argument: 'fuck' is not a string, got %T", args["fuck"]) + } + list, err := r.GetPostsResolver(ctx, fuck) + if list == nil { + return nil, err + } + res := []map[string]interface{}{} + for _, item := range list { + itemMap, err := model.StructToMap(item) + if err != nil { + return nil, err + } + res = append(res, itemMap) + } + return res, nil + }) + excute.AddResolver("testNullableEnum", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + enumValue, ok := models.TestEnumMap[args["enum"].(string)] + if !ok { + return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) + } + enum := &enumValue + res, err := r.TestNullableEnumResolver(ctx, enum) + return res, err + }) + excute.AddResolver("testPostEnum", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + enumValue, ok := models.TestEnumMap[args["enum"].(string)] + if !ok { + return nil, fmt.Errorf("argument: 'enum' is not a models.TestEnum, got %T", args["enum"]) + } + enum := &enumValue + res, err := r.TestPostEnumResolver(ctx, enum) + return res, err + }) + excute.AddResolver("testPostId", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pid, e := graphql.Parser.NodeStore.Scalars["ID"].ScalarType.ParseValue(args["id"], nil) + if e != nil { + return nil, e + } + id, ok := pid.(int64) + if !ok { + return nil, fmt.Errorf("argument: 'id' is not a int64, got %T", args["id"]) + } + res, err := r.TestPostIdResolver(ctx, id) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("testPostInput", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + input, err := models.MapToTestInput(args["input"].(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("argument: 'input' can not convert to models.TestInput, got %T", args["input"]) + } + res, err := r.TestPostInputResolver(ctx, input) + return res, err + }) + excute.AddResolver("testPostInt", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pid, e := graphql.Parser.NodeStore.Scalars["Boolean"].ScalarType.ParseValue(args["id"], nil) + if e != nil { + return nil, e + } + id, ok := pid.(bool) + if !ok { + return nil, fmt.Errorf("argument: 'id' is not a bool, got %T", args["id"]) + } + res, err := r.TestPostIntResolver(ctx, id) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("createPost", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + input, err := models.MapToTestInput(args["input"].(map[string]interface{})) + if err != nil { + return nil, fmt.Errorf("argument: 'input' can not convert to models.TestInput, got %T", args["input"]) + } + res, err := r.CreatePostResolver(ctx, input) + if res == nil { + return nil, err + } + return model.StructToMap(res) + }) + excute.AddResolver("login", func(ctx *context.Context, args map[string]any, resolve resolve.Resolve) (interface{}, error) { + r := resolve.(*Resolver) + pname, e := graphql.Parser.NodeStore.Scalars["String"].ScalarType.ParseValue(args["name"], nil) + if e != nil { + return nil, e + } + name, ok := pname.(string) + if !ok { + return nil, fmt.Errorf("argument: 'name' is not a string, got %T", args["name"]) + } + res, err := r.LoginResolver(ctx, name) + if res == nil { + return nil, err + } + return model.TypeToMap(res) + }) } diff --git a/example/user/resolver/query.go b/example/user/resolver/query.go index 7e527b1..553b5e0 100644 --- a/example/user/resolver/query.go +++ b/example/user/resolver/query.go @@ -2,61 +2,62 @@ package resolver import ( - "fmt" - "github.com/light-speak/lighthouse/context" - "github.com/light-speak/lighthouse/graphql/model" - "github.com/light-speak/lighthouse/log" - "user/models" + "fmt" + "github.com/light-speak/lighthouse/context" + "user/models" + "github.com/light-speak/lighthouse/log" + "github.com/light-speak/lighthouse/graphql/model" ) -func (r *Resolver) GetPostsResolver(ctx *context.Context, fuck string) ([]*models.Post, error) { - // Func:GetPosts user code start. Do not remove this comment. - posts := []*models.Post{} - db := model.GetDB() - db.Find(&posts) - return posts, nil - // Func:GetPosts user code end. Do not remove this comment. -} -func (r *Resolver) TestPostIntResolver(ctx *context.Context, id bool) (*models.Post, error) { - // Func:TestPostInt user code start. Do not remove this comment. - return nil, nil - // Func:TestPostInt user code end. Do not remove this comment. -} -func (r *Resolver) TestPostIdResolver(ctx *context.Context, id int64) (*models.Post, error) { + +func (r *Resolver) TestPostIdResolver(ctx *context.Context,id int64) (*models.Post, error) { // Func:TestPostId user code start. Do not remove this comment. log.Debug().Msgf("id: %d", id) return nil, nil - // Func:TestPostId user code end. Do not remove this comment. + // Func:TestPostId user code end. Do not remove this comment. } -func (r *Resolver) TestPostInputResolver(ctx *context.Context, input *models.TestInput) (string, error) { - // Func:TestPostInput user code start. Do not remove this comment. - res := fmt.Sprintf("input: %+v", input) - return res, nil - // Func:TestPostInput user code end. Do not remove this comment. +func (r *Resolver) GetPostResolver(ctx *context.Context,fuck string) (*models.Post, error) { + // Func:GetPost user code start. Do not remove this comment. + log.Debug().Msg("GetPostResolver") + db := model.GetDB() + post := &models.Post{} + db.Where("id = ?", fuck).First(post) + return post, nil + // Func:GetPost user code end. Do not remove this comment. } -func (r *Resolver) TestNullableEnumResolver(ctx *context.Context, enum *models.TestEnum) (string, error) { +func (r *Resolver) TestNullableEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { // Func:TestNullableEnum user code start. Do not remove this comment. panic("not implement") - // Func:TestNullableEnum user code end. Do not remove this comment. + // Func:TestNullableEnum user code end. Do not remove this comment. +} +func (r *Resolver) GetPostIdsResolver(ctx *context.Context) ([]int64, error) { + // Func:GetPostIds user code start. Do not remove this comment. + return []int64{1, 2, 3}, nil + // Func:GetPostIds user code end. Do not remove this comment. +} +func (r *Resolver) TestPostIntResolver(ctx *context.Context,id bool) (*models.Post, error) { + // Func:TestPostInt user code start. Do not remove this comment. + return nil, nil + // Func:TestPostInt user code end. Do not remove this comment. } -func (r *Resolver) TestPostEnumResolver(ctx *context.Context, enum *models.TestEnum) (string, error) { +func (r *Resolver) TestPostEnumResolver(ctx *context.Context,enum *models.TestEnum) (string, error) { // Func:TestPostEnum user code start. Do not remove this comment. log.Debug().Msgf("enum: %+v", enum) res := fmt.Sprintf("啥也不是!:%v", *enum == models.TestEnumA) return res, nil - // Func:TestPostEnum user code end. Do not remove this comment. + // Func:TestPostEnum user code end. Do not remove this comment. } -func (r *Resolver) GetPostIdsResolver(ctx *context.Context) ([]int64, error) { - // Func:GetPostIds user code start. Do not remove this comment. - return []int64{1, 2, 3}, nil - // Func:GetPostIds user code end. Do not remove this comment. +func (r *Resolver) TestPostInputResolver(ctx *context.Context,input *models.TestInput) (string, error) { + // Func:TestPostInput user code start. Do not remove this comment. + res := fmt.Sprintf("input: %+v", input) + return res, nil + // Func:TestPostInput user code end. Do not remove this comment. } -func (r *Resolver) GetPostResolver(ctx *context.Context, fuck string) (*models.Post, error) { - // Func:GetPost user code start. Do not remove this comment. - log.Debug().Msg("GetPostResolver") +func (r *Resolver) GetPostsResolver(ctx *context.Context,fuck string) ([]*models.Post, error) { + // Func:GetPosts user code start. Do not remove this comment. + posts := []*models.Post{} db := model.GetDB() - post := &models.Post{} - db.Where("id = ?", fuck).First(post) - return post, nil - // Func:GetPost user code end. Do not remove this comment. -} + db.Find(&posts) + return posts, nil + // Func:GetPosts user code end. Do not remove this comment. +} \ No newline at end of file diff --git a/example/user/schema/comment.graphql b/example/user/schema/comment.graphql index 430d8ba..c17f7e4 100644 --- a/example/user/schema/comment.graphql +++ b/example/user/schema/comment.graphql @@ -2,7 +2,7 @@ type Comment @model { content: String! commentableId: ID! @index(name: "commentable") commentableType: CommentableType! @index(name: "commentable") - commentable: Commentable! + commentable: Commentable! @morphTo } enum CommentableType { diff --git a/example/user/schema/post.graphqls b/example/user/schema/post.graphqls index 063f0e1..a677dda 100644 --- a/example/user/schema/post.graphqls +++ b/example/user/schema/post.graphqls @@ -1,4 +1,4 @@ -type Post @key(fields: "id") @softDeleteModel { +type Post @softDeleteModel { title: String! @index content: String! userId: ID! @index diff --git a/example/user/schema/user.graphql b/example/user/schema/user.graphql index 3f0e6ff..f9c9764 100644 --- a/example/user/schema/user.graphql +++ b/example/user/schema/user.graphql @@ -1,5 +1,5 @@ "test user" -type User implements HasName @key(fields: "id") @model { +type User implements HasName @model { name: String! @index "五二零" myPosts: [Post!]! @hasMany(relation: "post", foreignKey: "user_id") diff --git a/graphql/ast/directive/morphto.go b/graphql/ast/directive/morphto.go new file mode 100644 index 0000000..0e18b37 --- /dev/null +++ b/graphql/ast/directive/morphto.go @@ -0,0 +1,36 @@ +package directive + +import ( + "fmt" + + "github.com/light-speak/lighthouse/errors" + "github.com/light-speak/lighthouse/graphql/ast" + "github.com/light-speak/lighthouse/utils" +) + +func handlerMorphTo(f *ast.Field, d *ast.Directive, store *ast.NodeStore, parent ast.Node) errors.GraphqlErrorInterface { + relation := &ast.Relation{ + RelationType: ast.RelationTypeMorphTo, + } + if morphType := d.GetArg("morphType"); morphType != nil { + relation.MorphType = morphType.Value.(string) + } else { + relation.MorphType = fmt.Sprintf("%s_type", utils.LcFirst(f.Name)) + } + if morphKey := d.GetArg("morphKey"); morphKey != nil { + relation.MorphKey = morphKey.Value.(string) + } else { + relation.MorphKey = fmt.Sprintf("%s_id", utils.LcFirst(f.Name)) + } + if reference := d.GetArg("reference"); reference != nil { + relation.Reference = reference.Value.(string) + } else { + relation.Reference = "id" + } + f.Relation = relation + return nil +} + +func init() { + ast.AddFieldDirective("morphTo", handlerMorphTo) +} diff --git a/graphql/ast/funcs.go b/graphql/ast/funcs.go index 9209509..eed97c5 100644 --- a/graphql/ast/funcs.go +++ b/graphql/ast/funcs.go @@ -40,6 +40,10 @@ func Fields(fields map[string]*Field) string { if goType == "PaginateInfo" { goType = "model.PaginateInfo" } + realType := field.Type.GetRealType() + if realType.Kind != KindScalar && realType.Kind != KindEnum && goType != "interface{}" { + goType = fmt.Sprintf("*%s", goType) + } line := fmt.Sprintf(" %s %s %s", utils.UcFirst(utils.CamelCase(field.Name)), goType, genTag(field)) lines = append(lines, line) diff --git a/graphql/ast/node.go b/graphql/ast/node.go index 039255b..92a4578 100644 --- a/graphql/ast/node.go +++ b/graphql/ast/node.go @@ -320,16 +320,21 @@ type Field struct { type RelationType string const ( - RelationTypeBelongsTo RelationType = "RelationTypeBelongsTo" - RelationTypeHasMany RelationType = "RelationTypeHasMany" - RelationTypeHasOne RelationType = "RelationTypeHasOne" + RelationTypeBelongsTo RelationType = "RelationTypeBelongsTo" + RelationTypeHasMany RelationType = "RelationTypeHasMany" + RelationTypeHasOne RelationType = "RelationTypeHasOne" + RelationTypeMorphTo RelationType = "RelationTypeMorphTo" + RelationTypeMorphMany RelationType = "RelationTypeMorphMany" + RelationTypeBelongsToMany RelationType = "RelationTypeBelongsToMany" ) type Relation struct { + RelationType RelationType `json:"relationType"` Name string `json:"name"` ForeignKey string `json:"foreignKey"` Reference string `json:"reference"` - RelationType RelationType `json:"relationType"` + MorphType string `json:"morphType"` + MorphKey string `json:"morphKey"` } func (f *Field) Validate(store *NodeStore, objectFields map[string]*Field, objectNode Node, location Location, fragments map[string]*Fragment, args map[string]*Argument) errors.GraphqlErrorInterface { diff --git a/graphql/excute/resolver.go b/graphql/excute/resolver.go index 989de38..1d70626 100644 --- a/graphql/excute/resolver.go +++ b/graphql/excute/resolver.go @@ -1,6 +1,8 @@ package excute import ( + "sync" + "github.com/light-speak/lighthouse/context" "github.com/light-speak/lighthouse/errors" "github.com/light-speak/lighthouse/graphql/ast" @@ -51,12 +53,41 @@ func processObjectResult(ctx *context.Context, field *ast.Field, r interface{}) } data := make(map[string]interface{}) + var wg sync.WaitGroup + errChan := make(chan errors.GraphqlErrorInterface, len(field.Children)) + resultChan := make(chan struct { + key string + value interface{} + }, len(field.Children)) + for _, child := range field.Children { - v, err := mergeData(ctx, child, r.(map[string]interface{})) - if err != nil { - return nil, true, err - } - data[child.Name] = v + wg.Add(1) + go func(c *ast.Field) { + defer wg.Done() + v, err := mergeData(ctx, c, r.(map[string]interface{})) + if err != nil { + errChan <- err + return + } + resultChan <- struct { + key string + value interface{} + }{c.Name, v} + }(child) + } + + go func() { + wg.Wait() + close(errChan) + close(resultChan) + }() + + if err := <-errChan; err != nil { + return nil, true, err + } + + for result := range resultChan { + data[result.key] = result.value } return data, true, nil @@ -71,18 +102,34 @@ func processListResult(ctx *context.Context, field *ast.Field, realType *ast.Typ } } - data := make([]map[string]interface{}, 0) - for _, ri := range result { - riData := make(map[string]interface{}) - for _, child := range field.Children { - v, err := mergeData(ctx, child, ri) - if err != nil { - return nil, true, err + data := make([]map[string]interface{}, len(result)) + var wg sync.WaitGroup + errChan := make(chan errors.GraphqlErrorInterface, len(result)) + + for i, ri := range result { + wg.Add(1) + go func(index int, item map[string]interface{}) { + defer wg.Done() + riData := make(map[string]interface{}) + for _, child := range field.Children { + v, err := mergeData(ctx, child, item) + if err != nil { + errChan <- err + return + } + riData[child.Name] = v } - riData[child.Name] = v - } - data = append(data, riData) + data[index] = riData + }(i, ri) } + + wg.Wait() + close(errChan) + + if len(errChan) > 0 { + return nil, true, <-errChan + } + return data, true, nil } @@ -96,12 +143,42 @@ func processSingleResult(ctx *context.Context, field *ast.Field, realType *ast.T } data := make(map[string]interface{}) + var wg sync.WaitGroup + errChan := make(chan errors.GraphqlErrorInterface, len(field.Children)) + resultChan := make(chan struct { + key string + value interface{} + }, len(field.Children)) + for _, child := range field.Children { - v, err := mergeData(ctx, child, result) - if err != nil { - return nil, true, err - } - data[child.Name] = v + wg.Add(1) + go func(c *ast.Field) { + defer wg.Done() + v, err := mergeData(ctx, c, result) + if err != nil { + errChan <- err + return + } + resultChan <- struct { + key string + value interface{} + }{c.Name, v} + }(child) + } + + go func() { + wg.Wait() + close(errChan) + close(resultChan) + }() + + if err := <-errChan; err != nil { + return nil, true, err } + + for result := range resultChan { + data[result.key] = result.value + } + return data, true, nil } diff --git a/graphql/parser/directives.go b/graphql/parser/directives.go index b412a39..5ac99db 100644 --- a/graphql/parser/directives.go +++ b/graphql/parser/directives.go @@ -74,43 +74,6 @@ func (p *Parser) addReservedDirective() { }, }, }) - // external - p.AddDirectiveDefinition(&ast.DirectiveDefinition{ - Name: "external", - Locations: []ast.Location{ast.LocationFieldDefinition}, - }) - // requires - p.AddDirectiveDefinition(&ast.DirectiveDefinition{ - Name: "requires", - Locations: []ast.Location{ast.LocationFieldDefinition}, - }) - // provides - p.AddDirectiveDefinition(&ast.DirectiveDefinition{ - Name: "provides", - Locations: []ast.Location{ast.LocationFieldDefinition}, - }) - // key - p.AddDirectiveDefinition(&ast.DirectiveDefinition{ - Name: "key", - Locations: []ast.Location{ast.LocationObject, ast.LocationInterface}, - Args: map[string]*ast.Argument{ - "fields": { - Name: "fields", - Type: &ast.TypeRef{ - Kind: ast.KindList, - OfType: &ast.TypeRef{ - Kind: ast.KindScalar, - Name: "String", - }, - }, - }, - }, - }) - // extends - p.AddDirectiveDefinition(&ast.DirectiveDefinition{ - Name: "extends", - Locations: []ast.Location{ast.LocationObject}, - }) p.addFilterDirective() p.addReturnDirective() @@ -352,6 +315,44 @@ func (p *Parser) addRelationDirective() { }, }, }) + // hasOne + p.AddDirectiveDefinition(&ast.DirectiveDefinition{ + Name: "hasOne", Description: utils.StrPtr("The field is a relationship with another model."), + Locations: []ast.Location{ast.LocationFieldDefinition}, + Args: map[string]*ast.Argument{ + "relation": { + Name: "relation", + Type: &ast.TypeRef{Kind: ast.KindNonNull, OfType: &ast.TypeRef{Kind: ast.KindScalar, Name: "String"}}, + }, + "foreignKey": { + Name: "foreignKey", + Type: &ast.TypeRef{Kind: ast.KindScalar, Name: "String"}, + }, + "reference": { + Name: "reference", + Type: &ast.TypeRef{Kind: ast.KindScalar, Name: "String"}, + }, + }, + }) + // morphTo + p.AddDirectiveDefinition(&ast.DirectiveDefinition{ + Name: "morphTo", Description: utils.StrPtr("The field is a relationship with another model."), + Locations: []ast.Location{ast.LocationFieldDefinition}, + Args: map[string]*ast.Argument{ + "morphType": { + Name: "morphType", + Type: &ast.TypeRef{Kind: ast.KindScalar, Name: "String"}, + }, + "morphKey": { + Name: "morphKey", + Type: &ast.TypeRef{Kind: ast.KindScalar, Name: "String"}, + }, + "reference": { + Name: "reference", + Type: &ast.TypeRef{Kind: ast.KindScalar, Name: "String"}, + }, + }, + }) } func (p *Parser) addObjectDirective() {