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

@ -104,7 +104,7 @@ type ComplexityRoot struct {
CreatedAt func(childComplexity int) int
Name func(childComplexity int) int
Position func(childComplexity int) int
TaskGroupID func(childComplexity int) int
TaskGroup func(childComplexity int) int
TaskID func(childComplexity int) int
}
@ -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,12 +217,25 @@ func (r *teamResolver) Projects(ctx context.Context, obj *pg.Team) ([]pg.Project
return r.Repository.GetAllProjectsForTeam(ctx, obj.TeamID)
}
// Mutation returns MutationResolver implementation.
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
// Organization returns OrganizationResolver implementation.
func (r *Resolver) Organization() OrganizationResolver { return &organizationResolver{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 }

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;