138 lines
3.8 KiB
Go
138 lines
3.8 KiB
Go
package localserver
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
type ZoneRequestScheduler struct {
|
|
handlersPublishers []chan<- *ZoneRequest
|
|
}
|
|
|
|
type ZoneRequest struct {
|
|
ReqType string `json:"reqType"`
|
|
Payload map[string]interface{} `json:"payload"`
|
|
From string `json:"from"`
|
|
}
|
|
|
|
type ZoneResponse struct {
|
|
Type string `json:"type"`
|
|
To string `json:"to"`
|
|
From string `json:"from"`
|
|
Payload map[string]interface{} `json:"payload"`
|
|
}
|
|
|
|
type ZoneRequestHandler interface {
|
|
Init(ctx context.Context, authorizedMembers []string) (err error)
|
|
Subscribe(ctx context.Context, publisher <-chan *ZoneRequest) (reqChan chan *ZoneRequest, done chan struct{}, errCh chan error)
|
|
handleZoneRequest(ctx context.Context, req *ZoneRequest) (err error)
|
|
}
|
|
|
|
func verifyFieldsString(payload map[string]interface{}, fields ...string) (err error) {
|
|
for _, field := range fields {
|
|
if _, ok := payload[field]; !ok {
|
|
err = fmt.Errorf("no field %s in payload", field)
|
|
return
|
|
} else if _, ok := payload[field].(string); !ok {
|
|
err = fmt.Errorf("field %s in payload is not a string", field)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func verifyFieldsBool(payload map[string]interface{}, fields ...string) (err error) {
|
|
for _, field := range fields {
|
|
if _, ok := payload[field]; !ok {
|
|
err = fmt.Errorf("no field %s in payload", field)
|
|
return
|
|
} else if _, ok := payload[field].(bool); !ok {
|
|
err = fmt.Errorf("field %s in payload is not a bool", field)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func verifyFieldsFloat64(payload map[string]interface{}, fields ...string) (err error) {
|
|
for _, field := range fields {
|
|
if _, ok := payload[field]; !ok {
|
|
err = fmt.Errorf("no field %s in payload", field)
|
|
return
|
|
} else if _, ok := payload[field].(float64); !ok {
|
|
err = fmt.Errorf("field %s in payload is not a float64", field)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func verifyFieldsSliceInterface(payload map[string]interface{}, fields ...string) (err error) {
|
|
for _, field := range fields {
|
|
if _, ok := payload[field]; !ok {
|
|
err = fmt.Errorf("no field %s in payload", field)
|
|
return
|
|
} else if _, ok := payload[field].([]any); !ok {
|
|
err = fmt.Errorf("field %s in payload is not a []interface{}", field)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func NewZoneRequestScheduler(authorizedMembers []string, handlers ...ZoneRequestHandler) (zoneRequestScheduler *ZoneRequestScheduler, handlerErrCh chan error) {
|
|
zoneRequestScheduler = new(ZoneRequestScheduler)
|
|
zoneRequestScheduler.handlersPublishers = make([]chan<- *ZoneRequest, 0)
|
|
handlerErrCh = make(chan error)
|
|
reqChans := []chan *ZoneRequest{}
|
|
for _, handler := range handlers {
|
|
publisher := make(chan *ZoneRequest)
|
|
zoneRequestScheduler.handlersPublishers = append(zoneRequestScheduler.handlersPublishers, publisher)
|
|
reqChan, done, errCh := handler.Subscribe(context.Background(), publisher)
|
|
go func(done <-chan struct{}, errCh <-chan error) {
|
|
for {
|
|
select {
|
|
case <-done:
|
|
return
|
|
case handlerErrCh <- <-errCh:
|
|
}
|
|
}
|
|
}(done, errCh)
|
|
reqChans = append(reqChans, reqChan)
|
|
}
|
|
|
|
for _, reqChan := range reqChans {
|
|
go func(reqChan <-chan *ZoneRequest) {
|
|
done, errCh := zoneRequestScheduler.Schedule(reqChan)
|
|
for {
|
|
select {
|
|
case <-done:
|
|
return
|
|
case e := <-errCh:
|
|
logger.Println("from there", e)
|
|
}
|
|
}
|
|
}(reqChan)
|
|
}
|
|
for i, handler := range handlers {
|
|
if ierr := handler.Init(context.Background(), authorizedMembers); ierr != nil {
|
|
logger.Println(ierr)
|
|
}
|
|
logger.Println("init done for handler", i)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zrs *ZoneRequestScheduler) Schedule(reqChan <-chan *ZoneRequest) (done chan struct{}, errCh chan error) {
|
|
done, errCh = make(chan struct{}), make(chan error)
|
|
go func() {
|
|
for req := range reqChan {
|
|
for _, publisher := range zrs.handlersPublishers {
|
|
publisher <- req
|
|
}
|
|
}
|
|
done <- struct{}{}
|
|
}()
|
|
return
|
|
}
|