129 lines
3.3 KiB
Go
129 lines
3.3 KiB
Go
// Copyright © 2023 Roberto Hidalgo <event-gateway@un.rob.mx>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
package http
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"git.rob.mx/nidito/chinampa/pkg/logger"
|
|
"git.rob.mx/nidito/event-gateway/internal/config"
|
|
"git.rob.mx/nidito/event-gateway/internal/payload"
|
|
"git.rob.mx/nidito/event-gateway/internal/sink"
|
|
"git.rob.mx/nidito/event-gateway/internal/sink/types"
|
|
types1 "git.rob.mx/nidito/event-gateway/internal/source/types"
|
|
"github.com/julienschmidt/httprouter"
|
|
|
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/codes"
|
|
"go.opentelemetry.io/otel/trace"
|
|
)
|
|
|
|
var log = logger.Sub("event-gateway.source.http")
|
|
|
|
type listener struct {
|
|
ID string
|
|
Path string `json:"path"`
|
|
Event types.Event
|
|
}
|
|
|
|
type Source struct {
|
|
Router *httprouter.Router
|
|
listeners map[string]*listener
|
|
byPath map[string]*listener
|
|
}
|
|
|
|
var _ config.Source = &Source{}
|
|
|
|
func New(router *httprouter.Router) *Source {
|
|
if router == nil {
|
|
router = httprouter.New()
|
|
}
|
|
|
|
s := &Source{
|
|
Router: router,
|
|
}
|
|
s.Initialize()
|
|
return s
|
|
}
|
|
|
|
func (src *Source) Kind() types1.Kind {
|
|
return types1.HTTP
|
|
}
|
|
|
|
func (src *Source) Initialize() {
|
|
src.listeners = map[string]*listener{}
|
|
src.byPath = map[string]*listener{}
|
|
handler := otelhttp.NewHandler(src, "http-event")
|
|
src.Router.Handler("GET", "/-/:source", handler)
|
|
src.Router.Handler("POST", "/-/:source", handler)
|
|
}
|
|
|
|
func (src *Source) Register(l *config.Listener) error {
|
|
source := &listener{ID: l.ID, Event: l.Event}
|
|
if err := json.Unmarshal(l.Source.Config, &source); err != nil {
|
|
return err
|
|
}
|
|
|
|
if source.Path == "" {
|
|
return fmt.Errorf("no path set for http source %s: %+v", l.ID, l.Source.Config)
|
|
}
|
|
|
|
log.Debugf("http: registering %s (%s)", source.ID, source.Path)
|
|
src.listeners[source.ID] = source
|
|
src.byPath[source.Path] = source
|
|
return nil
|
|
}
|
|
|
|
func (src *Source) Deregister(id string) {
|
|
if existing, ok := src.listeners[id]; ok {
|
|
log.Debugf("http: deregistering %s (%s)", id, existing.Path)
|
|
delete(src.byPath, existing.Path)
|
|
delete(src.listeners, id)
|
|
}
|
|
}
|
|
|
|
func (src *Source) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
p := httprouter.ParamsFromContext(r.Context())
|
|
var response string
|
|
sourceID := p.ByName("source")
|
|
source, ok := src.byPath[sourceID]
|
|
span := trace.SpanFromContext(r.Context())
|
|
if ok {
|
|
span.SetAttributes(attribute.String("source", source.ID))
|
|
log.Infof("Incoming event from source: http:%s", source.ID)
|
|
response = "processed"
|
|
|
|
go func(r *http.Request) {
|
|
ctx := payload.Context(r.Context(), &Payload{Req: r.Clone(context.Background())})
|
|
if err := sink.Dispatch(ctx, source.Event); err != nil {
|
|
log.Errorf("could not trigger source: %s\n", err)
|
|
span.RecordError(err)
|
|
span.SetStatus(codes.Error, "could not trigger source")
|
|
span.End()
|
|
return
|
|
}
|
|
span.SetStatus(codes.Ok, "")
|
|
}(r)
|
|
} else {
|
|
span.SetAttributes(attribute.String("source", sourceID))
|
|
span.RecordError(fmt.Errorf("no listener for source"))
|
|
span.End()
|
|
response = "ignored"
|
|
log.Debugf("Ignoring unknown source: http:%s", sourceID)
|
|
}
|
|
|
|
w.Write([]byte(response))
|
|
}
|
|
|
|
func (src *Source) Events() []string {
|
|
res := []string{}
|
|
for _, l := range src.listeners {
|
|
res = append(res, l.ID)
|
|
}
|
|
return res
|
|
}
|