diff --git a/cmd/event-service/di/inject_application.go b/cmd/event-service/di/inject_application.go index c3a066c..2904a9f 100644 --- a/cmd/event-service/di/inject_application.go +++ b/cmd/event-service/di/inject_application.go @@ -18,4 +18,5 @@ var applicationSet = wire.NewSet( app.NewUpdateMetricsHandler, app.NewAddPublicKeyToMonitorHandler, + app.NewGetEventHandler, ) diff --git a/cmd/event-service/di/wire_gen.go b/cmd/event-service/di/wire_gen.go index c5be0ba..c7c062d 100644 --- a/cmd/event-service/di/wire_gen.go +++ b/cmd/event-service/di/wire_gen.go @@ -69,11 +69,13 @@ func BuildService(contextContext context.Context, configConfig config.Config) (S subscriber := sqlite.NewSubscriber(pubSub, db) updateMetricsHandler := app.NewUpdateMetricsHandler(genericTransactionProvider, subscriber, logger, prometheusPrometheus) addPublicKeyToMonitorHandler := app.NewAddPublicKeyToMonitorHandler(genericTransactionProvider, logger, prometheusPrometheus) + getEventHandler := app.NewGetEventHandler(genericTransactionProvider, logger, prometheusPrometheus) application := app.Application{ SaveReceivedEvent: saveReceivedEventHandler, ProcessSavedEvent: processSavedEventHandler, UpdateMetrics: updateMetricsHandler, AddPublicKeyToMonitor: addPublicKeyToMonitorHandler, + GetEvent: getEventHandler, } server := http.NewServer(configConfig, logger, application, prometheusPrometheus) bootstrapRelaySource := relays.NewBootstrapRelaySource() diff --git a/go.mod b/go.mod index 93f0028..0c00b95 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,10 @@ require ( github.com/ThreeDotsLabs/watermill v1.3.1 github.com/ThreeDotsLabs/watermill-googlecloud v1.0.13 github.com/boreq/errors v0.1.0 + github.com/boreq/rest v0.1.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 github.com/google/wire v0.5.0 + github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/go-multierror v1.1.1 github.com/mattn/go-sqlite3 v1.14.18 diff --git a/go.sum b/go.sum index a243d00..5a6bcc0 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/boreq/errors v0.1.0 h1:aJIXv9JnyR5KtxFpQ8/AiblH3nfYmr1e1yoTze/5A1k= github.com/boreq/errors v0.1.0/go.mod h1:B3dsXzhYvfgUXp7ViU/moPYM4PojgQ9MiQ21uvY6qqQ= +github.com/boreq/rest v0.1.0 h1:bAx31Rp1KrXHkCOlzqAtLKdh74xbly2SHkv9k3vX3iA= +github.com/boreq/rest v0.1.0/go.mod h1:Ckfx0qLDdPbS081820aWkkqvwhlrbv0SDu8UBDY4k7w= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= @@ -146,6 +148,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9 github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc= github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= diff --git a/service/app/app.go b/service/app/app.go index 4014b8c..5af7688 100644 --- a/service/app/app.go +++ b/service/app/app.go @@ -70,6 +70,7 @@ type Application struct { ProcessSavedEvent *ProcessSavedEventHandler UpdateMetrics *UpdateMetricsHandler AddPublicKeyToMonitor *AddPublicKeyToMonitorHandler + GetEvent *GetEventHandler } type ReceivedEvent struct { diff --git a/service/app/handler_get_event.go b/service/app/handler_get_event.go new file mode 100644 index 0000000..175753a --- /dev/null +++ b/service/app/handler_get_event.go @@ -0,0 +1,52 @@ +package app + +import ( + "context" + + "github.com/boreq/errors" + "github.com/planetary-social/nos-event-service/internal/logging" + "github.com/planetary-social/nos-event-service/service/domain" +) + +type GetEvent struct { + id domain.EventId +} + +func NewGetEvent(id domain.EventId) GetEvent { + return GetEvent{id: id} +} + +type GetEventHandler struct { + transactionProvider TransactionProvider + logger logging.Logger + metrics Metrics +} + +func NewGetEventHandler( + transactionProvider TransactionProvider, + logger logging.Logger, + metrics Metrics, +) *GetEventHandler { + return &GetEventHandler{ + transactionProvider: transactionProvider, + logger: logger.New("getEventHandler"), + metrics: metrics, + } +} + +func (h *GetEventHandler) Handle(ctx context.Context, cmd GetEvent) (event domain.Event, err error) { + defer h.metrics.StartApplicationCall("getEvent").End(&err) + + if err := h.transactionProvider.Transact(ctx, func(ctx context.Context, adapters Adapters) error { + tmp, err := adapters.Events.Get(ctx, cmd.id) + if err != nil { + return errors.Wrap(err, "error getting the event") + } + event = tmp + return nil + }); err != nil { + return domain.Event{}, errors.Wrap(err, "transaction error") + } + + return event, nil +} diff --git a/service/domain/event.go b/service/domain/event.go index e3b7052..743849e 100644 --- a/service/domain/event.go +++ b/service/domain/event.go @@ -40,7 +40,7 @@ func NewEvent(libevent nostr.Event) (Event, error) { return Event{}, errors.New("invalid signature") } - id, err := NewEventId(libevent.ID) + id, err := NewEventIdFromHex(libevent.ID) if err != nil { return Event{}, errors.Wrap(err, "error creating an event id") } diff --git a/service/domain/event_id.go b/service/domain/event_id.go index 1eb22d1..98ac8cc 100644 --- a/service/domain/event_id.go +++ b/service/domain/event_id.go @@ -12,7 +12,7 @@ type EventId struct { s string } -func NewEventId(s string) (EventId, error) { +func NewEventIdFromHex(s string) (EventId, error) { b, err := hex.DecodeString(s) if err != nil { return EventId{}, errors.Wrap(err, "error decoding hex") @@ -27,7 +27,7 @@ func NewEventId(s string) (EventId, error) { } func MustNewEventId(s string) EventId { - v, err := NewEventId(s) + v, err := NewEventIdFromHex(s) if err != nil { panic(err) } @@ -46,7 +46,7 @@ func NewEventIdFromNote(s string) (EventId, error) { if !ok { return EventId{}, errors.New("library returned invalid type") } - return NewEventId(s) + return NewEventIdFromHex(s) } func (id EventId) Hex() string { diff --git a/service/domain/relays/relay_connection.go b/service/domain/relays/relay_connection.go index 8273da3..1663bba 100644 --- a/service/domain/relays/relay_connection.go +++ b/service/domain/relays/relay_connection.go @@ -357,7 +357,7 @@ func (r *RelayConnection) handleMessage(messageBytes []byte) (err error) { WithField("message", string(messageBytes)). Message("received a message (ok)") - eventID, err := domain.NewEventId(v.EventID) + eventID, err := domain.NewEventIdFromHex(v.EventID) if err != nil { return errors.Wrap(err, "error creating an event") } diff --git a/service/ports/http/http.go b/service/ports/http/http.go index eb1590e..58f93d0 100644 --- a/service/ports/http/http.go +++ b/service/ports/http/http.go @@ -2,10 +2,13 @@ package http import ( "context" + "encoding/json" "net" "net/http" "github.com/boreq/errors" + "github.com/boreq/rest" + "github.com/gorilla/mux" "github.com/gorilla/websocket" "github.com/nbd-wtf/go-nostr" "github.com/planetary-social/nos-event-service/internal/logging" @@ -56,11 +59,12 @@ func (s *Server) ListenAndServe(ctx context.Context) error { return http.Serve(listener, mux) } -func (s *Server) createMux() *http.ServeMux { - mux := http.NewServeMux() - mux.Handle("/metrics", promhttp.HandlerFor(s.prometheus.Registry(), promhttp.HandlerOpts{})) - mux.HandleFunc("/", s.serveWs) - return mux +func (s *Server) createMux() http.Handler { + r := mux.NewRouter() + r.Handle("/metrics", promhttp.HandlerFor(s.prometheus.Registry(), promhttp.HandlerOpts{})) + r.HandleFunc("/events/{id}", rest.Wrap(s.serveEvents)) + r.HandleFunc("/", s.serveWs) + return r } func (s *Server) serveWs(rw http.ResponseWriter, r *http.Request) { @@ -91,6 +95,41 @@ func (s *Server) serveWs(rw http.ResponseWriter, r *http.Request) { } } +func (s *Server) serveEvents(r *http.Request) rest.RestResponse { + switch r.Method { + case http.MethodGet: + vars := mux.Vars(r) + idString := vars["id"] + + eventID, err := domain.NewEventIdFromHex(idString) + if err != nil { + return rest.ErrBadRequest.WithMessage("event id must be in hex") + } + + event, err := s.app.GetEvent.Handle(r.Context(), app.NewGetEvent(eventID)) + if err != nil { + if errors.Is(err, app.ErrEventNotFound) { + return rest.ErrNotFound + } + + s.logger.Error().WithError(err).Message("error getting an event") + return rest.ErrInternalServerError + } + + return rest.NewResponse(newGetEventTransport(event)) + default: + return rest.ErrMethodNotAllowed + } +} + +type getEventTransport struct { + Event json.RawMessage `json:"event"` +} + +func newGetEventTransport(event domain.Event) getEventTransport { + return getEventTransport{Event: event.Raw()} +} + func (s *Server) handleConnection(ctx context.Context, conn *websocket.Conn) error { ctx, cancel := context.WithCancel(ctx) defer cancel() diff --git a/service/ports/sqlitepubsub/event_saved.go b/service/ports/sqlitepubsub/event_saved.go index 0b43e17..c8afb49 100644 --- a/service/ports/sqlitepubsub/event_saved.go +++ b/service/ports/sqlitepubsub/event_saved.go @@ -58,7 +58,7 @@ func (s *EventSavedEventSubscriber) handleMessage(ctx context.Context, msg *sqli return errors.Wrap(err, "error unmarshaling") } - eventID, err := domain.NewEventId(transport.EventID) + eventID, err := domain.NewEventIdFromHex(transport.EventID) if err != nil { return errors.Wrap(err, "error creating an account id") }