working private event notifications.

This commit is contained in:
Jason Kulatunga 2023-09-08 11:27:38 -07:00
parent 862e3d6ea7
commit 6a3afe150e
5 changed files with 78 additions and 25 deletions

View File

@ -357,7 +357,10 @@ func (sr *SqliteRepository) UpsertResource(ctx context.Context, wrappedResourceM
//wrappedFhirResourceModel.SetResourceRaw(wrappedResourceModel.ResourceRaw) //wrappedFhirResourceModel.SetResourceRaw(wrappedResourceModel.ResourceRaw)
} }
sr.EventBus.Message <- fmt.Sprintf("resource.upsert %s/%s", wrappedResourceModel.SourceResourceType, wrappedResourceModel.SourceResourceID) sr.EventBus.Message <- sse.EventBusMessage{
Message: fmt.Sprintf("resource.upsert %s/%s", wrappedResourceModel.SourceResourceType, wrappedResourceModel.SourceResourceID),
UserID: currentUser.ID.String(),
}
createResult := sr.GormClient.WithContext(ctx).Where(models.OriginBase{ createResult := sr.GormClient.WithContext(ctx).Where(models.OriginBase{
SourceID: wrappedFhirResourceModel.GetSourceID(), SourceID: wrappedFhirResourceModel.GetSourceID(),
SourceResourceID: wrappedFhirResourceModel.GetSourceResourceID(), SourceResourceID: wrappedFhirResourceModel.GetSourceResourceID(),

View File

@ -5,6 +5,7 @@ import (
"github.com/fastenhealth/fasten-onprem/backend/pkg/web/sse" "github.com/fastenhealth/fasten-onprem/backend/pkg/web/sse"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"io" "io"
"log"
) )
// SSEStream is a handler for the server sent event stream (notifications from background processes) // SSEStream is a handler for the server sent event stream (notifications from background processes)
@ -19,15 +20,16 @@ func SSEStream(c *gin.Context) {
//databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository) //databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
v, ok := c.Get(pkg.ContextKeyTypeSSEClientChannel) v, ok := c.Get(pkg.ContextKeyTypeSSEClientChannel)
if !ok { if !ok {
log.Printf("could not get client channel from context")
return return
} }
clientChan, ok := v.(sse.ClientChan) listener, ok := v.(sse.EventBusListener)
if !ok { if !ok {
return return
} }
c.Stream(func(w io.Writer) bool { c.Stream(func(w io.Writer) bool {
// Stream message to client from message channel // Stream message to client from message channel
if msg, ok := <-clientChan; ok { if msg, ok := <-listener.ResponseChan; ok {
c.SSEvent("message", msg) c.SSEvent("message", msg)
return true return true
} }

View File

@ -1,7 +1,9 @@
package middleware package middleware
import ( import (
"fmt"
"github.com/fastenhealth/fasten-onprem/backend/pkg" "github.com/fastenhealth/fasten-onprem/backend/pkg"
"github.com/fastenhealth/fasten-onprem/backend/pkg/database"
"github.com/fastenhealth/fasten-onprem/backend/pkg/web/sse" "github.com/fastenhealth/fasten-onprem/backend/pkg/web/sse"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -22,19 +24,31 @@ func SSEEventBusServerMiddleware() gin.HandlerFunc {
bus := sse.GetEventBusServer() bus := sse.GetEventBusServer()
return func(c *gin.Context) { return func(c *gin.Context) {
//get a reference to the current user
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
foundUser, err := databaseRepo.GetCurrentUser(c)
if err != nil || foundUser == nil {
c.Error(fmt.Errorf("could not find user"))
return
}
// Initialize client channel // Initialize client channel
clientChan := make(sse.ClientChan) clientListener := sse.EventBusListener{
ResponseChan: make(chan string),
UserID: foundUser.ID.String(),
}
// Send new connection to event server // Send new connection to event server
bus.NewClients <- clientChan bus.NewListener <- clientListener
defer func() { defer func() {
// Send closed connection to event server // Send closed connection to event server
bus.ClosedClients <- clientChan bus.ClosedListener <- clientListener
}() }()
c.Set(pkg.ContextKeyTypeSSEEventBusServer, bus) c.Set(pkg.ContextKeyTypeSSEEventBusServer, bus)
c.Set(pkg.ContextKeyTypeSSEClientChannel, clientChan) c.Set(pkg.ContextKeyTypeSSEClientChannel, clientListener)
c.Next() c.Next()
} }

View File

@ -39,7 +39,10 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) {
// check if access to database // check if access to database
bus := sse.GetEventBusServer() bus := sse.GetEventBusServer()
bus.Message <- "sse heartbeat" bus.Message <- sse.EventBusMessage{
UserID: "heartbeat",
Message: "sse heartbeat",
}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"success": true, "success": true,

View File

@ -24,10 +24,10 @@ func GetEventBusServer() *EventBus {
if singletonEventBusInstance == nil { if singletonEventBusInstance == nil {
fmt.Println("Creating single instance now.") fmt.Println("Creating single instance now.")
singletonEventBusInstance = &EventBus{ singletonEventBusInstance = &EventBus{
Message: make(chan string), Message: make(chan EventBusMessage),
NewClients: make(chan chan string), NewListener: make(chan EventBusListener),
ClosedClients: make(chan chan string), ClosedListener: make(chan EventBusListener),
TotalClients: make(map[chan string]bool), TotalRoomListeners: make(map[string][]EventBusListener),
} }
// Start processing requests // Start processing requests
@ -46,16 +46,26 @@ func GetEventBusServer() *EventBus {
// and broadcasting events to those clients. // and broadcasting events to those clients.
type EventBus struct { type EventBus struct {
// Events are pushed to this channel by the main events-gathering routine // Events are pushed to this channel by the main events-gathering routine
Message chan string Message chan EventBusMessage
// New client connections // New client connections
NewClients chan chan string NewListener chan EventBusListener
// Closed client connections // Closed client connections
ClosedClients chan chan string ClosedListener chan EventBusListener
// Total client connections // Total client connections
TotalClients map[chan string]bool TotalRoomListeners map[string][]EventBusListener
}
type EventBusListener struct {
ResponseChan chan string
UserID string
}
type EventBusMessage struct {
UserID string
Message string
} }
// It Listens all incoming requests from clients. // It Listens all incoming requests from clients.
@ -65,21 +75,42 @@ func (bus *EventBus) listen() {
for { for {
select { select {
// Add new available client // Add new available client
case client := <-bus.NewClients: case listener := <-bus.NewListener:
bus.TotalClients[client] = true //check if this userId room already exists, or create it
log.Printf("Client added. %d registered clients", len(bus.TotalClients)) if _, exists := bus.TotalRoomListeners[listener.UserID]; !exists {
bus.TotalRoomListeners[listener.UserID] = []EventBusListener{}
}
bus.TotalRoomListeners[listener.UserID] = append(bus.TotalRoomListeners[listener.UserID], listener)
log.Printf("Listener added to room: `%s`. %d registered listeners", listener.UserID, len(bus.TotalRoomListeners[listener.UserID]))
// Remove closed client // Remove closed client
case client := <-bus.ClosedClients: case listener := <-bus.ClosedListener:
delete(bus.TotalClients, client) if _, exists := bus.TotalRoomListeners[listener.UserID]; !exists {
close(client) log.Printf("Room `%s` not found", listener.UserID)
log.Printf("Removed client. %d registered clients", len(bus.TotalClients)) continue
} else {
//loop through all the listeners in the room and remove the one that matches
for i, v := range bus.TotalRoomListeners[listener.UserID] {
if v.ResponseChan == listener.ResponseChan {
bus.TotalRoomListeners[listener.UserID] = append(bus.TotalRoomListeners[listener.UserID][:i], bus.TotalRoomListeners[listener.UserID][i+1:]...)
close(listener.ResponseChan)
log.Printf("Removed listener from room: `%s`. %d registered clients", listener.UserID, len(bus.TotalRoomListeners[listener.UserID]))
break
}
}
}
// Broadcast message to client // Broadcast message to client
case eventMsg := <-bus.Message: case eventMsg := <-bus.Message:
for clientMessageChan := range bus.TotalClients { if _, exists := bus.TotalRoomListeners[eventMsg.UserID]; !exists {
clientMessageChan <- eventMsg log.Printf("Room `%s` not found, could not send message: `%s`", eventMsg.UserID, eventMsg.Message)
} continue
} else {
for _, roomListener := range bus.TotalRoomListeners[eventMsg.UserID] {
roomListener.ResponseChan <- eventMsg.Message
}
}
} }
} }
} }