ev/app/gql/resolver.go

176 lines
5.1 KiB
Go
Raw Normal View History

2022-08-23 21:24:13 -06:00
package gql
import (
"context"
"fmt"
"net/http"
"os"
"reflect"
"runtime/debug"
2022-10-30 09:18:08 -06:00
"time"
2022-08-23 21:24:13 -06:00
2022-10-30 09:18:08 -06:00
"github.com/99designs/gqlgen/graphql"
2022-08-23 21:24:13 -06:00
"github.com/99designs/gqlgen/graphql/handler"
2022-10-30 09:18:08 -06:00
"github.com/99designs/gqlgen/graphql/handler/extension"
"github.com/99designs/gqlgen/graphql/handler/lru"
"github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/gorilla/websocket"
2022-08-23 21:24:13 -06:00
"github.com/ravilushqa/otelgqlgen"
2022-11-20 10:13:36 -07:00
"github.com/vektah/gqlparser/v2/gqlerror"
2022-10-30 09:18:08 -06:00
"github.com/sour-is/ev/app/gql/graphiql"
2022-08-23 21:24:13 -06:00
"github.com/sour-is/ev/app/gql/playground"
"github.com/sour-is/ev/app/msgbus"
"github.com/sour-is/ev/app/salty"
"github.com/sour-is/ev/internal/graph/generated"
"github.com/sour-is/ev/internal/lg"
2022-09-04 08:34:22 -06:00
"github.com/sour-is/ev/pkg/es"
2022-08-23 21:24:13 -06:00
"github.com/sour-is/ev/pkg/gql"
)
type Resolver struct {
msgbus.MsgbusResolver
salty.SaltyResolver
2022-09-04 08:34:22 -06:00
es.EventResolver
2022-08-23 21:24:13 -06:00
}
func New(ctx context.Context, resolvers ...interface{ RegisterHTTP(*http.ServeMux) }) (*Resolver, error) {
_, span := lg.Span(ctx)
2022-08-23 21:24:13 -06:00
defer span.End()
r := &Resolver{}
v := reflect.ValueOf(r)
v = reflect.Indirect(v)
noop := reflect.ValueOf(&noop{})
outer:
for _, idx := range reflect.VisibleFields(v.Type()) {
field := v.FieldByIndex(idx.Index)
for i := range resolvers {
rs := reflect.ValueOf(resolvers[i])
if field.IsNil() && rs.Type().Implements(field.Type()) {
span.AddEvent(fmt.Sprint("found ", field.Type().Name()))
field.Set(rs)
2022-09-04 08:34:22 -06:00
continue outer
2022-08-23 21:24:13 -06:00
}
}
2022-09-04 08:34:22 -06:00
span.AddEvent(fmt.Sprint("default ", field.Type().Name()))
2022-08-23 21:24:13 -06:00
field.Set(noop)
}
return r, nil
}
// Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return r }
// Query returns generated.QueryResolver implementation.
func (r *Resolver) Mutation() generated.MutationResolver { return r }
// Subscription returns generated.SubscriptionResolver implementation.
func (r *Resolver) Subscription() generated.SubscriptionResolver { return r }
// ChainMiddlewares will check all embeded resolvers for a GetMiddleware func and add to handler.
func (r *Resolver) ChainMiddlewares(h http.Handler) http.Handler {
v := reflect.ValueOf(r) // Get reflected value of *Resolver
v = reflect.Indirect(v) // Get the pointed value (returns a zero value on nil)
n := v.NumField() // Get number of fields to iterate over.
for i := 0; i < n; i++ {
f := v.Field(i)
if !f.CanInterface() { // Skip non-interface types.
continue
}
if iface, ok := f.Interface().(interface {
GetMiddleware() func(http.Handler) http.Handler
}); ok {
h = iface.GetMiddleware()(h) // Append only items that fulfill the interface.
}
}
return h
}
func (r *Resolver) RegisterHTTP(mux *http.ServeMux) {
2022-10-30 09:18:08 -06:00
gql := NewServer(generated.NewExecutableSchema(generated.Config{Resolvers: r}))
2022-08-23 21:24:13 -06:00
gql.SetRecoverFunc(NoopRecover)
gql.Use(otelgqlgen.Middleware())
2022-10-30 09:18:08 -06:00
mux.Handle("/graphiql", graphiql.Handler("GraphiQL playground", "/gql"))
mux.Handle("/gql", lg.Htrace(r.ChainMiddlewares(gql), "gql"))
2022-10-30 09:18:08 -06:00
mux.Handle("/playground", playground.Handler("GraphQL playground", "/gql"))
2022-08-23 21:24:13 -06:00
}
type noop struct{}
func NoopRecover(ctx context.Context, err interface{}) error {
if err, ok := err.(string); ok && err == "not implemented" {
return gqlerror.Errorf("not implemented")
}
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr)
debug.PrintStack()
return gqlerror.Errorf("internal system error")
}
var _ msgbus.MsgbusResolver = (*noop)(nil)
var _ salty.SaltyResolver = (*noop)(nil)
2022-09-04 08:34:22 -06:00
var _ es.EventResolver = (*noop)(nil)
2022-08-23 21:24:13 -06:00
func (*noop) CreateSaltyUser(ctx context.Context, nick string, pubkey string) (*salty.SaltyUser, error) {
panic("not implemented")
}
func (*noop) Posts(ctx context.Context, streamID string, paging *gql.PageInput) (*gql.Connection, error) {
panic("not implemented")
}
func (*noop) SaltyUser(ctx context.Context, nick string) (*salty.SaltyUser, error) {
panic("not implemented")
}
func (*noop) PostAdded(ctx context.Context, streamID string, after int64) (<-chan *msgbus.PostEvent, error) {
panic("not implemented")
}
2022-09-04 08:34:22 -06:00
func (*noop) Events(ctx context.Context, streamID string, paging *gql.PageInput) (*gql.Connection, error) {
panic("not implemented")
}
func (*noop) EventAdded(ctx context.Context, streamID string, after int64) (<-chan *es.GQLEvent, error) {
panic("not implemented")
}
2022-11-20 10:13:36 -07:00
func (*noop) TruncateStream(ctx context.Context, streamID string, index int64) (bool, error) {
panic("not implemented")
}
2022-08-23 21:24:13 -06:00
func (*noop) RegisterHTTP(*http.ServeMux) {}
2022-10-30 09:18:08 -06:00
func NewServer(es graphql.ExecutableSchema) *handler.Server {
srv := handler.New(es)
srv.AddTransport(transport.Websocket{
Upgrader: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
switch r.Header.Get("Origin") {
case "https://ev.sour.is", "https://www.graphqlbin.com":
return true
default:
return false
}
},
},
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.Introspection{})
srv.Use(extension.AutomaticPersistedQuery{
Cache: lru.New(100),
})
return srv
}