docs: reorder & testing

This commit is contained in:
Ilia Denisov
2026-05-07 00:58:53 +03:00
committed by GitHub
parent f446c6a2ac
commit 604fe40bcf
148 changed files with 9150 additions and 2757 deletions
+247
View File
@@ -0,0 +1,247 @@
package notification
import (
"fmt"
"galaxy/backend/push"
"galaxy/transcoder"
"github.com/google/uuid"
)
// preMarshaledEvent adapts a pre-encoded FlatBuffers payload to the
// push.Event interface. The factory below pre-encodes the payload at
// construction time so the kind-specific build error surfaces inside
// the dispatcher (where it can drive retry / dead-letter logic) rather
// than inside push.Service.PublishClientEvent.
type preMarshaledEvent struct {
kind string
payload []byte
}
func (e preMarshaledEvent) Kind() string { return e.kind }
func (e preMarshaledEvent) Marshal() ([]byte, error) { return e.payload, nil }
// buildClientPushEvent maps a catalog kind together with the producer
// payload map onto a typed push.Event. Every catalog kind has a
// FlatBuffers schema in `pkg/schema/fbs/notification.fbs`; an unknown
// kind falls back to push.JSONEvent so a misconfigured producer keeps
// the pipeline flowing while the catalog catches up.
func buildClientPushEvent(kind string, payload map[string]any) (push.Event, error) {
switch kind {
case KindLobbyInviteReceived:
gameID, err := mapUUID(payload, "game_id")
if err != nil {
return nil, err
}
inviter, err := mapUUID(payload, "inviter_user_id")
if err != nil {
return nil, err
}
bytes, err := transcoder.LobbyInviteReceivedEventToPayload(&transcoder.LobbyInviteReceivedEvent{
GameID: gameID,
InviterUserID: inviter,
})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindLobbyInviteRevoked:
gameID, err := mapUUID(payload, "game_id")
if err != nil {
return nil, err
}
bytes, err := transcoder.LobbyInviteRevokedEventToPayload(&transcoder.LobbyInviteRevokedEvent{GameID: gameID})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindLobbyApplicationSubmitted:
gameID, err := mapUUID(payload, "game_id")
if err != nil {
return nil, err
}
appID, err := mapUUID(payload, "application_id")
if err != nil {
return nil, err
}
bytes, err := transcoder.LobbyApplicationSubmittedEventToPayload(&transcoder.LobbyApplicationSubmittedEvent{
GameID: gameID,
ApplicationID: appID,
})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindLobbyApplicationApproved:
gameID, err := mapUUID(payload, "game_id")
if err != nil {
return nil, err
}
bytes, err := transcoder.LobbyApplicationApprovedEventToPayload(&transcoder.LobbyApplicationApprovedEvent{GameID: gameID})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindLobbyApplicationRejected:
gameID, err := mapUUID(payload, "game_id")
if err != nil {
return nil, err
}
bytes, err := transcoder.LobbyApplicationRejectedEventToPayload(&transcoder.LobbyApplicationRejectedEvent{GameID: gameID})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindLobbyMembershipRemoved:
bytes, err := transcoder.LobbyMembershipRemovedEventToPayload(&transcoder.LobbyMembershipRemovedEvent{
Reason: mapStringOpt(payload, "reason"),
})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindLobbyMembershipBlocked:
gameID, err := mapUUID(payload, "game_id")
if err != nil {
return nil, err
}
bytes, err := transcoder.LobbyMembershipBlockedEventToPayload(&transcoder.LobbyMembershipBlockedEvent{
GameID: gameID,
Reason: mapStringOpt(payload, "reason"),
})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindLobbyRaceNameRegistered:
raceName, err := mapString(payload, "race_name")
if err != nil {
return nil, err
}
bytes, err := transcoder.LobbyRaceNameRegisteredEventToPayload(&transcoder.LobbyRaceNameRegisteredEvent{RaceName: raceName})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindLobbyRaceNamePending:
raceName, err := mapString(payload, "race_name")
if err != nil {
return nil, err
}
bytes, err := transcoder.LobbyRaceNamePendingEventToPayload(&transcoder.LobbyRaceNamePendingEvent{
RaceName: raceName,
ExpiresAt: mapStringOpt(payload, "expires_at"),
})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindLobbyRaceNameExpired:
raceName, err := mapString(payload, "race_name")
if err != nil {
return nil, err
}
bytes, err := transcoder.LobbyRaceNameExpiredEventToPayload(&transcoder.LobbyRaceNameExpiredEvent{RaceName: raceName})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindRuntimeImagePullFailed:
gameID, err := mapUUID(payload, "game_id")
if err != nil {
return nil, err
}
bytes, err := transcoder.RuntimeImagePullFailedEventToPayload(&transcoder.RuntimeImagePullFailedEvent{
GameID: gameID,
ImageRef: mapStringOpt(payload, "image_ref"),
})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindRuntimeContainerStartFailed:
gameID, err := mapUUID(payload, "game_id")
if err != nil {
return nil, err
}
bytes, err := transcoder.RuntimeContainerStartFailedEventToPayload(&transcoder.RuntimeContainerStartFailedEvent{GameID: gameID})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
case KindRuntimeStartConfigInvalid:
gameID, err := mapUUID(payload, "game_id")
if err != nil {
return nil, err
}
bytes, err := transcoder.RuntimeStartConfigInvalidEventToPayload(&transcoder.RuntimeStartConfigInvalidEvent{
GameID: gameID,
Reason: mapStringOpt(payload, "reason"),
})
if err != nil {
return nil, err
}
return preMarshaledEvent{kind: kind, payload: bytes}, nil
}
return push.JSONEvent{EventKind: kind, Payload: payload}, nil
}
// mapUUID extracts a required UUID-shaped field from the producer
// payload. Producers stringify uuid values before assembling Intent
// payloads, so the JSON-roundtripped form is `string`.
func mapUUID(payload map[string]any, key string) (uuid.UUID, error) {
raw, ok := payload[key]
if !ok {
return uuid.Nil, fmt.Errorf("notification payload: %s is missing", key)
}
str, ok := raw.(string)
if !ok {
return uuid.Nil, fmt.Errorf("notification payload: %s must be a string, got %T", key, raw)
}
parsed, err := uuid.Parse(str)
if err != nil {
return uuid.Nil, fmt.Errorf("notification payload: %s is not a uuid: %w", key, err)
}
return parsed, nil
}
// mapString extracts a required string field from the producer payload.
func mapString(payload map[string]any, key string) (string, error) {
raw, ok := payload[key]
if !ok {
return "", fmt.Errorf("notification payload: %s is missing", key)
}
str, ok := raw.(string)
if !ok {
return "", fmt.Errorf("notification payload: %s must be a string, got %T", key, raw)
}
if str == "" {
return "", fmt.Errorf("notification payload: %s is empty", key)
}
return str, nil
}
// mapStringOpt returns the string value for key, or "" when the key is
// missing or carries a non-string value.
func mapStringOpt(payload map[string]any, key string) string {
raw, ok := payload[key]
if !ok {
return ""
}
str, _ := raw.(string)
return str
}