diff --git a/platform/domain/game_client_bridge.go b/platform/domain/game_client_bridge.go index ea7f3f7..1b7c9dc 100644 --- a/platform/domain/game_client_bridge.go +++ b/platform/domain/game_client_bridge.go @@ -130,6 +130,7 @@ type GameClientBridgeDataPackDeclaration struct { DatabaseUserVersion int LogParserRefs []string ConfigMapRefs []string + DataRefs []string } type GameClientBridgeOperationKind string @@ -528,6 +529,7 @@ func CopyGameClientBridgeManifest(value GameClientBridgeManifest) GameClientBrid for index := range value.DataPacks { value.DataPacks[index].LogParserRefs = CopyStringSlice(value.DataPacks[index].LogParserRefs) value.DataPacks[index].ConfigMapRefs = CopyStringSlice(value.DataPacks[index].ConfigMapRefs) + value.DataPacks[index].DataRefs = CopyStringSlice(value.DataPacks[index].DataRefs) } value.OperationTemplates = append([]GameClientBridgeOperationTemplateDeclaration(nil), value.OperationTemplates...) value.Pages = append([]GameClientBridgePageContract(nil), value.Pages...) diff --git a/platform/domain/game_client_bridge_test.go b/platform/domain/game_client_bridge_test.go index c390c12..e96d68f 100644 --- a/platform/domain/game_client_bridge_test.go +++ b/platform/domain/game_client_bridge_test.go @@ -10,7 +10,7 @@ func TestCopyGameClientBridgeDeclarationsCopiesQueryTemplateSlices(t *testing.T) Target: GameClientBridgeLogProjectionTargetDeclaration{Collection: "users", UpsertKeys: []string{"steamId"}, CaptureMappings: map[string]string{"steamId": "steamId"}, FixedValues: map[string]string{"source": "stdout"}, ObservedAtField: "lastLoginAt"}, Presence: &GameClientBridgeLogProjectionPresenceDeclaration{TimestampField: "lastLoginAt", ActiveWindowSeconds: 600, ActivityTarget: &GameClientBridgeLogProjectionTargetDeclaration{Collection: "activity", UpsertKeys: []string{"steamId"}, CaptureMappings: map[string]string{"steamId": "steamId"}}}, }}, - DataPacks: []GameClientBridgeDataPackDeclaration{{Key: "db-v1", LogParserRefs: []string{"logs.json"}, ConfigMapRefs: []string{"config.json"}}}, + DataPacks: []GameClientBridgeDataPackDeclaration{{Key: "db-v1", LogParserRefs: []string{"logs.json"}, ConfigMapRefs: []string{"config.json"}, DataRefs: []string{"data.json"}}}, OperationTemplates: []GameClientBridgeOperationTemplateDeclaration{{Key: "player.fame.set"}}, Pages: []GameClientBridgePageContract{{PageKey: "players", QueryTemplateKeys: []string{"player.lookup"}, OperationKeys: []string{"player.fame.set"}}}, } @@ -21,10 +21,11 @@ func TestCopyGameClientBridgeDeclarationsCopiesQueryTemplateSlices(t *testing.T) manifestCopy.LogProjections[0].Target.CaptureMappings["steamId"] = "mutated" manifestCopy.LogProjections[0].Presence.ActivityTarget.CaptureMappings["steamId"] = "mutated" manifestCopy.DataPacks[0].LogParserRefs[0] = "mutated" + manifestCopy.DataPacks[0].DataRefs[0] = "mutated" manifestCopy.OperationTemplates[0].Key = "mutated" manifestCopy.Pages[0].QueryTemplateKeys[0] = "mutated" manifestCopy.Pages[0].OperationKeys[0] = "mutated" - if manifest.QueryTemplates[0].Key != "player.lookup" || manifest.QueryTemplates[0].RowTarget.ColumnMappings["userId"] != "user_id" || manifest.LogProjections[0].StreamKeys[0] != "process.stdout" || manifest.LogProjections[0].Target.CaptureMappings["steamId"] != "steamId" || manifest.LogProjections[0].Presence.ActivityTarget.CaptureMappings["steamId"] != "steamId" || manifest.DataPacks[0].LogParserRefs[0] != "logs.json" || manifest.OperationTemplates[0].Key != "player.fame.set" || manifest.Pages[0].QueryTemplateKeys[0] != "player.lookup" || manifest.Pages[0].OperationKeys[0] != "player.fame.set" { + if manifest.QueryTemplates[0].Key != "player.lookup" || manifest.QueryTemplates[0].RowTarget.ColumnMappings["userId"] != "user_id" || manifest.LogProjections[0].StreamKeys[0] != "process.stdout" || manifest.LogProjections[0].Target.CaptureMappings["steamId"] != "steamId" || manifest.LogProjections[0].Presence.ActivityTarget.CaptureMappings["steamId"] != "steamId" || manifest.DataPacks[0].LogParserRefs[0] != "logs.json" || manifest.DataPacks[0].DataRefs[0] != "data.json" || manifest.OperationTemplates[0].Key != "player.fame.set" || manifest.Pages[0].QueryTemplateKeys[0] != "player.lookup" || manifest.Pages[0].OperationKeys[0] != "player.fame.set" { t.Fatalf("manifest copy aliases query template declarations: source=%#v copy=%#v", manifest, manifestCopy) } diff --git a/platform/dto/resources.go b/platform/dto/resources.go index 0f465c3..8029e64 100644 --- a/platform/dto/resources.go +++ b/platform/dto/resources.go @@ -354,6 +354,7 @@ type GameClientBridgeDataPackDeclarationBody struct { DatabaseUserVersion int `json:"databaseUserVersion"` LogParserRefs []string `json:"logParserRefs"` ConfigMapRefs []string `json:"configMapRefs"` + DataRefs []string `json:"dataRefs,omitempty"` } type GameClientBridgeOperationSafetyBody struct { @@ -1244,7 +1245,7 @@ func (body GameClientBridgeManifestBody) ToDomain() domain.GameClientBridgeManif } dataPacks := make([]domain.GameClientBridgeDataPackDeclaration, len(body.DataPacks)) for index, dataPack := range body.DataPacks { - dataPacks[index] = domain.GameClientBridgeDataPackDeclaration{Key: dataPack.Key, DatabaseUserVersion: dataPack.DatabaseUserVersion, LogParserRefs: domain.CopyStringSlice(dataPack.LogParserRefs), ConfigMapRefs: domain.CopyStringSlice(dataPack.ConfigMapRefs)} + dataPacks[index] = domain.GameClientBridgeDataPackDeclaration{Key: dataPack.Key, DatabaseUserVersion: dataPack.DatabaseUserVersion, LogParserRefs: domain.CopyStringSlice(dataPack.LogParserRefs), ConfigMapRefs: domain.CopyStringSlice(dataPack.ConfigMapRefs), DataRefs: domain.CopyStringSlice(dataPack.DataRefs)} } operationTemplates := make([]domain.GameClientBridgeOperationTemplateDeclaration, len(body.OperationTemplates)) for index, template := range body.OperationTemplates { @@ -1730,7 +1731,7 @@ func gameClientBridgeManifestFromDomain(value domain.GameClientBridgeManifest) G } dataPacks := make([]GameClientBridgeDataPackDeclarationBody, len(value.DataPacks)) for index, dataPack := range value.DataPacks { - dataPacks[index] = GameClientBridgeDataPackDeclarationBody{Key: dataPack.Key, DatabaseUserVersion: dataPack.DatabaseUserVersion, LogParserRefs: domain.CopyStringSlice(dataPack.LogParserRefs), ConfigMapRefs: domain.CopyStringSlice(dataPack.ConfigMapRefs)} + dataPacks[index] = GameClientBridgeDataPackDeclarationBody{Key: dataPack.Key, DatabaseUserVersion: dataPack.DatabaseUserVersion, LogParserRefs: domain.CopyStringSlice(dataPack.LogParserRefs), ConfigMapRefs: domain.CopyStringSlice(dataPack.ConfigMapRefs), DataRefs: domain.CopyStringSlice(dataPack.DataRefs)} } operationTemplates := make([]GameClientBridgeOperationTemplateDeclarationBody, len(value.OperationTemplates)) for index, template := range value.OperationTemplates { diff --git a/platform/dto/resources_test.go b/platform/dto/resources_test.go index 26a2882..aa55e16 100644 --- a/platform/dto/resources_test.go +++ b/platform/dto/resources_test.go @@ -145,14 +145,14 @@ func TestGameClientBridgeQueryTemplateDeclarationRoundTripIsSafe(t *testing.T) { Target: GameClientBridgeLogProjectionTargetDeclarationBody{Collection: "users", UpsertKeys: []string{"steamId"}, CaptureMappings: map[string]string{"steamId": "steamId"}, FixedValues: map[string]string{"source": "stdout"}, ObservedAtField: "lastLoginAt"}, Presence: &GameClientBridgeLogProjectionPresenceDeclarationBody{TimestampField: "lastLoginAt", ActiveWindowSeconds: 600, ActivityTarget: &GameClientBridgeLogProjectionTargetDeclarationBody{Collection: "activity", UpsertKeys: []string{"steamId"}, CaptureMappings: map[string]string{"steamId": "steamId"}}, Announcement: GameClientBridgeLogProjectionAnnouncementDeclarationBody{ProfileKey: "scum-client", CommandType: "announcement.send", TextField: "message", NewTextTemplate: "welcome {{name}}", ReturningTextTemplate: "welcome back {{name}}"}}, }}, - DataPacks: []GameClientBridgeDataPackDeclarationBody{{Key: "db-v1", DatabaseUserVersion: 1, LogParserRefs: []string{"data/logs.json"}, ConfigMapRefs: []string{"data/config.json"}}}, + DataPacks: []GameClientBridgeDataPackDeclarationBody{{Key: "db-v1", DatabaseUserVersion: 1, LogParserRefs: []string{"data/logs.json"}, ConfigMapRefs: []string{"data/config.json"}, DataRefs: []string{"data/items.json"}}}, CommandRetentionSeconds: 86400, MaxCommands: 1000, Pages: []GameClientBridgePageContractBody{{PageKey: "players", QueryTemplateKeys: []string{"player.lookup"}}}, } domainManifest := body.ToDomain() - if len(domainManifest.QueryTemplates) != 1 || domainManifest.QueryTemplates[0].SQLRef != "sql/player-lookup.sql" || domainManifest.QueryTemplates[0].PollIntervalSeconds != 3 || domainManifest.QueryTemplates[0].RowTarget.Collection != "users" || domainManifest.QueryTemplates[0].RowTarget.WriteMode != "merge" || len(domainManifest.LogProjections) != 1 || domainManifest.LogProjections[0].Presence.ActiveWindowSeconds != 600 || len(domainManifest.DataPacks) != 1 || domainManifest.Pages[0].QueryTemplateKeys[0] != "player.lookup" { + if len(domainManifest.QueryTemplates) != 1 || domainManifest.QueryTemplates[0].SQLRef != "sql/player-lookup.sql" || domainManifest.QueryTemplates[0].PollIntervalSeconds != 3 || domainManifest.QueryTemplates[0].RowTarget.Collection != "users" || domainManifest.QueryTemplates[0].RowTarget.WriteMode != "merge" || len(domainManifest.LogProjections) != 1 || domainManifest.LogProjections[0].Presence.ActiveWindowSeconds != 600 || len(domainManifest.DataPacks) != 1 || domainManifest.DataPacks[0].DataRefs[0] != "data/items.json" || domainManifest.Pages[0].QueryTemplateKeys[0] != "player.lookup" { t.Fatalf("query template conversion lost declaration fields: %#v", domainManifest) } domainManifest.QueryTemplates[0].RowTarget.ColumnMappings["userId"] = "mutated" @@ -170,6 +170,11 @@ func TestGameClientBridgeQueryTemplateDeclarationRoundTripIsSafe(t *testing.T) { t.Fatal("query template page keys alias request DTO data") } domainManifest.Pages[0].QueryTemplateKeys[0] = "player.lookup" + domainManifest.DataPacks[0].DataRefs[0] = "mutated" + if body.DataPacks[0].DataRefs[0] != "data/items.json" { + t.Fatal("data pack data refs alias request DTO data") + } + domainManifest.DataPacks[0].DataRefs[0] = "data/items.json" response := gameClientBridgeManifestFromDomain(domainManifest) response.LogProjections[0].Target.FixedValues["source"] = "mutated" diff --git a/platform/validator/resources.go b/platform/validator/resources.go index 970a8f0..cd1886f 100644 --- a/platform/validator/resources.go +++ b/platform/validator/resources.go @@ -640,7 +640,9 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid if dataPack.DatabaseUserVersion < 1 || len(dataPack.LogParserRefs) == 0 || len(dataPack.ConfigMapRefs) == 0 { violations = append(violations, prefix+" must declare a database version and parser/config assets") } - for _, ref := range append(domain.CopyStringSlice(dataPack.LogParserRefs), dataPack.ConfigMapRefs...) { + refs := append(domain.CopyStringSlice(dataPack.LogParserRefs), dataPack.ConfigMapRefs...) + refs = append(refs, dataPack.DataRefs...) + for _, ref := range refs { if !safeRelativeJSONRef(ref) { violations = append(violations, prefix+" asset reference is invalid") }