feat: updates
This commit is contained in:
parent
8c54eefcdd
commit
f65e9ca666
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/sour-is/ev/app/salty"
|
"github.com/sour-is/ev/app/salty"
|
||||||
"github.com/sour-is/ev/internal/graph/generated"
|
"github.com/sour-is/ev/internal/graph/generated"
|
||||||
"github.com/sour-is/ev/internal/logz"
|
"github.com/sour-is/ev/internal/logz"
|
||||||
|
"github.com/sour-is/ev/pkg/es"
|
||||||
"github.com/sour-is/ev/pkg/gql"
|
"github.com/sour-is/ev/pkg/gql"
|
||||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||||
)
|
)
|
||||||
|
@ -22,6 +23,7 @@ import (
|
||||||
type Resolver struct {
|
type Resolver struct {
|
||||||
msgbus.MsgbusResolver
|
msgbus.MsgbusResolver
|
||||||
salty.SaltyResolver
|
salty.SaltyResolver
|
||||||
|
es.EventResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, resolvers ...interface{ RegisterHTTP(*http.ServeMux) }) (*Resolver, error) {
|
func New(ctx context.Context, resolvers ...interface{ RegisterHTTP(*http.ServeMux) }) (*Resolver, error) {
|
||||||
|
@ -43,13 +45,12 @@ outer:
|
||||||
if field.IsNil() && rs.Type().Implements(field.Type()) {
|
if field.IsNil() && rs.Type().Implements(field.Type()) {
|
||||||
span.AddEvent(fmt.Sprint("found ", field.Type().Name()))
|
span.AddEvent(fmt.Sprint("found ", field.Type().Name()))
|
||||||
field.Set(rs)
|
field.Set(rs)
|
||||||
break outer
|
continue outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span.AddEvent(fmt.Sprint("default ", field.Type().Name()))
|
span.AddEvent(fmt.Sprint("default ", field.Type().Name()))
|
||||||
|
|
||||||
field.Set(noop)
|
field.Set(noop)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
|
@ -107,6 +108,7 @@ func NoopRecover(ctx context.Context, err interface{}) error {
|
||||||
|
|
||||||
var _ msgbus.MsgbusResolver = (*noop)(nil)
|
var _ msgbus.MsgbusResolver = (*noop)(nil)
|
||||||
var _ salty.SaltyResolver = (*noop)(nil)
|
var _ salty.SaltyResolver = (*noop)(nil)
|
||||||
|
var _ es.EventResolver = (*noop)(nil)
|
||||||
|
|
||||||
func (*noop) CreateSaltyUser(ctx context.Context, nick string, pubkey string) (*salty.SaltyUser, error) {
|
func (*noop) CreateSaltyUser(ctx context.Context, nick string, pubkey string) (*salty.SaltyUser, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
|
@ -120,4 +122,11 @@ func (*noop) SaltyUser(ctx context.Context, nick string) (*salty.SaltyUser, erro
|
||||||
func (*noop) PostAdded(ctx context.Context, streamID string, after int64) (<-chan *msgbus.PostEvent, error) {
|
func (*noop) PostAdded(ctx context.Context, streamID string, after int64) (<-chan *msgbus.PostEvent, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
func (*noop) RegisterHTTP(*http.ServeMux) {}
|
func (*noop) RegisterHTTP(*http.ServeMux) {}
|
||||||
|
|
|
@ -435,14 +435,21 @@ func (e *PostEvent) SetEventMeta(eventMeta event.Meta) {
|
||||||
}
|
}
|
||||||
e.eventMeta = eventMeta
|
e.eventMeta = eventMeta
|
||||||
}
|
}
|
||||||
func (e *PostEvent) MarshalBinary() ([]byte, error) {
|
func (e *PostEvent) Values() any {
|
||||||
j := struct {
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return struct {
|
||||||
Payload []byte
|
Payload []byte
|
||||||
Tags []string
|
Tags []string
|
||||||
}{
|
}{
|
||||||
Payload: e.payload,
|
Payload: e.payload,
|
||||||
Tags: e.tags,
|
Tags: e.tags,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
func (e *PostEvent) MarshalBinary() ([]byte, error) {
|
||||||
|
j := e.Values()
|
||||||
return json.Marshal(&j)
|
return json.Marshal(&j)
|
||||||
}
|
}
|
||||||
func (e *PostEvent) UnmarshalBinary(b []byte) error {
|
func (e *PostEvent) UnmarshalBinary(b []byte) error {
|
||||||
|
|
142
app/salty/salty-addr.go
Normal file
142
app/salty/salty-addr.go
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
package salty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/keys-pub/keys"
|
||||||
|
"github.com/sour-is/ev/internal/logz"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config represents a Salty Config for a User which at a minimum is required
|
||||||
|
// to have an Endpoint and Key (Public Key)
|
||||||
|
type Config struct {
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Capabilities struct {
|
||||||
|
AcceptEncoding string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Capabilities) String() string {
|
||||||
|
return fmt.Sprint("accept-encoding: ", c.AcceptEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Addr struct {
|
||||||
|
User string
|
||||||
|
Domain string
|
||||||
|
|
||||||
|
capabilities Capabilities
|
||||||
|
discoveredDomain string
|
||||||
|
dns DNSResolver
|
||||||
|
endpoint *url.URL
|
||||||
|
key *keys.EdX25519PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAddr parsers a Salty Address for a user into it's user and domain
|
||||||
|
// parts and returns an Addr object with the User and Domain and a method
|
||||||
|
// for returning the expected User's Well-Known URI
|
||||||
|
func (s *service) ParseAddr(addr string) (*Addr, error) {
|
||||||
|
parts := strings.Split(strings.ToLower(addr), "@")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, fmt.Errorf("expected nick@domain found %q", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Addr{User: parts[0], Domain: parts[1], dns: s.dns}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Addr) String() string {
|
||||||
|
return fmt.Sprintf("%s@%s", a.User, a.Domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the Hex(SHA256Sum()) of the Address
|
||||||
|
func (a *Addr) Hash() string {
|
||||||
|
return fmt.Sprintf("%x", sha256.Sum256([]byte(strings.ToLower(a.String()))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// URI returns the Well-Known URI for this Addr
|
||||||
|
func (a *Addr) URI() string {
|
||||||
|
return fmt.Sprintf("https://%s/.well-known/salty/%s.json", a.DiscoveredDomain(), a.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashURI returns the Well-Known HashURI for this Addr
|
||||||
|
func (a *Addr) HashURI() string {
|
||||||
|
return fmt.Sprintf("https://%s/.well-known/salty/%s.json", a.DiscoveredDomain(), a.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscoveredDomain returns the discovered domain (if any) of fallbacks to the Domain
|
||||||
|
func (a *Addr) DiscoveredDomain() string {
|
||||||
|
if a.discoveredDomain != "" {
|
||||||
|
return a.discoveredDomain
|
||||||
|
}
|
||||||
|
return a.Domain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Addr) Refresh(ctx context.Context) error {
|
||||||
|
ctx, span := logz.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
span.AddEvent(fmt.Sprintf("Looking up SRV record for _salty._tcp.%s", a.Domain))
|
||||||
|
if target, _, err := a.dns.LookupSRV(ctx, "salty", "tcp", a.Domain); err == nil {
|
||||||
|
a.discoveredDomain = target
|
||||||
|
span.AddEvent(fmt.Sprintf("Discovered salty services %s", a.discoveredDomain))
|
||||||
|
} else if err != nil {
|
||||||
|
span.AddEvent(fmt.Sprintf("error looking up SRV record for _salty._tcp.%s : %s", a.Domain, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
config, cap, err := fetchConfig(ctx, a.HashURI())
|
||||||
|
if err != nil {
|
||||||
|
// Fallback to plain user nick
|
||||||
|
config, cap, err = fetchConfig(ctx, a.URI())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error looking up user %s: %w", a, err)
|
||||||
|
}
|
||||||
|
key, err := keys.NewEdX25519PublicKeyFromID(keys.ID(config.Key))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing public key %s: %w", config.Key, err)
|
||||||
|
}
|
||||||
|
a.key = key
|
||||||
|
|
||||||
|
u, err := url.Parse(config.Endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing endpoint %s: %w", config.Endpoint, err)
|
||||||
|
}
|
||||||
|
a.endpoint = u
|
||||||
|
a.capabilities = cap
|
||||||
|
|
||||||
|
span.AddEvent(fmt.Sprintf("Discovered endpoint: %v", a.endpoint))
|
||||||
|
span.AddEvent(fmt.Sprintf("Discovered capability: %v", a.capabilities))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchConfig(ctx context.Context, addr string) (config Config, cap Capabilities, err error) {
|
||||||
|
ctx, span := logz.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
var req *http.Request
|
||||||
|
req, err = http.NewRequestWithContext(ctx, http.MethodGet, addr, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = json.NewDecoder(res.Body).Decode(&config); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cap.AcceptEncoding = res.Header.Get("Accept-Encoding")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -19,12 +20,21 @@ import (
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DNSResolver interface {
|
||||||
|
LookupSRV(ctx context.Context, service, proto, name string) (string, []*net.SRV, error)
|
||||||
|
}
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
baseURL string
|
baseURL string
|
||||||
es *es.EventStore
|
es *es.EventStore
|
||||||
|
dns DNSResolver
|
||||||
|
|
||||||
Mresolver_create_salty_user syncint64.Counter
|
m_create_user syncint64.Counter
|
||||||
Mresolver_salty_user syncint64.Counter
|
m_get_user syncint64.Counter
|
||||||
|
m_api_ping syncint64.Counter
|
||||||
|
m_api_register syncint64.Counter
|
||||||
|
m_api_lookup syncint64.Counter
|
||||||
|
m_api_send syncint64.Counter
|
||||||
}
|
}
|
||||||
type contextKey struct {
|
type contextKey struct {
|
||||||
name string
|
name string
|
||||||
|
@ -51,13 +61,25 @@ func New(ctx context.Context, es *es.EventStore, baseURL string) (*service, erro
|
||||||
|
|
||||||
m := logz.Meter(ctx)
|
m := logz.Meter(ctx)
|
||||||
|
|
||||||
svc := &service{baseURL: baseURL, es: es}
|
svc := &service{baseURL: baseURL, es: es, dns: net.DefaultResolver}
|
||||||
|
|
||||||
var err, errs error
|
var err, errs error
|
||||||
svc.Mresolver_create_salty_user, err = m.SyncInt64().Counter("resolver_create_salty_user")
|
svc.m_create_user, err = m.SyncInt64().Counter("salty_create_user")
|
||||||
errs = multierr.Append(errs, err)
|
errs = multierr.Append(errs, err)
|
||||||
|
|
||||||
svc.Mresolver_salty_user, err = m.SyncInt64().Counter("resolver_salty_user")
|
svc.m_get_user, err = m.SyncInt64().Counter("salty_get_user")
|
||||||
|
errs = multierr.Append(errs, err)
|
||||||
|
|
||||||
|
svc.m_api_ping, err = m.SyncInt64().Counter("salty_api_ping")
|
||||||
|
errs = multierr.Append(errs, err)
|
||||||
|
|
||||||
|
svc.m_api_register, err = m.SyncInt64().Counter("salty_api_register")
|
||||||
|
errs = multierr.Append(errs, err)
|
||||||
|
|
||||||
|
svc.m_api_lookup, err = m.SyncInt64().Counter("salty_api_lookup")
|
||||||
|
errs = multierr.Append(errs, err)
|
||||||
|
|
||||||
|
svc.m_api_send, err = m.SyncInt64().Counter("salty_api_send")
|
||||||
errs = multierr.Append(errs, err)
|
errs = multierr.Append(errs, err)
|
||||||
span.RecordError(err)
|
span.RecordError(err)
|
||||||
|
|
||||||
|
@ -73,6 +95,12 @@ func (s *service) BaseURL() string {
|
||||||
func (s *service) RegisterHTTP(mux *http.ServeMux) {
|
func (s *service) RegisterHTTP(mux *http.ServeMux) {
|
||||||
mux.Handle("/.well-known/salty/", logz.Htrace(s, "lookup"))
|
mux.Handle("/.well-known/salty/", logz.Htrace(s, "lookup"))
|
||||||
}
|
}
|
||||||
|
func (s *service) RegisterAPIv1(mux *http.ServeMux) {
|
||||||
|
mux.HandleFunc("/ping", s.apiv1)
|
||||||
|
mux.HandleFunc("/register", s.apiv1)
|
||||||
|
mux.HandleFunc("/lookup/", s.apiv1)
|
||||||
|
mux.HandleFunc("/send", s.apiv1)
|
||||||
|
}
|
||||||
func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
ctx, span := logz.Span(ctx)
|
ctx, span := logz.Span(ctx)
|
||||||
|
@ -112,7 +140,7 @@ func (s *service) CreateSaltyUser(ctx context.Context, nick string, pub string)
|
||||||
ctx, span := logz.Span(ctx)
|
ctx, span := logz.Span(ctx)
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
s.Mresolver_create_salty_user.Add(ctx, 1)
|
s.m_create_user.Add(ctx, 1)
|
||||||
|
|
||||||
streamID := fmt.Sprintf("saltyuser-%x", sha256.Sum256([]byte(strings.ToLower(nick))))
|
streamID := fmt.Sprintf("saltyuser-%x", sha256.Sum256([]byte(strings.ToLower(nick))))
|
||||||
span.AddEvent(streamID)
|
span.AddEvent(streamID)
|
||||||
|
@ -142,7 +170,7 @@ func (s *service) SaltyUser(ctx context.Context, nick string) (*SaltyUser, error
|
||||||
ctx, span := logz.Span(ctx)
|
ctx, span := logz.Span(ctx)
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
s.Mresolver_salty_user.Add(ctx, 1)
|
s.m_get_user.Add(ctx, 1)
|
||||||
|
|
||||||
streamID := fmt.Sprintf("saltyuser-%x", sha256.Sum256([]byte(strings.ToLower(nick))))
|
streamID := fmt.Sprintf("saltyuser-%x", sha256.Sum256([]byte(strings.ToLower(nick))))
|
||||||
span.AddEvent(streamID)
|
span.AddEvent(streamID)
|
||||||
|
@ -168,3 +196,54 @@ func (s *service) GetMiddleware() func(http.Handler) http.Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) apiv1(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
ctx, span := logz.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
switch r.Method {
|
||||||
|
case http.MethodGet:
|
||||||
|
switch {
|
||||||
|
case r.URL.Path == "/ping":
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_, _ = w.Write([]byte(`{}`))
|
||||||
|
|
||||||
|
case strings.HasPrefix(r.URL.Path, "/lookup/"):
|
||||||
|
addr, err := s.ParseAddr(strings.TrimPrefix(r.URL.Path, "/lookup/"))
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = addr.Refresh(ctx)
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
json.NewEncoder(w).Encode(addr)
|
||||||
|
return
|
||||||
|
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case http.MethodPost:
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/register":
|
||||||
|
|
||||||
|
case "/send":
|
||||||
|
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
"github.com/99designs/gqlgen/plugin/federation/fedruntime"
|
"github.com/99designs/gqlgen/plugin/federation/fedruntime"
|
||||||
"github.com/sour-is/ev/app/msgbus"
|
"github.com/sour-is/ev/app/msgbus"
|
||||||
"github.com/sour-is/ev/app/salty"
|
"github.com/sour-is/ev/app/salty"
|
||||||
"github.com/sour-is/ev/internal/graph/model"
|
"github.com/sour-is/ev/pkg/es"
|
||||||
"github.com/sour-is/ev/pkg/es/event"
|
"github.com/sour-is/ev/pkg/es/event"
|
||||||
"github.com/sour-is/ev/pkg/gql"
|
"github.com/sour-is/ev/pkg/gql"
|
||||||
gqlparser "github.com/vektah/gqlparser/v2"
|
gqlparser "github.com/vektah/gqlparser/v2"
|
||||||
|
@ -58,10 +58,11 @@ type ComplexityRoot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
Event struct {
|
Event struct {
|
||||||
EventID func(childComplexity int) int
|
Bytes func(childComplexity int) int
|
||||||
EventMeta func(childComplexity int) int
|
EventID func(childComplexity int) int
|
||||||
ID func(childComplexity int) int
|
ID func(childComplexity int) int
|
||||||
Values func(childComplexity int) int
|
Meta func(childComplexity int) int
|
||||||
|
Values func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
Meta struct {
|
Meta struct {
|
||||||
|
@ -123,7 +124,7 @@ type QueryResolver interface {
|
||||||
SaltyUser(ctx context.Context, nick string) (*salty.SaltyUser, error)
|
SaltyUser(ctx context.Context, nick string) (*salty.SaltyUser, error)
|
||||||
}
|
}
|
||||||
type SubscriptionResolver interface {
|
type SubscriptionResolver interface {
|
||||||
EventAdded(ctx context.Context, streamID string, after int64) (<-chan *model.Event, error)
|
EventAdded(ctx context.Context, streamID string, after int64) (<-chan *es.GQLEvent, error)
|
||||||
PostAdded(ctx context.Context, streamID string, after int64) (<-chan *msgbus.PostEvent, error)
|
PostAdded(ctx context.Context, streamID string, after int64) (<-chan *msgbus.PostEvent, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +157,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Connection.Paging(childComplexity), true
|
return e.complexity.Connection.Paging(childComplexity), true
|
||||||
|
|
||||||
|
case "Event.bytes":
|
||||||
|
if e.complexity.Event.Bytes == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Event.Bytes(childComplexity), true
|
||||||
|
|
||||||
case "Event.eventID":
|
case "Event.eventID":
|
||||||
if e.complexity.Event.EventID == nil {
|
if e.complexity.Event.EventID == nil {
|
||||||
break
|
break
|
||||||
|
@ -163,13 +171,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Event.EventID(childComplexity), true
|
return e.complexity.Event.EventID(childComplexity), true
|
||||||
|
|
||||||
case "Event.eventMeta":
|
|
||||||
if e.complexity.Event.EventMeta == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.complexity.Event.EventMeta(childComplexity), true
|
|
||||||
|
|
||||||
case "Event.id":
|
case "Event.id":
|
||||||
if e.complexity.Event.ID == nil {
|
if e.complexity.Event.ID == nil {
|
||||||
break
|
break
|
||||||
|
@ -177,6 +178,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Event.ID(childComplexity), true
|
return e.complexity.Event.ID(childComplexity), true
|
||||||
|
|
||||||
|
case "Event.meta":
|
||||||
|
if e.complexity.Event.Meta == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Event.Meta(childComplexity), true
|
||||||
|
|
||||||
case "Event.values":
|
case "Event.values":
|
||||||
if e.complexity.Event.Values == nil {
|
if e.complexity.Event.Values == nil {
|
||||||
break
|
break
|
||||||
|
@ -491,12 +499,13 @@ extend type Subscription {
|
||||||
eventAdded(streamID: String! after: Int! = -1): Event
|
eventAdded(streamID: String! after: Int! = -1): Event
|
||||||
}
|
}
|
||||||
|
|
||||||
type Event implements Edge {
|
type Event implements Edge @goModel(model: "github.com/sour-is/ev/pkg/es.GQLEvent") {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
eventID: String!
|
eventID: String!
|
||||||
values: Map!
|
values: Map!
|
||||||
eventMeta: Meta!
|
bytes: String!
|
||||||
|
meta: Meta!
|
||||||
}`, BuiltIn: false},
|
}`, BuiltIn: false},
|
||||||
{Name: "../../../pkg/gql/common.graphqls", Input: `scalar Time
|
{Name: "../../../pkg/gql/common.graphqls", Input: `scalar Time
|
||||||
scalar Map
|
scalar Map
|
||||||
|
@ -877,7 +886,7 @@ func (ec *executionContext) fieldContext_Connection_edges(ctx context.Context, f
|
||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Event_id(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Event_id(ctx context.Context, field graphql.CollectedField, obj *es.GQLEvent) (ret graphql.Marshaler) {
|
||||||
fc, err := ec.fieldContext_Event_id(ctx, field)
|
fc, err := ec.fieldContext_Event_id(ctx, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
|
@ -891,7 +900,7 @@ func (ec *executionContext) _Event_id(ctx context.Context, field graphql.Collect
|
||||||
}()
|
}()
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return obj.ID, nil
|
return obj.ID(), nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
|
@ -912,7 +921,7 @@ func (ec *executionContext) fieldContext_Event_id(ctx context.Context, field gra
|
||||||
fc = &graphql.FieldContext{
|
fc = &graphql.FieldContext{
|
||||||
Object: "Event",
|
Object: "Event",
|
||||||
Field: field,
|
Field: field,
|
||||||
IsMethod: false,
|
IsMethod: true,
|
||||||
IsResolver: false,
|
IsResolver: false,
|
||||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
return nil, errors.New("field of type ID does not have child fields")
|
return nil, errors.New("field of type ID does not have child fields")
|
||||||
|
@ -921,7 +930,7 @@ func (ec *executionContext) fieldContext_Event_id(ctx context.Context, field gra
|
||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Event_eventID(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Event_eventID(ctx context.Context, field graphql.CollectedField, obj *es.GQLEvent) (ret graphql.Marshaler) {
|
||||||
fc, err := ec.fieldContext_Event_eventID(ctx, field)
|
fc, err := ec.fieldContext_Event_eventID(ctx, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
|
@ -935,7 +944,7 @@ func (ec *executionContext) _Event_eventID(ctx context.Context, field graphql.Co
|
||||||
}()
|
}()
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return obj.EventID, nil
|
return obj.EventID(), nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
|
@ -956,7 +965,7 @@ func (ec *executionContext) fieldContext_Event_eventID(ctx context.Context, fiel
|
||||||
fc = &graphql.FieldContext{
|
fc = &graphql.FieldContext{
|
||||||
Object: "Event",
|
Object: "Event",
|
||||||
Field: field,
|
Field: field,
|
||||||
IsMethod: false,
|
IsMethod: true,
|
||||||
IsResolver: false,
|
IsResolver: false,
|
||||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
return nil, errors.New("field of type String does not have child fields")
|
return nil, errors.New("field of type String does not have child fields")
|
||||||
|
@ -965,7 +974,7 @@ func (ec *executionContext) fieldContext_Event_eventID(ctx context.Context, fiel
|
||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Event_values(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Event_values(ctx context.Context, field graphql.CollectedField, obj *es.GQLEvent) (ret graphql.Marshaler) {
|
||||||
fc, err := ec.fieldContext_Event_values(ctx, field)
|
fc, err := ec.fieldContext_Event_values(ctx, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
|
@ -979,7 +988,7 @@ func (ec *executionContext) _Event_values(ctx context.Context, field graphql.Col
|
||||||
}()
|
}()
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return obj.Values, nil
|
return obj.Values(), nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
|
@ -1000,7 +1009,7 @@ func (ec *executionContext) fieldContext_Event_values(ctx context.Context, field
|
||||||
fc = &graphql.FieldContext{
|
fc = &graphql.FieldContext{
|
||||||
Object: "Event",
|
Object: "Event",
|
||||||
Field: field,
|
Field: field,
|
||||||
IsMethod: false,
|
IsMethod: true,
|
||||||
IsResolver: false,
|
IsResolver: false,
|
||||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
return nil, errors.New("field of type Map does not have child fields")
|
return nil, errors.New("field of type Map does not have child fields")
|
||||||
|
@ -1009,8 +1018,8 @@ func (ec *executionContext) fieldContext_Event_values(ctx context.Context, field
|
||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Event_eventMeta(ctx context.Context, field graphql.CollectedField, obj *model.Event) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Event_bytes(ctx context.Context, field graphql.CollectedField, obj *es.GQLEvent) (ret graphql.Marshaler) {
|
||||||
fc, err := ec.fieldContext_Event_eventMeta(ctx, field)
|
fc, err := ec.fieldContext_Event_bytes(ctx, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
|
@ -1023,7 +1032,51 @@ func (ec *executionContext) _Event_eventMeta(ctx context.Context, field graphql.
|
||||||
}()
|
}()
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return obj.EventMeta, nil
|
return obj.Bytes()
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(string)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) fieldContext_Event_bytes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
|
fc = &graphql.FieldContext{
|
||||||
|
Object: "Event",
|
||||||
|
Field: field,
|
||||||
|
IsMethod: true,
|
||||||
|
IsResolver: false,
|
||||||
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
|
return nil, errors.New("field of type String does not have child fields")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return fc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Event_meta(ctx context.Context, field graphql.CollectedField, obj *es.GQLEvent) (ret graphql.Marshaler) {
|
||||||
|
fc, err := ec.fieldContext_Event_meta(ctx, field)
|
||||||
|
if err != nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return obj.Meta(), nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
|
@ -1040,11 +1093,11 @@ func (ec *executionContext) _Event_eventMeta(ctx context.Context, field graphql.
|
||||||
return ec.marshalNMeta2ᚖgithubᚗcomᚋsourᚑisᚋevᚋpkgᚋesᚋeventᚐMeta(ctx, field.Selections, res)
|
return ec.marshalNMeta2ᚖgithubᚗcomᚋsourᚑisᚋevᚋpkgᚋesᚋeventᚐMeta(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) fieldContext_Event_eventMeta(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
func (ec *executionContext) fieldContext_Event_meta(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
fc = &graphql.FieldContext{
|
fc = &graphql.FieldContext{
|
||||||
Object: "Event",
|
Object: "Event",
|
||||||
Field: field,
|
Field: field,
|
||||||
IsMethod: false,
|
IsMethod: true,
|
||||||
IsResolver: false,
|
IsResolver: false,
|
||||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
switch field.Name {
|
switch field.Name {
|
||||||
|
@ -2269,7 +2322,7 @@ func (ec *executionContext) _Subscription_eventAdded(ctx context.Context, field
|
||||||
}
|
}
|
||||||
return func(ctx context.Context) graphql.Marshaler {
|
return func(ctx context.Context) graphql.Marshaler {
|
||||||
select {
|
select {
|
||||||
case res, ok := <-resTmp.(<-chan *model.Event):
|
case res, ok := <-resTmp.(<-chan *es.GQLEvent):
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2277,7 +2330,7 @@ func (ec *executionContext) _Subscription_eventAdded(ctx context.Context, field
|
||||||
w.Write([]byte{'{'})
|
w.Write([]byte{'{'})
|
||||||
graphql.MarshalString(field.Alias).MarshalGQL(w)
|
graphql.MarshalString(field.Alias).MarshalGQL(w)
|
||||||
w.Write([]byte{':'})
|
w.Write([]byte{':'})
|
||||||
ec.marshalOEvent2ᚖgithubᚗcomᚋsourᚑisᚋevᚋinternalᚋgraphᚋmodelᚐEvent(ctx, field.Selections, res).MarshalGQL(w)
|
ec.marshalOEvent2ᚖgithubᚗcomᚋsourᚑisᚋevᚋpkgᚋesᚐGQLEvent(ctx, field.Selections, res).MarshalGQL(w)
|
||||||
w.Write([]byte{'}'})
|
w.Write([]byte{'}'})
|
||||||
})
|
})
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
@ -2300,8 +2353,10 @@ func (ec *executionContext) fieldContext_Subscription_eventAdded(ctx context.Con
|
||||||
return ec.fieldContext_Event_eventID(ctx, field)
|
return ec.fieldContext_Event_eventID(ctx, field)
|
||||||
case "values":
|
case "values":
|
||||||
return ec.fieldContext_Event_values(ctx, field)
|
return ec.fieldContext_Event_values(ctx, field)
|
||||||
case "eventMeta":
|
case "bytes":
|
||||||
return ec.fieldContext_Event_eventMeta(ctx, field)
|
return ec.fieldContext_Event_bytes(ctx, field)
|
||||||
|
case "meta":
|
||||||
|
return ec.fieldContext_Event_meta(ctx, field)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no field named %q was found under type Event", field.Name)
|
return nil, fmt.Errorf("no field named %q was found under type Event", field.Name)
|
||||||
},
|
},
|
||||||
|
@ -4263,9 +4318,7 @@ func (ec *executionContext) _Edge(ctx context.Context, sel ast.SelectionSet, obj
|
||||||
switch obj := (obj).(type) {
|
switch obj := (obj).(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
case model.Event:
|
case *es.GQLEvent:
|
||||||
return ec._Event(ctx, sel, &obj)
|
|
||||||
case *model.Event:
|
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
|
@ -4321,7 +4374,7 @@ func (ec *executionContext) _Connection(ctx context.Context, sel ast.SelectionSe
|
||||||
|
|
||||||
var eventImplementors = []string{"Event", "Edge"}
|
var eventImplementors = []string{"Event", "Edge"}
|
||||||
|
|
||||||
func (ec *executionContext) _Event(ctx context.Context, sel ast.SelectionSet, obj *model.Event) graphql.Marshaler {
|
func (ec *executionContext) _Event(ctx context.Context, sel ast.SelectionSet, obj *es.GQLEvent) graphql.Marshaler {
|
||||||
fields := graphql.CollectFields(ec.OperationContext, sel, eventImplementors)
|
fields := graphql.CollectFields(ec.OperationContext, sel, eventImplementors)
|
||||||
out := graphql.NewFieldSet(fields)
|
out := graphql.NewFieldSet(fields)
|
||||||
var invalids uint32
|
var invalids uint32
|
||||||
|
@ -4350,9 +4403,16 @@ func (ec *executionContext) _Event(ctx context.Context, sel ast.SelectionSet, ob
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
case "eventMeta":
|
case "bytes":
|
||||||
|
|
||||||
out.Values[i] = ec._Event_eventMeta(ctx, field, obj)
|
out.Values[i] = ec._Event_bytes(ctx, field, obj)
|
||||||
|
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
|
case "meta":
|
||||||
|
|
||||||
|
out.Values[i] = ec._Event_meta(ctx, field, obj)
|
||||||
|
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
|
@ -5658,7 +5718,7 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalOEvent2ᚖgithubᚗcomᚋsourᚑisᚋevᚋinternalᚋgraphᚋmodelᚐEvent(ctx context.Context, sel ast.SelectionSet, v *model.Event) graphql.Marshaler {
|
func (ec *executionContext) marshalOEvent2ᚖgithubᚗcomᚋsourᚑisᚋevᚋpkgᚋesᚐGQLEvent(ctx context.Context, sel ast.SelectionSet, v *es.GQLEvent) graphql.Marshaler {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,3 @@
|
||||||
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
|
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
|
||||||
|
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/sour-is/ev/pkg/es/event"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Event struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
EventID string `json:"eventID"`
|
|
||||||
Values map[string]interface{} `json:"values"`
|
|
||||||
EventMeta *event.Meta `json:"eventMeta"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Event) IsEdge() {}
|
|
||||||
|
|
28
main.go
28
main.go
|
@ -68,6 +68,8 @@ func run(ctx context.Context) error {
|
||||||
enable := set(strings.Fields(env("EV_ENABLE", "salty msgbus gql peers"))...)
|
enable := set(strings.Fields(env("EV_ENABLE", "salty msgbus gql peers"))...)
|
||||||
var svcs []interface{ RegisterHTTP(*http.ServeMux) }
|
var svcs []interface{ RegisterHTTP(*http.ServeMux) }
|
||||||
|
|
||||||
|
svcs = append(svcs, es)
|
||||||
|
|
||||||
if enable.Has("salty") {
|
if enable.Has("salty") {
|
||||||
span.AddEvent("Enable Salty")
|
span.AddEvent("Enable Salty")
|
||||||
salty, err := salty.New(ctx, es, path.Join(env("EV_BASE_URL", "http://"+s.Addr), "inbox"))
|
salty, err := salty.New(ctx, es, path.Join(env("EV_BASE_URL", "http://"+s.Addr), "inbox"))
|
||||||
|
@ -148,10 +150,15 @@ func env(name, defaultValue string) string {
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
func httpMux(fns ...interface{ RegisterHTTP(*http.ServeMux) }) http.Handler {
|
func httpMux(fns ...interface{ RegisterHTTP(*http.ServeMux) }) http.Handler {
|
||||||
mux := http.NewServeMux()
|
mux := newMux()
|
||||||
for _, fn := range fns {
|
for _, fn := range fns {
|
||||||
fn.RegisterHTTP(mux)
|
fn.RegisterHTTP(mux.ServeMux)
|
||||||
|
|
||||||
|
if fn, ok := fn.(interface{ RegisterAPIv1(*http.ServeMux) }); ok {
|
||||||
|
fn.RegisterAPIv1(mux.api)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cors.AllowAll().Handler(mux)
|
return cors.AllowAll().Handler(mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,3 +190,20 @@ func (s Set[T]) String() string {
|
||||||
b.WriteString(")")
|
b.WriteString(")")
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mux struct {
|
||||||
|
*http.ServeMux
|
||||||
|
api *http.ServeMux
|
||||||
|
}
|
||||||
|
func newMux() *mux {
|
||||||
|
mux := &mux{
|
||||||
|
api: http.NewServeMux(),
|
||||||
|
ServeMux: http.NewServeMux(),
|
||||||
|
}
|
||||||
|
mux.Handle("/api/v1/", http.StripPrefix("/api/v1/", mux.api))
|
||||||
|
|
||||||
|
return mux
|
||||||
|
}
|
||||||
|
func (m mux) HandleAPIv1(pattern string, handler http.Handler) {
|
||||||
|
m.api.Handle(pattern, handler)
|
||||||
|
}
|
|
@ -14,10 +14,11 @@ extend type Subscription {
|
||||||
eventAdded(streamID: String! after: Int! = -1): Event
|
eventAdded(streamID: String! after: Int! = -1): Event
|
||||||
}
|
}
|
||||||
|
|
||||||
type Event implements Edge {
|
type Event implements Edge @goModel(model: "github.com/sour-is/ev/pkg/es.GQLEvent") {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
|
||||||
eventID: String!
|
eventID: String!
|
||||||
values: Map!
|
values: Map!
|
||||||
eventMeta: Meta!
|
bytes: String!
|
||||||
|
meta: Meta!
|
||||||
}
|
}
|
|
@ -247,11 +247,22 @@ func embedJSON(s string) json.RawMessage {
|
||||||
return []byte(fmt.Sprintf(`"%s"`, strings.Replace(s, `"`, `\"`, -1)))
|
return []byte(fmt.Sprintf(`"%s"`, strings.Replace(s, `"`, `\"`, -1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func values(e Event) map[string]any {
|
func Values(e Event) map[string]any {
|
||||||
m := make(map[string]any)
|
var a any = e
|
||||||
v := reflect.ValueOf(e)
|
|
||||||
for _, idx := range reflect.VisibleFields(v) {
|
if e, ok := e.(interface{ Values() any }); ok {
|
||||||
field := v.FieldByIndex(idx.Index)
|
a = e.Values()
|
||||||
m[field.]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m := make(map[string]any)
|
||||||
|
v := reflect.Indirect(reflect.ValueOf(a))
|
||||||
|
for _, idx := range reflect.VisibleFields(v.Type()) {
|
||||||
|
if !idx.IsExported() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
field := v.FieldByIndex(idx.Index)
|
||||||
|
m[idx.Name] = field.Interface()
|
||||||
|
}
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
128
pkg/es/graph.go
Normal file
128
pkg/es/graph.go
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
package es
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sour-is/ev/internal/logz"
|
||||||
|
"github.com/sour-is/ev/pkg/es/event"
|
||||||
|
"github.com/sour-is/ev/pkg/gql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EventResolver interface {
|
||||||
|
Events(ctx context.Context, streamID string, paging *gql.PageInput) (*gql.Connection, error)
|
||||||
|
EventAdded(ctx context.Context, streamID string, after int64) (<-chan *GQLEvent, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (es *EventStore) Events(ctx context.Context, streamID string, paging *gql.PageInput) (*gql.Connection, error) {
|
||||||
|
ctx, span := logz.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
lis, err := es.Read(ctx, streamID, paging.GetIdx(0), paging.GetCount(30))
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
edges := make([]gql.Edge, 0, len(lis))
|
||||||
|
for i := range lis {
|
||||||
|
span.AddEvent(fmt.Sprint("event ", i, " of ", len(lis)))
|
||||||
|
edges = append(edges, &GQLEvent{lis[i]})
|
||||||
|
}
|
||||||
|
|
||||||
|
var first, last uint64
|
||||||
|
if first, err = es.FirstIndex(ctx, streamID); err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if last, err = es.LastIndex(ctx, streamID); err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &gql.Connection{
|
||||||
|
Paging: &gql.PageInfo{
|
||||||
|
Next: lis.Last().EventMeta().Position < last,
|
||||||
|
Prev: lis.First().EventMeta().Position > first,
|
||||||
|
Begin: lis.First().EventMeta().Position,
|
||||||
|
End: lis.Last().EventMeta().Position,
|
||||||
|
},
|
||||||
|
Edges: edges,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
func (e *EventStore) EventAdded(ctx context.Context, streamID string, after int64) (<-chan *GQLEvent, error) {
|
||||||
|
ctx, span := logz.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
es := e.EventStream()
|
||||||
|
if es == nil {
|
||||||
|
return nil, fmt.Errorf("EventStore does not implement streaming")
|
||||||
|
}
|
||||||
|
|
||||||
|
sub, err := es.Subscribe(ctx, streamID, after)
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan *GQLEvent)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ctx, span := logz.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
err := sub.Close(ctx)
|
||||||
|
span.RecordError(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for sub.Recv(ctx) {
|
||||||
|
events, err := sub.Events(ctx)
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
span.AddEvent(fmt.Sprintf("received %d events", len(events)))
|
||||||
|
|
||||||
|
for i := range events {
|
||||||
|
select {
|
||||||
|
case ch <- &GQLEvent{events[i]}:
|
||||||
|
continue
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
func (*EventStore) RegisterHTTP(*http.ServeMux) {}
|
||||||
|
|
||||||
|
type GQLEvent struct {
|
||||||
|
e event.Event
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *GQLEvent) ID() string {
|
||||||
|
return "Event/" + e.e.EventMeta().GetEventID()
|
||||||
|
}
|
||||||
|
func (e *GQLEvent) EventID() string {
|
||||||
|
return e.e.EventMeta().GetEventID()
|
||||||
|
}
|
||||||
|
func (e *GQLEvent) Values() map[string]interface{} {
|
||||||
|
return event.Values(e.e)
|
||||||
|
}
|
||||||
|
func (e *GQLEvent) Bytes() (string, error) {
|
||||||
|
b, err := e.e.MarshalBinary()
|
||||||
|
return string(b), err
|
||||||
|
}
|
||||||
|
func (e *GQLEvent) Meta() *event.Meta {
|
||||||
|
meta := e.e.EventMeta()
|
||||||
|
return &meta
|
||||||
|
}
|
||||||
|
func (e *GQLEvent) IsEdge() {}
|
Loading…
Reference in New Issue
Block a user