功能修改
This commit is contained in:
@@ -39,6 +39,8 @@ type AIConfigRecommendationResponse struct {
|
||||
Key string `json:"key"`
|
||||
SuggestedConfig string `json:"suggestedConfig,omitempty"`
|
||||
DiffSummary string `json:"diffSummary"`
|
||||
DiffID string `json:"diffId,omitempty"`
|
||||
ExpiresAt string `json:"expiresAt,omitempty"`
|
||||
}
|
||||
|
||||
type AIInvocationSafeErrorResponse struct {
|
||||
@@ -82,6 +84,8 @@ func AIInvocationFromDomain(response domain.AIInvocationResponse) AIInvocationRe
|
||||
Key: response.ConfigRecommendation.Key,
|
||||
SuggestedConfig: response.ConfigRecommendation.SuggestedConfig,
|
||||
DiffSummary: response.ConfigRecommendation.DiffSummary,
|
||||
DiffID: response.ConfigRecommendation.DiffID,
|
||||
ExpiresAt: response.ConfigRecommendation.ExpiresAt,
|
||||
}
|
||||
}
|
||||
var safeError *AIInvocationSafeErrorResponse
|
||||
|
||||
@@ -122,25 +122,56 @@ type ClientManagerLifecycleInputRequest struct {
|
||||
}
|
||||
|
||||
type ClientManagerLifecycleInputResponse struct {
|
||||
InstallationID string `json:"installationId"`
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
ProfileKey string `json:"profileKey"`
|
||||
Operation string `json:"operation"`
|
||||
ArtifactID string `json:"artifactId,omitempty"`
|
||||
Checksum string `json:"checksum,omitempty"`
|
||||
TargetOS string `json:"targetOs"`
|
||||
TargetArch string `json:"targetArch"`
|
||||
Version string `json:"version,omitempty"`
|
||||
SourceRevision string `json:"sourceRevision,omitempty"`
|
||||
KeyGeneration int `json:"keyGeneration"`
|
||||
DeploymentGeneration int `json:"deploymentGeneration"`
|
||||
ExecutableRef string `json:"executableRef"`
|
||||
Arguments []string `json:"arguments"`
|
||||
AutoStart bool `json:"autoStart"`
|
||||
StartupTimeoutSeconds int `json:"startupTimeoutSeconds"`
|
||||
StopTimeoutSeconds int `json:"stopTimeoutSeconds"`
|
||||
HealthConfirmationSeconds int `json:"healthConfirmationSeconds"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
InstallationID string `json:"installationId"`
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
ProfileKey string `json:"profileKey"`
|
||||
Operation string `json:"operation"`
|
||||
ArtifactID string `json:"artifactId,omitempty"`
|
||||
Checksum string `json:"checksum,omitempty"`
|
||||
TargetOS string `json:"targetOs"`
|
||||
TargetArch string `json:"targetArch"`
|
||||
Version string `json:"version,omitempty"`
|
||||
SourceRevision string `json:"sourceRevision,omitempty"`
|
||||
KeyGeneration int `json:"keyGeneration"`
|
||||
DeploymentGeneration int `json:"deploymentGeneration"`
|
||||
ExecutableRef string `json:"executableRef"`
|
||||
Arguments []string `json:"arguments"`
|
||||
AutoStart bool `json:"autoStart"`
|
||||
StartupTimeoutSeconds int `json:"startupTimeoutSeconds"`
|
||||
StopTimeoutSeconds int `json:"stopTimeoutSeconds"`
|
||||
HealthConfirmationSeconds int `json:"healthConfirmationSeconds"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
CompanionConfig *ClientManagerCompanionConfigInputResponse `json:"companionConfig,omitempty"`
|
||||
}
|
||||
|
||||
type ClientManagerCompanionConfigInputResponse struct {
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
ConfigTemplateKey string `json:"configTemplateKey"`
|
||||
ConfigTemplateRef string `json:"configTemplateRef"`
|
||||
ConfigOutputRef string `json:"configOutputRef"`
|
||||
ConfigSchemaRef string `json:"configSchemaRef"`
|
||||
ConfigFormat string `json:"configFormat"`
|
||||
PlatformBaseURLSource string `json:"platformBaseUrlSource"`
|
||||
InstallationID string `json:"installationId"`
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
PluginID string `json:"pluginId"`
|
||||
ProfileKey string `json:"profileKey"`
|
||||
ArtifactID string `json:"artifactId"`
|
||||
Version string `json:"version"`
|
||||
SourceRevision string `json:"sourceRevision"`
|
||||
TargetOS string `json:"targetOs"`
|
||||
TargetArch string `json:"targetArch"`
|
||||
KeyGeneration int `json:"keyGeneration"`
|
||||
DeploymentGeneration int `json:"deploymentGeneration"`
|
||||
Capabilities []string `json:"capabilities"`
|
||||
RegistrationProof string `json:"registrationProof"`
|
||||
ProofMaterialSource string `json:"proofMaterialSource"`
|
||||
ProofMaterialEnv string `json:"proofMaterialEnv"`
|
||||
SessionMode string `json:"sessionMode"`
|
||||
TLSPolicy string `json:"tlsPolicy"`
|
||||
HeartbeatIntervalSeconds int `json:"heartbeatIntervalSeconds"`
|
||||
CommandPollIntervalSeconds int `json:"commandPollIntervalSeconds"`
|
||||
RequestTimeoutSeconds int `json:"requestTimeoutSeconds"`
|
||||
}
|
||||
|
||||
type ClientManagerRegisterRequest struct {
|
||||
@@ -219,7 +250,17 @@ func (request ClientManagerLifecycleInputRequest) ToDomain() domain.ClientManage
|
||||
|
||||
func ClientManagerLifecycleInputFromDomain(value domain.ClientManagerLifecycleInput) ClientManagerLifecycleInputResponse {
|
||||
value = domain.CopyClientManagerLifecycleInput(value)
|
||||
return ClientManagerLifecycleInputResponse{InstallationID: value.InstallationID, ServerInstanceID: value.ServerInstanceID, ProfileKey: value.ProfileKey, Operation: string(value.Operation), ArtifactID: value.ArtifactID, Checksum: value.Checksum, TargetOS: value.TargetOS, TargetArch: value.TargetArch, Version: value.Version, SourceRevision: value.SourceRevision, KeyGeneration: value.KeyGeneration, DeploymentGeneration: value.DeploymentGeneration, ExecutableRef: value.ExecutableRef, Arguments: value.Arguments, AutoStart: value.AutoStart, StartupTimeoutSeconds: value.StartupTimeoutSeconds, StopTimeoutSeconds: value.StopTimeoutSeconds, HealthConfirmationSeconds: value.HealthConfirmationSeconds, IdempotencyKey: value.IdempotencyKey}
|
||||
response := ClientManagerLifecycleInputResponse{InstallationID: value.InstallationID, ServerInstanceID: value.ServerInstanceID, ProfileKey: value.ProfileKey, Operation: string(value.Operation), ArtifactID: value.ArtifactID, Checksum: value.Checksum, TargetOS: value.TargetOS, TargetArch: value.TargetArch, Version: value.Version, SourceRevision: value.SourceRevision, KeyGeneration: value.KeyGeneration, DeploymentGeneration: value.DeploymentGeneration, ExecutableRef: value.ExecutableRef, Arguments: value.Arguments, AutoStart: value.AutoStart, StartupTimeoutSeconds: value.StartupTimeoutSeconds, StopTimeoutSeconds: value.StopTimeoutSeconds, HealthConfirmationSeconds: value.HealthConfirmationSeconds, IdempotencyKey: value.IdempotencyKey}
|
||||
if value.CompanionConfig != nil {
|
||||
companion := ClientManagerCompanionConfigInputFromDomain(*value.CompanionConfig)
|
||||
response.CompanionConfig = &companion
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func ClientManagerCompanionConfigInputFromDomain(value domain.ClientManagerCompanionConfigInput) ClientManagerCompanionConfigInputResponse {
|
||||
value = domain.CopyClientManagerCompanionConfigInput(value)
|
||||
return ClientManagerCompanionConfigInputResponse{SchemaVersion: value.SchemaVersion, ConfigTemplateKey: value.ConfigTemplateKey, ConfigTemplateRef: value.ConfigTemplateRef, ConfigOutputRef: value.ConfigOutputRef, ConfigSchemaRef: value.ConfigSchemaRef, ConfigFormat: value.ConfigFormat, PlatformBaseURLSource: value.PlatformBaseURLSource, InstallationID: value.InstallationID, ServerInstanceID: value.ServerInstanceID, PluginID: value.PluginID, ProfileKey: value.ProfileKey, ArtifactID: value.ArtifactID, Version: value.Version, SourceRevision: value.SourceRevision, TargetOS: value.TargetOS, TargetArch: value.TargetArch, KeyGeneration: value.KeyGeneration, DeploymentGeneration: value.DeploymentGeneration, Capabilities: value.Capabilities, RegistrationProof: value.RegistrationProof, ProofMaterialSource: value.ProofMaterialSource, ProofMaterialEnv: value.ProofMaterialEnv, SessionMode: value.SessionMode, TLSPolicy: value.TLSPolicy, HeartbeatIntervalSeconds: value.HeartbeatIntervalSeconds, CommandPollIntervalSeconds: value.CommandPollIntervalSeconds, RequestTimeoutSeconds: value.RequestTimeoutSeconds}
|
||||
}
|
||||
|
||||
func (request ClientManagerRegisterRequest) ToDomain() domain.ClientManagerRegisterRequest {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
func TestClientManagerLifecycleInputProjectsRunOnlyCompanionConfigWithoutRawMaterial(t *testing.T) {
|
||||
response := ClientManagerLifecycleInputFromDomain(domain.ClientManagerLifecycleInput{
|
||||
InstallationID: "installation-1",
|
||||
ServerInstanceID: "server-1",
|
||||
ProfileKey: "scum-client-manager",
|
||||
Operation: domain.ClientManagerOperationDeploy,
|
||||
DeploymentGeneration: 2,
|
||||
CompanionConfig: &domain.ClientManagerCompanionConfigInput{
|
||||
SchemaVersion: 1,
|
||||
ConfigTemplateKey: "client-config",
|
||||
ConfigTemplateRef: "config.yaml.example",
|
||||
ConfigOutputRef: "config.yaml",
|
||||
ConfigSchemaRef: "schemas/companion/config.schema.json",
|
||||
ConfigFormat: "yaml",
|
||||
PlatformBaseURLSource: "run-control",
|
||||
InstallationID: "installation-1",
|
||||
ServerInstanceID: "server-1",
|
||||
PluginID: "game.scum",
|
||||
ProfileKey: "scum-client-manager",
|
||||
ArtifactID: "artifact-1",
|
||||
Version: "1.0.0",
|
||||
SourceRevision: "revision-1",
|
||||
TargetOS: "windows",
|
||||
TargetArch: "amd64",
|
||||
KeyGeneration: 3,
|
||||
DeploymentGeneration: 2,
|
||||
Capabilities: []string{"component.register", "component.heartbeat", "component.health", "game-client.bridge"},
|
||||
RegistrationProof: "hmac-sha256",
|
||||
ProofMaterialSource: "component-package",
|
||||
ProofMaterialEnv: "SCUM_COMPONENT_PROOF",
|
||||
SessionMode: "component-session",
|
||||
TLSPolicy: "verify-system-roots",
|
||||
HeartbeatIntervalSeconds: 30,
|
||||
CommandPollIntervalSeconds: 5,
|
||||
RequestTimeoutSeconds: 15,
|
||||
},
|
||||
})
|
||||
if response.CompanionConfig == nil || response.CompanionConfig.ProofMaterialEnv != "SCUM_COMPONENT_PROOF" {
|
||||
t.Fatalf("expected run-only companion projection, got %+v", response.CompanionConfig)
|
||||
}
|
||||
encoded, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal lifecycle input: %v", err)
|
||||
}
|
||||
serialized := string(encoded)
|
||||
for _, forbidden := range []string{`"proofMaterial":`, `"authKey":`, `"componentKey":`, `"sessionToken":`, `"secretRef":`, "/Users/", "run.socket"} {
|
||||
if strings.Contains(serialized, forbidden) {
|
||||
t.Fatalf("run lifecycle input leaked %q: %s", forbidden, serialized)
|
||||
}
|
||||
}
|
||||
|
||||
browserProjection, err := json.Marshal(ClientManagerLifecycleViewFromDomain(domain.ClientManagerLifecycleView{Installation: domain.ClientManagerInstallation{ID: "installation-1"}}))
|
||||
if err != nil {
|
||||
t.Fatalf("marshal browser lifecycle projection: %v", err)
|
||||
}
|
||||
if strings.Contains(string(browserProjection), "companionConfig") || strings.Contains(string(browserProjection), "proofMaterialEnv") {
|
||||
t.Fatalf("browser lifecycle projection exposed run-only config: %s", browserProjection)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
type GameClientBridgeQueueRequest struct {
|
||||
ProfileKey string `json:"profileKey"`
|
||||
CommandType string `json:"commandType"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
Priority int `json:"priority,omitempty"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type GameClientBridgeClaimRequest struct {
|
||||
SessionToken string `json:"sessionToken"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
type GameClientBridgeAckRequest struct {
|
||||
SessionToken string `json:"sessionToken"`
|
||||
FencingToken uint64 `json:"fencingToken"`
|
||||
}
|
||||
|
||||
type GameClientBridgeResultRequest struct {
|
||||
SessionToken string `json:"sessionToken"`
|
||||
FencingToken uint64 `json:"fencingToken"`
|
||||
Status string `json:"status"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Payload map[string]any `json:"payload,omitempty"`
|
||||
}
|
||||
|
||||
type GameClientBridgeSnapshotIngestRequest struct {
|
||||
SessionToken string `json:"sessionToken"`
|
||||
Type string `json:"type"`
|
||||
SchemaVersion string `json:"schemaVersion"`
|
||||
StreamKey string `json:"streamKey"`
|
||||
Sequence uint64 `json:"sequence"`
|
||||
ObservedAt time.Time `json:"observedAt"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
KeepForSeconds int `json:"keepForSeconds"`
|
||||
MaxRecords int `json:"maxRecords,omitempty"`
|
||||
}
|
||||
|
||||
type GameClientBridgeCancelRequest struct {
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
type GameClientBridgeSnapshotQuery struct {
|
||||
ProfileKey string `json:"profileKey,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
StreamKey string `json:"streamKey,omitempty"`
|
||||
ObservedAfter time.Time `json:"observedAfter,omitempty"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
// GameClientBridgeCommandResultResponse is the browser-safe projection of a
|
||||
// terminal command result. Result payloads pass the same bounded JSON
|
||||
// validation as command inputs before they can be persisted.
|
||||
type GameClientBridgeCommandResultResponse struct {
|
||||
Status string `json:"status"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Payload map[string]any `json:"payload,omitempty"`
|
||||
CompletedAt time.Time `json:"completedAt"`
|
||||
}
|
||||
|
||||
type GameClientBridgeCommandCancellationResponse struct {
|
||||
RequestedBy string `json:"requestedBy,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
CancelledAt time.Time `json:"cancelledAt"`
|
||||
}
|
||||
|
||||
type GameClientBridgeCommandResponse struct {
|
||||
ID string `json:"id"`
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
PluginID string `json:"pluginId"`
|
||||
ProfileKey string `json:"profileKey"`
|
||||
CommandType string `json:"commandType"`
|
||||
Priority int `json:"priority"`
|
||||
State string `json:"state"`
|
||||
ApprovalState string `json:"approvalState"`
|
||||
RequesterID string `json:"requesterId,omitempty"`
|
||||
ResultSummary string `json:"resultSummary,omitempty"`
|
||||
Result *GameClientBridgeCommandResultResponse `json:"result,omitempty"`
|
||||
Cancellation *GameClientBridgeCommandCancellationResponse `json:"cancellation,omitempty"`
|
||||
AuditReferences []string `json:"auditReferences,omitempty"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
CompletedAt time.Time `json:"completedAt,omitempty"`
|
||||
}
|
||||
|
||||
type GameClientBridgeRetentionResponse struct {
|
||||
KeepForSeconds int `json:"keepForSeconds"`
|
||||
MaxRecords int `json:"maxRecords,omitempty"`
|
||||
}
|
||||
|
||||
type GameClientBridgeSnapshotResponse struct {
|
||||
ID string `json:"id"`
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
PluginID string `json:"pluginId"`
|
||||
ProfileKey string `json:"profileKey"`
|
||||
Type string `json:"type"`
|
||||
SchemaVersion string `json:"schemaVersion"`
|
||||
StreamKey string `json:"streamKey"`
|
||||
Sequence uint64 `json:"sequence"`
|
||||
ObservedAt time.Time `json:"observedAt"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
Retention GameClientBridgeRetentionResponse `json:"retention"`
|
||||
AuditReferences []string `json:"auditReferences,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type GameClientBridgeCommandListResponse struct {
|
||||
Items []GameClientBridgeCommandResponse `json:"items"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type GameClientBridgeSnapshotListResponse struct {
|
||||
Items []GameClientBridgeSnapshotResponse `json:"items"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type GameClientBridgeClaimedCommandResponse struct {
|
||||
ID string `json:"id"`
|
||||
ProfileKey string `json:"profileKey"`
|
||||
CommandType string `json:"commandType"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
Priority int `json:"priority"`
|
||||
FencingToken uint64 `json:"fencingToken"`
|
||||
ClaimedAt time.Time `json:"claimedAt"`
|
||||
LeaseExpiresAt time.Time `json:"leaseExpiresAt"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type GameClientBridgeClaimResponse struct {
|
||||
Items []GameClientBridgeClaimedCommandResponse `json:"items"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
// GameClientBridgeClaimBatchResponse is kept as a descriptive alias for API
|
||||
// handlers that name the operation after its bounded batch semantics.
|
||||
type GameClientBridgeClaimBatchResponse = GameClientBridgeClaimResponse
|
||||
|
||||
type GameClientBridgeAckResponse struct {
|
||||
CommandID string `json:"commandId"`
|
||||
State string `json:"state"`
|
||||
FencingToken uint64 `json:"fencingToken"`
|
||||
AcknowledgedAt time.Time `json:"acknowledgedAt"`
|
||||
}
|
||||
|
||||
type GameClientBridgeResultResponse struct {
|
||||
CommandID string `json:"commandId"`
|
||||
State string `json:"state"`
|
||||
Result GameClientBridgeCommandResultResponse `json:"result"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
CompletedAt time.Time `json:"completedAt"`
|
||||
}
|
||||
|
||||
type GameClientBridgeCancelResponse struct {
|
||||
CommandID string `json:"commandId"`
|
||||
State string `json:"state"`
|
||||
Cancellation GameClientBridgeCommandCancellationResponse `json:"cancellation"`
|
||||
AuditReferences []string `json:"auditReferences,omitempty"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type GameClientBridgeSnapshotIngestResponse struct {
|
||||
SnapshotID string `json:"snapshotId"`
|
||||
ProfileKey string `json:"profileKey"`
|
||||
Type string `json:"type"`
|
||||
SchemaVersion string `json:"schemaVersion"`
|
||||
StreamKey string `json:"streamKey"`
|
||||
Sequence uint64 `json:"sequence"`
|
||||
AcceptedAt time.Time `json:"acceptedAt"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type GameClientBridgeAuditReferenceResponse struct {
|
||||
ID string `json:"id"`
|
||||
CommandID string `json:"commandId,omitempty"`
|
||||
SnapshotID string `json:"snapshotId,omitempty"`
|
||||
AuditEventID string `json:"auditEventId"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
type GameClientBridgeAuditReferenceListResponse struct {
|
||||
Items []GameClientBridgeAuditReferenceResponse `json:"items"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type GameClientBridgeProfileDeclarationResponse struct {
|
||||
PluginID string `json:"pluginId"`
|
||||
ProfileKey string `json:"profileKey"`
|
||||
Available bool `json:"available"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
CommandTypes []string `json:"commandTypes"`
|
||||
SnapshotTypes []string `json:"snapshotTypes"`
|
||||
QueryTemplateKeys []string `json:"queryTemplateKeys"`
|
||||
}
|
||||
|
||||
type GameClientBridgeStatusResponse struct {
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
PluginID string `json:"pluginId"`
|
||||
Available bool `json:"available"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Profiles []GameClientBridgeProfileDeclarationResponse `json:"profiles"`
|
||||
}
|
||||
|
||||
func (request GameClientBridgeQueueRequest) ToDomain(serverID, pluginID string) domain.GameClientBridgeQueueRequest {
|
||||
return domain.GameClientBridgeQueueRequest{ServerInstanceID: serverID, PluginID: pluginID, ProfileKey: request.ProfileKey, CommandType: request.CommandType, Payload: domain.CopyGameClientBridgePayload(request.Payload), IdempotencyKey: request.IdempotencyKey, Priority: request.Priority, ExpiresAt: request.ExpiresAt}
|
||||
}
|
||||
|
||||
func (request GameClientBridgeClaimRequest) ToDomain() domain.GameClientBridgeClaimRequest {
|
||||
return domain.GameClientBridgeClaimRequest{SessionToken: request.SessionToken, Limit: request.Limit}
|
||||
}
|
||||
|
||||
func (request GameClientBridgeAckRequest) ToDomain(commandID string) domain.GameClientBridgeAckRequest {
|
||||
return domain.GameClientBridgeAckRequest{SessionToken: request.SessionToken, CommandID: commandID, FencingToken: request.FencingToken}
|
||||
}
|
||||
|
||||
func (request GameClientBridgeResultRequest) ToDomain(commandID string) domain.GameClientBridgeResultRequest {
|
||||
return domain.GameClientBridgeResultRequest{SessionToken: request.SessionToken, CommandID: commandID, FencingToken: request.FencingToken, Status: domain.GameClientBridgeResultStatus(request.Status), Summary: request.Summary, Payload: domain.CopyGameClientBridgePayload(request.Payload)}
|
||||
}
|
||||
|
||||
func (request GameClientBridgeCancelRequest) ToDomain(commandID string) domain.GameClientBridgeCancelRequest {
|
||||
return domain.GameClientBridgeCancelRequest{CommandID: commandID, Reason: request.Reason}
|
||||
}
|
||||
|
||||
func (request GameClientBridgeSnapshotIngestRequest) ToDomain() domain.GameClientBridgeSnapshotIngestRequest {
|
||||
return domain.GameClientBridgeSnapshotIngestRequest{SessionToken: request.SessionToken, Type: request.Type, SchemaVersion: request.SchemaVersion, StreamKey: request.StreamKey, Sequence: request.Sequence, ObservedAt: request.ObservedAt, Payload: domain.CopyGameClientBridgePayload(request.Payload), Retention: domain.GameClientBridgeRetention{KeepForSeconds: request.KeepForSeconds, MaxRecords: request.MaxRecords}}
|
||||
}
|
||||
|
||||
func (query GameClientBridgeSnapshotQuery) ToDomain(serverID, pluginID string) domain.GameClientBridgeSnapshotQuery {
|
||||
return domain.GameClientBridgeSnapshotQuery{ServerInstanceID: serverID, PluginID: pluginID, ProfileKey: query.ProfileKey, Type: query.Type, StreamKey: query.StreamKey, ObservedAfter: query.ObservedAfter, Limit: query.Limit}
|
||||
}
|
||||
|
||||
func GameClientBridgeCommandFromDomain(value domain.GameClientBridgeCommand) GameClientBridgeCommandResponse {
|
||||
value = domain.CopyGameClientBridgeCommand(value)
|
||||
response := GameClientBridgeCommandResponse{
|
||||
ID: value.ID,
|
||||
ServerInstanceID: value.ServerInstanceID,
|
||||
PluginID: value.PluginID,
|
||||
ProfileKey: value.ProfileKey,
|
||||
CommandType: value.CommandType,
|
||||
Priority: value.Priority,
|
||||
State: string(value.State),
|
||||
ApprovalState: string(value.ApprovalState),
|
||||
RequesterID: value.RequesterID,
|
||||
ResultSummary: value.Result.Summary,
|
||||
AuditReferences: copyStrings(value.AuditReferences),
|
||||
ExpiresAt: value.ExpiresAt,
|
||||
CreatedAt: value.CreatedAt,
|
||||
UpdatedAt: value.UpdatedAt,
|
||||
CompletedAt: value.CompletedAt,
|
||||
}
|
||||
if hasGameClientBridgeResult(value.Result) {
|
||||
result := gameClientBridgeCommandResultFromDomain(value.Result)
|
||||
response.Result = &result
|
||||
}
|
||||
if hasGameClientBridgeCancellation(value.Cancellation) {
|
||||
cancellation := gameClientBridgeCommandCancellationFromDomain(value.Cancellation)
|
||||
response.Cancellation = &cancellation
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func GameClientBridgeSnapshotFromDomain(value domain.GameClientBridgeSnapshot) GameClientBridgeSnapshotResponse {
|
||||
value = domain.CopyGameClientBridgeSnapshot(value)
|
||||
return GameClientBridgeSnapshotResponse{
|
||||
ID: value.ID,
|
||||
ServerInstanceID: value.ServerInstanceID,
|
||||
PluginID: value.PluginID,
|
||||
ProfileKey: value.ProfileKey,
|
||||
Type: value.Type,
|
||||
SchemaVersion: value.SchemaVersion,
|
||||
StreamKey: value.StreamKey,
|
||||
Sequence: value.Sequence,
|
||||
ObservedAt: value.ObservedAt,
|
||||
Payload: value.Payload,
|
||||
Retention: gameClientBridgeRetentionFromDomain(value.Retention),
|
||||
AuditReferences: copyStrings(value.AuditReferences),
|
||||
CreatedAt: value.CreatedAt,
|
||||
ExpiresAt: value.ExpiresAt,
|
||||
}
|
||||
}
|
||||
|
||||
func GameClientBridgeClaimedCommandFromDomain(value domain.GameClientBridgeCommand) GameClientBridgeClaimedCommandResponse {
|
||||
value = domain.CopyGameClientBridgeCommand(value)
|
||||
return GameClientBridgeClaimedCommandResponse{ID: value.ID, ProfileKey: value.ProfileKey, CommandType: value.CommandType, Payload: value.Payload, Priority: value.Priority, FencingToken: value.Claim.FencingToken, ClaimedAt: value.Claim.ClaimedAt, LeaseExpiresAt: value.Claim.LeaseExpiresAt, ExpiresAt: value.ExpiresAt}
|
||||
}
|
||||
|
||||
func GameClientBridgeCommandsFromDomain(values []domain.GameClientBridgeCommand) GameClientBridgeCommandListResponse {
|
||||
items := make([]GameClientBridgeCommandResponse, len(values))
|
||||
for index, value := range values {
|
||||
items[index] = GameClientBridgeCommandFromDomain(value)
|
||||
}
|
||||
return GameClientBridgeCommandListResponse{Items: items, Count: len(items)}
|
||||
}
|
||||
|
||||
func GameClientBridgeSnapshotsFromDomain(values []domain.GameClientBridgeSnapshot) GameClientBridgeSnapshotListResponse {
|
||||
items := make([]GameClientBridgeSnapshotResponse, len(values))
|
||||
for index, value := range values {
|
||||
items[index] = GameClientBridgeSnapshotFromDomain(value)
|
||||
}
|
||||
return GameClientBridgeSnapshotListResponse{Items: items, Count: len(items)}
|
||||
}
|
||||
|
||||
func GameClientBridgeClaimResponseFromDomain(values []domain.GameClientBridgeCommand) GameClientBridgeClaimResponse {
|
||||
items := make([]GameClientBridgeClaimedCommandResponse, len(values))
|
||||
for index, value := range values {
|
||||
items[index] = GameClientBridgeClaimedCommandFromDomain(value)
|
||||
}
|
||||
return GameClientBridgeClaimResponse{Items: items, Count: len(items)}
|
||||
}
|
||||
|
||||
func GameClientBridgeClaimBatchFromDomain(values []domain.GameClientBridgeCommand) GameClientBridgeClaimBatchResponse {
|
||||
return GameClientBridgeClaimResponseFromDomain(values)
|
||||
}
|
||||
|
||||
func GameClientBridgeAckFromDomain(value domain.GameClientBridgeCommand) GameClientBridgeAckResponse {
|
||||
return GameClientBridgeAckResponse{CommandID: value.ID, State: string(value.State), FencingToken: value.Claim.FencingToken, AcknowledgedAt: value.Claim.AcknowledgedAt}
|
||||
}
|
||||
|
||||
func GameClientBridgeResultFromDomain(value domain.GameClientBridgeCommand) GameClientBridgeResultResponse {
|
||||
value = domain.CopyGameClientBridgeCommand(value)
|
||||
return GameClientBridgeResultResponse{CommandID: value.ID, State: string(value.State), Result: gameClientBridgeCommandResultFromDomain(value.Result), UpdatedAt: value.UpdatedAt, CompletedAt: value.CompletedAt}
|
||||
}
|
||||
|
||||
func GameClientBridgeCancelFromDomain(value domain.GameClientBridgeCommand) GameClientBridgeCancelResponse {
|
||||
value = domain.CopyGameClientBridgeCommand(value)
|
||||
return GameClientBridgeCancelResponse{CommandID: value.ID, State: string(value.State), Cancellation: gameClientBridgeCommandCancellationFromDomain(value.Cancellation), AuditReferences: copyStrings(value.AuditReferences), UpdatedAt: value.UpdatedAt}
|
||||
}
|
||||
|
||||
func GameClientBridgeSnapshotIngestFromDomain(value domain.GameClientBridgeSnapshot) GameClientBridgeSnapshotIngestResponse {
|
||||
return GameClientBridgeSnapshotIngestResponse{SnapshotID: value.ID, ProfileKey: value.ProfileKey, Type: value.Type, SchemaVersion: value.SchemaVersion, StreamKey: value.StreamKey, Sequence: value.Sequence, AcceptedAt: value.CreatedAt, ExpiresAt: value.ExpiresAt}
|
||||
}
|
||||
|
||||
func GameClientBridgeAuditReferenceFromDomain(value domain.GameClientBridgeAuditReference) GameClientBridgeAuditReferenceResponse {
|
||||
return GameClientBridgeAuditReferenceResponse{ID: value.ID, CommandID: value.CommandID, SnapshotID: value.SnapshotID, AuditEventID: value.AuditEventID, CreatedAt: value.CreatedAt}
|
||||
}
|
||||
|
||||
func GameClientBridgeAuditReferencesFromDomain(values []domain.GameClientBridgeAuditReference) GameClientBridgeAuditReferenceListResponse {
|
||||
items := make([]GameClientBridgeAuditReferenceResponse, len(values))
|
||||
for index, value := range values {
|
||||
items[index] = GameClientBridgeAuditReferenceFromDomain(value)
|
||||
}
|
||||
return GameClientBridgeAuditReferenceListResponse{Items: items, Count: len(items)}
|
||||
}
|
||||
|
||||
func GameClientBridgeStatusFromDomain(value domain.GameClientBridgeStatus) GameClientBridgeStatusResponse {
|
||||
value = domain.CopyGameClientBridgeStatus(value)
|
||||
profiles := make([]GameClientBridgeProfileDeclarationResponse, len(value.Profiles))
|
||||
for index, profile := range value.Profiles {
|
||||
profiles[index] = GameClientBridgeProfileDeclarationResponse{PluginID: profile.PluginID, ProfileKey: profile.ProfileKey, Available: profile.Available, Reason: profile.Reason, CommandTypes: nonNilStrings(profile.CommandTypes), SnapshotTypes: nonNilStrings(profile.SnapshotTypes), QueryTemplateKeys: nonNilStrings(profile.QueryTemplateKeys)}
|
||||
}
|
||||
return GameClientBridgeStatusResponse{ServerInstanceID: value.ServerInstanceID, PluginID: value.PluginID, Available: value.Available, Reason: value.Reason, Profiles: profiles}
|
||||
}
|
||||
|
||||
func gameClientBridgeCommandResultFromDomain(value domain.GameClientBridgeResult) GameClientBridgeCommandResultResponse {
|
||||
value = domain.CopyGameClientBridgeResult(value)
|
||||
return GameClientBridgeCommandResultResponse{Status: string(value.Status), Summary: value.Summary, Payload: value.Payload, CompletedAt: value.CompletedAt}
|
||||
}
|
||||
|
||||
func gameClientBridgeCommandCancellationFromDomain(value domain.GameClientBridgeCancellation) GameClientBridgeCommandCancellationResponse {
|
||||
return GameClientBridgeCommandCancellationResponse{RequestedBy: value.RequestedBy, Reason: value.Reason, CancelledAt: value.CancelledAt}
|
||||
}
|
||||
|
||||
func gameClientBridgeRetentionFromDomain(value domain.GameClientBridgeRetention) GameClientBridgeRetentionResponse {
|
||||
return GameClientBridgeRetentionResponse{KeepForSeconds: value.KeepForSeconds, MaxRecords: value.MaxRecords}
|
||||
}
|
||||
|
||||
func hasGameClientBridgeResult(value domain.GameClientBridgeResult) bool {
|
||||
return value.Status != "" || value.Summary != "" || value.Payload != nil || value.CompletedBy != "" || !value.CompletedAt.IsZero()
|
||||
}
|
||||
|
||||
func hasGameClientBridgeCancellation(value domain.GameClientBridgeCancellation) bool {
|
||||
return value.RequestedBy != "" || value.Reason != "" || !value.CancelledAt.IsZero()
|
||||
}
|
||||
|
||||
func copyStrings(values []string) []string {
|
||||
if values == nil {
|
||||
return nil
|
||||
}
|
||||
result := make([]string, len(values))
|
||||
copy(result, values)
|
||||
return result
|
||||
}
|
||||
|
||||
func nonNilStrings(values []string) []string {
|
||||
if values == nil {
|
||||
return []string{}
|
||||
}
|
||||
return copyStrings(values)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGameClientBridgeCompanionDeclarationRoundTripsWithoutMaterial(t *testing.T) {
|
||||
body := GameClientBridgeManifestBody{
|
||||
CommandRetentionSeconds: 86400,
|
||||
MaxCommands: 1000,
|
||||
Companion: &GameClientBridgeCompanionDeclarationBody{
|
||||
ProfileKey: "scum-client-manager",
|
||||
ConfigTemplateKey: "client-config",
|
||||
ConfigSchemaRef: "schemas/companion/config.schema.json",
|
||||
ConfigFormat: "yaml",
|
||||
PlatformBaseURLSource: "run-control",
|
||||
RegistrationProof: "hmac-sha256",
|
||||
ProofMaterialSource: "component-package",
|
||||
ProofMaterialEnv: "SCUM_COMPONENT_PROOF",
|
||||
SessionMode: "component-session",
|
||||
TLSPolicy: "verify-system-roots",
|
||||
HeartbeatIntervalSeconds: 30,
|
||||
CommandPollIntervalSeconds: 5,
|
||||
RequestTimeoutSeconds: 15,
|
||||
},
|
||||
}
|
||||
domainValue := body.ToDomain()
|
||||
if domainValue.Companion.ProfileKey != "scum-client-manager" || domainValue.Companion.TLSPolicy != "verify-system-roots" {
|
||||
t.Fatalf("companion declaration conversion lost fields: %+v", domainValue.Companion)
|
||||
}
|
||||
projection := gameClientBridgeManifestFromDomain(domainValue)
|
||||
encoded, err := json.Marshal(projection)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal companion declaration: %v", err)
|
||||
}
|
||||
serialized := string(encoded)
|
||||
for _, forbidden := range []string{"authKey", "componentKey", "sessionToken", "credential", "secretRef", "hostPath", "runSocket"} {
|
||||
if strings.Contains(serialized, forbidden) {
|
||||
t.Fatalf("companion declaration exposed %q: %s", forbidden, serialized)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
func TestGameClientBridgeRequestConversionsInjectScopeAndCopyPayloads(t *testing.T) {
|
||||
payload := map[string]any{"players": []any{map[string]any{"name": "Alice"}}}
|
||||
queue := GameClientBridgeQueueRequest{ProfileKey: "scum-client", CommandType: "player.lookup", Payload: payload, IdempotencyKey: "lookup-1", Priority: 5, ExpiresAt: time.Now().UTC().Add(time.Minute)}.ToDomain("server-1", "game.scum")
|
||||
claim := GameClientBridgeClaimRequest{SessionToken: "session", Limit: 5}.ToDomain()
|
||||
ack := GameClientBridgeAckRequest{SessionToken: "session", FencingToken: 7}.ToDomain("command-1")
|
||||
result := GameClientBridgeResultRequest{SessionToken: "session", FencingToken: 7, Status: "succeeded", Payload: payload}.ToDomain("command-1")
|
||||
cancel := GameClientBridgeCancelRequest{Reason: "operator request"}.ToDomain("command-1")
|
||||
snapshot := GameClientBridgeSnapshotIngestRequest{SessionToken: "session", Type: "players", SchemaVersion: "1", StreamKey: "current", Sequence: 2, Payload: payload, KeepForSeconds: 60, MaxRecords: 4}.ToDomain()
|
||||
query := GameClientBridgeSnapshotQuery{ProfileKey: "scum-client", Type: "players", StreamKey: "current", Limit: 10}.ToDomain("server-1", "game.scum")
|
||||
|
||||
if queue.ServerInstanceID != "server-1" || queue.PluginID != "game.scum" || claim.Limit != 5 || ack.CommandID != "command-1" || result.CommandID != "command-1" || cancel.CommandID != "command-1" || snapshot.Retention.MaxRecords != 4 || query.ServerInstanceID != "server-1" || query.PluginID != "game.scum" {
|
||||
t.Fatalf("request conversion lost route or body fields: queue=%#v claim=%#v ack=%#v result=%#v cancel=%#v snapshot=%#v query=%#v", queue, claim, ack, result, cancel, snapshot, query)
|
||||
}
|
||||
queue.Payload["players"].([]any)[0].(map[string]any)["name"] = "queue"
|
||||
result.Payload["players"].([]any)[0].(map[string]any)["name"] = "result"
|
||||
snapshot.Payload["players"].([]any)[0].(map[string]any)["name"] = "snapshot"
|
||||
if got := payload["players"].([]any)[0].(map[string]any)["name"]; got != "Alice" {
|
||||
t.Fatalf("request conversion aliases source payload: %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGameClientBridgeBrowserProjectionsAreCompleteAndOmitInternalData(t *testing.T) {
|
||||
now := time.Date(2026, 7, 20, 10, 0, 0, 0, time.UTC)
|
||||
command := domain.GameClientBridgeCommand{
|
||||
ID: "command-1", ServerInstanceID: "server-1", PluginID: "game.scum", ProfileKey: "scum-client", CommandType: "announcement.send", Payload: map[string]any{"message": "internal command payload"}, IdempotencyKey: "internal-idempotency", Priority: 9,
|
||||
State: domain.GameClientBridgeCommandCancelled, ApprovalState: domain.GameClientBridgeApprovalApproved, RequesterID: "operator-1",
|
||||
Claim: domain.GameClientBridgeClaim{SessionID: "internal-session-secret", InstallationID: "internal-installation", DeploymentGeneration: 9, FencingToken: 42, LeaseExpiresAt: now.Add(time.Minute)},
|
||||
Result: domain.GameClientBridgeResult{Status: domain.GameClientBridgeResultCancelled, Summary: "cancelled safely", Payload: map[string]any{"code": "cancelled"}, CompletedBy: "internal-completing-session", CompletedAt: now.Add(3 * time.Minute)},
|
||||
Cancellation: domain.GameClientBridgeCancellation{RequestedBy: "operator-2", Reason: "operator request", CancelledAt: now.Add(2 * time.Minute)},
|
||||
AuditReferences: []string{"audit-1"}, ExpiresAt: now.Add(10 * time.Minute), CreatedAt: now, UpdatedAt: now.Add(3 * time.Minute), CompletedAt: now.Add(3 * time.Minute),
|
||||
}
|
||||
snapshot := domain.GameClientBridgeSnapshot{ID: "snapshot-1", ServerInstanceID: "server-1", PluginID: "game.scum", ProfileKey: "scum-client", Type: "players", SchemaVersion: "1", StreamKey: "current", Sequence: 2, SourceSessionID: "internal-source-session", ObservedAt: now, Payload: map[string]any{"players": []any{map[string]any{"name": "Alice"}}}, Retention: domain.GameClientBridgeRetention{KeepForSeconds: 3600, MaxRecords: 10}, AuditReferences: []string{"audit-2"}, CreatedAt: now.Add(time.Second), ExpiresAt: now.Add(time.Hour)}
|
||||
|
||||
commandProjection := GameClientBridgeCommandFromDomain(command)
|
||||
snapshotProjection := GameClientBridgeSnapshotFromDomain(snapshot)
|
||||
if commandProjection.Result == nil || commandProjection.Result.Status != "cancelled" || commandProjection.Cancellation == nil || commandProjection.Cancellation.Reason != "operator request" || commandProjection.Priority != 9 || commandProjection.AuditReferences[0] != "audit-1" || !commandProjection.CompletedAt.Equal(command.CompletedAt) {
|
||||
t.Fatalf("incomplete command projection: %#v", commandProjection)
|
||||
}
|
||||
if snapshotProjection.ProfileKey != "scum-client" || snapshotProjection.Retention.KeepForSeconds != 3600 || snapshotProjection.AuditReferences[0] != "audit-2" || !snapshotProjection.CreatedAt.Equal(snapshot.CreatedAt) {
|
||||
t.Fatalf("incomplete snapshot projection: %#v", snapshotProjection)
|
||||
}
|
||||
|
||||
commandProjection.Result.Payload["code"] = "changed"
|
||||
commandProjection.AuditReferences[0] = "changed"
|
||||
snapshotProjection.Payload["players"].([]any)[0].(map[string]any)["name"] = "changed"
|
||||
snapshotProjection.AuditReferences[0] = "changed"
|
||||
if command.Result.Payload["code"] != "cancelled" || command.AuditReferences[0] != "audit-1" || snapshot.Payload["players"].([]any)[0].(map[string]any)["name"] != "Alice" || snapshot.AuditReferences[0] != "audit-2" {
|
||||
t.Fatal("browser projection aliases domain data")
|
||||
}
|
||||
|
||||
encoded, err := json.Marshal(struct {
|
||||
Command GameClientBridgeCommandResponse `json:"command"`
|
||||
Snapshot GameClientBridgeSnapshotResponse `json:"snapshot"`
|
||||
}{commandProjection, snapshotProjection})
|
||||
if err != nil {
|
||||
t.Fatalf("marshal projections: %v", err)
|
||||
}
|
||||
text := string(encoded)
|
||||
for _, forbidden := range []string{"internal-session-secret", "internal-installation", "internal-completing-session", "internal-source-session", "internal-idempotency", "internal command payload", "fencingToken", "deploymentGeneration", "sourceSessionId", "completedBy", "idempotencyKey", "leaseExpiresAt"} {
|
||||
if strings.Contains(text, forbidden) {
|
||||
t.Fatalf("browser projection leaked %q: %s", forbidden, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGameClientBridgeListAndCompanionResponseConversions(t *testing.T) {
|
||||
now := time.Date(2026, 7, 20, 10, 0, 0, 0, time.UTC)
|
||||
command := domain.GameClientBridgeCommand{
|
||||
ID: "command-1", ProfileKey: "scum-client", CommandType: "diagnostic.safe", Payload: map[string]any{"scope": "health"}, Priority: 3, State: domain.GameClientBridgeCommandSucceeded,
|
||||
Claim: domain.GameClientBridgeClaim{SessionID: "private-session", FencingToken: 7, ClaimedAt: now, AcknowledgedAt: now.Add(time.Second), LeaseExpiresAt: now.Add(time.Minute)},
|
||||
Result: domain.GameClientBridgeResult{Status: domain.GameClientBridgeResultSucceeded, Summary: "ok", Payload: map[string]any{"healthy": true}, CompletedBy: "private-session", CompletedAt: now.Add(2 * time.Second)},
|
||||
Cancellation: domain.GameClientBridgeCancellation{RequestedBy: "operator", Reason: "superseded", CancelledAt: now.Add(3 * time.Second)}, AuditReferences: []string{"audit-1"}, ExpiresAt: now.Add(5 * time.Minute), UpdatedAt: now.Add(2 * time.Second), CompletedAt: now.Add(2 * time.Second),
|
||||
}
|
||||
snapshot := domain.GameClientBridgeSnapshot{ID: "snapshot-1", ProfileKey: "scum-client", Type: "players", SchemaVersion: "1", StreamKey: "current", Sequence: 4, CreatedAt: now, ExpiresAt: now.Add(time.Hour)}
|
||||
|
||||
claim := GameClientBridgeClaimBatchFromDomain([]domain.GameClientBridgeCommand{command})
|
||||
ack := GameClientBridgeAckFromDomain(command)
|
||||
result := GameClientBridgeResultFromDomain(command)
|
||||
cancel := GameClientBridgeCancelFromDomain(command)
|
||||
ingest := GameClientBridgeSnapshotIngestFromDomain(snapshot)
|
||||
commands := GameClientBridgeCommandsFromDomain([]domain.GameClientBridgeCommand{command})
|
||||
snapshots := GameClientBridgeSnapshotsFromDomain([]domain.GameClientBridgeSnapshot{snapshot})
|
||||
audits := GameClientBridgeAuditReferencesFromDomain([]domain.GameClientBridgeAuditReference{{ID: "reference-1", CommandID: "command-1", AuditEventID: "audit-1", CreatedAt: now}})
|
||||
if claim.Count != 1 || claim.Items[0].FencingToken != 7 || claim.Items[0].ProfileKey != "scum-client" || ack.CommandID != "command-1" || !ack.AcknowledgedAt.Equal(now.Add(time.Second)) || result.Result.Summary != "ok" || cancel.Cancellation.Reason != "superseded" || ingest.SnapshotID != "snapshot-1" || commands.Count != 1 || snapshots.Count != 1 || audits.Count != 1 {
|
||||
t.Fatalf("unexpected responses: claim=%#v ack=%#v result=%#v cancel=%#v ingest=%#v", claim, ack, result, cancel, ingest)
|
||||
}
|
||||
claim.Items[0].Payload["scope"] = "changed"
|
||||
result.Result.Payload["healthy"] = false
|
||||
if command.Payload["scope"] != "health" || command.Result.Payload["healthy"] != true {
|
||||
t.Fatal("companion response aliases domain payload")
|
||||
}
|
||||
for name, value := range map[string]any{"claim": claim, "ack": ack, "result": result, "cancel": cancel, "ingest": ingest, "commands": commands, "snapshots": snapshots, "audits": audits} {
|
||||
encoded, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal %s response: %v", name, err)
|
||||
}
|
||||
if strings.Contains(string(encoded), "private-session") || strings.Contains(string(encoded), "sessionId") || strings.Contains(string(encoded), "completedBy") {
|
||||
t.Fatalf("%s response exposes a private session: %s", name, encoded)
|
||||
}
|
||||
}
|
||||
|
||||
if GameClientBridgeCommandsFromDomain(nil).Items == nil || GameClientBridgeSnapshotsFromDomain(nil).Items == nil || GameClientBridgeClaimBatchFromDomain(nil).Items == nil || GameClientBridgeAuditReferencesFromDomain(nil).Items == nil {
|
||||
t.Fatal("list converters must serialize empty items as [] instead of null")
|
||||
}
|
||||
}
|
||||
@@ -92,14 +92,18 @@ type RunJobResultRequest struct {
|
||||
}
|
||||
|
||||
type RunJobExecutionInputBody struct {
|
||||
WorkspaceScope string `json:"workspaceScope,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
ExpectedVersion int `json:"expectedVersion,omitempty"`
|
||||
ExpectedChecksum string `json:"expectedChecksum,omitempty"`
|
||||
MaxReadBytes int `json:"maxReadBytes,omitempty"`
|
||||
RemoteAdapterKey string `json:"remoteAdapterKey,omitempty"`
|
||||
RemoteAdapterKind string `json:"remoteAdapterKind,omitempty"`
|
||||
TimeoutSeconds int `json:"timeoutSeconds,omitempty"`
|
||||
WorkspaceScope string `json:"workspaceScope,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
ExpectedVersion int `json:"expectedVersion,omitempty"`
|
||||
ExpectedChecksum string `json:"expectedChecksum,omitempty"`
|
||||
MaxReadBytes int `json:"maxReadBytes,omitempty"`
|
||||
RemoteAdapterKey string `json:"remoteAdapterKey,omitempty"`
|
||||
RemoteAdapterKind string `json:"remoteAdapterKind,omitempty"`
|
||||
TimeoutSeconds int `json:"timeoutSeconds,omitempty"`
|
||||
PluginID string `json:"pluginId,omitempty"`
|
||||
LifecycleOperation string `json:"lifecycleOperation,omitempty"`
|
||||
TargetVersion string `json:"targetVersion,omitempty"`
|
||||
Inputs map[string]string `json:"inputs,omitempty"`
|
||||
}
|
||||
|
||||
type RunJobExecutionResultBody struct {
|
||||
@@ -525,7 +529,7 @@ func RunJobAssignmentFromDomain(assignment domain.RunJobAssignment) RunJobAssign
|
||||
State: assignment.State,
|
||||
Progress: progressReportFromDomain(assignment.Progress),
|
||||
ResultRef: assignment.ResultRef,
|
||||
ExecutionInput: RunJobExecutionInputBody{WorkspaceScope: assignment.ExecutionInput.WorkspaceScope, Content: assignment.ExecutionInput.Content, ExpectedVersion: assignment.ExecutionInput.ExpectedVersion, ExpectedChecksum: assignment.ExecutionInput.ExpectedChecksum, MaxReadBytes: assignment.ExecutionInput.MaxReadBytes, RemoteAdapterKey: assignment.ExecutionInput.RemoteAdapterKey, RemoteAdapterKind: assignment.ExecutionInput.RemoteAdapterKind, TimeoutSeconds: assignment.ExecutionInput.TimeoutSeconds},
|
||||
ExecutionInput: RunJobExecutionInputBody{WorkspaceScope: assignment.ExecutionInput.WorkspaceScope, Content: assignment.ExecutionInput.Content, ExpectedVersion: assignment.ExecutionInput.ExpectedVersion, ExpectedChecksum: assignment.ExecutionInput.ExpectedChecksum, MaxReadBytes: assignment.ExecutionInput.MaxReadBytes, RemoteAdapterKey: assignment.ExecutionInput.RemoteAdapterKey, RemoteAdapterKind: assignment.ExecutionInput.RemoteAdapterKind, TimeoutSeconds: assignment.ExecutionInput.TimeoutSeconds, PluginID: assignment.ExecutionInput.PluginID, LifecycleOperation: assignment.ExecutionInput.LifecycleOperation, TargetVersion: assignment.ExecutionInput.TargetVersion, Inputs: domain.CopyStringMap(assignment.ExecutionInput.Inputs)},
|
||||
LeaseToken: assignment.LeaseToken,
|
||||
Attempt: assignment.Attempt,
|
||||
MaxAttempts: assignment.MaxAttempts,
|
||||
|
||||
@@ -82,12 +82,14 @@ type RemoteAdapterDeclarationListResponse struct {
|
||||
}
|
||||
|
||||
type RemoteAdapterRequestBody struct {
|
||||
DeclarationKey string `json:"declarationKey"`
|
||||
TargetKey string `json:"targetKey"`
|
||||
Capability string `json:"capability"`
|
||||
TimeoutSeconds int `json:"timeoutSeconds,omitempty"`
|
||||
MaxAttempts int `json:"maxAttempts,omitempty"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
DeclarationKey string `json:"declarationKey"`
|
||||
TargetKey string `json:"targetKey"`
|
||||
Capability string `json:"capability"`
|
||||
TimeoutSeconds int `json:"timeoutSeconds,omitempty"`
|
||||
MaxAttempts int `json:"maxAttempts,omitempty"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
InputRef string `json:"inputRef,omitempty"`
|
||||
Inputs map[string]string `json:"inputs,omitempty"`
|
||||
}
|
||||
|
||||
type RemoteAdapterResponse struct {
|
||||
@@ -149,7 +151,7 @@ func RemoteAdapterDeclarationsFromDomain(declarations []domain.RemoteAdapterDecl
|
||||
}
|
||||
|
||||
func (request RemoteAdapterRequestBody) ToDomain(serverInstanceID string) domain.RemoteAdapterRequest {
|
||||
return domain.RemoteAdapterRequest{ServerInstanceID: serverInstanceID, DeclarationKey: request.DeclarationKey, TargetKey: request.TargetKey, Capability: request.Capability, TimeoutSeconds: request.TimeoutSeconds, MaxAttempts: request.MaxAttempts, IdempotencyKey: request.IdempotencyKey}
|
||||
return domain.RemoteAdapterRequest{ServerInstanceID: serverInstanceID, DeclarationKey: request.DeclarationKey, TargetKey: request.TargetKey, Capability: request.Capability, TimeoutSeconds: request.TimeoutSeconds, MaxAttempts: request.MaxAttempts, IdempotencyKey: request.IdempotencyKey, InputRef: request.InputRef, Inputs: domain.CopyStringMap(request.Inputs)}
|
||||
}
|
||||
|
||||
func RemoteAdapterFromDomain(result domain.RemoteAdapterResult) RemoteAdapterResponse {
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
type CapacityAdmissionRequest struct {
|
||||
ServerInstanceID string `json:"serverInstanceId,omitempty"`
|
||||
RunEndpointID string `json:"runEndpointId,omitempty"`
|
||||
Capability string `json:"capability"`
|
||||
TargetKey string `json:"targetKey,omitempty"`
|
||||
IdempotencyKey string `json:"idempotencyKey,omitempty"`
|
||||
}
|
||||
|
||||
type CapacityAdmissionDecisionResponse struct {
|
||||
Accepted bool `json:"accepted"`
|
||||
State string `json:"state"`
|
||||
Reason string `json:"reason"`
|
||||
RetryAfterSeconds int `json:"retryAfterSeconds,omitempty"`
|
||||
ServerInstanceID string `json:"serverInstanceId,omitempty"`
|
||||
RunEndpointID string `json:"runEndpointId,omitempty"`
|
||||
Capability string `json:"capability"`
|
||||
TargetKey string `json:"targetKey,omitempty"`
|
||||
MaxJobs int `json:"maxJobs"`
|
||||
RunningJobs int `json:"runningJobs"`
|
||||
QueuedJobs int `json:"queuedJobs"`
|
||||
PressureCodes []string `json:"pressureCodes,omitempty"`
|
||||
CheckedAt time.Time `json:"checkedAt"`
|
||||
AlertID string `json:"alertId,omitempty"`
|
||||
AuditEventID string `json:"auditEventId,omitempty"`
|
||||
}
|
||||
|
||||
type EndpointCapacityProjectionResponse struct {
|
||||
RunEndpointID string `json:"runEndpointId"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Status string `json:"status"`
|
||||
Capabilities []string `json:"capabilities"`
|
||||
MaxJobs int `json:"maxJobs"`
|
||||
RunningJobs int `json:"runningJobs"`
|
||||
QueuedJobs int `json:"queuedJobs"`
|
||||
LogBacklogBatches int `json:"logBacklogBatches,omitempty"`
|
||||
ArtifactBacklogChunks int `json:"artifactBacklogChunks,omitempty"`
|
||||
PressureCodes []string `json:"pressureCodes,omitempty"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
LastHeartbeatAt time.Time `json:"lastHeartbeatAt"`
|
||||
LastAdmissionDecision string `json:"lastAdmissionDecision,omitempty"`
|
||||
LastAdmissionReason string `json:"lastAdmissionReason,omitempty"`
|
||||
LastAdmissionCheckedAt time.Time `json:"lastAdmissionCheckedAt,omitempty"`
|
||||
}
|
||||
|
||||
type ProductionCapacitySummaryResponse struct {
|
||||
Endpoints []EndpointCapacityProjectionResponse `json:"endpoints"`
|
||||
TotalMaxJobs int `json:"totalMaxJobs"`
|
||||
TotalRunningJobs int `json:"totalRunningJobs"`
|
||||
TotalQueuedJobs int `json:"totalQueuedJobs"`
|
||||
ActiveAlerts int `json:"activeAlerts"`
|
||||
GeneratedAt time.Time `json:"generatedAt"`
|
||||
}
|
||||
|
||||
type AlertResponse struct {
|
||||
ID string `json:"id"`
|
||||
SourceKind string `json:"sourceKind"`
|
||||
SourceID string `json:"sourceId"`
|
||||
RuleKey string `json:"ruleKey"`
|
||||
Severity string `json:"severity"`
|
||||
State string `json:"state"`
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message"`
|
||||
OccurrenceCount int `json:"occurrenceCount"`
|
||||
Retryable bool `json:"retryable"`
|
||||
RetryAfterSeconds int `json:"retryAfterSeconds,omitempty"`
|
||||
LastJobID string `json:"lastJobId,omitempty"`
|
||||
LastAuditEventID string `json:"lastAuditEventId,omitempty"`
|
||||
LastSeenAt time.Time `json:"lastSeenAt"`
|
||||
AcknowledgedBy string `json:"acknowledgedBy,omitempty"`
|
||||
AcknowledgedAt time.Time `json:"acknowledgedAt,omitempty"`
|
||||
ResolvedBy string `json:"resolvedBy,omitempty"`
|
||||
ResolvedAt time.Time `json:"resolvedAt,omitempty"`
|
||||
ResolutionNote string `json:"resolutionNote,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type AlertListResponse struct {
|
||||
Items []AlertResponse `json:"items"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type AlertAcknowledgeRequest struct {
|
||||
Note string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type AlertResolveRequest struct {
|
||||
Note string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type AlertRetryRequest struct {
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
}
|
||||
|
||||
type AlertRetryResponse struct {
|
||||
Status string `json:"status"`
|
||||
Alert AlertResponse `json:"alert"`
|
||||
Decision CapacityAdmissionDecisionResponse `json:"decision"`
|
||||
}
|
||||
|
||||
type PluginLifecycleActionRequest struct {
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
Operation string `json:"operation"`
|
||||
TargetVersion string `json:"targetVersion,omitempty"`
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
Confirmed bool `json:"confirmed"`
|
||||
}
|
||||
|
||||
type PluginLifecycleInstallationResponse struct {
|
||||
ID string `json:"id"`
|
||||
PluginID string `json:"pluginId"`
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
CurrentVersion string `json:"currentVersion,omitempty"`
|
||||
TargetVersion string `json:"targetVersion,omitempty"`
|
||||
PreviousVersion string `json:"previousVersion,omitempty"`
|
||||
DesiredState string `json:"desiredState"`
|
||||
CurrentState string `json:"currentState"`
|
||||
LastOperation string `json:"lastOperation,omitempty"`
|
||||
Compatibility string `json:"compatibility,omitempty"`
|
||||
DependencyState string `json:"dependencyState,omitempty"`
|
||||
JobID string `json:"jobId,omitempty"`
|
||||
AlertID string `json:"alertId,omitempty"`
|
||||
AuditEventID string `json:"auditEventId,omitempty"`
|
||||
FailureReason string `json:"failureReason,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type PluginLifecycleListResponse struct {
|
||||
Items []PluginLifecycleInstallationResponse `json:"items"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type PluginLifecycleActionResponse struct {
|
||||
Status string `json:"status"`
|
||||
Installation PluginLifecycleInstallationResponse `json:"installation"`
|
||||
Job JobResponse `json:"job"`
|
||||
Decision CapacityAdmissionDecisionResponse `json:"decision"`
|
||||
Alert *AlertResponse `json:"alert,omitempty"`
|
||||
}
|
||||
|
||||
type AIConfigDiffPreviewResponse struct {
|
||||
ID string `json:"id"`
|
||||
RequestID string `json:"requestId"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
ServerInstanceID string `json:"serverInstanceId"`
|
||||
PluginID string `json:"pluginId,omitempty"`
|
||||
ProviderID string `json:"providerId,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Key string `json:"key"`
|
||||
ConfigVersion int `json:"configVersion"`
|
||||
CurrentConfigChecksum string `json:"currentConfigChecksum,omitempty"`
|
||||
ProposedConfig string `json:"proposedConfig,omitempty"`
|
||||
DiffSummary string `json:"diffSummary"`
|
||||
State string `json:"state"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
ApprovedBy string `json:"approvedBy,omitempty"`
|
||||
ApprovedAt time.Time `json:"approvedAt,omitempty"`
|
||||
CancelledBy string `json:"cancelledBy,omitempty"`
|
||||
CancelledAt time.Time `json:"cancelledAt,omitempty"`
|
||||
JobID string `json:"jobId,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type AIConfigDiffListResponse struct {
|
||||
Items []AIConfigDiffPreviewResponse `json:"items"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
type AIConfigDiffApprovalRequest struct {
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
}
|
||||
|
||||
type AIConfigDiffApprovalResponse struct {
|
||||
Preview AIConfigDiffPreviewResponse `json:"preview"`
|
||||
Dispatch ServerConfigWriteDispatchResponse `json:"dispatch"`
|
||||
}
|
||||
|
||||
func (request CapacityAdmissionRequest) ToDomain() domain.CapacityAdmissionRequest {
|
||||
return domain.CapacityAdmissionRequest{ServerInstanceID: request.ServerInstanceID, RunEndpointID: request.RunEndpointID, Capability: request.Capability, TargetKey: request.TargetKey, IdempotencyKey: request.IdempotencyKey}
|
||||
}
|
||||
|
||||
func CapacityDecisionFromDomain(decision domain.CapacityAdmissionDecision) CapacityAdmissionDecisionResponse {
|
||||
decision = domain.CopyCapacityAdmissionDecision(decision)
|
||||
return CapacityAdmissionDecisionResponse{Accepted: decision.Accepted, State: string(decision.State), Reason: decision.Reason, RetryAfterSeconds: decision.RetryAfterSeconds, ServerInstanceID: decision.ServerInstanceID, RunEndpointID: decision.RunEndpointID, Capability: decision.Capability, TargetKey: decision.TargetKey, MaxJobs: decision.MaxJobs, RunningJobs: decision.RunningJobs, QueuedJobs: decision.QueuedJobs, PressureCodes: pressureCodesFromDomain(decision.PressureCodes), CheckedAt: decision.CheckedAt, AlertID: decision.AlertID, AuditEventID: decision.AuditEventID}
|
||||
}
|
||||
|
||||
func ProductionCapacityFromDomain(summary domain.ProductionCapacitySummary) ProductionCapacitySummaryResponse {
|
||||
summary = domain.CopyProductionCapacitySummary(summary)
|
||||
items := make([]EndpointCapacityProjectionResponse, len(summary.Endpoints))
|
||||
for i, endpoint := range summary.Endpoints {
|
||||
items[i] = EndpointCapacityProjectionResponse{RunEndpointID: endpoint.RunEndpointID, DisplayName: endpoint.DisplayName, Status: string(endpoint.Status), Capabilities: endpoint.Capabilities, MaxJobs: endpoint.MaxJobs, RunningJobs: endpoint.RunningJobs, QueuedJobs: endpoint.QueuedJobs, LogBacklogBatches: endpoint.LogBacklogBatches, ArtifactBacklogChunks: endpoint.ArtifactBacklogChunks, PressureCodes: pressureCodesFromDomain(endpoint.PressureCodes), Summary: endpoint.Summary, LastHeartbeatAt: endpoint.LastHeartbeatAt, LastAdmissionDecision: string(endpoint.LastAdmissionDecision), LastAdmissionReason: endpoint.LastAdmissionReason, LastAdmissionCheckedAt: endpoint.LastAdmissionCheckedAt}
|
||||
}
|
||||
return ProductionCapacitySummaryResponse{Endpoints: items, TotalMaxJobs: summary.TotalMaxJobs, TotalRunningJobs: summary.TotalRunningJobs, TotalQueuedJobs: summary.TotalQueuedJobs, ActiveAlerts: summary.ActiveAlerts, GeneratedAt: summary.GeneratedAt}
|
||||
}
|
||||
|
||||
func AlertFromDomain(alert domain.AlertRecord) AlertResponse {
|
||||
alert = domain.CopyAlertRecord(alert)
|
||||
return AlertResponse{ID: alert.ID, SourceKind: alert.SourceKind, SourceID: alert.SourceID, RuleKey: alert.RuleKey, Severity: string(alert.Severity), State: string(alert.State), Title: alert.Title, Message: alert.Message, OccurrenceCount: alert.OccurrenceCount, Retryable: alert.Retryable, RetryAfterSeconds: alert.RetryAfterSeconds, LastJobID: alert.LastJobID, LastAuditEventID: alert.LastAuditEventID, LastSeenAt: alert.LastSeenAt, AcknowledgedBy: alert.AcknowledgedBy, AcknowledgedAt: alert.AcknowledgedAt, ResolvedBy: alert.ResolvedBy, ResolvedAt: alert.ResolvedAt, ResolutionNote: alert.ResolutionNote, CreatedAt: alert.CreatedAt, UpdatedAt: alert.UpdatedAt}
|
||||
}
|
||||
|
||||
func AlertListFromDomain(alerts []domain.AlertRecord) AlertListResponse {
|
||||
items := make([]AlertResponse, len(alerts))
|
||||
for i, alert := range alerts {
|
||||
items[i] = AlertFromDomain(alert)
|
||||
}
|
||||
return AlertListResponse{Items: items, Count: len(items)}
|
||||
}
|
||||
|
||||
func AlertRetryFromDomain(result domain.AlertRetryResult) AlertRetryResponse {
|
||||
result = domain.CopyAlertRetryResult(result)
|
||||
return AlertRetryResponse{Status: result.Status, Alert: AlertFromDomain(result.Alert), Decision: CapacityDecisionFromDomain(result.Decision)}
|
||||
}
|
||||
|
||||
func (request PluginLifecycleActionRequest) ToDomain(pluginID string) domain.PluginLifecycleRequest {
|
||||
return domain.PluginLifecycleRequest{PluginID: pluginID, ServerInstanceID: request.ServerInstanceID, Operation: domain.PluginLifecycleOperation(request.Operation), TargetVersion: request.TargetVersion, IdempotencyKey: request.IdempotencyKey, Confirmed: request.Confirmed}
|
||||
}
|
||||
|
||||
func PluginLifecycleFromDomain(installation domain.PluginLifecycleInstallation) PluginLifecycleInstallationResponse {
|
||||
installation = domain.CopyPluginLifecycleInstallation(installation)
|
||||
return PluginLifecycleInstallationResponse{ID: installation.ID, PluginID: installation.PluginID, ServerInstanceID: installation.ServerInstanceID, CurrentVersion: installation.CurrentVersion, TargetVersion: installation.TargetVersion, PreviousVersion: installation.PreviousVersion, DesiredState: string(installation.DesiredState), CurrentState: string(installation.CurrentState), LastOperation: string(installation.LastOperation), Compatibility: installation.Compatibility, DependencyState: string(installation.DependencyState), JobID: installation.JobID, AlertID: installation.AlertID, AuditEventID: installation.AuditEventID, FailureReason: installation.FailureReason, CreatedAt: installation.CreatedAt, UpdatedAt: installation.UpdatedAt}
|
||||
}
|
||||
|
||||
func PluginLifecycleListFromDomain(installations []domain.PluginLifecycleInstallation) PluginLifecycleListResponse {
|
||||
items := make([]PluginLifecycleInstallationResponse, len(installations))
|
||||
for i, installation := range installations {
|
||||
items[i] = PluginLifecycleFromDomain(installation)
|
||||
}
|
||||
return PluginLifecycleListResponse{Items: items, Count: len(items)}
|
||||
}
|
||||
|
||||
func PluginLifecycleResultFromDomain(result domain.PluginLifecycleResult) PluginLifecycleActionResponse {
|
||||
result = domain.CopyPluginLifecycleResult(result)
|
||||
var alert *AlertResponse
|
||||
if result.Alert != nil {
|
||||
item := AlertFromDomain(*result.Alert)
|
||||
alert = &item
|
||||
}
|
||||
return PluginLifecycleActionResponse{Status: result.Status, Installation: PluginLifecycleFromDomain(result.Installation), Job: JobFromDomain(result.Job), Decision: CapacityDecisionFromDomain(result.Decision), Alert: alert}
|
||||
}
|
||||
|
||||
func AIConfigDiffFromDomain(preview domain.AIConfigDiffPreview) AIConfigDiffPreviewResponse {
|
||||
preview = domain.CopyAIConfigDiffPreview(preview)
|
||||
return AIConfigDiffPreviewResponse{ID: preview.ID, RequestID: preview.RequestID, CreatedBy: preview.CreatedBy, ServerInstanceID: preview.ServerInstanceID, PluginID: preview.PluginID, ProviderID: preview.ProviderID, Model: preview.Model, Key: preview.Key, ConfigVersion: preview.ConfigVersion, CurrentConfigChecksum: preview.CurrentConfigChecksum, ProposedConfig: preview.ProposedConfig, DiffSummary: preview.DiffSummary, State: string(preview.State), ExpiresAt: preview.ExpiresAt, ApprovedBy: preview.ApprovedBy, ApprovedAt: preview.ApprovedAt, CancelledBy: preview.CancelledBy, CancelledAt: preview.CancelledAt, JobID: preview.JobID, CreatedAt: preview.CreatedAt, UpdatedAt: preview.UpdatedAt}
|
||||
}
|
||||
|
||||
func AIConfigDiffListFromDomain(previews []domain.AIConfigDiffPreview) AIConfigDiffListResponse {
|
||||
items := make([]AIConfigDiffPreviewResponse, len(previews))
|
||||
for i, preview := range previews {
|
||||
items[i] = AIConfigDiffFromDomain(preview)
|
||||
}
|
||||
return AIConfigDiffListResponse{Items: items, Count: len(items)}
|
||||
}
|
||||
|
||||
func (request AIConfigDiffApprovalRequest) ToDomain(diffID string) domain.AIConfigDiffApprovalRequest {
|
||||
return domain.AIConfigDiffApprovalRequest{DiffID: diffID, IdempotencyKey: request.IdempotencyKey}
|
||||
}
|
||||
|
||||
func AIConfigDiffApprovalFromDomain(result domain.AIConfigDiffApprovalResult) AIConfigDiffApprovalResponse {
|
||||
result = domain.CopyAIConfigDiffApprovalResult(result)
|
||||
return AIConfigDiffApprovalResponse{Preview: AIConfigDiffFromDomain(result.Preview), Dispatch: ServerConfigWriteDispatchFromDomain(result.Dispatch)}
|
||||
}
|
||||
|
||||
func pressureCodesFromDomain(codes []domain.CapacityPressureCode) []string {
|
||||
if codes == nil {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, len(codes))
|
||||
for i, code := range codes {
|
||||
out[i] = string(code)
|
||||
}
|
||||
return out
|
||||
}
|
||||
+285
-129
@@ -126,17 +126,17 @@ type AIProviderStatusRequest struct {
|
||||
}
|
||||
|
||||
type AIProviderResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Kind domain.AIProviderKind `json:"kind"`
|
||||
BaseURL string `json:"baseUrl"`
|
||||
APIKeyConfigured bool `json:"apiKeyConfigured"`
|
||||
Models []string `json:"models"`
|
||||
DefaultModel string `json:"defaultModel,omitempty"`
|
||||
RelayMode domain.AIRelayMode `json:"relayMode"`
|
||||
TimeoutMS int `json:"timeoutMs"`
|
||||
Status domain.AIProviderStatus `json:"status"`
|
||||
RedactionPolicy string `json:"redactionPolicy"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Kind domain.AIProviderKind `json:"kind"`
|
||||
BaseURLConfigured bool `json:"baseUrlConfigured"`
|
||||
APIKeyConfigured bool `json:"apiKeyConfigured"`
|
||||
Models []string `json:"models"`
|
||||
DefaultModel string `json:"defaultModel,omitempty"`
|
||||
RelayMode domain.AIRelayMode `json:"relayMode"`
|
||||
TimeoutMS int `json:"timeoutMs"`
|
||||
Status domain.AIProviderStatus `json:"status"`
|
||||
RedactionPolicy string `json:"redactionPolicy"`
|
||||
}
|
||||
|
||||
type AIProviderListResponse struct {
|
||||
@@ -195,7 +195,15 @@ type GamePluginManifestServerBody struct {
|
||||
}
|
||||
|
||||
type GamePluginManifestAIBody struct {
|
||||
Purposes []string `json:"purposes,omitempty"`
|
||||
Purposes []string `json:"purposes,omitempty"`
|
||||
Mediation string `json:"mediation,omitempty"`
|
||||
ConfigWritePolicy string `json:"configWritePolicy,omitempty"`
|
||||
}
|
||||
|
||||
type GamePluginProductionLifecycleBody struct {
|
||||
Operations []string `json:"operations"`
|
||||
DependencyPolicy string `json:"dependencyPolicy"`
|
||||
ApprovalRequired []string `json:"approvalRequired"`
|
||||
}
|
||||
|
||||
type GamePluginRemoteAccessBody struct {
|
||||
@@ -206,22 +214,89 @@ type GamePluginRemoteAccessBody struct {
|
||||
LogTransfer bool `json:"logTransfer,omitempty"`
|
||||
}
|
||||
|
||||
type GameClientBridgeCommandDeclarationBody struct {
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Permission string `json:"permission"`
|
||||
ApprovalLevel string `json:"approvalLevel"`
|
||||
PayloadSchemaRef string `json:"payloadSchemaRef"`
|
||||
ResultSchemaRef string `json:"resultSchemaRef,omitempty"`
|
||||
TimeoutSeconds int `json:"timeoutSeconds"`
|
||||
MaxPayloadBytes int `json:"maxPayloadBytes"`
|
||||
}
|
||||
|
||||
type GameClientBridgeSnapshotDeclarationBody struct {
|
||||
Type string `json:"type"`
|
||||
SchemaVersion string `json:"schemaVersion"`
|
||||
SchemaRef string `json:"schemaRef"`
|
||||
KeepForSeconds int `json:"keepForSeconds"`
|
||||
MaxRecords int `json:"maxRecords"`
|
||||
}
|
||||
|
||||
type GameClientBridgeQueryTemplateDeclarationBody struct {
|
||||
Key string `json:"key"`
|
||||
Title string `json:"title"`
|
||||
Permission string `json:"permission"`
|
||||
Engine string `json:"engine"`
|
||||
TransportKey string `json:"transportKey"`
|
||||
TargetKey string `json:"targetKey"`
|
||||
ParameterSchemaRef string `json:"parameterSchemaRef"`
|
||||
ResultSchemaRef string `json:"resultSchemaRef"`
|
||||
MaxRows int `json:"maxRows"`
|
||||
TimeoutSeconds int `json:"timeoutSeconds"`
|
||||
}
|
||||
|
||||
type GameClientBridgePageContractBody struct {
|
||||
PageKey string `json:"pageKey"`
|
||||
CommandTypes []string `json:"commandTypes,omitempty"`
|
||||
SnapshotTypes []string `json:"snapshotTypes,omitempty"`
|
||||
QueryTemplateKeys []string `json:"queryTemplateKeys,omitempty"`
|
||||
}
|
||||
|
||||
type GameClientBridgeCompanionDeclarationBody struct {
|
||||
ProfileKey string `json:"profileKey"`
|
||||
ConfigTemplateKey string `json:"configTemplateKey"`
|
||||
ConfigSchemaRef string `json:"configSchemaRef"`
|
||||
ConfigFormat string `json:"configFormat"`
|
||||
PlatformBaseURLSource string `json:"platformBaseUrlSource"`
|
||||
RegistrationProof string `json:"registrationProof"`
|
||||
ProofMaterialSource string `json:"proofMaterialSource"`
|
||||
ProofMaterialEnv string `json:"proofMaterialEnv"`
|
||||
SessionMode string `json:"sessionMode"`
|
||||
TLSPolicy string `json:"tlsPolicy"`
|
||||
HeartbeatIntervalSeconds int `json:"heartbeatIntervalSeconds"`
|
||||
CommandPollIntervalSeconds int `json:"commandPollIntervalSeconds"`
|
||||
RequestTimeoutSeconds int `json:"requestTimeoutSeconds"`
|
||||
}
|
||||
|
||||
type GameClientBridgeManifestBody struct {
|
||||
Commands []GameClientBridgeCommandDeclarationBody `json:"commands"`
|
||||
Snapshots []GameClientBridgeSnapshotDeclarationBody `json:"snapshots"`
|
||||
QueryTemplates []GameClientBridgeQueryTemplateDeclarationBody `json:"queryTemplates,omitempty"`
|
||||
CommandRetentionSeconds int `json:"commandRetentionSeconds"`
|
||||
MaxCommands int `json:"maxCommands"`
|
||||
Pages []GameClientBridgePageContractBody `json:"pages,omitempty"`
|
||||
Companion *GameClientBridgeCompanionDeclarationBody `json:"companion,omitempty"`
|
||||
}
|
||||
|
||||
type GamePluginManifestBody struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Version string `json:"version"`
|
||||
Kind string `json:"kind"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Server GamePluginManifestServerBody `json:"server"`
|
||||
Bridge GamePluginBridgeBody `json:"bridge,omitempty"`
|
||||
Capabilities []string `json:"capabilities"`
|
||||
Permissions []string `json:"permissions"`
|
||||
Actions PluginLifecycleActionsBody `json:"actions"`
|
||||
Pages []GamePluginPageBody `json:"pages,omitempty"`
|
||||
AI GamePluginManifestAIBody `json:"ai,omitempty"`
|
||||
RemoteAccess GamePluginRemoteAccessBody `json:"remoteAccess,omitempty"`
|
||||
RuntimeProfiles GamePluginRuntimeProfilesBody `json:"runtimeProfiles,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Version string `json:"version"`
|
||||
Kind string `json:"kind"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Server GamePluginManifestServerBody `json:"server"`
|
||||
Bridge GamePluginBridgeBody `json:"bridge,omitempty"`
|
||||
Capabilities []string `json:"capabilities"`
|
||||
Permissions []string `json:"permissions"`
|
||||
Actions PluginLifecycleActionsBody `json:"actions"`
|
||||
Pages []GamePluginPageBody `json:"pages,omitempty"`
|
||||
AI GamePluginManifestAIBody `json:"ai,omitempty"`
|
||||
ProductionLifecycle GamePluginProductionLifecycleBody `json:"productionLifecycle"`
|
||||
RemoteAccess GamePluginRemoteAccessBody `json:"remoteAccess,omitempty"`
|
||||
RuntimeProfiles GamePluginRuntimeProfilesBody `json:"runtimeProfiles,omitempty"`
|
||||
GameClientBridge GameClientBridgeManifestBody `json:"gameClientBridge,omitempty"`
|
||||
}
|
||||
|
||||
type GamePluginManifestRegistrationRequest struct {
|
||||
@@ -230,50 +305,54 @@ type GamePluginManifestRegistrationRequest struct {
|
||||
}
|
||||
|
||||
type GamePluginCreateRequest struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Version string `json:"version"`
|
||||
ServerType string `json:"serverType"`
|
||||
ServerDisplayName string `json:"serverDisplayName,omitempty"`
|
||||
SupportedOS []string `json:"supportedOs,omitempty"`
|
||||
ManifestRef string `json:"manifestRef"`
|
||||
CreateFormSchemaRef string `json:"createFormSchemaRef"`
|
||||
RequiredRunCapabilities []string `json:"requiredRunCapabilities"`
|
||||
DeclaredPermissions []string `json:"declaredPermissions,omitempty"`
|
||||
Permissions PluginPermissionsResponse `json:"permissions"`
|
||||
LifecycleActions PluginLifecycleActionsBody `json:"lifecycleActions,omitempty"`
|
||||
BridgeActions []string `json:"bridgeActions,omitempty"`
|
||||
Pages []GamePluginPageBody `json:"pages,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
AIPurposes []string `json:"aiPurposes,omitempty"`
|
||||
RemoteAccess GamePluginRemoteAccessBody `json:"remoteAccess,omitempty"`
|
||||
RuntimeProfiles GamePluginRuntimeProfilesBody `json:"runtimeProfiles,omitempty"`
|
||||
ValidationViolations []string `json:"validationViolations,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Version string `json:"version"`
|
||||
ServerType string `json:"serverType"`
|
||||
ServerDisplayName string `json:"serverDisplayName,omitempty"`
|
||||
SupportedOS []string `json:"supportedOs,omitempty"`
|
||||
ManifestRef string `json:"manifestRef"`
|
||||
CreateFormSchemaRef string `json:"createFormSchemaRef"`
|
||||
RequiredRunCapabilities []string `json:"requiredRunCapabilities"`
|
||||
DeclaredPermissions []string `json:"declaredPermissions,omitempty"`
|
||||
Permissions PluginPermissionsResponse `json:"permissions"`
|
||||
LifecycleActions PluginLifecycleActionsBody `json:"lifecycleActions,omitempty"`
|
||||
BridgeActions []string `json:"bridgeActions,omitempty"`
|
||||
Pages []GamePluginPageBody `json:"pages,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
AIPurposes []string `json:"aiPurposes,omitempty"`
|
||||
ProductionLifecycle GamePluginProductionLifecycleBody `json:"productionLifecycle,omitempty"`
|
||||
RemoteAccess GamePluginRemoteAccessBody `json:"remoteAccess,omitempty"`
|
||||
RuntimeProfiles GamePluginRuntimeProfilesBody `json:"runtimeProfiles,omitempty"`
|
||||
GameClientBridge GameClientBridgeManifestBody `json:"gameClientBridge,omitempty"`
|
||||
ValidationViolations []string `json:"validationViolations,omitempty"`
|
||||
}
|
||||
|
||||
type GamePluginResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Version string `json:"version"`
|
||||
ServerType string `json:"serverType"`
|
||||
ServerDisplayName string `json:"serverDisplayName,omitempty"`
|
||||
SupportedOS []string `json:"supportedOs,omitempty"`
|
||||
ManifestRef string `json:"manifestRef"`
|
||||
CreateFormSchemaRef string `json:"createFormSchemaRef"`
|
||||
RequiredRunCapabilities []string `json:"requiredRunCapabilities"`
|
||||
DeclaredPermissions []string `json:"declaredPermissions"`
|
||||
Permissions PluginPermissionsResponse `json:"permissions"`
|
||||
LifecycleActions PluginLifecycleActionsBody `json:"lifecycleActions"`
|
||||
BridgeActions []string `json:"bridgeActions"`
|
||||
Pages []GamePluginPageBody `json:"pages"`
|
||||
Tags []string `json:"tags"`
|
||||
AIPurposes []string `json:"aiPurposes"`
|
||||
RemoteAccess GamePluginRemoteAccessBody `json:"remoteAccess,omitempty"`
|
||||
RuntimeProfiles GamePluginRuntimeProfilesBody `json:"runtimeProfiles,omitempty"`
|
||||
ValidationViolations []string `json:"validationViolations,omitempty"`
|
||||
Status domain.GamePluginStatus `json:"status"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Version string `json:"version"`
|
||||
ServerType string `json:"serverType"`
|
||||
ServerDisplayName string `json:"serverDisplayName,omitempty"`
|
||||
SupportedOS []string `json:"supportedOs,omitempty"`
|
||||
ManifestRef string `json:"manifestRef"`
|
||||
CreateFormSchemaRef string `json:"createFormSchemaRef"`
|
||||
RequiredRunCapabilities []string `json:"requiredRunCapabilities"`
|
||||
DeclaredPermissions []string `json:"declaredPermissions"`
|
||||
Permissions PluginPermissionsResponse `json:"permissions"`
|
||||
LifecycleActions PluginLifecycleActionsBody `json:"lifecycleActions"`
|
||||
BridgeActions []string `json:"bridgeActions"`
|
||||
Pages []GamePluginPageBody `json:"pages"`
|
||||
Tags []string `json:"tags"`
|
||||
AIPurposes []string `json:"aiPurposes"`
|
||||
ProductionLifecycle GamePluginProductionLifecycleBody `json:"productionLifecycle"`
|
||||
RemoteAccess GamePluginRemoteAccessBody `json:"remoteAccess,omitempty"`
|
||||
RuntimeProfiles GamePluginRuntimeProfilesBody `json:"runtimeProfiles,omitempty"`
|
||||
GameClientBridge GameClientBridgeManifestBody `json:"gameClientBridge,omitempty"`
|
||||
ValidationViolations []string `json:"validationViolations,omitempty"`
|
||||
Status domain.GamePluginStatus `json:"status"`
|
||||
}
|
||||
|
||||
type GamePluginListResponse struct {
|
||||
@@ -282,28 +361,30 @@ type GamePluginListResponse struct {
|
||||
}
|
||||
|
||||
type MarketplacePluginResponse struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Version string `json:"version"`
|
||||
ServerType string `json:"serverType"`
|
||||
ServerDisplayName string `json:"serverDisplayName,omitempty"`
|
||||
SupportedOS []string `json:"supportedOs,omitempty"`
|
||||
ManifestRef string `json:"manifestRef"`
|
||||
CreateFormSchemaRef string `json:"createFormSchemaRef"`
|
||||
Capabilities []string `json:"capabilities"`
|
||||
DeclaredPermissions []string `json:"declaredPermissions"`
|
||||
Permissions PluginPermissionsResponse `json:"permissions"`
|
||||
LifecycleActions PluginLifecycleActionsBody `json:"lifecycleActions"`
|
||||
BridgeActions []string `json:"bridgeActions"`
|
||||
Pages []GamePluginPageBody `json:"pages"`
|
||||
Tags []string `json:"tags"`
|
||||
AIPurposes []string `json:"aiPurposes"`
|
||||
RemoteAccess GamePluginRemoteAccessBody `json:"remoteAccess,omitempty"`
|
||||
RuntimeProfiles GamePluginRuntimeProfilesBody `json:"runtimeProfiles,omitempty"`
|
||||
ValidationViolations []string `json:"validationViolations,omitempty"`
|
||||
Status domain.GamePluginStatus `json:"status"`
|
||||
Source string `json:"source"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Version string `json:"version"`
|
||||
ServerType string `json:"serverType"`
|
||||
ServerDisplayName string `json:"serverDisplayName,omitempty"`
|
||||
SupportedOS []string `json:"supportedOs,omitempty"`
|
||||
ManifestRef string `json:"manifestRef"`
|
||||
CreateFormSchemaRef string `json:"createFormSchemaRef"`
|
||||
Capabilities []string `json:"capabilities"`
|
||||
DeclaredPermissions []string `json:"declaredPermissions"`
|
||||
Permissions PluginPermissionsResponse `json:"permissions"`
|
||||
LifecycleActions PluginLifecycleActionsBody `json:"lifecycleActions"`
|
||||
BridgeActions []string `json:"bridgeActions"`
|
||||
Pages []GamePluginPageBody `json:"pages"`
|
||||
Tags []string `json:"tags"`
|
||||
AIPurposes []string `json:"aiPurposes"`
|
||||
ProductionLifecycle GamePluginProductionLifecycleBody `json:"productionLifecycle"`
|
||||
RemoteAccess GamePluginRemoteAccessBody `json:"remoteAccess,omitempty"`
|
||||
RuntimeProfiles GamePluginRuntimeProfilesBody `json:"runtimeProfiles,omitempty"`
|
||||
GameClientBridge GameClientBridgeManifestBody `json:"gameClientBridge,omitempty"`
|
||||
ValidationViolations []string `json:"validationViolations,omitempty"`
|
||||
Status domain.GamePluginStatus `json:"status"`
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
type MarketplacePluginListResponse struct {
|
||||
@@ -520,10 +601,13 @@ type FileOperationDispatchResponse struct {
|
||||
}
|
||||
|
||||
type RunCapacityResponse struct {
|
||||
MaxJobs int `json:"maxJobs"`
|
||||
RunningJobs int `json:"runningJobs"`
|
||||
QueuedJobs int `json:"queuedJobs"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
MaxJobs int `json:"maxJobs"`
|
||||
RunningJobs int `json:"runningJobs"`
|
||||
QueuedJobs int `json:"queuedJobs"`
|
||||
LogBacklogBatches int `json:"logBacklogBatches,omitempty"`
|
||||
ArtifactBacklogChunks int `json:"artifactBacklogChunks,omitempty"`
|
||||
PressureCodes []string `json:"pressureCodes,omitempty"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
}
|
||||
|
||||
type RunEndpointCreateRequest struct {
|
||||
@@ -816,21 +900,23 @@ func (request GamePluginManifestRegistrationRequest) ToDomain() domain.GamePlugi
|
||||
return domain.GamePluginManifestRegistration{
|
||||
ManifestRef: request.ManifestRef,
|
||||
Manifest: domain.GamePluginManifest{
|
||||
ID: request.Manifest.ID,
|
||||
Name: request.Manifest.Name,
|
||||
Description: request.Manifest.Description,
|
||||
Version: request.Manifest.Version,
|
||||
Kind: request.Manifest.Kind,
|
||||
Tags: domain.CopyStringSlice(request.Manifest.Tags),
|
||||
Server: request.Manifest.Server.ToDomain(),
|
||||
Bridge: request.Manifest.Bridge.ToDomain(),
|
||||
Capabilities: domain.CopyStringSlice(request.Manifest.Capabilities),
|
||||
Permissions: domain.CopyStringSlice(request.Manifest.Permissions),
|
||||
Actions: request.Manifest.Actions.ToDomain(),
|
||||
Pages: pagesToDomain(request.Manifest.Pages),
|
||||
AI: request.Manifest.AI.ToDomain(),
|
||||
RemoteAccess: request.Manifest.RemoteAccess.ToDomain(),
|
||||
RuntimeProfiles: request.Manifest.RuntimeProfiles.ToDomain(),
|
||||
ID: request.Manifest.ID,
|
||||
Name: request.Manifest.Name,
|
||||
Description: request.Manifest.Description,
|
||||
Version: request.Manifest.Version,
|
||||
Kind: request.Manifest.Kind,
|
||||
Tags: domain.CopyStringSlice(request.Manifest.Tags),
|
||||
Server: request.Manifest.Server.ToDomain(),
|
||||
Bridge: request.Manifest.Bridge.ToDomain(),
|
||||
Capabilities: domain.CopyStringSlice(request.Manifest.Capabilities),
|
||||
Permissions: domain.CopyStringSlice(request.Manifest.Permissions),
|
||||
Actions: request.Manifest.Actions.ToDomain(),
|
||||
Pages: pagesToDomain(request.Manifest.Pages),
|
||||
AI: request.Manifest.AI.ToDomain(),
|
||||
ProductionLifecycle: request.Manifest.ProductionLifecycle.ToDomain(),
|
||||
RemoteAccess: request.Manifest.RemoteAccess.ToDomain(),
|
||||
RuntimeProfiles: request.Manifest.RuntimeProfiles.ToDomain(),
|
||||
GameClientBridge: request.Manifest.GameClientBridge.ToDomain(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -849,7 +935,11 @@ func (server GamePluginManifestServerBody) ToDomain() domain.GamePluginManifestS
|
||||
}
|
||||
|
||||
func (ai GamePluginManifestAIBody) ToDomain() domain.GamePluginManifestAI {
|
||||
return domain.GamePluginManifestAI{Purposes: domain.CopyStringSlice(ai.Purposes)}
|
||||
return domain.GamePluginManifestAI{Purposes: domain.CopyStringSlice(ai.Purposes), Mediation: ai.Mediation, ConfigWritePolicy: ai.ConfigWritePolicy}
|
||||
}
|
||||
|
||||
func (lifecycle GamePluginProductionLifecycleBody) ToDomain() domain.GamePluginProductionLifecycle {
|
||||
return domain.GamePluginProductionLifecycle{Operations: domain.CopyStringSlice(lifecycle.Operations), DependencyPolicy: lifecycle.DependencyPolicy, ApprovalRequired: domain.CopyStringSlice(lifecycle.ApprovalRequired)}
|
||||
}
|
||||
|
||||
func (remote GamePluginRemoteAccessBody) ToDomain() domain.GamePluginRemoteAccess {
|
||||
@@ -862,6 +952,30 @@ func (remote GamePluginRemoteAccessBody) ToDomain() domain.GamePluginRemoteAcces
|
||||
}
|
||||
}
|
||||
|
||||
func (body GameClientBridgeManifestBody) ToDomain() domain.GameClientBridgeManifest {
|
||||
commands := make([]domain.GameClientBridgeCommandDeclaration, len(body.Commands))
|
||||
for index, command := range body.Commands {
|
||||
commands[index] = domain.GameClientBridgeCommandDeclaration{Type: command.Type, Title: command.Title, Permission: command.Permission, ApprovalLevel: domain.GameClientBridgeApprovalLevel(command.ApprovalLevel), PayloadSchemaRef: command.PayloadSchemaRef, ResultSchemaRef: command.ResultSchemaRef, TimeoutSeconds: command.TimeoutSeconds, MaxPayloadBytes: command.MaxPayloadBytes}
|
||||
}
|
||||
snapshots := make([]domain.GameClientBridgeSnapshotDeclaration, len(body.Snapshots))
|
||||
for index, snapshot := range body.Snapshots {
|
||||
snapshots[index] = domain.GameClientBridgeSnapshotDeclaration{Type: snapshot.Type, SchemaVersion: snapshot.SchemaVersion, SchemaRef: snapshot.SchemaRef, Retention: domain.GameClientBridgeRetention{KeepForSeconds: snapshot.KeepForSeconds, MaxRecords: snapshot.MaxRecords}}
|
||||
}
|
||||
queryTemplates := make([]domain.GameClientBridgeQueryTemplateDeclaration, len(body.QueryTemplates))
|
||||
for index, template := range body.QueryTemplates {
|
||||
queryTemplates[index] = domain.GameClientBridgeQueryTemplateDeclaration{Key: template.Key, Title: template.Title, Permission: template.Permission, Engine: template.Engine, TransportKey: template.TransportKey, TargetKey: template.TargetKey, ParameterSchemaRef: template.ParameterSchemaRef, ResultSchemaRef: template.ResultSchemaRef, MaxRows: template.MaxRows, TimeoutSeconds: template.TimeoutSeconds}
|
||||
}
|
||||
pages := make([]domain.GameClientBridgePageContract, len(body.Pages))
|
||||
for index, page := range body.Pages {
|
||||
pages[index] = domain.GameClientBridgePageContract{PageKey: page.PageKey, CommandTypes: domain.CopyStringSlice(page.CommandTypes), SnapshotTypes: domain.CopyStringSlice(page.SnapshotTypes), QueryTemplateKeys: domain.CopyStringSlice(page.QueryTemplateKeys)}
|
||||
}
|
||||
companion := domain.GameClientBridgeCompanionDeclaration{}
|
||||
if body.Companion != nil {
|
||||
companion = domain.GameClientBridgeCompanionDeclaration{ProfileKey: body.Companion.ProfileKey, ConfigTemplateKey: body.Companion.ConfigTemplateKey, ConfigSchemaRef: body.Companion.ConfigSchemaRef, ConfigFormat: body.Companion.ConfigFormat, PlatformBaseURLSource: body.Companion.PlatformBaseURLSource, RegistrationProof: body.Companion.RegistrationProof, ProofMaterialSource: body.Companion.ProofMaterialSource, ProofMaterialEnv: body.Companion.ProofMaterialEnv, SessionMode: body.Companion.SessionMode, TLSPolicy: body.Companion.TLSPolicy, HeartbeatIntervalSeconds: body.Companion.HeartbeatIntervalSeconds, CommandPollIntervalSeconds: body.Companion.CommandPollIntervalSeconds, RequestTimeoutSeconds: body.Companion.RequestTimeoutSeconds}
|
||||
}
|
||||
return domain.GameClientBridgeManifest{Commands: commands, Snapshots: snapshots, QueryTemplates: queryTemplates, Retention: domain.GameClientBridgeRetention{KeepForSeconds: body.CommandRetentionSeconds, MaxRecords: body.MaxCommands}, Pages: pages, Companion: companion}
|
||||
}
|
||||
|
||||
func (actions PluginLifecycleActionsBody) ToDomain() domain.PluginLifecycleActions {
|
||||
return domain.PluginLifecycleActions{
|
||||
Install: actions.Install,
|
||||
@@ -891,8 +1005,10 @@ func (request GamePluginCreateRequest) ToDomain() domain.GamePlugin {
|
||||
Pages: pagesToDomain(request.Pages),
|
||||
Tags: domain.CopyStringSlice(request.Tags),
|
||||
AIPurposes: domain.CopyStringSlice(request.AIPurposes),
|
||||
ProductionLifecycle: request.ProductionLifecycle.ToDomain(),
|
||||
RemoteAccess: request.RemoteAccess.ToDomain(),
|
||||
RuntimeProfiles: request.RuntimeProfiles.ToDomain(),
|
||||
GameClientBridge: request.GameClientBridge.ToDomain(),
|
||||
ValidationViolations: domain.CopyStringSlice(request.ValidationViolations),
|
||||
}
|
||||
}
|
||||
@@ -1073,17 +1189,17 @@ func UserListFromDomain(users []domain.User) UserListResponse {
|
||||
func AIProviderFromDomain(provider domain.AIProvider) AIProviderResponse {
|
||||
provider = domain.CopyAIProvider(provider)
|
||||
return AIProviderResponse{
|
||||
ID: provider.ID,
|
||||
Name: provider.Name,
|
||||
Kind: provider.Kind,
|
||||
BaseURL: provider.BaseURL,
|
||||
APIKeyConfigured: provider.APIKeyRef != "",
|
||||
Models: provider.Models,
|
||||
DefaultModel: provider.DefaultModel,
|
||||
RelayMode: provider.RelayMode,
|
||||
TimeoutMS: provider.TimeoutMS,
|
||||
Status: provider.Status,
|
||||
RedactionPolicy: provider.RedactionPolicy,
|
||||
ID: provider.ID,
|
||||
Name: provider.Name,
|
||||
Kind: provider.Kind,
|
||||
BaseURLConfigured: provider.BaseURL != "",
|
||||
APIKeyConfigured: provider.APIKeyRef != "",
|
||||
Models: provider.Models,
|
||||
DefaultModel: provider.DefaultModel,
|
||||
RelayMode: provider.RelayMode,
|
||||
TimeoutMS: provider.TimeoutMS,
|
||||
Status: provider.Status,
|
||||
RedactionPolicy: provider.RedactionPolicy,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1135,8 +1251,10 @@ func GamePluginFromDomain(plugin domain.GamePlugin) GamePluginResponse {
|
||||
Pages: pagesFromDomain(plugin.Pages),
|
||||
Tags: plugin.Tags,
|
||||
AIPurposes: plugin.AIPurposes,
|
||||
ProductionLifecycle: productionLifecycleFromDomain(plugin.ProductionLifecycle),
|
||||
RemoteAccess: remoteAccessFromDomain(plugin.RemoteAccess),
|
||||
RuntimeProfiles: runtimeProfilesFromDomain(plugin.RuntimeProfiles),
|
||||
GameClientBridge: gameClientBridgeManifestFromDomain(plugin.GameClientBridge),
|
||||
ValidationViolations: plugin.ValidationViolations,
|
||||
Status: plugin.Status,
|
||||
}
|
||||
@@ -1224,14 +1342,46 @@ func MarketplacePluginFromDomain(plugin domain.PluginMarketplacePlugin) Marketpl
|
||||
Pages: pagesFromDomain(plugin.Pages),
|
||||
Tags: plugin.Tags,
|
||||
AIPurposes: plugin.AIPurposes,
|
||||
ProductionLifecycle: productionLifecycleFromDomain(plugin.ProductionLifecycle),
|
||||
RemoteAccess: remoteAccessFromDomain(plugin.RemoteAccess),
|
||||
RuntimeProfiles: runtimeProfilesFromDomain(plugin.RuntimeProfiles),
|
||||
GameClientBridge: gameClientBridgeManifestFromDomain(plugin.GameClientBridge),
|
||||
ValidationViolations: plugin.ValidationViolations,
|
||||
Status: plugin.Status,
|
||||
Source: plugin.Source,
|
||||
}
|
||||
}
|
||||
|
||||
func productionLifecycleFromDomain(lifecycle domain.GamePluginProductionLifecycle) GamePluginProductionLifecycleBody {
|
||||
lifecycle = domain.CopyGamePluginProductionLifecycle(lifecycle)
|
||||
return GamePluginProductionLifecycleBody{Operations: lifecycle.Operations, DependencyPolicy: lifecycle.DependencyPolicy, ApprovalRequired: lifecycle.ApprovalRequired}
|
||||
}
|
||||
|
||||
func gameClientBridgeManifestFromDomain(value domain.GameClientBridgeManifest) GameClientBridgeManifestBody {
|
||||
value = domain.CopyGameClientBridgeManifest(value)
|
||||
commands := make([]GameClientBridgeCommandDeclarationBody, len(value.Commands))
|
||||
for index, command := range value.Commands {
|
||||
commands[index] = GameClientBridgeCommandDeclarationBody{Type: command.Type, Title: command.Title, Permission: command.Permission, ApprovalLevel: string(command.ApprovalLevel), PayloadSchemaRef: command.PayloadSchemaRef, ResultSchemaRef: command.ResultSchemaRef, TimeoutSeconds: command.TimeoutSeconds, MaxPayloadBytes: command.MaxPayloadBytes}
|
||||
}
|
||||
snapshots := make([]GameClientBridgeSnapshotDeclarationBody, len(value.Snapshots))
|
||||
for index, snapshot := range value.Snapshots {
|
||||
snapshots[index] = GameClientBridgeSnapshotDeclarationBody{Type: snapshot.Type, SchemaVersion: snapshot.SchemaVersion, SchemaRef: snapshot.SchemaRef, KeepForSeconds: snapshot.Retention.KeepForSeconds, MaxRecords: snapshot.Retention.MaxRecords}
|
||||
}
|
||||
queryTemplates := make([]GameClientBridgeQueryTemplateDeclarationBody, len(value.QueryTemplates))
|
||||
for index, template := range value.QueryTemplates {
|
||||
queryTemplates[index] = GameClientBridgeQueryTemplateDeclarationBody{Key: template.Key, Title: template.Title, Permission: template.Permission, Engine: template.Engine, TransportKey: template.TransportKey, TargetKey: template.TargetKey, ParameterSchemaRef: template.ParameterSchemaRef, ResultSchemaRef: template.ResultSchemaRef, MaxRows: template.MaxRows, TimeoutSeconds: template.TimeoutSeconds}
|
||||
}
|
||||
pages := make([]GameClientBridgePageContractBody, len(value.Pages))
|
||||
for index, page := range value.Pages {
|
||||
pages[index] = GameClientBridgePageContractBody{PageKey: page.PageKey, CommandTypes: page.CommandTypes, SnapshotTypes: page.SnapshotTypes, QueryTemplateKeys: page.QueryTemplateKeys}
|
||||
}
|
||||
var companion *GameClientBridgeCompanionDeclarationBody
|
||||
if value.Companion.ProfileKey != "" {
|
||||
companion = &GameClientBridgeCompanionDeclarationBody{ProfileKey: value.Companion.ProfileKey, ConfigTemplateKey: value.Companion.ConfigTemplateKey, ConfigSchemaRef: value.Companion.ConfigSchemaRef, ConfigFormat: value.Companion.ConfigFormat, PlatformBaseURLSource: value.Companion.PlatformBaseURLSource, RegistrationProof: value.Companion.RegistrationProof, ProofMaterialSource: value.Companion.ProofMaterialSource, ProofMaterialEnv: value.Companion.ProofMaterialEnv, SessionMode: value.Companion.SessionMode, TLSPolicy: value.Companion.TLSPolicy, HeartbeatIntervalSeconds: value.Companion.HeartbeatIntervalSeconds, CommandPollIntervalSeconds: value.Companion.CommandPollIntervalSeconds, RequestTimeoutSeconds: value.Companion.RequestTimeoutSeconds}
|
||||
}
|
||||
return GameClientBridgeManifestBody{Commands: commands, Snapshots: snapshots, QueryTemplates: queryTemplates, CommandRetentionSeconds: value.Retention.KeepForSeconds, MaxCommands: value.Retention.MaxRecords, Pages: pages, Companion: companion}
|
||||
}
|
||||
|
||||
func MarketplacePluginListFromDomain(plugins []domain.PluginMarketplacePlugin) MarketplacePluginListResponse {
|
||||
items := make([]MarketplacePluginResponse, len(plugins))
|
||||
for i, plugin := range plugins {
|
||||
@@ -1606,19 +1756,25 @@ func pagesFromDomain(pages []domain.GamePluginPage) []GamePluginPageBody {
|
||||
|
||||
func capacityFromDomain(capacity domain.RunCapacity) RunCapacityResponse {
|
||||
return RunCapacityResponse{
|
||||
MaxJobs: capacity.MaxJobs,
|
||||
RunningJobs: capacity.RunningJobs,
|
||||
QueuedJobs: capacity.QueuedJobs,
|
||||
Summary: capacity.Summary,
|
||||
MaxJobs: capacity.MaxJobs,
|
||||
RunningJobs: capacity.RunningJobs,
|
||||
QueuedJobs: capacity.QueuedJobs,
|
||||
LogBacklogBatches: capacity.LogBacklogBatches,
|
||||
ArtifactBacklogChunks: capacity.ArtifactBacklogChunks,
|
||||
PressureCodes: domain.CopyStringSlice(capacity.PressureCodes),
|
||||
Summary: capacity.Summary,
|
||||
}
|
||||
}
|
||||
|
||||
func capacityToDomain(capacity RunCapacityResponse) domain.RunCapacity {
|
||||
return domain.RunCapacity{
|
||||
MaxJobs: capacity.MaxJobs,
|
||||
RunningJobs: capacity.RunningJobs,
|
||||
QueuedJobs: capacity.QueuedJobs,
|
||||
Summary: capacity.Summary,
|
||||
MaxJobs: capacity.MaxJobs,
|
||||
RunningJobs: capacity.RunningJobs,
|
||||
QueuedJobs: capacity.QueuedJobs,
|
||||
LogBacklogBatches: capacity.LogBacklogBatches,
|
||||
ArtifactBacklogChunks: capacity.ArtifactBacklogChunks,
|
||||
PressureCodes: domain.CopyStringSlice(capacity.PressureCodes),
|
||||
Summary: capacity.Summary,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
func TestAIProviderResponseExposesOnlyKeyPresence(t *testing.T) {
|
||||
func TestAIProviderResponseExposesOnlySecretPresence(t *testing.T) {
|
||||
responseType := reflect.TypeOf(AIProviderResponse{})
|
||||
if _, ok := responseType.FieldByName("BaseURL"); ok {
|
||||
t.Fatal("AI provider response must not expose the provider base URL")
|
||||
}
|
||||
if _, ok := responseType.FieldByName("APIKey"); ok {
|
||||
t.Fatal("AI provider response must not expose raw API key")
|
||||
}
|
||||
@@ -21,6 +25,9 @@ func TestAIProviderResponseExposesOnlyKeyPresence(t *testing.T) {
|
||||
if _, ok := responseType.FieldByName("APIKeyConfigured"); !ok {
|
||||
t.Fatal("AI provider response must expose API key presence")
|
||||
}
|
||||
if _, ok := responseType.FieldByName("BaseURLConfigured"); !ok {
|
||||
t.Fatal("AI provider response must expose base URL presence")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAIProviderFromDomainCopiesModels(t *testing.T) {
|
||||
@@ -47,6 +54,9 @@ func TestAIProviderFromDomainCopiesModels(t *testing.T) {
|
||||
if !response.APIKeyConfigured {
|
||||
t.Fatal("expected configured API key presence")
|
||||
}
|
||||
if !response.BaseURLConfigured {
|
||||
t.Fatal("expected configured base URL presence")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGamePluginManifestRegistrationToDomainCopiesSlices(t *testing.T) {
|
||||
@@ -113,3 +123,49 @@ func TestGamePluginFromDomainCopiesRegistryMetadata(t *testing.T) {
|
||||
t.Fatalf("expected plugin response registry metadata to be copied, got %+v", plugin)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGameClientBridgeQueryTemplateDeclarationRoundTripIsSafe(t *testing.T) {
|
||||
body := GameClientBridgeManifestBody{
|
||||
QueryTemplates: []GameClientBridgeQueryTemplateDeclarationBody{{
|
||||
Key: "player.lookup", Title: "Player lookup", Permission: "server.game-client.read", Engine: "sqlite", TransportKey: "sqlite-db", TargetKey: "db/sqlite",
|
||||
ParameterSchemaRef: "schemas/bridge/query/player-lookup.parameters.schema.json", ResultSchemaRef: "schemas/bridge/query/player-lookup.result.schema.json", MaxRows: 50, TimeoutSeconds: 10,
|
||||
}},
|
||||
CommandRetentionSeconds: 86400,
|
||||
MaxCommands: 1000,
|
||||
Pages: []GameClientBridgePageContractBody{{PageKey: "players", QueryTemplateKeys: []string{"player.lookup"}}},
|
||||
}
|
||||
|
||||
domainManifest := body.ToDomain()
|
||||
if len(domainManifest.QueryTemplates) != 1 || domainManifest.QueryTemplates[0].TransportKey != "sqlite-db" || domainManifest.QueryTemplates[0].MaxRows != 50 || domainManifest.Pages[0].QueryTemplateKeys[0] != "player.lookup" {
|
||||
t.Fatalf("query template conversion lost declaration fields: %#v", domainManifest)
|
||||
}
|
||||
domainManifest.Pages[0].QueryTemplateKeys[0] = "mutated"
|
||||
if body.Pages[0].QueryTemplateKeys[0] != "player.lookup" {
|
||||
t.Fatal("query template page keys alias request DTO data")
|
||||
}
|
||||
domainManifest.Pages[0].QueryTemplateKeys[0] = "player.lookup"
|
||||
|
||||
response := gameClientBridgeManifestFromDomain(domainManifest)
|
||||
response.Pages[0].QueryTemplateKeys[0] = "mutated"
|
||||
if domainManifest.Pages[0].QueryTemplateKeys[0] != "player.lookup" {
|
||||
t.Fatal("query template page keys alias domain data")
|
||||
}
|
||||
|
||||
encoded, err := json.Marshal(response.QueryTemplates[0])
|
||||
if err != nil {
|
||||
t.Fatalf("marshal safe query template projection: %v", err)
|
||||
}
|
||||
var projection map[string]any
|
||||
if err := json.Unmarshal(encoded, &projection); err != nil {
|
||||
t.Fatalf("decode safe query template projection: %v", err)
|
||||
}
|
||||
expectedFields := []string{"key", "title", "permission", "engine", "transportKey", "targetKey", "parameterSchemaRef", "resultSchemaRef", "maxRows", "timeoutSeconds"}
|
||||
if len(projection) != len(expectedFields) {
|
||||
t.Fatalf("query template projection contains unexpected fields: %s", encoded)
|
||||
}
|
||||
for _, field := range expectedFields {
|
||||
if _, exists := projection[field]; !exists {
|
||||
t.Fatalf("query template projection is missing %q: %s", field, encoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
)
|
||||
|
||||
func TestRuntimeLogEventDeclarationRoundTripUsesSafeProjection(t *testing.T) {
|
||||
body := GamePluginRuntimeProfilesBody{
|
||||
LogEvents: []RuntimeLogEventBody{{
|
||||
Key: "chat-message", Title: "Chat message", SourceKey: "chat-log", EventType: "chat.message",
|
||||
Permission: "server.logs.read", SchemaRef: "schemas/log-events/chat-message.schema.json", RetentionDays: 30, Severity: "info",
|
||||
}},
|
||||
}
|
||||
|
||||
profiles := body.ToDomain()
|
||||
if len(profiles.LogEvents) != 1 || profiles.LogEvents[0].EventType != "chat.message" || profiles.LogEvents[0].Severity != "info" {
|
||||
t.Fatalf("log event conversion lost declaration fields: %+v", profiles.LogEvents)
|
||||
}
|
||||
profiles.LogEvents[0].Title = "Mutated"
|
||||
if body.LogEvents[0].Title != "Chat message" {
|
||||
t.Fatal("log event domain conversion aliases request DTO data")
|
||||
}
|
||||
|
||||
projection := runtimeProfilesFromDomain(domain.GamePluginRuntimeProfiles{LogEvents: profiles.LogEvents})
|
||||
encoded, err := json.Marshal(projection.LogEvents[0])
|
||||
if err != nil {
|
||||
t.Fatalf("marshal log event projection: %v", err)
|
||||
}
|
||||
var fields map[string]any
|
||||
if err := json.Unmarshal(encoded, &fields); err != nil {
|
||||
t.Fatalf("decode log event projection: %v", err)
|
||||
}
|
||||
expected := []string{"key", "title", "sourceKey", "eventType", "permission", "schemaRef", "retentionDays", "severity"}
|
||||
if len(fields) != len(expected) {
|
||||
t.Fatalf("log event projection contains unexpected fields: %s", encoded)
|
||||
}
|
||||
for _, field := range expected {
|
||||
if _, exists := fields[field]; !exists {
|
||||
t.Fatalf("log event projection is missing %q: %s", field, encoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,17 @@ type RuntimeLogSourceBody struct {
|
||||
RetentionDays int `json:"retentionDays,omitempty"`
|
||||
}
|
||||
|
||||
type RuntimeLogEventBody struct {
|
||||
Key string `json:"key"`
|
||||
Title string `json:"title"`
|
||||
SourceKey string `json:"sourceKey"`
|
||||
EventType string `json:"eventType"`
|
||||
Permission string `json:"permission"`
|
||||
SchemaRef string `json:"schemaRef"`
|
||||
RetentionDays int `json:"retentionDays"`
|
||||
Severity string `json:"severity"`
|
||||
}
|
||||
|
||||
type RuntimeTransportProfileBody struct {
|
||||
Key string `json:"key"`
|
||||
Kind string `json:"kind"`
|
||||
@@ -145,6 +156,7 @@ type GamePluginRuntimeProfilesBody struct {
|
||||
DependencyProbes []RuntimeDependencyProbeBody `json:"dependencyProbes,omitempty"`
|
||||
InstallPlans []RuntimeInstallPlanBody `json:"installPlans,omitempty"`
|
||||
LogSources []RuntimeLogSourceBody `json:"logSources,omitempty"`
|
||||
LogEvents []RuntimeLogEventBody `json:"logEvents,omitempty"`
|
||||
TransportProfiles []RuntimeTransportProfileBody `json:"transportProfiles,omitempty"`
|
||||
ClientManagers []RuntimeClientManagerProfileBody `json:"clientManagers,omitempty"`
|
||||
}
|
||||
@@ -170,6 +182,9 @@ func (body GamePluginRuntimeProfilesBody) ToDomain() domain.GamePluginRuntimePro
|
||||
for _, item := range body.LogSources {
|
||||
profiles.LogSources = append(profiles.LogSources, domain.RuntimeLogSource{Key: item.Key, Kind: item.Kind, TargetKey: item.TargetKey, StreamKey: item.StreamKey, CursorKind: item.CursorKind, RetentionDays: item.RetentionDays})
|
||||
}
|
||||
for _, item := range body.LogEvents {
|
||||
profiles.LogEvents = append(profiles.LogEvents, domain.RuntimeLogEvent{Key: item.Key, Title: item.Title, SourceKey: item.SourceKey, EventType: item.EventType, Permission: item.Permission, SchemaRef: item.SchemaRef, RetentionDays: item.RetentionDays, Severity: domain.RuntimeLogEventSeverity(item.Severity)})
|
||||
}
|
||||
for _, item := range body.TransportProfiles {
|
||||
profiles.TransportProfiles = append(profiles.TransportProfiles, domain.RuntimeTransportProfile{Key: item.Key, Kind: item.Kind, TargetKey: item.TargetKey, Capabilities: domain.CopyStringSlice(item.Capabilities)})
|
||||
}
|
||||
@@ -217,6 +232,9 @@ func runtimeProfilesFromDomain(profiles domain.GamePluginRuntimeProfiles) GamePl
|
||||
for _, item := range profiles.LogSources {
|
||||
body.LogSources = append(body.LogSources, RuntimeLogSourceBody{Key: item.Key, Kind: item.Kind, TargetKey: item.TargetKey, StreamKey: item.StreamKey, CursorKind: item.CursorKind, RetentionDays: item.RetentionDays})
|
||||
}
|
||||
for _, item := range profiles.LogEvents {
|
||||
body.LogEvents = append(body.LogEvents, RuntimeLogEventBody{Key: item.Key, Title: item.Title, SourceKey: item.SourceKey, EventType: item.EventType, Permission: item.Permission, SchemaRef: item.SchemaRef, RetentionDays: item.RetentionDays, Severity: string(item.Severity)})
|
||||
}
|
||||
for _, item := range profiles.TransportProfiles {
|
||||
body.TransportProfiles = append(body.TransportProfiles, RuntimeTransportProfileBody{Key: item.Key, Kind: item.Kind, TargetKey: item.TargetKey, Capabilities: item.Capabilities})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user