From e0aa12b1267bc80247e465b74fa98a383460af12 Mon Sep 17 00:00:00 2001 From: erius Date: Wed, 26 Jun 2024 04:45:04 +0300 Subject: [PATCH] Separated graphql schema into multiple files and moved them to graph/schema dir, update the gqlgen.yml accordingly Removed unnecesary bindings for ID and Int in gqlgen.yml Minor changes to Makefile docker-clean target Reduced docker-compose postgres healthcheck interval for faster local db connections Added pagination to graphql schema, the paginated queries are WIP Removed unneeded fields from the model structs Added allowComment check when adding comments Switched gorm logger to Info mode App now panics if the specified APP_STORAGE in env doesn't exist Reworked database methods, still barely working and are WIP --- Makefile | 4 +- .../postgres/docker-compose.yml | 2 +- gqlgen.yml | 14 +- graph/generated.go | 1649 ++++++++++++++--- graph/model/data.go | 17 +- graph/model/models_gen.go | 26 + ...ema.resolvers.go => mutation.resolvers.go} | 23 +- graph/query.resolvers.go | 49 + graph/schema.graphqls | 44 - graph/schema/mutation.graphqls | 22 + graph/schema/pagination.graphqls | 25 + graph/schema/query.graphqls | 21 + internal/storage/db/database.go | 97 +- internal/storage/db/postgres.go | 6 +- internal/storage/memory.go | 145 +- internal/storage/storage.go | 38 +- 16 files changed, 1813 insertions(+), 369 deletions(-) rename graph/{schema.resolvers.go => mutation.resolvers.go} (60%) create mode 100644 graph/query.resolvers.go delete mode 100644 graph/schema.graphqls create mode 100644 graph/schema/mutation.graphqls create mode 100644 graph/schema/pagination.graphqls create mode 100644 graph/schema/query.graphqls diff --git a/Makefile b/Makefile index f872a5d..e5c2934 100644 --- a/Makefile +++ b/Makefile @@ -13,4 +13,6 @@ go-generate: clean: docker-clean docker-clean: - docker container rm postgres-ozon-task-1 postgres-postgres-1 && docker volume rm postgres_postgres-data + -docker container rm postgres-ozon-task-1 postgres-postgres-1 standalone-ozon-task-1;\ + docker volume rm postgres_postgres-data;\ + docker network rm postgres_default standalone_defaul\ diff --git a/deployments/docker-compose/postgres/docker-compose.yml b/deployments/docker-compose/postgres/docker-compose.yml index 119a88f..b4fab78 100644 --- a/deployments/docker-compose/postgres/docker-compose.yml +++ b/deployments/docker-compose/postgres/docker-compose.yml @@ -13,7 +13,7 @@ services: env_file: postgres.env healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] - interval: 5s + interval: 1s timeout: 5s retries: 5 volumes: diff --git a/gqlgen.yml b/gqlgen.yml index 7dec225..052d563 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -1,6 +1,6 @@ # Where are all the schema files located? globs are supported eg src/**/*.graphqls schema: - - graph/*.graphqls + - graph/schema/*.graphqls # Where should the generated server code go? exec: @@ -75,14 +75,6 @@ autobind: # your liking models: ID: - model: - - github.com/99designs/gqlgen/graphql.ID - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - - github.com/99designs/gqlgen/graphql.Uint + model: github.com/99designs/gqlgen/graphql.Uint Int: - model: - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 + model: github.com/99designs/gqlgen/graphql.Int diff --git a/graph/generated.go b/graph/generated.go index 864e4da..68193ca 100644 --- a/graph/generated.go +++ b/graph/generated.go @@ -39,7 +39,9 @@ type Config struct { } type ResolverRoot interface { + Comment() CommentResolver Mutation() MutationResolver + Post() PostResolver Query() QueryResolver } @@ -55,36 +57,70 @@ type ComplexityRoot struct { Author func(childComplexity int) int Contents func(childComplexity int) int ID func(childComplexity int) int - Post func(childComplexity int) int - Replies func(childComplexity int) int - ReplyTo func(childComplexity int) int + Replies func(childComplexity int, first uint, after uint) int + } + + CommentsConnection struct { + Edges func(childComplexity int) int + PageInfo func(childComplexity int) int + } + + CommentsEdge struct { + Cursor func(childComplexity int) int + Node func(childComplexity int) int } Mutation struct { - AddComment func(childComplexity int, input *model.CommentInput) int - AddPost func(childComplexity int, input *model.PostInput) int + AddComment func(childComplexity int, input model.CommentInput) int + AddPost func(childComplexity int, input model.PostInput) int + } + + PageInfo struct { + EndCursor func(childComplexity int) int + HasNextPage func(childComplexity int) int + StartCursor func(childComplexity int) int } Post struct { AllowComments func(childComplexity int) int Author func(childComplexity int) int - Comments func(childComplexity int) int + Comments func(childComplexity int, first uint, after uint) int Contents func(childComplexity int) int ID func(childComplexity int) int Title func(childComplexity int) int } + PostsConnection struct { + Edges func(childComplexity int) int + PageInfo func(childComplexity int) int + } + + PostsEdge struct { + Cursor func(childComplexity int) int + Node func(childComplexity int) int + } + Query struct { - AllPosts func(childComplexity int) int + Comment func(childComplexity int, id uint) int + Post func(childComplexity int, id uint) int + Posts func(childComplexity int, first uint, after uint) int } } +type CommentResolver interface { + Replies(ctx context.Context, obj *model.Comment, first uint, after uint) (*model.CommentsConnection, error) +} type MutationResolver interface { - AddPost(ctx context.Context, input *model.PostInput) (*model.AddResult, error) - AddComment(ctx context.Context, input *model.CommentInput) (*model.AddResult, error) + AddPost(ctx context.Context, input model.PostInput) (*model.AddResult, error) + AddComment(ctx context.Context, input model.CommentInput) (*model.AddResult, error) +} +type PostResolver interface { + Comments(ctx context.Context, obj *model.Post, first uint, after uint) (*model.CommentsConnection, error) } type QueryResolver interface { - AllPosts(ctx context.Context) ([]*model.Post, error) + Post(ctx context.Context, id uint) (*model.Post, error) + Posts(ctx context.Context, first uint, after uint) (*model.PostsConnection, error) + Comment(ctx context.Context, id uint) (*model.Comment, error) } type executableSchema struct { @@ -134,26 +170,45 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Comment.ID(childComplexity), true - case "Comment.post": - if e.complexity.Comment.Post == nil { - break - } - - return e.complexity.Comment.Post(childComplexity), true - case "Comment.replies": if e.complexity.Comment.Replies == nil { break } - return e.complexity.Comment.Replies(childComplexity), true + args, err := ec.field_Comment_replies_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } - case "Comment.replyTo": - if e.complexity.Comment.ReplyTo == nil { + return e.complexity.Comment.Replies(childComplexity, args["first"].(uint), args["after"].(uint)), true + + case "CommentsConnection.edges": + if e.complexity.CommentsConnection.Edges == nil { break } - return e.complexity.Comment.ReplyTo(childComplexity), true + return e.complexity.CommentsConnection.Edges(childComplexity), true + + case "CommentsConnection.pageInfo": + if e.complexity.CommentsConnection.PageInfo == nil { + break + } + + return e.complexity.CommentsConnection.PageInfo(childComplexity), true + + case "CommentsEdge.cursor": + if e.complexity.CommentsEdge.Cursor == nil { + break + } + + return e.complexity.CommentsEdge.Cursor(childComplexity), true + + case "CommentsEdge.node": + if e.complexity.CommentsEdge.Node == nil { + break + } + + return e.complexity.CommentsEdge.Node(childComplexity), true case "Mutation.addComment": if e.complexity.Mutation.AddComment == nil { @@ -165,7 +220,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Mutation.AddComment(childComplexity, args["input"].(*model.CommentInput)), true + return e.complexity.Mutation.AddComment(childComplexity, args["input"].(model.CommentInput)), true case "Mutation.addPost": if e.complexity.Mutation.AddPost == nil { @@ -177,7 +232,28 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Mutation.AddPost(childComplexity, args["input"].(*model.PostInput)), true + return e.complexity.Mutation.AddPost(childComplexity, args["input"].(model.PostInput)), true + + case "PageInfo.endCursor": + if e.complexity.PageInfo.EndCursor == nil { + break + } + + return e.complexity.PageInfo.EndCursor(childComplexity), true + + case "PageInfo.hasNextPage": + if e.complexity.PageInfo.HasNextPage == nil { + break + } + + return e.complexity.PageInfo.HasNextPage(childComplexity), true + + case "PageInfo.startCursor": + if e.complexity.PageInfo.StartCursor == nil { + break + } + + return e.complexity.PageInfo.StartCursor(childComplexity), true case "Post.allowComments": if e.complexity.Post.AllowComments == nil { @@ -198,7 +274,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in break } - return e.complexity.Post.Comments(childComplexity), true + args, err := ec.field_Post_comments_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Post.Comments(childComplexity, args["first"].(uint), args["after"].(uint)), true case "Post.contents": if e.complexity.Post.Contents == nil { @@ -221,12 +302,69 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Post.Title(childComplexity), true - case "Query.allPosts": - if e.complexity.Query.AllPosts == nil { + case "PostsConnection.edges": + if e.complexity.PostsConnection.Edges == nil { break } - return e.complexity.Query.AllPosts(childComplexity), true + return e.complexity.PostsConnection.Edges(childComplexity), true + + case "PostsConnection.pageInfo": + if e.complexity.PostsConnection.PageInfo == nil { + break + } + + return e.complexity.PostsConnection.PageInfo(childComplexity), true + + case "PostsEdge.cursor": + if e.complexity.PostsEdge.Cursor == nil { + break + } + + return e.complexity.PostsEdge.Cursor(childComplexity), true + + case "PostsEdge.node": + if e.complexity.PostsEdge.Node == nil { + break + } + + return e.complexity.PostsEdge.Node(childComplexity), true + + case "Query.comment": + if e.complexity.Query.Comment == nil { + break + } + + args, err := ec.field_Query_comment_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Comment(childComplexity, args["id"].(uint)), true + + case "Query.post": + if e.complexity.Query.Post == nil { + break + } + + args, err := ec.field_Query_post_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Post(childComplexity, args["id"].(uint)), true + + case "Query.posts": + if e.complexity.Query.Posts == nil { + break + } + + args, err := ec.field_Query_posts_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.Posts(childComplexity, args["first"].(uint), args["after"].(uint)), true } return 0, false @@ -334,7 +472,7 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil } -//go:embed "schema.graphqls" +//go:embed "schema/mutation.graphqls" "schema/pagination.graphqls" "schema/query.graphqls" var sourcesFS embed.FS func sourceData(filename string) string { @@ -346,7 +484,9 @@ func sourceData(filename string) string { } var sources = []*ast.Source{ - {Name: "schema.graphqls", Input: sourceData("schema.graphqls"), BuiltIn: false}, + {Name: "schema/mutation.graphqls", Input: sourceData("schema/mutation.graphqls"), BuiltIn: false}, + {Name: "schema/pagination.graphqls", Input: sourceData("schema/pagination.graphqls"), BuiltIn: false}, + {Name: "schema/query.graphqls", Input: sourceData("schema/query.graphqls"), BuiltIn: false}, } var parsedSchema = gqlparser.MustLoadSchema(sources...) @@ -354,13 +494,37 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...) // region ***************************** args.gotpl ***************************** +func (ec *executionContext) field_Comment_replies_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 uint + if tmp, ok := rawArgs["first"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + arg0, err = ec.unmarshalNID2uint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["first"] = arg0 + var arg1 uint + if tmp, ok := rawArgs["after"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + arg1, err = ec.unmarshalNID2uint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["after"] = arg1 + return args, nil +} + func (ec *executionContext) field_Mutation_addComment_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 *model.CommentInput + var arg0 model.CommentInput if tmp, ok := rawArgs["input"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) - arg0, err = ec.unmarshalOCommentInput2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentInput(ctx, tmp) + arg0, err = ec.unmarshalNCommentInput2gitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentInput(ctx, tmp) if err != nil { return nil, err } @@ -372,10 +536,10 @@ func (ec *executionContext) field_Mutation_addComment_args(ctx context.Context, func (ec *executionContext) field_Mutation_addPost_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 *model.PostInput + var arg0 model.PostInput if tmp, ok := rawArgs["input"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) - arg0, err = ec.unmarshalOPostInput2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostInput(ctx, tmp) + arg0, err = ec.unmarshalNPostInput2gitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostInput(ctx, tmp) if err != nil { return nil, err } @@ -384,6 +548,30 @@ func (ec *executionContext) field_Mutation_addPost_args(ctx context.Context, raw return args, nil } +func (ec *executionContext) field_Post_comments_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 uint + if tmp, ok := rawArgs["first"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + arg0, err = ec.unmarshalNID2uint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["first"] = arg0 + var arg1 uint + if tmp, ok := rawArgs["after"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + arg1, err = ec.unmarshalNID2uint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["after"] = arg1 + return args, nil +} + func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -399,6 +587,60 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs return args, nil } +func (ec *executionContext) field_Query_comment_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 uint + if tmp, ok := rawArgs["id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + arg0, err = ec.unmarshalNID2uint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_post_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 uint + if tmp, ok := rawArgs["id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + arg0, err = ec.unmarshalNID2uint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_posts_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 uint + if tmp, ok := rawArgs["first"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + arg0, err = ec.unmarshalNID2uint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["first"] = arg0 + var arg1 uint + if tmp, ok := rawArgs["after"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + arg1, err = ec.unmarshalNID2uint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["after"] = arg1 + return args, nil +} + func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -525,64 +767,6 @@ func (ec *executionContext) fieldContext_Comment_id(_ context.Context, field gra return fc, nil } -func (ec *executionContext) _Comment_post(ctx context.Context, field graphql.CollectedField, obj *model.Comment) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Comment_post(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Post, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*model.Post) - fc.Result = res - return ec.marshalNPost2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPost(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Comment_post(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Comment", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_Post_id(ctx, field) - case "title": - return ec.fieldContext_Post_title(ctx, field) - case "author": - return ec.fieldContext_Post_author(ctx, field) - case "contents": - return ec.fieldContext_Post_contents(ctx, field) - case "comments": - return ec.fieldContext_Post_comments(ctx, field) - case "allowComments": - return ec.fieldContext_Post_allowComments(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Post", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _Comment_author(ctx context.Context, field graphql.CollectedField, obj *model.Comment) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Comment_author(ctx, field) if err != nil { @@ -671,61 +855,6 @@ func (ec *executionContext) fieldContext_Comment_contents(_ context.Context, fie return fc, nil } -func (ec *executionContext) _Comment_replyTo(ctx context.Context, field graphql.CollectedField, obj *model.Comment) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Comment_replyTo(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.ReplyTo, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.(*model.Comment) - fc.Result = res - return ec.marshalOComment2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐComment(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Comment_replyTo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Comment", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_Comment_id(ctx, field) - case "post": - return ec.fieldContext_Comment_post(ctx, field) - case "author": - return ec.fieldContext_Comment_author(ctx, field) - case "contents": - return ec.fieldContext_Comment_contents(ctx, field) - case "replyTo": - return ec.fieldContext_Comment_replyTo(ctx, field) - case "replies": - return ec.fieldContext_Comment_replies(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Comment", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _Comment_replies(ctx context.Context, field graphql.CollectedField, obj *model.Comment) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Comment_replies(ctx, field) if err != nil { @@ -740,7 +869,7 @@ func (ec *executionContext) _Comment_replies(ctx context.Context, field graphql. }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Replies, nil + return ec.resolvers.Comment().Replies(rctx, obj, fc.Args["first"].(uint), fc.Args["after"].(uint)) }) if err != nil { ec.Error(ctx, err) @@ -752,29 +881,232 @@ func (ec *executionContext) _Comment_replies(ctx context.Context, field graphql. } return graphql.Null } - res := resTmp.([]*model.Comment) + res := resTmp.(*model.CommentsConnection) fc.Result = res - return ec.marshalNComment2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentᚄ(ctx, field.Selections, res) + return ec.marshalNCommentsConnection2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentsConnection(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Comment_replies(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Comment_replies(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Comment", Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "edges": + return ec.fieldContext_CommentsConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_CommentsConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CommentsConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Comment_replies_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _CommentsConnection_edges(ctx context.Context, field graphql.CollectedField, obj *model.CommentsConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CommentsConnection_edges(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Edges, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.CommentsEdge) + fc.Result = res + return ec.marshalNCommentsEdge2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentsEdgeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_CommentsConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CommentsConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "cursor": + return ec.fieldContext_CommentsEdge_cursor(ctx, field) + case "node": + return ec.fieldContext_CommentsEdge_node(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CommentsEdge", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _CommentsConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *model.CommentsConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CommentsConnection_pageInfo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PageInfo, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.PageInfo) + fc.Result = res + return ec.marshalNPageInfo2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPageInfo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_CommentsConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CommentsConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "startCursor": + return ec.fieldContext_PageInfo_startCursor(ctx, field) + case "endCursor": + return ec.fieldContext_PageInfo_endCursor(ctx, field) + case "hasNextPage": + return ec.fieldContext_PageInfo_hasNextPage(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _CommentsEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *model.CommentsEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CommentsEdge_cursor(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Cursor, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(uint) + fc.Result = res + return ec.marshalNID2uint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_CommentsEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CommentsEdge", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _CommentsEdge_node(ctx context.Context, field graphql.CollectedField, obj *model.CommentsEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CommentsEdge_node(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Node, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.Comment) + fc.Result = res + return ec.marshalNComment2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐComment(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_CommentsEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CommentsEdge", + Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "id": return ec.fieldContext_Comment_id(ctx, field) - case "post": - return ec.fieldContext_Comment_post(ctx, field) case "author": return ec.fieldContext_Comment_author(ctx, field) case "contents": return ec.fieldContext_Comment_contents(ctx, field) - case "replyTo": - return ec.fieldContext_Comment_replyTo(ctx, field) case "replies": return ec.fieldContext_Comment_replies(ctx, field) } @@ -798,7 +1130,7 @@ func (ec *executionContext) _Mutation_addPost(ctx context.Context, field graphql }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().AddPost(rctx, fc.Args["input"].(*model.PostInput)) + return ec.resolvers.Mutation().AddPost(rctx, fc.Args["input"].(model.PostInput)) }) if err != nil { ec.Error(ctx, err) @@ -857,7 +1189,7 @@ func (ec *executionContext) _Mutation_addComment(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().AddComment(rctx, fc.Args["input"].(*model.CommentInput)) + return ec.resolvers.Mutation().AddComment(rctx, fc.Args["input"].(model.CommentInput)) }) if err != nil { ec.Error(ctx, err) @@ -902,6 +1234,138 @@ func (ec *executionContext) fieldContext_Mutation_addComment(ctx context.Context return fc, nil } +func (ec *executionContext) _PageInfo_startCursor(ctx context.Context, field graphql.CollectedField, obj *model.PageInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PageInfo_startCursor(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.StartCursor, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(uint) + fc.Result = res + return ec.marshalNID2uint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PageInfo_startCursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PageInfo", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PageInfo_endCursor(ctx context.Context, field graphql.CollectedField, obj *model.PageInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PageInfo_endCursor(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EndCursor, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(uint) + fc.Result = res + return ec.marshalNID2uint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PageInfo_endCursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PageInfo", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PageInfo_hasNextPage(ctx context.Context, field graphql.CollectedField, obj *model.PageInfo) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PageInfo_hasNextPage(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.HasNextPage, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PageInfo_hasNextPage(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PageInfo", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Post_id(ctx context.Context, field graphql.CollectedField, obj *model.Post) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Post_id(ctx, field) if err != nil { @@ -1092,7 +1556,7 @@ func (ec *executionContext) _Post_comments(ctx context.Context, field graphql.Co }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Comments, nil + return ec.resolvers.Post().Comments(rctx, obj, fc.Args["first"].(uint), fc.Args["after"].(uint)) }) if err != nil { ec.Error(ctx, err) @@ -1104,35 +1568,38 @@ func (ec *executionContext) _Post_comments(ctx context.Context, field graphql.Co } return graphql.Null } - res := resTmp.([]*model.Comment) + res := resTmp.(*model.CommentsConnection) fc.Result = res - return ec.marshalNComment2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentᚄ(ctx, field.Selections, res) + return ec.marshalNCommentsConnection2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentsConnection(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Post_comments(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Post_comments(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Post", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "id": - return ec.fieldContext_Comment_id(ctx, field) - case "post": - return ec.fieldContext_Comment_post(ctx, field) - case "author": - return ec.fieldContext_Comment_author(ctx, field) - case "contents": - return ec.fieldContext_Comment_contents(ctx, field) - case "replyTo": - return ec.fieldContext_Comment_replyTo(ctx, field) - case "replies": - return ec.fieldContext_Comment_replies(ctx, field) + case "edges": + return ec.fieldContext_CommentsConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_CommentsConnection_pageInfo(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type Comment", field.Name) + return nil, fmt.Errorf("no field named %q was found under type CommentsConnection", field.Name) }, } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Post_comments_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } return fc, nil } @@ -1180,8 +1647,8 @@ func (ec *executionContext) fieldContext_Post_allowComments(_ context.Context, f return fc, nil } -func (ec *executionContext) _Query_allPosts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_allPosts(ctx, field) +func (ec *executionContext) _PostsConnection_edges(ctx context.Context, field graphql.CollectedField, obj *model.PostsConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PostsConnection_edges(ctx, field) if err != nil { return graphql.Null } @@ -1194,7 +1661,7 @@ func (ec *executionContext) _Query_allPosts(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().AllPosts(rctx) + return obj.Edges, nil }) if err != nil { ec.Error(ctx, err) @@ -1206,12 +1673,216 @@ func (ec *executionContext) _Query_allPosts(ctx context.Context, field graphql.C } return graphql.Null } - res := resTmp.([]*model.Post) + res := resTmp.([]*model.PostsEdge) fc.Result = res - return ec.marshalNPost2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostᚄ(ctx, field.Selections, res) + return ec.marshalNPostsEdge2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostsEdgeᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_allPosts(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostsConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostsConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "cursor": + return ec.fieldContext_PostsEdge_cursor(ctx, field) + case "node": + return ec.fieldContext_PostsEdge_node(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PostsEdge", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _PostsConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *model.PostsConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PostsConnection_pageInfo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PageInfo, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.PageInfo) + fc.Result = res + return ec.marshalNPageInfo2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPageInfo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PostsConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostsConnection", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "startCursor": + return ec.fieldContext_PageInfo_startCursor(ctx, field) + case "endCursor": + return ec.fieldContext_PageInfo_endCursor(ctx, field) + case "hasNextPage": + return ec.fieldContext_PageInfo_hasNextPage(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _PostsEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *model.PostsEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PostsEdge_cursor(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Cursor, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(uint) + fc.Result = res + return ec.marshalNID2uint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PostsEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostsEdge", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PostsEdge_node(ctx context.Context, field graphql.CollectedField, obj *model.PostsEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PostsEdge_node(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Node, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.Post) + fc.Result = res + return ec.marshalNPost2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPost(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PostsEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostsEdge", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Post_id(ctx, field) + case "title": + return ec.fieldContext_Post_title(ctx, field) + case "author": + return ec.fieldContext_Post_author(ctx, field) + case "contents": + return ec.fieldContext_Post_contents(ctx, field) + case "comments": + return ec.fieldContext_Post_comments(ctx, field) + case "allowComments": + return ec.fieldContext_Post_allowComments(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Post", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_post(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_post(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Post(rctx, fc.Args["id"].(uint)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.Post) + fc.Result = res + return ec.marshalNPost2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPost(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_post(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -1235,6 +1906,143 @@ func (ec *executionContext) fieldContext_Query_allPosts(_ context.Context, field return nil, fmt.Errorf("no field named %q was found under type Post", field.Name) }, } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_post_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_posts(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_posts(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Posts(rctx, fc.Args["first"].(uint), fc.Args["after"].(uint)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.PostsConnection) + fc.Result = res + return ec.marshalNPostsConnection2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostsConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_posts(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "edges": + return ec.fieldContext_PostsConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_PostsConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PostsConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_posts_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_comment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_comment(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Comment(rctx, fc.Args["id"].(uint)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.Comment) + fc.Result = res + return ec.marshalNComment2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐComment(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_comment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Comment_id(ctx, field) + case "author": + return ec.fieldContext_Comment_author(ctx, field) + case "contents": + return ec.fieldContext_Comment_contents(ctx, field) + case "replies": + return ec.fieldContext_Comment_replies(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Comment", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_comment_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } return fc, nil } @@ -3301,27 +4109,139 @@ func (ec *executionContext) _Comment(ctx context.Context, sel ast.SelectionSet, case "id": out.Values[i] = ec._Comment_id(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "post": - out.Values[i] = ec._Comment_post(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "author": out.Values[i] = ec._Comment_author(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "contents": out.Values[i] = ec._Comment_contents(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "replies": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Comment_replies(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var commentsConnectionImplementors = []string{"CommentsConnection"} + +func (ec *executionContext) _CommentsConnection(ctx context.Context, sel ast.SelectionSet, obj *model.CommentsConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, commentsConnectionImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CommentsConnection") + case "edges": + out.Values[i] = ec._CommentsConnection_edges(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "replyTo": - out.Values[i] = ec._Comment_replyTo(ctx, field, obj) - case "replies": - out.Values[i] = ec._Comment_replies(ctx, field, obj) + case "pageInfo": + out.Values[i] = ec._CommentsConnection_pageInfo(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var commentsEdgeImplementors = []string{"CommentsEdge"} + +func (ec *executionContext) _CommentsEdge(ctx context.Context, sel ast.SelectionSet, obj *model.CommentsEdge) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, commentsEdgeImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CommentsEdge") + case "cursor": + out.Values[i] = ec._CommentsEdge_cursor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "node": + out.Values[i] = ec._CommentsEdge_node(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -3404,6 +4324,55 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return out } +var pageInfoImplementors = []string{"PageInfo"} + +func (ec *executionContext) _PageInfo(ctx context.Context, sel ast.SelectionSet, obj *model.PageInfo) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, pageInfoImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PageInfo") + case "startCursor": + out.Values[i] = ec._PageInfo_startCursor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "endCursor": + out.Values[i] = ec._PageInfo_endCursor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "hasNextPage": + out.Values[i] = ec._PageInfo_hasNextPage(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var postImplementors = []string{"Post"} func (ec *executionContext) _Post(ctx context.Context, sel ast.SelectionSet, obj *model.Post) graphql.Marshaler { @@ -3418,30 +4387,149 @@ func (ec *executionContext) _Post(ctx context.Context, sel ast.SelectionSet, obj case "id": out.Values[i] = ec._Post_id(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "title": out.Values[i] = ec._Post_title(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "author": out.Values[i] = ec._Post_author(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "contents": out.Values[i] = ec._Post_contents(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "comments": - out.Values[i] = ec._Post_comments(ctx, field, obj) + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Post_comments(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "allowComments": + out.Values[i] = ec._Post_allowComments(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var postsConnectionImplementors = []string{"PostsConnection"} + +func (ec *executionContext) _PostsConnection(ctx context.Context, sel ast.SelectionSet, obj *model.PostsConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postsConnectionImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PostsConnection") + case "edges": + out.Values[i] = ec._PostsConnection_edges(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "allowComments": - out.Values[i] = ec._Post_allowComments(ctx, field, obj) + case "pageInfo": + out.Values[i] = ec._PostsConnection_pageInfo(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var postsEdgeImplementors = []string{"PostsEdge"} + +func (ec *executionContext) _PostsEdge(ctx context.Context, sel ast.SelectionSet, obj *model.PostsEdge) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postsEdgeImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PostsEdge") + case "cursor": + out.Values[i] = ec._PostsEdge_cursor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "node": + out.Values[i] = ec._PostsEdge_node(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -3487,7 +4575,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Query") - case "allPosts": + case "post": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -3496,7 +4584,51 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_allPosts(ctx, field) + res = ec._Query_post(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "posts": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_posts(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "comment": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_comment(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -3895,7 +5027,40 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se return res } -func (ec *executionContext) marshalNComment2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Comment) graphql.Marshaler { +func (ec *executionContext) marshalNComment2gitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐComment(ctx context.Context, sel ast.SelectionSet, v model.Comment) graphql.Marshaler { + return ec._Comment(ctx, sel, &v) +} + +func (ec *executionContext) marshalNComment2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐComment(ctx context.Context, sel ast.SelectionSet, v *model.Comment) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Comment(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNCommentInput2gitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentInput(ctx context.Context, v interface{}) (model.CommentInput, error) { + res, err := ec.unmarshalInputCommentInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNCommentsConnection2gitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentsConnection(ctx context.Context, sel ast.SelectionSet, v model.CommentsConnection) graphql.Marshaler { + return ec._CommentsConnection(ctx, sel, &v) +} + +func (ec *executionContext) marshalNCommentsConnection2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentsConnection(ctx context.Context, sel ast.SelectionSet, v *model.CommentsConnection) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._CommentsConnection(ctx, sel, v) +} + +func (ec *executionContext) marshalNCommentsEdge2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentsEdgeᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.CommentsEdge) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -3919,7 +5084,7 @@ func (ec *executionContext) marshalNComment2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋoz if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNComment2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐComment(ctx, sel, v[i]) + ret[i] = ec.marshalNCommentsEdge2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentsEdge(ctx, sel, v[i]) } if isLen1 { f(i) @@ -3939,14 +5104,14 @@ func (ec *executionContext) marshalNComment2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋoz return ret } -func (ec *executionContext) marshalNComment2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐComment(ctx context.Context, sel ast.SelectionSet, v *model.Comment) graphql.Marshaler { +func (ec *executionContext) marshalNCommentsEdge2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentsEdge(ctx context.Context, sel ast.SelectionSet, v *model.CommentsEdge) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._Comment(ctx, sel, v) + return ec._CommentsEdge(ctx, sel, v) } func (ec *executionContext) unmarshalNID2uint(ctx context.Context, v interface{}) (uint, error) { @@ -3985,7 +5150,50 @@ func (ec *executionContext) marshalNID2ᚖuint(ctx context.Context, sel ast.Sele return res } -func (ec *executionContext) marshalNPost2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Post) graphql.Marshaler { +func (ec *executionContext) marshalNPageInfo2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPageInfo(ctx context.Context, sel ast.SelectionSet, v *model.PageInfo) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PageInfo(ctx, sel, v) +} + +func (ec *executionContext) marshalNPost2gitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPost(ctx context.Context, sel ast.SelectionSet, v model.Post) graphql.Marshaler { + return ec._Post(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPost2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPost(ctx context.Context, sel ast.SelectionSet, v *model.Post) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Post(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNPostInput2gitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostInput(ctx context.Context, v interface{}) (model.PostInput, error) { + res, err := ec.unmarshalInputPostInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNPostsConnection2gitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostsConnection(ctx context.Context, sel ast.SelectionSet, v model.PostsConnection) graphql.Marshaler { + return ec._PostsConnection(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPostsConnection2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostsConnection(ctx context.Context, sel ast.SelectionSet, v *model.PostsConnection) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PostsConnection(ctx, sel, v) +} + +func (ec *executionContext) marshalNPostsEdge2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostsEdgeᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.PostsEdge) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -4009,7 +5217,7 @@ func (ec *executionContext) marshalNPost2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozon if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNPost2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPost(ctx, sel, v[i]) + ret[i] = ec.marshalNPostsEdge2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostsEdge(ctx, sel, v[i]) } if isLen1 { f(i) @@ -4029,14 +5237,14 @@ func (ec *executionContext) marshalNPost2ᚕᚖgitᚗobamnaᚗruᚋeriusᚋozon return ret } -func (ec *executionContext) marshalNPost2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPost(ctx context.Context, sel ast.SelectionSet, v *model.Post) graphql.Marshaler { +func (ec *executionContext) marshalNPostsEdge2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostsEdge(ctx context.Context, sel ast.SelectionSet, v *model.PostsEdge) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { ec.Errorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._Post(ctx, sel, v) + return ec._PostsEdge(ctx, sel, v) } func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { @@ -4333,21 +5541,6 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast return res } -func (ec *executionContext) marshalOComment2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐComment(ctx context.Context, sel ast.SelectionSet, v *model.Comment) graphql.Marshaler { - if v == nil { - return graphql.Null - } - return ec._Comment(ctx, sel, v) -} - -func (ec *executionContext) unmarshalOCommentInput2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐCommentInput(ctx context.Context, v interface{}) (*model.CommentInput, error) { - if v == nil { - return nil, nil - } - res, err := ec.unmarshalInputCommentInput(ctx, v) - return &res, graphql.ErrorOnPath(ctx, err) -} - func (ec *executionContext) unmarshalOID2ᚖuint(ctx context.Context, v interface{}) (*uint, error) { if v == nil { return nil, nil @@ -4364,14 +5557,6 @@ func (ec *executionContext) marshalOID2ᚖuint(ctx context.Context, sel ast.Sele return res } -func (ec *executionContext) unmarshalOPostInput2ᚖgitᚗobamnaᚗruᚋeriusᚋozonᚑtaskᚋgraphᚋmodelᚐPostInput(ctx context.Context, v interface{}) (*model.PostInput, error) { - if v == nil { - return nil, nil - } - res, err := ec.unmarshalInputPostInput(ctx, v) - return &res, graphql.ErrorOnPath(ctx, err) -} - func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) { if v == nil { return nil, nil diff --git a/graph/model/data.go b/graph/model/data.go index f6ca80a..81632d3 100644 --- a/graph/model/data.go +++ b/graph/model/data.go @@ -15,18 +15,10 @@ type Comment struct { UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` - // db foreign key to reference parent post - PostID uint - - Post *Post `json:"post"` - Author string `json:"author"` - Contents string `json:"contents"` - - // db key to reference parent comment - ReplyToID *uint - - ReplyTo *Comment `json:"reply_to,omitempty"` - Replies []*Comment `json:"replies" gorm:"many2many:comment_replies"` + PostID uint `json:"post_id"` + Author string `json:"author"` + Contents string `json:"contents"` + Replies []*Comment `json:"replies" gorm:"many2many:comment_replies"` } type Post struct { @@ -49,6 +41,7 @@ func PostFromInput(input *PostInput) *Post { Title: input.Title, Contents: input.Contents, AllowComments: input.AllowComments, + Comments: make([]*Comment, 0), } } diff --git a/graph/model/models_gen.go b/graph/model/models_gen.go index 475457f..2f0b557 100644 --- a/graph/model/models_gen.go +++ b/graph/model/models_gen.go @@ -2,8 +2,34 @@ package model +type CommentsConnection struct { + Edges []*CommentsEdge `json:"edges"` + PageInfo *PageInfo `json:"pageInfo"` +} + +type CommentsEdge struct { + Cursor uint `json:"cursor"` + Node *Comment `json:"node"` +} + type Mutation struct { } +type PageInfo struct { + StartCursor uint `json:"startCursor"` + EndCursor uint `json:"endCursor"` + HasNextPage bool `json:"hasNextPage"` +} + +type PostsConnection struct { + Edges []*PostsEdge `json:"edges"` + PageInfo *PageInfo `json:"pageInfo"` +} + +type PostsEdge struct { + Cursor uint `json:"cursor"` + Node *Post `json:"node"` +} + type Query struct { } diff --git a/graph/schema.resolvers.go b/graph/mutation.resolvers.go similarity index 60% rename from graph/schema.resolvers.go rename to graph/mutation.resolvers.go index 9088087..b3dfec7 100644 --- a/graph/schema.resolvers.go +++ b/graph/mutation.resolvers.go @@ -12,31 +12,26 @@ import ( ) // AddPost is the resolver for the add_post field. -func (r *mutationResolver) AddPost(ctx context.Context, input *model.PostInput) (*model.AddResult, error) { - return r.Storage.AddPost(input) +func (r *mutationResolver) AddPost(ctx context.Context, input model.PostInput) (*model.AddResult, error) { + return r.Storage.AddPost(&input) } // AddComment is the resolver for the add_comment field. -func (r *mutationResolver) AddComment(ctx context.Context, input *model.CommentInput) (*model.AddResult, error) { +func (r *mutationResolver) AddComment(ctx context.Context, input model.CommentInput) (*model.AddResult, error) { if len(input.Contents) > model.CommentLengthLimit { return nil, fmt.Errorf("exceeded max comment length of %d chars", model.CommentLengthLimit) } - if input.ParentCommentID == nil && input.ParentPostID == nil { + switch { + case input.ParentPostID != nil: + return r.Storage.AddCommentToPost(&input) + case input.ParentCommentID != nil: + return r.Storage.AddReplyToComment(&input) + default: return nil, fmt.Errorf("parent post or parent comment ids weren't specified") } - return r.Storage.AddComment(input) -} - -// AllPosts is the resolver for the all_posts field. -func (r *queryResolver) AllPosts(ctx context.Context) ([]*model.Post, error) { - return r.Storage.GetPosts() } // Mutation returns MutationResolver implementation. func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } -// Query returns QueryResolver implementation. -func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } - type mutationResolver struct{ *Resolver } -type queryResolver struct{ *Resolver } diff --git a/graph/query.resolvers.go b/graph/query.resolvers.go new file mode 100644 index 0000000..7b53909 --- /dev/null +++ b/graph/query.resolvers.go @@ -0,0 +1,49 @@ +package graph + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.49 + +import ( + "context" + + "git.obamna.ru/erius/ozon-task/graph/model" +) + +// Replies is the resolver for the replies field. +func (r *commentResolver) Replies(ctx context.Context, obj *model.Comment, first uint, after uint) (*model.CommentsConnection, error) { + return r.Storage.GetReplies(obj, first, after, ctx) +} + +// Comments is the resolver for the comments field. +func (r *postResolver) Comments(ctx context.Context, obj *model.Post, first uint, after uint) (*model.CommentsConnection, error) { + return r.Storage.GetComments(obj, first, after, ctx) +} + +// Post is the resolver for the post field. +func (r *queryResolver) Post(ctx context.Context, id uint) (*model.Post, error) { + return r.Storage.GetPost(id, ctx) +} + +// Posts is the resolver for the posts field. +func (r *queryResolver) Posts(ctx context.Context, first uint, after uint) (*model.PostsConnection, error) { + return r.Storage.GetPosts(first, after, ctx) +} + +// Comment is the resolver for the comment field. +func (r *queryResolver) Comment(ctx context.Context, id uint) (*model.Comment, error) { + return r.Storage.GetComment(id, ctx) +} + +// Comment returns CommentResolver implementation. +func (r *Resolver) Comment() CommentResolver { return &commentResolver{r} } + +// Post returns PostResolver implementation. +func (r *Resolver) Post() PostResolver { return &postResolver{r} } + +// Query returns QueryResolver implementation. +func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } + +type commentResolver struct{ *Resolver } +type postResolver struct{ *Resolver } +type queryResolver struct{ *Resolver } diff --git a/graph/schema.graphqls b/graph/schema.graphqls deleted file mode 100644 index 189b1cd..0000000 --- a/graph/schema.graphqls +++ /dev/null @@ -1,44 +0,0 @@ -type Post { - id: ID! - title: String! - author: String! - contents: String! - comments: [Comment!]! - allowComments: Boolean! -} - -type Comment { - id: ID! - post: Post! - author: String! - contents: String! - replyTo: Comment - replies: [Comment!]! -} - -type Query { - allPosts: [Post!]! -} - -type Mutation { - addPost(input: PostInput): AddResult! - addComment(input: CommentInput): AddResult! -} - -input PostInput { - title: String! - author: String! - contents: String! - allowComments: Boolean! = true -} - -input CommentInput { - parentPostId: ID - parentCommentId: ID - author: String! - contents: String! -} - -type AddResult { - itemId: ID! -} diff --git a/graph/schema/mutation.graphqls b/graph/schema/mutation.graphqls new file mode 100644 index 0000000..9f9e661 --- /dev/null +++ b/graph/schema/mutation.graphqls @@ -0,0 +1,22 @@ +type Mutation { + addPost(input: PostInput!): AddResult! + addComment(input: CommentInput!): AddResult! +} + +input PostInput { + title: String! + author: String! + contents: String! + allowComments: Boolean! = true +} + +input CommentInput { + parentPostId: ID + parentCommentId: ID + author: String! + contents: String! +} + +type AddResult { + itemId: ID! +} diff --git a/graph/schema/pagination.graphqls b/graph/schema/pagination.graphqls new file mode 100644 index 0000000..363554b --- /dev/null +++ b/graph/schema/pagination.graphqls @@ -0,0 +1,25 @@ +type PostsConnection { + edges: [PostsEdge!]! + pageInfo: PageInfo! +} + +type CommentsConnection { + edges: [CommentsEdge!]! + pageInfo: PageInfo! +} + +type PostsEdge { + cursor: ID! + node: Post! +} + +type CommentsEdge { + cursor: ID! + node: Comment! +} + +type PageInfo { + startCursor: ID! + endCursor: ID! + hasNextPage: Boolean! +} diff --git a/graph/schema/query.graphqls b/graph/schema/query.graphqls new file mode 100644 index 0000000..c2e8788 --- /dev/null +++ b/graph/schema/query.graphqls @@ -0,0 +1,21 @@ +type Query { + post(id: ID!): Post! + posts(first: ID!, after: ID!): PostsConnection! + comment(id: ID!): Comment! +} + +type Post { + id: ID! + title: String! + author: String! + contents: String! + comments(first: ID!, after: ID!): CommentsConnection! + allowComments: Boolean! +} + +type Comment { + id: ID! + author: String! + contents: String! + replies(first: ID!, after: ID!): CommentsConnection! +} diff --git a/internal/storage/db/database.go b/internal/storage/db/database.go index 19b6197..1de6719 100644 --- a/internal/storage/db/database.go +++ b/internal/storage/db/database.go @@ -1,7 +1,12 @@ package db import ( + "context" + "fmt" + "log" + "git.obamna.ru/erius/ozon-task/graph/model" + "github.com/99designs/gqlgen/graphql" "gorm.io/gorm" ) @@ -15,48 +20,64 @@ func (s *Database) AddPost(input *model.PostInput) (*model.AddResult, error) { return &model.AddResult{ItemID: &post.ID}, err } -func (s *Database) AddComment(input *model.CommentInput) (*model.AddResult, error) { +func (s *Database) AddReplyToComment(input *model.CommentInput) (*model.AddResult, error) { comment := model.CommentFromInput(input) - if input.ParentPostID == nil { - // multiple operations performed in one transaction - err := s.db.Transaction(func(tx *gorm.DB) error { - var parent model.Comment - // find parent comment in db - err := s.db.First(&parent, *input.ParentCommentID).Error - if err != nil { - return err - } - // set new comment fields to reference parent post and comment - comment.ReplyTo = &parent - comment.PostID = parent.PostID - // insert comment - err = s.db.Create(comment).Error - if err != nil { - return err - } - // add new reply to parent and save - parent.Replies = append(parent.Replies, comment) - err = s.db.Save(parent).Error - if err != nil { - return err - } - return nil - }) - if err != nil { - return nil, err - } - } else { - comment.PostID = *input.ParentPostID + // multiple operations performed in one transaction + err := s.db.Transaction(func(tx *gorm.DB) error { + // insert comment err := s.db.Create(comment).Error if err != nil { - return nil, err + return err } - } - return &model.AddResult{ItemID: &comment.ID}, nil + // add new reply to parent + err = s.db.Table("comment_replies").Create(map[string]interface{}{ + "comment_id": *input.ParentCommentID, + "reply_id": comment.ID, + }).Error + if err != nil { + return err + } + return nil + }) + return &model.AddResult{ItemID: &comment.ID}, err } -func (s Database) GetPosts() ([]*model.Post, error) { - var posts []*model.Post - err := s.db.Find(&posts).Error - return posts, err +func (s *Database) AddCommentToPost(input *model.CommentInput) (*model.AddResult, error) { + comment := model.CommentFromInput(input) + var allowComments bool + err := s.db.Table("posts").Select("allow_comments").Where("id = ?", *input.ParentPostID).Scan(&allowComments).Error + if err != nil { + return nil, err + } + if !allowComments { + return nil, fmt.Errorf("author disabled comments for this post") + } + err = s.db.Create(comment).Error + return &model.AddResult{ItemID: &comment.ID}, err +} + +func (s *Database) GetPost(id uint, ctx context.Context) (*model.Post, error) { + log.Println(graphql.CollectAllFields(ctx)) + log.Println(graphql.CollectFieldsCtx(ctx, nil)) + var post model.Post + err := s.db.Find(&post, id).Error + return &post, err +} + +func (s *Database) GetComment(id uint, ctx context.Context) (*model.Comment, error) { + var comment model.Comment + err := s.db.Find(&comment, id).Error + return &comment, err +} + +func (s *Database) GetPosts(first uint, after uint, ctx context.Context) (*model.PostsConnection, error) { + return nil, nil +} + +func (s *Database) GetComments(post *model.Post, first uint, after uint, ctx context.Context) (*model.CommentsConnection, error) { + return nil, nil +} + +func (s *Database) GetReplies(comment *model.Comment, first uint, after uint, ctx context.Context) (*model.CommentsConnection, error) { + return nil, nil } diff --git a/internal/storage/db/postgres.go b/internal/storage/db/postgres.go index 3da0fa8..e19b0c3 100644 --- a/internal/storage/db/postgres.go +++ b/internal/storage/db/postgres.go @@ -8,6 +8,7 @@ import ( "git.obamna.ru/erius/ozon-task/graph/model" "gorm.io/driver/postgres" "gorm.io/gorm" + "gorm.io/gorm/logger" ) var ( @@ -22,7 +23,10 @@ var ( func InitPostgres() (*Database, error) { log.Printf("connecting to PostgreSQL database at %s...", con) // PrepareStmt is true for caching complex sql statements when adding comments or replies - db, err := gorm.Open(postgres.Open(con), &gorm.Config{PrepareStmt: true}) + db, err := gorm.Open(postgres.Open(con), &gorm.Config{ + PrepareStmt: true, + Logger: logger.Default.LogMode(logger.Info), + }) if err != nil { log.Printf("failed to connect to database: %s", err) return nil, err diff --git a/internal/storage/memory.go b/internal/storage/memory.go index ac8a556..7dc8662 100644 --- a/internal/storage/memory.go +++ b/internal/storage/memory.go @@ -1,6 +1,9 @@ package storage import ( + "context" + "fmt" + "git.obamna.ru/erius/ozon-task/graph/model" ) @@ -22,20 +25,142 @@ func InitInMemory() *InMemory { func (s *InMemory) AddPost(input *model.PostInput) (*model.AddResult, error) { post := model.PostFromInput(input) - post.ID = s.postId - s.posts = append(s.posts, post) - s.postId++ + s.insertPost(post) return &model.AddResult{ItemID: &post.ID}, nil } -func (s *InMemory) AddComment(input *model.CommentInput) (*model.AddResult, error) { - comment := model.CommentFromInput(input) - comment.ID = s.commentId - s.comments = append(s.comments, comment) - s.commentId++ +func (s *InMemory) AddReplyToComment(input *model.CommentInput) (*model.AddResult, error) { + if !s.commentExists(*input.ParentCommentID) { + return nil, &IDNotFoundError{objName: "comment", id: *input.ParentCommentID} + } + comment, parent := model.CommentFromInput(input), s.comments[*input.ParentCommentID] + s.insertComment(comment) + parent.Replies = append(parent.Replies, comment) return &model.AddResult{ItemID: &comment.ID}, nil } -func (s InMemory) GetPosts() ([]*model.Post, error) { - return s.posts, nil +func (s *InMemory) AddCommentToPost(input *model.CommentInput) (*model.AddResult, error) { + if !s.postExists(*input.ParentPostID) { + return nil, &IDNotFoundError{objName: "post", id: *input.ParentPostID} + } + parent := s.posts[*input.ParentPostID] + if !parent.AllowComments { + return nil, fmt.Errorf("author disabled comments for this post") + } + comment := model.CommentFromInput(input) + s.insertComment(comment) + parent.Comments = append(parent.Comments, comment) + return &model.AddResult{ItemID: &comment.ID}, nil +} + +func (s *InMemory) GetPost(id uint, ctx context.Context) (*model.Post, error) { + if !s.postExists(id) { + return nil, &IDNotFoundError{objName: "post", id: id} + } + return s.posts[id], nil +} + +func (s *InMemory) GetComment(id uint, ctx context.Context) (*model.Comment, error) { + if !s.commentExists(id) { + return nil, &IDNotFoundError{objName: "comment", id: id} + } + return s.comments[id], nil +} + +func (s *InMemory) GetPosts(first uint, after uint, ctx context.Context) (*model.PostsConnection, error) { + if !s.postExists(after) { + return nil, &IDNotFoundError{objName: "post", id: after} + } + nextPage, until := true, after+first + if !s.postExists(until) { + nextPage = false + until = uint(len(s.posts)) + } + info, edges := &model.PageInfo{ + StartCursor: after, + EndCursor: until - 1, + HasNextPage: nextPage, + }, make([]*model.PostsEdge, until-after) + for i, p := range s.posts[after:until] { + edges[i] = &model.PostsEdge{ + Cursor: p.ID, + Node: p, + } + } + return &model.PostsConnection{ + Edges: edges, + PageInfo: info, + }, nil +} + +func (s *InMemory) GetComments(post *model.Post, first uint, after uint, ctx context.Context) (*model.CommentsConnection, error) { + if !s.commentExists(after) { + return nil, &IDNotFoundError{objName: "comment", id: after} + } + nextPage, until := true, after+first + if !s.commentExists(until) { + nextPage = false + until = uint(len(s.comments)) + } + info, edges := &model.PageInfo{ + StartCursor: after, + EndCursor: until - 1, + HasNextPage: nextPage, + }, make([]*model.CommentsEdge, until-after) + for i, c := range post.Comments[after:until] { + edges[i] = &model.CommentsEdge{ + Cursor: c.ID, + Node: c, + } + } + return &model.CommentsConnection{ + Edges: edges, + PageInfo: info, + }, nil +} + +func (s *InMemory) GetReplies(comment *model.Comment, first uint, after uint, ctx context.Context) (*model.CommentsConnection, error) { + if !s.commentExists(after) { + return nil, &IDNotFoundError{objName: "comment", id: after} + } + nextPage, until := true, after+first + if !s.commentExists(until) { + nextPage = false + until = uint(len(s.comments)) + } + info, edges := &model.PageInfo{ + StartCursor: after, + EndCursor: until - 1, + HasNextPage: nextPage, + }, make([]*model.CommentsEdge, until-after) + for i, c := range comment.Replies[after:until] { + edges[i] = &model.CommentsEdge{ + Cursor: c.ID, + Node: c, + } + } + return &model.CommentsConnection{ + Edges: edges, + PageInfo: info, + }, nil +} + +func (s *InMemory) postExists(id uint) bool { + return id < uint(len(s.posts)) +} + +func (s *InMemory) commentExists(id uint) bool { + return id < uint(len(s.comments)) +} + +func (s *InMemory) insertComment(comment *model.Comment) { + comment.ID = s.commentId + s.comments = append(s.comments, comment) + s.commentId++ +} + +func (s *InMemory) insertPost(post *model.Post) { + post.ID = s.postId + s.posts = append(s.posts, post) + s.postId++ } diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 800bf32..d82552b 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -1,6 +1,8 @@ package storage import ( + "context" + "fmt" "log" "os" @@ -8,20 +10,47 @@ import ( "git.obamna.ru/erius/ozon-task/internal/storage/db" ) +type IDNotFoundError struct { + objName string + id uint +} + +func (e *IDNotFoundError) Error() string { + return fmt.Sprintf("%s with id %d doesn't exist", e.objName, e.id) +} + +// storage types enum const ( inMemory = "inmemory" postgres = "postgres" ) -var storage = os.Getenv("APP_STORAGE") +var storage, storageSpecified = os.LookupEnv("APP_STORAGE") type Storage interface { AddPost(input *model.PostInput) (*model.AddResult, error) - AddComment(input *model.CommentInput) (*model.AddResult, error) - GetPosts() ([]*model.Post, error) + + // assumes that input.ParentCommentID is not nil + AddReplyToComment(input *model.CommentInput) (*model.AddResult, error) + + // assumes that input.ParentPostID is not nil + AddCommentToPost(input *model.CommentInput) (*model.AddResult, error) + + // passing query context to analyze requested fields and prevent overfetching + GetPost(id uint, ctx context.Context) (*model.Post, error) + GetComment(id uint, ctx context.Context) (*model.Comment, error) + + // returns paginated data in the form of model.*Connection (passing context to prevent overfetching) + GetPosts(first uint, after uint, ctx context.Context) (*model.PostsConnection, error) + GetComments(post *model.Post, first uint, after uint, ctx context.Context) (*model.CommentsConnection, error) + GetReplies(comment *model.Comment, first uint, after uint, ctx context.Context) (*model.CommentsConnection, error) } func InitStorage() (Storage, error) { + if !storageSpecified { + log.Println("APP_STORAGE isn't specified, falling back to default in-memory storage", storage) + return InitInMemory(), nil + } log.Printf("initializing storage of type %s...", storage) switch storage { case inMemory: @@ -29,7 +58,6 @@ func InitStorage() (Storage, error) { case postgres: return db.InitPostgres() default: - log.Printf("storage of type %s doesn't exists, falling back to default in-memory storage", storage) - return InitInMemory(), nil + return nil, fmt.Errorf("storage of type %s doesn't exists, change the value of APP_STORAGE env variable", storage) } }