From e022e7914a98e21ec1d80149e7427847a3534d73 Mon Sep 17 00:00:00 2001 From: Jordan Knott Date: Fri, 10 Apr 2020 21:22:22 -0500 Subject: [PATCH] feature(api): update gqlgen & add complexity limit --- api/go.mod | 3 ++- api/go.sum | 11 +++++++++++ api/graph/generated.go | 32 ++++++++++++++++---------------- api/graph/graph.go | 31 ++++++++++++++++++++++++++++--- api/graph/schema.graphqls | 2 +- api/graph/schema.resolvers.go | 33 ++++++++++++++++++++++++--------- api/pg/pg.go | 1 + api/pg/querier.go | 1 + api/pg/task_group.sql.go | 17 +++++++++++++++++ api/query/task_group.sql | 3 +++ 10 files changed, 104 insertions(+), 30 deletions(-) diff --git a/api/go.mod b/api/go.mod index a19f04d..5efc2ee 100644 --- a/api/go.mod +++ b/api/go.mod @@ -3,7 +3,7 @@ module github.com/jordanknott/project-citadel/api go 1.13 require ( - github.com/99designs/gqlgen v0.11.1 + github.com/99designs/gqlgen v0.11.3 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-chi/chi v3.3.2+incompatible github.com/go-chi/cors v1.0.0 @@ -13,6 +13,7 @@ require ( github.com/lib/pq v1.0.0 github.com/pkg/errors v0.8.1 github.com/sirupsen/logrus v1.4.2 + github.com/urfave/cli v1.20.0 // indirect github.com/vektah/gqlparser/v2 v2.0.1 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 ) diff --git a/api/go.sum b/api/go.sum index 53fce33..e5f16ce 100644 --- a/api/go.sum +++ b/api/go.sum @@ -1,10 +1,15 @@ github.com/99designs/gqlgen v0.11.1 h1:QoSL8/AAJ2T3UOeQbdnBR32JcG4pO08+P/g5jdbFkUg= github.com/99designs/gqlgen v0.11.1/go.mod h1:vjFOyBZ7NwDl+GdSD4PFn7BQn5Fy7ohJwXn7Vk8zz+c= +github.com/99designs/gqlgen v0.11.3 h1:oFSxl1DFS9X///uHV3y6CEfpcXWrDUxVblR4Xib2bs4= +github.com/99designs/gqlgen v0.11.3/go.mod h1:RgX5GRRdDWNkh4pBrdzNpNPFVsdoUFY2+adM6nb1N+4= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0= github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -56,8 +61,12 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -69,6 +78,8 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k= +github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg= github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U= github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o= diff --git a/api/graph/generated.go b/api/graph/generated.go index 2638a97..4e968bc 100644 --- a/api/graph/generated.go +++ b/api/graph/generated.go @@ -101,11 +101,11 @@ type ComplexityRoot struct { } Task struct { - CreatedAt func(childComplexity int) int - Name func(childComplexity int) int - Position func(childComplexity int) int - TaskGroupID func(childComplexity int) int - TaskID func(childComplexity int) int + CreatedAt func(childComplexity int) int + Name func(childComplexity int) int + Position func(childComplexity int) int + TaskGroup func(childComplexity int) int + TaskID func(childComplexity int) int } TaskGroup struct { @@ -164,7 +164,7 @@ type QueryResolver interface { TaskGroups(ctx context.Context) ([]pg.TaskGroup, error) } type TaskResolver interface { - TaskGroupID(ctx context.Context, obj *pg.Task) (string, error) + TaskGroup(ctx context.Context, obj *pg.Task) (*pg.TaskGroup, error) } type TaskGroupResolver interface { ProjectID(ctx context.Context, obj *pg.TaskGroup) (string, error) @@ -505,12 +505,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Task.Position(childComplexity), true - case "Task.taskGroupID": - if e.complexity.Task.TaskGroupID == nil { + case "Task.taskGroup": + if e.complexity.Task.TaskGroup == nil { break } - return e.complexity.Task.TaskGroupID(childComplexity), true + return e.complexity.Task.TaskGroup(childComplexity), true case "Task.taskID": if e.complexity.Task.TaskID == nil { @@ -740,7 +740,7 @@ type TaskGroup { type Task { taskID: ID! - taskGroupID: String! + taskGroup: TaskGroup! createdAt: Time! name: String! position: Float! @@ -2382,7 +2382,7 @@ func (ec *executionContext) _Task_taskID(ctx context.Context, field graphql.Coll return ec.marshalNID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, field.Selections, res) } -func (ec *executionContext) _Task_taskGroupID(ctx context.Context, field graphql.CollectedField, obj *pg.Task) (ret graphql.Marshaler) { +func (ec *executionContext) _Task_taskGroup(ctx context.Context, field graphql.CollectedField, obj *pg.Task) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) @@ -2399,7 +2399,7 @@ func (ec *executionContext) _Task_taskGroupID(ctx context.Context, field graphql ctx = graphql.WithFieldContext(ctx, fc) resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Task().TaskGroupID(rctx, obj) + return ec.resolvers.Task().TaskGroup(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -2411,9 +2411,9 @@ func (ec *executionContext) _Task_taskGroupID(ctx context.Context, field graphql } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*pg.TaskGroup) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNTaskGroup2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTaskGroup(ctx, field.Selections, res) } func (ec *executionContext) _Task_createdAt(ctx context.Context, field graphql.CollectedField, obj *pg.Task) (ret graphql.Marshaler) { @@ -4825,7 +4825,7 @@ func (ec *executionContext) _Task(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } - case "taskGroupID": + case "taskGroup": field := field out.Concurrently(i, func() (res graphql.Marshaler) { defer func() { @@ -4833,7 +4833,7 @@ func (ec *executionContext) _Task(ctx context.Context, sel ast.SelectionSet, obj ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Task_taskGroupID(ctx, field, obj) + res = ec._Task_taskGroup(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&invalids, 1) } diff --git a/api/graph/graph.go b/api/graph/graph.go index 9f3de21..6384673 100644 --- a/api/graph/graph.go +++ b/api/graph/graph.go @@ -2,21 +2,46 @@ package graph import ( "net/http" + "os" + "time" - "github.com/99designs/gqlgen/handler" + "github.com/99designs/gqlgen/graphql/handler" + "github.com/99designs/gqlgen/graphql/handler/extension" + "github.com/99designs/gqlgen/graphql/handler/lru" + "github.com/99designs/gqlgen/graphql/handler/transport" + "github.com/99designs/gqlgen/graphql/playground" "github.com/jordanknott/project-citadel/api/pg" ) // NewHandler returns a new graphql endpoint handler. func NewHandler(repo pg.Repository) http.Handler { - return handler.GraphQL(NewExecutableSchema(Config{ + srv := handler.New(NewExecutableSchema(Config{ Resolvers: &Resolver{ Repository: repo, }, })) + srv.AddTransport(transport.Websocket{ + KeepAlivePingInterval: 10 * time.Second, + }) + srv.AddTransport(transport.Options{}) + srv.AddTransport(transport.GET{}) + srv.AddTransport(transport.POST{}) + srv.AddTransport(transport.MultipartForm{}) + + srv.SetQueryCache(lru.New(1000)) + + srv.Use(extension.AutomaticPersistedQuery{ + Cache: lru.New(100), + }) + if isProd := os.Getenv("PRODUCTION") == "true"; isProd { + srv.Use(extension.FixedComplexityLimit(10)) + } else { + srv.Use(extension.Introspection{}) + } + return srv } // NewPlaygroundHandler returns a new GraphQL Playground handler. func NewPlaygroundHandler(endpoint string) http.Handler { - return handler.Playground("GraphQL Playground", endpoint) + return playground.Handler("GraphQL Playground", endpoint) } diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls index 19dfce4..830d56b 100644 --- a/api/graph/schema.graphqls +++ b/api/graph/schema.graphqls @@ -50,7 +50,7 @@ type TaskGroup { type Task { taskID: ID! - taskGroupID: String! + taskGroup: TaskGroup! createdAt: Time! name: String! position: Float! diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index a68f60c..65291e2 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -1,6 +1,7 @@ +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. -package graph import ( "context" @@ -198,8 +199,9 @@ func (r *queryResolver) TaskGroups(ctx context.Context) ([]pg.TaskGroup, error) return r.Repository.GetAllTaskGroups(ctx) } -func (r *taskResolver) TaskGroupID(ctx context.Context, obj *pg.Task) (string, error) { - return obj.TaskGroupID.String(), nil +func (r *taskResolver) TaskGroup(ctx context.Context, obj *pg.Task) (*pg.TaskGroup, error) { + taskGroup, err := r.Repository.GetTaskGroupByID(ctx, obj.TaskGroupID) + return &taskGroup, err } func (r *taskGroupResolver) ProjectID(ctx context.Context, obj *pg.TaskGroup) (string, error) { @@ -215,13 +217,26 @@ func (r *teamResolver) Projects(ctx context.Context, obj *pg.Team) ([]pg.Project return r.Repository.GetAllProjectsForTeam(ctx, obj.TeamID) } -func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } +// Mutation returns MutationResolver implementation. +func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } + +// Organization returns OrganizationResolver implementation. func (r *Resolver) Organization() OrganizationResolver { return &organizationResolver{r} } -func (r *Resolver) Project() ProjectResolver { return &projectResolver{r} } -func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } -func (r *Resolver) Task() TaskResolver { return &taskResolver{r} } -func (r *Resolver) TaskGroup() TaskGroupResolver { return &taskGroupResolver{r} } -func (r *Resolver) Team() TeamResolver { return &teamResolver{r} } + +// Project returns ProjectResolver implementation. +func (r *Resolver) Project() ProjectResolver { return &projectResolver{r} } + +// Query returns QueryResolver implementation. +func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } + +// Task returns TaskResolver implementation. +func (r *Resolver) Task() TaskResolver { return &taskResolver{r} } + +// TaskGroup returns TaskGroupResolver implementation. +func (r *Resolver) TaskGroup() TaskGroupResolver { return &taskGroupResolver{r} } + +// Team returns TeamResolver implementation. +func (r *Resolver) Team() TeamResolver { return &teamResolver{r} } type mutationResolver struct{ *Resolver } type organizationResolver struct{ *Resolver } diff --git a/api/pg/pg.go b/api/pg/pg.go index 760a953..29861f4 100644 --- a/api/pg/pg.go +++ b/api/pg/pg.go @@ -27,6 +27,7 @@ type Repository interface { GetAllTaskGroups(ctx context.Context) ([]TaskGroup, error) GetAllOrganizations(ctx context.Context) ([]Organization, error) GetTaskGroupsForProject(ctx context.Context, projectID uuid.UUID) ([]TaskGroup, error) + GetTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (TaskGroup, error) CreateOrganization(ctx context.Context, arg CreateOrganizationParams) (Organization, error) GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error) CreateTask(ctx context.Context, arg CreateTaskParams) (Task, error) diff --git a/api/pg/querier.go b/api/pg/querier.go index fabd4d4..36307f7 100644 --- a/api/pg/querier.go +++ b/api/pg/querier.go @@ -30,6 +30,7 @@ type Querier interface { GetAllUserAccounts(ctx context.Context) ([]UserAccount, error) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error) GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error) + GetTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (TaskGroup, error) GetTaskGroupsForProject(ctx context.Context, projectID uuid.UUID) ([]TaskGroup, error) GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error) GetTeamByID(ctx context.Context, teamID uuid.UUID) (Team, error) diff --git a/api/pg/task_group.sql.go b/api/pg/task_group.sql.go index 3561fe9..59b8640 100644 --- a/api/pg/task_group.sql.go +++ b/api/pg/task_group.sql.go @@ -73,6 +73,23 @@ func (q *Queries) GetAllTaskGroups(ctx context.Context) ([]TaskGroup, error) { return items, nil } +const getTaskGroupByID = `-- name: GetTaskGroupByID :one +SELECT task_group_id, project_id, created_at, name, position FROM task_group WHERE task_group_id = $1 +` + +func (q *Queries) GetTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (TaskGroup, error) { + row := q.db.QueryRowContext(ctx, getTaskGroupByID, taskGroupID) + var i TaskGroup + err := row.Scan( + &i.TaskGroupID, + &i.ProjectID, + &i.CreatedAt, + &i.Name, + &i.Position, + ) + return i, err +} + const getTaskGroupsForProject = `-- name: GetTaskGroupsForProject :many SELECT task_group_id, project_id, created_at, name, position FROM task_group WHERE project_id = $1 ` diff --git a/api/query/task_group.sql b/api/query/task_group.sql index 70701d9..061d241 100644 --- a/api/query/task_group.sql +++ b/api/query/task_group.sql @@ -7,3 +7,6 @@ SELECT * FROM task_group WHERE project_id = $1; -- name: GetAllTaskGroups :many SELECT * FROM task_group; + +-- name: GetTaskGroupByID :one +SELECT * FROM task_group WHERE task_group_id = $1;