refactor: split go-pkg

This commit is contained in:
xuu
2023-07-12 17:35:02 -06:00
parent d67d768c1e
commit 0f665d3484
80 changed files with 785 additions and 3993 deletions

View File

@@ -1,37 +0,0 @@
scalar Time
scalar Map
type Connection @goModel(model: "go.sour.is/ev/pkg/gql.Connection") {
paging: PageInfo!
edges: [Edge!]!
}
input PageInput @goModel(model: "go.sour.is/ev/pkg/gql.PageInput") {
after: Int = 0
before: Int
count: Int = 30
}
type PageInfo @goModel(model: "go.sour.is/ev/pkg/gql.PageInfo") {
next: Boolean!
prev: Boolean!
begin: Int!
end: Int!
}
interface Edge @goModel(model: "go.sour.is/ev/pkg/gql.Edge"){
id: ID!
}
directive @goModel(
model: String
models: [String!]
) on OBJECT | INPUT_OBJECT | SCALAR | ENUM | INTERFACE | UNION
directive @goField(
forceResolver: Boolean
name: String
) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION
directive @goTag(
key: String!
value: String
) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION

View File

@@ -1,67 +0,0 @@
package gql
import (
"context"
"encoding/json"
"go.sour.is/ev/pkg/es/event"
)
type Edge interface {
IsEdge()
}
type Connection struct {
Paging *PageInfo `json:"paging"`
Edges []Edge `json:"edges"`
}
type PostEvent struct {
ID string `json:"id"`
Payload string `json:"payload"`
Tags []string `json:"tags"`
Meta *event.Meta `json:"meta"`
}
func (PostEvent) IsEdge() {}
func (e *PostEvent) PayloadJSON(ctx context.Context) (m map[string]interface{}, err error) {
err = json.Unmarshal([]byte(e.Payload), &m)
return
}
type PageInfo struct {
Next bool `json:"next"`
Prev bool `json:"prev"`
Begin uint64 `json:"begin"`
End uint64 `json:"end"`
}
type PageInput struct {
After *int64 `json:"after"`
Before *int64 `json:"before"`
Count *int64 `json:"count"`
}
func (p *PageInput) GetIdx(v int64) int64 {
if p == nil {
// pass
} else if p.Before != nil {
return (*p.Before)
} else if p.After != nil {
return *p.After
}
return v
}
func (p *PageInput) GetCount(v int64) int64 {
if p == nil || p.Count == nil {
return v
} else if p.Before != nil {
return -(*p.Count)
} else if p.After != nil {
return *p.Count
}
return *p.Count
}

View File

@@ -1,14 +0,0 @@
package gql
import "context"
func ToContext[K comparable, V any](ctx context.Context, key K, value V) context.Context {
return context.WithValue(ctx, key, value)
}
func FromContext[K comparable, V any](ctx context.Context, key K) V {
var empty V
if v, ok := ctx.Value(key).(V); ok {
return v
}
return empty
}

View File

@@ -1,120 +0,0 @@
package graphiql
import (
"html/template"
"net/http"
"net/url"
)
var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{.title}}</title>
<style>
body {
height: 100%;
margin: 0;
width: 100%;
overflow: hidden;
}
#graphiql {
height: 100vh;
}
</style>
<script
src="https://cdn.jsdelivr.net/npm/react@{{.reactVersion}}/umd/react.production.min.js"
integrity="{{.reactSRI}}"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/react-dom@{{.reactVersion}}/umd/react-dom.production.min.js"
integrity="{{.reactDOMSRI}}"
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/graphiql@{{.version}}/graphiql.min.css"
x-integrity="{{.cssSRI}}"
crossorigin="anonymous"
/>
</head>
<body>
<div id="graphiql">Loading...</div>
<script
src="https://cdn.jsdelivr.net/npm/graphiql@{{.version}}/graphiql.min.js"
x-integrity="{{.jsSRI}}"
crossorigin="anonymous"
></script>
<script>
{{- if .endpointIsAbsolute}}
const url = {{.endpoint}};
const subscriptionUrl = {{.subscriptionEndpoint}};
{{- else}}
const url = location.protocol + '//' + location.host + {{.endpoint}};
const wsProto = location.protocol == 'https:' ? 'wss:' : 'ws:';
const subscriptionUrl = wsProto + '//' + location.host + {{.endpoint}};
{{- end}}
const fetcher = GraphiQL.createFetcher({ url, subscriptionUrl });
ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: fetcher,
isHeadersEditorEnabled: true,
shouldPersistHeaders: true
}),
document.getElementById('graphiql'),
);
</script>
</body>
</html>
`))
// Handler responsible for setting up the playground
func Handler(title string, endpoint string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html; charset=UTF-8")
err := page.Execute(w, map[string]interface{}{
"title": title,
"endpoint": endpoint,
"endpointIsAbsolute": endpointHasScheme(endpoint),
"subscriptionEndpoint": getSubscriptionEndpoint(endpoint),
"version": "2.4.1",
"reactVersion": "17.0.2",
"cssSRI": "sha256-bGeEsMhcAqeXBjh2w0eQzBTFAxwlxhM0PKIKqMshlnk=",
"jsSRI": "sha256-s+f7CFAPSUIygFnRC2nfoiEKd3liCUy+snSdYFAoLUc=",
"reactSRI": "sha256-Ipu/TQ50iCCVZBUsZyNJfxrDk0E2yhaEIz0vqI+kFG8=",
"reactDOMSRI": "sha256-nbMykgB6tsOFJ7OdVmPpdqMFVk4ZsqWocT6issAPUF0=",
})
if err != nil {
panic(err)
}
}
}
// endpointHasScheme checks if the endpoint has a scheme.
func endpointHasScheme(endpoint string) bool {
u, err := url.Parse(endpoint)
return err == nil && u.Scheme != ""
}
// getSubscriptionEndpoint returns the subscription endpoint for the given
// endpoint if it is parsable as a URL, or an empty string.
func getSubscriptionEndpoint(endpoint string) string {
u, err := url.Parse(endpoint)
if err != nil {
return ""
}
switch u.Scheme {
case "https":
u.Scheme = "wss"
default:
u.Scheme = "ws"
}
return u.String()
}

View File

@@ -1,62 +0,0 @@
package playground
import (
"html/template"
"net/http"
)
var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8/>
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<link rel="shortcut icon" href="https://graphcool-playground.netlify.com/favicon.png">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/static/css/index.css"
integrity="{{ .cssSRI }}" crossorigin="anonymous"/>
<link rel="shortcut icon" href="https://cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/favicon.png"
integrity="{{ .faviconSRI }}" crossorigin="anonymous"/>
<script src="https://cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/static/js/middleware.js"
integrity="{{ .jsSRI }}" crossorigin="anonymous"></script>
<title>{{.title}}</title>
</head>
<body>
<style type="text/css">
html { font-family: "Open Sans", sans-serif; overflow: hidden; }
body { margin: 0; background: #172a3a; }
</style>
<div id="root"/>
<script type="text/javascript">
window.addEventListener('load', function (event) {
const root = document.getElementById('root');
root.classList.add('playgroundIn');
const wsProto = location.protocol == 'https:' ? 'wss:' : 'ws:'
GraphQLPlayground.init(root, {
endpoint: location.protocol + '//' + location.host + '{{.endpoint}}',
subscriptionsEndpoint: wsProto + '//' + location.host + '{{.endpoint }}',
shareEnabled: true,
settings: {
'request.credentials': 'same-origin'
}
})
})
</script>
</body>
</html>
`))
func Handler(title string, endpoint string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html")
err := page.Execute(w, map[string]string{
"title": title,
"endpoint": endpoint,
"version": "1.7.28",
"cssSRI": "sha256-dKnNLEFwKSVFpkpjRWe+o/jQDM6n/JsvQ0J3l5Dk3fc=",
"faviconSRI": "sha256-GhTyE+McTU79R4+pRO6ih+4TfsTOrpPwD8ReKFzb3PM=",
"jsSRI": "sha256-VVwEZwxs4qS5W7E+/9nXINYgr/BJRWKOi/rTMUdmmWg=",
})
if err != nil {
panic(err)
}
}
}

