128 lines
5.4 KiB
Go
128 lines
5.4 KiB
Go
package ports
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"galaxy/gamemaster/internal/domain/engineversion"
|
|
)
|
|
|
|
//go:generate go run go.uber.org/mock/mockgen -destination=../adapters/mocks/mock_engineversionstore.go -package=mocks galaxy/gamemaster/internal/ports EngineVersionStore
|
|
|
|
// EngineVersionStore stores the engine version registry rows used by
|
|
// Game Lobby's start flow and by GM's admin patch and registry CRUD
|
|
// surface. Adapters must preserve domain semantics:
|
|
//
|
|
// - Get returns engineversion.ErrNotFound when no row exists for
|
|
// version.
|
|
// - List with a nil status filter returns every row; with a non-nil
|
|
// filter, only rows whose status matches are returned.
|
|
// - Insert installs a fresh row and returns engineversion.ErrConflict
|
|
// when a row with the same `version` already exists. Adapters
|
|
// surface PostgreSQL unique violations through that sentinel so
|
|
// the service layer maps them to a `conflict` REST envelope.
|
|
// - Update applies a partial update; only fields whose pointer is
|
|
// non-nil are mutated. The `updated_at` column is always refreshed
|
|
// from input.Now.
|
|
// - Deprecate sets `status=deprecated` for an existing version with
|
|
// `updated_at = now`. It returns engineversion.ErrNotFound when no
|
|
// row exists. The call is idempotent: deprecating an already
|
|
// deprecated row succeeds with no further mutation.
|
|
// - Delete removes the row identified by version. Returns
|
|
// engineversion.ErrNotFound when no row matches. The service layer
|
|
// gates Delete behind an explicit IsReferencedByActiveRuntime probe
|
|
// so referenced rows surface engineversion.ErrInUse before the
|
|
// adapter is touched; adapters do not enforce that guard themselves.
|
|
// - IsReferencedByActiveRuntime reports whether any non-finished
|
|
// `runtime_records` row currently references the version through
|
|
// `current_engine_version`.
|
|
type EngineVersionStore interface {
|
|
// Get returns the row identified by version. Returns
|
|
// engineversion.ErrNotFound when no row exists.
|
|
Get(ctx context.Context, version string) (engineversion.EngineVersion, error)
|
|
|
|
// List returns every row whose status matches statusFilter when
|
|
// non-nil, or every row when nil. The order is adapter-defined.
|
|
List(ctx context.Context, statusFilter *engineversion.Status) ([]engineversion.EngineVersion, error)
|
|
|
|
// Insert installs record into the registry.
|
|
Insert(ctx context.Context, record engineversion.EngineVersion) error
|
|
|
|
// Update applies a partial update to the row identified by
|
|
// input.Version. Only fields whose pointer is non-nil are mutated.
|
|
// Returns engineversion.ErrNotFound when no row exists.
|
|
Update(ctx context.Context, input UpdateEngineVersionInput) error
|
|
|
|
// Deprecate sets `status=deprecated` for version and refreshes
|
|
// `updated_at` from now. Returns engineversion.ErrNotFound when no
|
|
// row exists. Calling Deprecate on an already-deprecated row
|
|
// succeeds with no mutation (idempotent).
|
|
Deprecate(ctx context.Context, version string, now time.Time) error
|
|
|
|
// Delete removes the row identified by version. Returns
|
|
// engineversion.ErrNotFound when no row matches. Adapters do not
|
|
// inspect runtime references; the service layer probes
|
|
// IsReferencedByActiveRuntime first and surfaces
|
|
// engineversion.ErrInUse independently.
|
|
Delete(ctx context.Context, version string) error
|
|
|
|
// IsReferencedByActiveRuntime reports whether any non-finished
|
|
// runtime row currently references version through
|
|
// `current_engine_version`. Used by the registry hard-delete path
|
|
// to surface engineversion.ErrInUse.
|
|
IsReferencedByActiveRuntime(ctx context.Context, version string) (bool, error)
|
|
}
|
|
|
|
// UpdateEngineVersionInput stores the arguments required to PATCH one
|
|
// engine version row. Pointer fields communicate «leave alone» (nil)
|
|
// vs. «write the value» (non-nil). At least one optional field must be
|
|
// set; otherwise the call is a no-op and Validate rejects it.
|
|
type UpdateEngineVersionInput struct {
|
|
// Version identifies the row to mutate.
|
|
Version string
|
|
|
|
// ImageRef is the new image reference. Nil leaves the column
|
|
// unchanged; non-nil must be non-empty.
|
|
ImageRef *string
|
|
|
|
// Options is the new options document (raw JSON). Nil leaves the
|
|
// column unchanged; non-nil writes the value verbatim.
|
|
Options *[]byte
|
|
|
|
// Status is the new status. Nil leaves the column unchanged;
|
|
// non-nil must be a known status.
|
|
Status *engineversion.Status
|
|
|
|
// Now stores the wall-clock used to refresh the `updated_at`
|
|
// column on every successful update.
|
|
Now time.Time
|
|
}
|
|
|
|
// Validate reports whether input contains a structurally valid PATCH
|
|
// request. Adapters call Validate before touching the store.
|
|
func (input UpdateEngineVersionInput) Validate() error {
|
|
if strings.TrimSpace(input.Version) == "" {
|
|
return fmt.Errorf("update engine version: version must not be empty")
|
|
}
|
|
if input.ImageRef == nil && input.Options == nil && input.Status == nil {
|
|
return fmt.Errorf("update engine version: at least one field must be set")
|
|
}
|
|
if input.ImageRef != nil && strings.TrimSpace(*input.ImageRef) == "" {
|
|
return fmt.Errorf(
|
|
"update engine version: image ref must not be empty when set",
|
|
)
|
|
}
|
|
if input.Status != nil && !input.Status.IsKnown() {
|
|
return fmt.Errorf(
|
|
"update engine version: status %q is unsupported",
|
|
*input.Status,
|
|
)
|
|
}
|
|
if input.Now.IsZero() {
|
|
return fmt.Errorf("update engine version: now must not be zero")
|
|
}
|
|
return nil
|
|
}
|