2024-10-03 19:03:18 +01:00
|
|
|
package library
|
|
|
|
|
|
|
|
import (
|
2024-12-31 15:53:45 +00:00
|
|
|
"errors"
|
2024-10-20 19:45:28 +01:00
|
|
|
"github.com/go-chi/chi/v5"
|
2024-10-03 19:03:18 +01:00
|
|
|
"github.com/google/uuid"
|
|
|
|
"io/fs"
|
2025-01-01 12:23:29 +00:00
|
|
|
"math/big"
|
2024-12-31 15:53:45 +00:00
|
|
|
"sync"
|
2024-10-03 19:03:18 +01:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Permissions struct {
|
2025-01-01 12:23:29 +00:00
|
|
|
// Authenticate allows the service to register with the nucleus authentication service and use OAuth2
|
|
|
|
Authenticate bool `validate:"required"`
|
|
|
|
// Router allows the service to serve web pages
|
|
|
|
Router bool `validate:"required"`
|
|
|
|
// Database allows the service to ask for a centralised database connection
|
|
|
|
Database bool `validate:"required"`
|
|
|
|
// BlobStorage allows the service to use the blob storage service
|
|
|
|
BlobStorage bool `validate:"required"`
|
|
|
|
// InterServiceCommunication allows the service to send and receive messages from other services
|
2024-10-03 19:03:18 +01:00
|
|
|
InterServiceCommunication bool `validate:"required"`
|
2025-01-01 12:23:29 +00:00
|
|
|
// Resources allows the service to access their resource directory
|
|
|
|
Resources bool `validate:"required"`
|
2024-10-03 19:03:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type Service struct {
|
|
|
|
Name string `validate:"required"`
|
|
|
|
Permissions Permissions `validate:"required"`
|
|
|
|
ServiceID uuid.UUID `validate:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type InterServiceMessage struct {
|
2025-01-01 12:23:29 +00:00
|
|
|
MessageID uuid.UUID `validate:"required"`
|
|
|
|
ServiceID uuid.UUID `validate:"required"`
|
|
|
|
ForServiceID uuid.UUID `validate:"required"`
|
|
|
|
MessageType MessageCode `validate:"required"`
|
|
|
|
SentAt time.Time `validate:"required"`
|
|
|
|
Message any `validate:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewServiceInitializationInformation creates a new ServiceInitializationInformation and is only ever meant to be called
|
|
|
|
// by fulgens or a compliant implementation of fulgens.
|
|
|
|
func NewServiceInitializationInformation(domain *string, outbox chan<- InterServiceMessage, inbox <-chan InterServiceMessage, router *chi.Mux, configuration map[string]interface{}, resourceDir fs.FS) ServiceInitializationInformation {
|
|
|
|
return ServiceInitializationInformation{
|
|
|
|
Domain: domain,
|
|
|
|
Outbox: outbox,
|
|
|
|
inbox: inbox,
|
|
|
|
Router: router,
|
|
|
|
Configuration: configuration,
|
|
|
|
ResourceDir: resourceDir,
|
|
|
|
}
|
2024-10-03 19:03:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type ServiceInitializationInformation struct {
|
2025-01-01 12:23:29 +00:00
|
|
|
Service *Service `validate:"required"`
|
|
|
|
Domain *string
|
2024-10-03 19:03:18 +01:00
|
|
|
Outbox chan<- InterServiceMessage `validate:"required"`
|
2025-01-01 12:23:29 +00:00
|
|
|
inbox <-chan InterServiceMessage `validate:"required"`
|
|
|
|
Router *chi.Mux
|
2024-10-03 19:03:18 +01:00
|
|
|
Configuration map[string]interface{}
|
|
|
|
ResourceDir fs.FS
|
|
|
|
}
|
2024-10-05 19:33:00 +01:00
|
|
|
|
2025-01-01 12:23:29 +00:00
|
|
|
// YesIAbsolutelyKnowWhatIAmDoingAndIWantToAccessTheRawInbox returns a channel that can be used to read messages from
|
|
|
|
// the inbox. This is a dangerous operation, can and will break the buffer, and you should most absolutely not use this
|
|
|
|
// unless you would like to handle the messages yourself with no outside help or synchronization.
|
|
|
|
//
|
|
|
|
// If you think you know what you're doing, **you probably don't**.
|
|
|
|
func (s *ServiceInitializationInformation) YesIAbsolutelyKnowWhatIAmDoingAndIWantToAccessTheRawInbox() <-chan InterServiceMessage {
|
|
|
|
return s.inbox
|
|
|
|
}
|
2024-10-05 19:33:00 +01:00
|
|
|
|
2025-01-01 12:23:29 +00:00
|
|
|
type ColumnType interface{}
|
|
|
|
|
|
|
|
type (
|
|
|
|
// String represents arbitrary sized text
|
|
|
|
String string
|
|
|
|
// Int32 represents a 32-bit signed integer
|
|
|
|
Int32 int32
|
|
|
|
// Int64 represents a 64-bit signed integer
|
|
|
|
Int64 int64
|
|
|
|
// IntInf represents an arbitrary sized signed integer
|
|
|
|
IntInf big.Int
|
|
|
|
// Float32 represents a 32-bit floating point number
|
|
|
|
Float32 float32
|
|
|
|
// Float64 represents a 64-bit floating point number
|
|
|
|
Float64 float64
|
|
|
|
// Boolean represents a boolean value
|
|
|
|
Boolean bool
|
|
|
|
// Blob represents an arbitrary sized binary object
|
|
|
|
Blob []byte
|
|
|
|
// UUID represents a UUID value
|
|
|
|
UUID uuid.UUID
|
|
|
|
// Time represents a time value
|
|
|
|
Time time.Time
|
2024-10-05 19:33:00 +01:00
|
|
|
)
|
|
|
|
|
2025-01-01 12:23:29 +00:00
|
|
|
type TableSchema map[string]ColumnType
|
|
|
|
type Row map[string]any
|
|
|
|
type Rows []Row
|
|
|
|
|
|
|
|
type QueryParameters struct {
|
|
|
|
Equal map[string]any
|
|
|
|
NotEqual map[string]any
|
|
|
|
GreaterThan map[string]any
|
|
|
|
LessThan map[string]any
|
|
|
|
GreaterThanOrEqual map[string]any
|
|
|
|
LessThanOrEqual map[string]any
|
2024-10-05 19:33:00 +01:00
|
|
|
}
|
2024-12-31 15:53:45 +00:00
|
|
|
|
2025-01-01 12:23:29 +00:00
|
|
|
type UpdateParameters map[string]any
|
|
|
|
|
|
|
|
type Database interface {
|
|
|
|
CreateTable(name string, schema TableSchema) error
|
|
|
|
DeleteTable(name string) error
|
|
|
|
InsertRow(name string, row Row) error
|
|
|
|
Delete(name string, params QueryParameters) error
|
2025-01-02 12:30:34 +00:00
|
|
|
Select(name string, params QueryParameters, schema TableSchema) (Rows, error)
|
2025-01-01 12:23:29 +00:00
|
|
|
Update(name string, params QueryParameters, update UpdateParameters) error
|
|
|
|
}
|
|
|
|
|
|
|
|
type MessageCode int
|
2024-12-31 15:53:45 +00:00
|
|
|
|
|
|
|
const (
|
2025-01-01 12:23:29 +00:00
|
|
|
Success MessageCode = iota
|
2024-12-31 15:53:45 +00:00
|
|
|
BadRequest
|
|
|
|
InternalError
|
|
|
|
Unauthorized
|
|
|
|
)
|
|
|
|
|
|
|
|
var buffer = make(map[uuid.UUID]InterServiceMessage)
|
|
|
|
var mutex = sync.Mutex{}
|
|
|
|
var arrived = make(chan uuid.UUID)
|
2025-01-01 12:23:29 +00:00
|
|
|
var ispStarted = false
|
2024-12-31 15:53:45 +00:00
|
|
|
|
2025-01-01 12:23:29 +00:00
|
|
|
func (s *ServiceInitializationInformation) StartISProcessor() {
|
|
|
|
if ispStarted {
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
ispStarted = true
|
|
|
|
}
|
|
|
|
listener := NewListener(s.inbox)
|
2024-12-31 15:53:45 +00:00
|
|
|
for {
|
2025-01-01 12:23:29 +00:00
|
|
|
msg := listener.AcceptMessage()
|
2024-12-31 15:53:45 +00:00
|
|
|
mutex.Lock()
|
|
|
|
buffer[msg.MessageID] = msg
|
|
|
|
mutex.Unlock()
|
|
|
|
arrived <- msg.MessageID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrTimeout = errors.New("timeout")
|
|
|
|
)
|
|
|
|
|
|
|
|
func AwaitISMessage(id uuid.UUID, timeout time.Duration) (InterServiceMessage, error) {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-time.After(timeout):
|
|
|
|
return InterServiceMessage{}, ErrTimeout
|
|
|
|
case msgID := <-arrived:
|
|
|
|
if msgID == id {
|
|
|
|
mutex.Lock()
|
|
|
|
msg := buffer[id]
|
|
|
|
delete(buffer, id)
|
|
|
|
mutex.Unlock()
|
2025-01-01 12:23:29 +00:00
|
|
|
if msg.MessageType != Success {
|
|
|
|
return msg, msg.Message.(error)
|
|
|
|
}
|
2024-12-31 15:53:45 +00:00
|
|
|
return msg, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-01 12:23:29 +00:00
|
|
|
type Listener interface {
|
|
|
|
AcceptMessage() InterServiceMessage
|
|
|
|
}
|
|
|
|
|
|
|
|
type DefaultListener <-chan InterServiceMessage
|
|
|
|
|
|
|
|
func NewListener(c <-chan InterServiceMessage) Listener {
|
|
|
|
return DefaultListener(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l DefaultListener) AcceptMessage() InterServiceMessage {
|
|
|
|
return <-l
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *ServiceInitializationInformation) SendISMessage(forService uuid.UUID, messageType MessageCode, message any) uuid.UUID {
|
2024-12-31 15:53:45 +00:00
|
|
|
id := uuid.New()
|
|
|
|
msg := InterServiceMessage{
|
|
|
|
MessageID: id,
|
2025-01-01 12:23:29 +00:00
|
|
|
ServiceID: s.Service.ServiceID,
|
2024-12-31 15:53:45 +00:00
|
|
|
ForServiceID: forService,
|
|
|
|
MessageType: messageType,
|
|
|
|
SentAt: time.Now(),
|
|
|
|
Message: message,
|
|
|
|
}
|
|
|
|
s.Outbox <- msg
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
|
2025-01-01 12:23:29 +00:00
|
|
|
func (s *ServiceInitializationInformation) SendAndAwaitISMessage(forService uuid.UUID, messageType MessageCode, message any, timeout time.Duration) (InterServiceMessage, error) {
|
|
|
|
id := s.SendISMessage(forService, messageType, message)
|
2024-12-31 15:53:45 +00:00
|
|
|
return AwaitISMessage(id, timeout)
|
|
|
|
}
|
2025-01-01 12:23:29 +00:00
|
|
|
|
|
|
|
func (s *ServiceInitializationInformation) GetDatabase() (Database, error) {
|
|
|
|
if !ispStarted {
|
|
|
|
go s.StartISProcessor()
|
|
|
|
}
|
|
|
|
|
|
|
|
response, err := s.SendAndAwaitISMessage(uuid.Nil, 0, nil, 5*time.Second)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return response.Message.(Database), nil
|
|
|
|
}
|