feature(api): update gqlgen & add complexity limit

This commit is contained in:
Jordan Knott 2020-04-10 21:22:22 -05:00
parent 6b41461a6a
commit e022e7914a
10 changed files with 104 additions and 30 deletions

View File

@ -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
)

View File

@ -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=

View File

@ -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)
}

View File

@ -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)
}

View File

@ -50,7 +50,7 @@ type TaskGroup {
type Task {
taskID: ID!
taskGroupID: String!
taskGroup: TaskGroup!
createdAt: Time!
name: String!
position: Float!

View File

@ -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 }

View File

@ -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)

View File

@ -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)

View File

@ -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
`

View File

@ -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;