View File

@@ -1,143 +0,0 @@
package resolver
import (
"context"
"fmt"
"net/http"
"os"
"reflect"
"runtime/debug"
"time"
"github.com/99designs/gqlgen/graphql"
"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/vektah/gqlparser/v2/gqlerror"
"github.com/gorilla/websocket"
"github.com/ravilushqa/otelgqlgen"
"go.sour.is/ev/internal/lg"
"go.sour.is/ev/pkg/gql/graphiql"
"go.sour.is/ev/pkg/gql/playground"
)
type BaseResolver interface {
ExecutableSchema() graphql.ExecutableSchema
BaseResolver() IsResolver
}
type Resolver[T BaseResolver] struct {
res T
CheckOrigin func(r *http.Request) bool
}
type IsResolver interface {
IsResolver()
}
var defaultCheckOrign = func(r *http.Request) bool {
return true
}
func New[T BaseResolver](ctx context.Context, base T, resolvers ...IsResolver) (*Resolver[T], error) {
_, span := lg.Span(ctx)
defer span.End()
noop := reflect.ValueOf(base.BaseResolver())
v := reflect.ValueOf(base)
v = reflect.Indirect(v)
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()) {
// log.Print("found ", field.Type().Name())
span.AddEvent(fmt.Sprint("found ", field.Type().Name()))
field.Set(rs)
continue outer
}
}
// log.Print(fmt.Sprint("default ", field.Type().Name()))
span.AddEvent(fmt.Sprint("default ", field.Type().Name()))
field.Set(noop)
}
return &Resolver[T]{res: base, CheckOrigin: defaultCheckOrign}, nil
}
func (r *Resolver[T]) Resolver() T {
return r.res
}
// ChainMiddlewares will check all embeded resolvers for a GetMiddleware func and add to handler.
func (r *Resolver[T]) ChainMiddlewares(h http.Handler) http.Handler {
v := reflect.ValueOf(r.Resolver()) // Get reflected value of *Resolver
v = reflect.Indirect(v) // Get the pointed value (returns a zero value on nil)
for _, idx := range reflect.VisibleFields(v.Type()) {
field := v.FieldByIndex(idx.Index)
// log.Print("middleware ", field.Type().Name())
if !field.CanInterface() { // Skip non-interface types.
continue
}
if iface, ok := field.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[T]) RegisterHTTP(mux *http.ServeMux) {
gql := NewServer(r.Resolver().ExecutableSchema(), r.CheckOrigin)
gql.SetRecoverFunc(NoopRecover)
gql.Use(otelgqlgen.Middleware())
mux.Handle("/gql", lg.Htrace(r.ChainMiddlewares(gql), "gql"))
mux.Handle("/graphiql", graphiql.Handler("GraphiQL playground", "/gql"))
mux.Handle("/playground", playground.Handler("GraphQL playground", "/gql"))
}
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")
}
func NewServer(es graphql.ExecutableSchema, checkOrigin func(*http.Request) bool) *handler.Server {
srv := handler.New(es)
srv.AddTransport(transport.Websocket{
Upgrader: websocket.Upgrader{
CheckOrigin: checkOrigin,
},
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
}