|
|
|
@@ -167,7 +167,7 @@ func ValidateGamePlugin(plugin domain.GamePlugin) error {
|
|
|
|
|
}
|
|
|
|
|
violations = append(violations, validateRuntimeProfileCapabilityDeclarations(plugin.RuntimeProfiles, plugin.RequiredRunCapabilities)...)
|
|
|
|
|
violations = append(violations, validateRuntimeLogEventPermissionDeclarations("runtimeProfiles.logEvents", plugin.RuntimeProfiles, plugin.DeclaredPermissions)...)
|
|
|
|
|
violations = append(violations, validateGameClientBridgeManifest("gameClientBridge", plugin.GameClientBridge, plugin.DeclaredPermissions, plugin.Pages, plugin.RuntimeProfiles)...)
|
|
|
|
|
violations = append(violations, validateGameClientBridgeManifest("gameClientBridge", plugin.GameClientBridge, plugin.DeclaredPermissions, plugin.RequiredRunCapabilities, plugin.Pages, plugin.RuntimeProfiles)...)
|
|
|
|
|
violations = append(violations, validatePluginCreateFields("createFields", plugin.CreateFields)...)
|
|
|
|
|
violations = append(violations, validatePluginAssetFiles("lifecycleAssets", plugin.LifecycleAssets)...)
|
|
|
|
|
violations = append(violations, validateSafePluginStrings("gamePlugin", pluginSafeStrings(plugin))...)
|
|
|
|
@@ -238,7 +238,7 @@ func ValidateGamePluginManifestRegistration(registration domain.GamePluginManife
|
|
|
|
|
}
|
|
|
|
|
violations = append(violations, validateRuntimeProfileCapabilityDeclarations(manifest.RuntimeProfiles, manifest.Capabilities)...)
|
|
|
|
|
violations = append(violations, validateRuntimeLogEventPermissionDeclarations("manifest.runtimeProfiles.logEvents", manifest.RuntimeProfiles, manifest.Permissions)...)
|
|
|
|
|
violations = append(violations, validateGameClientBridgeManifest("manifest.gameClientBridge", manifest.GameClientBridge, manifest.Permissions, manifest.Pages, manifest.RuntimeProfiles)...)
|
|
|
|
|
violations = append(violations, validateGameClientBridgeManifest("manifest.gameClientBridge", manifest.GameClientBridge, manifest.Permissions, manifest.Capabilities, manifest.Pages, manifest.RuntimeProfiles)...)
|
|
|
|
|
violations = append(violations, validatePluginAssetFileDeclarations("manifest.assetFiles", manifest.AssetFiles)...)
|
|
|
|
|
violations = append(violations, validatePluginAssetFiles("assetFiles", registration.AssetFiles)...)
|
|
|
|
|
violations = append(violations, validateRegistrationAssetCoverage(registration.Manifest.AssetFiles, registration.AssetFiles)...)
|
|
|
|
@@ -467,9 +467,9 @@ func ValidatePluginCreateInputs(fields []domain.PluginCreateField, inputs map[st
|
|
|
|
|
return finish(violations)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func validateGameClientBridgeManifest(field string, bridge domain.GameClientBridgeManifest, permissions []string, pages []domain.GamePluginPage, runtimeProfiles domain.GamePluginRuntimeProfiles) []string {
|
|
|
|
|
func validateGameClientBridgeManifest(field string, bridge domain.GameClientBridgeManifest, permissions []string, runCapabilities []string, pages []domain.GamePluginPage, runtimeProfiles domain.GamePluginRuntimeProfiles) []string {
|
|
|
|
|
companionPresent := bridge.Companion != (domain.GameClientBridgeCompanionDeclaration{})
|
|
|
|
|
if len(bridge.Commands) == 0 && len(bridge.Snapshots) == 0 && len(bridge.QueryTemplates) == 0 && len(bridge.LogProjections) == 0 && len(bridge.DataPacks) == 0 && len(bridge.Pages) == 0 && len(bridge.Features) == 0 && bridge.Retention.KeepForSeconds == 0 && bridge.Retention.MaxRecords == 0 && !companionPresent {
|
|
|
|
|
if len(bridge.Commands) == 0 && len(bridge.Snapshots) == 0 && len(bridge.QueryTemplates) == 0 && len(bridge.LogProjections) == 0 && len(bridge.LifecycleProjections) == 0 && len(bridge.DataPacks) == 0 && len(bridge.Pages) == 0 && len(bridge.Features) == 0 && bridge.Retention.KeepForSeconds == 0 && bridge.Retention.MaxRecords == 0 && !companionPresent {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
var violations []string
|
|
|
|
@@ -610,6 +610,12 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
|
|
|
|
if template.SQLRef != "" && !safeRelativeSQLRef(template.SQLRef) {
|
|
|
|
|
violations = append(violations, prefix+".sqlRef must reference a package-relative SQL asset")
|
|
|
|
|
}
|
|
|
|
|
if len(template.Projections) > 4 {
|
|
|
|
|
violations = append(violations, prefix+".projections must not exceed 4 targets")
|
|
|
|
|
}
|
|
|
|
|
for projectionIndex, projection := range template.Projections {
|
|
|
|
|
violations = append(violations, validateGameClientBridgeQueryProjection(fmt.Sprintf("%s.projections[%d]", prefix, projectionIndex), projection)...)
|
|
|
|
|
}
|
|
|
|
|
transport, exists := transports[template.TransportKey]
|
|
|
|
|
if !exists {
|
|
|
|
|
violations = append(violations, prefix+".transportKey must reference a declared runtime transport profile")
|
|
|
|
@@ -634,6 +640,18 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
|
|
|
|
logProjectionKeys[projection.Key] = struct{}{}
|
|
|
|
|
violations = append(violations, validateGameClientBridgeLogProjection(prefix, projection)...)
|
|
|
|
|
}
|
|
|
|
|
lifecycleProjectionKeys := map[string]struct{}{}
|
|
|
|
|
for index, projection := range bridge.LifecycleProjections {
|
|
|
|
|
prefix := fmt.Sprintf("%s.lifecycleProjections[%d]", field, index)
|
|
|
|
|
if !clientManagerIdentifierPattern.MatchString(projection.Key) {
|
|
|
|
|
violations = append(violations, prefix+".key is invalid")
|
|
|
|
|
}
|
|
|
|
|
if _, exists := lifecycleProjectionKeys[projection.Key]; exists {
|
|
|
|
|
violations = append(violations, prefix+".key is duplicated")
|
|
|
|
|
}
|
|
|
|
|
lifecycleProjectionKeys[projection.Key] = struct{}{}
|
|
|
|
|
violations = append(violations, validateGameClientBridgeLifecycleProjection(prefix, projection, runCapabilities)...)
|
|
|
|
|
}
|
|
|
|
|
dataPackKeys := map[string]struct{}{}
|
|
|
|
|
for index, dataPack := range bridge.DataPacks {
|
|
|
|
|
prefix := fmt.Sprintf("%s.dataPacks[%d]", field, index)
|
|
|
|
@@ -753,6 +771,174 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
|
|
|
|
return violations
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func validateGameClientBridgeQueryProjection(prefix string, projection domain.GameClientBridgeQueryProjectionDeclaration) []string {
|
|
|
|
|
var violations []string
|
|
|
|
|
if !gameClientBridgeCollectionPattern.MatchString(projection.Collection) {
|
|
|
|
|
violations = append(violations, prefix+".collection is invalid")
|
|
|
|
|
}
|
|
|
|
|
if projection.RowPath != "rows" {
|
|
|
|
|
violations = append(violations, prefix+".rowPath must be rows")
|
|
|
|
|
}
|
|
|
|
|
if projection.MatchField != "" && !gameClientBridgeFieldPattern.MatchString(projection.MatchField) {
|
|
|
|
|
violations = append(violations, prefix+".matchField is invalid")
|
|
|
|
|
}
|
|
|
|
|
if projection.MatchField == "" && projection.MatchValue != "" || projection.MatchField != "" && strings.TrimSpace(projection.MatchValue) == "" {
|
|
|
|
|
violations = append(violations, prefix+".matchField and matchValue must be declared together")
|
|
|
|
|
}
|
|
|
|
|
if len([]rune(projection.MatchValue)) > 120 {
|
|
|
|
|
violations = append(violations, prefix+".matchValue is too long")
|
|
|
|
|
}
|
|
|
|
|
if len(projection.UpsertKeys) < 1 || len(projection.UpsertKeys) > 8 {
|
|
|
|
|
violations = append(violations, prefix+".upsertKeys must contain between 1 and 8 fields")
|
|
|
|
|
}
|
|
|
|
|
projectedFields := map[string]struct{}{}
|
|
|
|
|
for destination, source := range projection.FieldMappings {
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(destination) || !gameClientBridgeFieldPattern.MatchString(source) {
|
|
|
|
|
violations = append(violations, prefix+".fieldMappings contains an invalid field")
|
|
|
|
|
}
|
|
|
|
|
projectedFields[destination] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
if len(projection.FixedValues) > 64 {
|
|
|
|
|
violations = append(violations, prefix+".fixedValues contains too many fields")
|
|
|
|
|
}
|
|
|
|
|
for destination, value := range projection.FixedValues {
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(destination) || len([]rune(value)) > 4096 {
|
|
|
|
|
violations = append(violations, prefix+".fixedValues contains an invalid field or oversized value")
|
|
|
|
|
}
|
|
|
|
|
if _, exists := projectedFields[destination]; exists {
|
|
|
|
|
violations = append(violations, prefix+" declares field "+destination+" more than once")
|
|
|
|
|
}
|
|
|
|
|
projectedFields[destination] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
if projection.ObservedAtField != "" {
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(projection.ObservedAtField) {
|
|
|
|
|
violations = append(violations, prefix+".observedAtField is invalid")
|
|
|
|
|
}
|
|
|
|
|
if _, exists := projectedFields[projection.ObservedAtField]; exists {
|
|
|
|
|
violations = append(violations, prefix+" declares field "+projection.ObservedAtField+" more than once")
|
|
|
|
|
}
|
|
|
|
|
projectedFields[projection.ObservedAtField] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
for _, key := range projection.UpsertKeys {
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(key) {
|
|
|
|
|
violations = append(violations, prefix+".upsertKeys contains an invalid field")
|
|
|
|
|
}
|
|
|
|
|
if len(projection.FieldMappings) > 0 {
|
|
|
|
|
if _, exists := projectedFields[key]; !exists {
|
|
|
|
|
violations = append(violations, prefix+".upsertKeys field "+key+" is not projected")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
violations = append(violations, duplicateViolations(prefix+".upsertKeys", projection.UpsertKeys)...)
|
|
|
|
|
return violations
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func validateGameClientBridgeLifecycleProjection(prefix string, projection domain.GameClientBridgeLifecycleProjectionDeclaration, pluginRunCapabilities []string) []string {
|
|
|
|
|
var violations []string
|
|
|
|
|
if len(projection.Capabilities) < 1 || len(projection.Capabilities) > 16 {
|
|
|
|
|
violations = append(violations, prefix+".capabilities must contain between 1 and 16 values")
|
|
|
|
|
}
|
|
|
|
|
for _, capability := range projection.Capabilities {
|
|
|
|
|
if !validPluginRunCapability(capability) {
|
|
|
|
|
violations = append(violations, prefix+".capabilities contains an invalid capability")
|
|
|
|
|
}
|
|
|
|
|
if !containsString(pluginRunCapabilities, capability) {
|
|
|
|
|
violations = append(violations, prefix+".capabilities must be declared by the plugin")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
violations = append(violations, duplicateViolations(prefix+".capabilities", projection.Capabilities)...)
|
|
|
|
|
if len(projection.ProcessStates) > 8 {
|
|
|
|
|
violations = append(violations, prefix+".processStates must not exceed 8")
|
|
|
|
|
}
|
|
|
|
|
for _, state := range projection.ProcessStates {
|
|
|
|
|
if !oneOf(state, "running", "stopped", "not-started", "exited") {
|
|
|
|
|
violations = append(violations, prefix+".processStates contains an invalid process state")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
violations = append(violations, duplicateViolations(prefix+".processStates", projection.ProcessStates)...)
|
|
|
|
|
violations = append(violations, validateGameClientBridgeBulkProjectionTarget(prefix+".target", projection.Target)...)
|
|
|
|
|
return violations
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func validateGameClientBridgeBulkProjectionTarget(prefix string, target domain.GameClientBridgeBulkProjectionTargetDeclaration) []string {
|
|
|
|
|
var violations []string
|
|
|
|
|
if !gameClientBridgeCollectionPattern.MatchString(target.Collection) {
|
|
|
|
|
violations = append(violations, prefix+".collection is invalid")
|
|
|
|
|
}
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(target.MatchField) {
|
|
|
|
|
violations = append(violations, prefix+".matchField is invalid")
|
|
|
|
|
}
|
|
|
|
|
if strings.TrimSpace(target.MatchValue) == "" || len([]rune(target.MatchValue)) > 120 {
|
|
|
|
|
violations = append(violations, prefix+".matchValue is invalid")
|
|
|
|
|
}
|
|
|
|
|
if len(target.FixedValues) < 1 || len(target.FixedValues) > 64 {
|
|
|
|
|
violations = append(violations, prefix+".fixedValues must contain between 1 and 64 fields")
|
|
|
|
|
}
|
|
|
|
|
for destination, value := range target.FixedValues {
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(destination) || len([]rune(value)) > 4096 {
|
|
|
|
|
violations = append(violations, prefix+".fixedValues contains an invalid field or oversized value")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if target.ObservedAtField != "" && !gameClientBridgeFieldPattern.MatchString(target.ObservedAtField) {
|
|
|
|
|
violations = append(violations, prefix+".observedAtField is invalid")
|
|
|
|
|
}
|
|
|
|
|
if target.ActivityTarget != nil {
|
|
|
|
|
violations = append(violations, validateGameClientBridgeBulkActivityTarget(prefix+".activityTarget", *target.ActivityTarget)...)
|
|
|
|
|
}
|
|
|
|
|
return violations
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func validateGameClientBridgeBulkActivityTarget(prefix string, target domain.GameClientBridgeBulkActivityTargetDeclaration) []string {
|
|
|
|
|
var violations []string
|
|
|
|
|
if !gameClientBridgeCollectionPattern.MatchString(target.Collection) {
|
|
|
|
|
violations = append(violations, prefix+".collection is invalid")
|
|
|
|
|
}
|
|
|
|
|
if len(target.RowMappings) < 1 || len(target.RowMappings) > 64 {
|
|
|
|
|
violations = append(violations, prefix+".rowMappings must contain between 1 and 64 mappings")
|
|
|
|
|
}
|
|
|
|
|
projectedFields := map[string]struct{}{}
|
|
|
|
|
for destination, source := range target.RowMappings {
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(destination) || !gameClientBridgeFieldPattern.MatchString(source) {
|
|
|
|
|
violations = append(violations, prefix+".rowMappings contains an invalid field")
|
|
|
|
|
}
|
|
|
|
|
projectedFields[destination] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
if len(target.FixedValues) > 64 {
|
|
|
|
|
violations = append(violations, prefix+".fixedValues contains too many fields")
|
|
|
|
|
}
|
|
|
|
|
for destination, value := range target.FixedValues {
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(destination) || len([]rune(value)) > 4096 {
|
|
|
|
|
violations = append(violations, prefix+".fixedValues contains an invalid field or oversized value")
|
|
|
|
|
}
|
|
|
|
|
if _, exists := projectedFields[destination]; exists {
|
|
|
|
|
violations = append(violations, prefix+" declares field "+destination+" more than once")
|
|
|
|
|
}
|
|
|
|
|
projectedFields[destination] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
if target.ObservedAtField != "" {
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(target.ObservedAtField) {
|
|
|
|
|
violations = append(violations, prefix+".observedAtField is invalid")
|
|
|
|
|
}
|
|
|
|
|
if _, exists := projectedFields[target.ObservedAtField]; exists {
|
|
|
|
|
violations = append(violations, prefix+" declares field "+target.ObservedAtField+" more than once")
|
|
|
|
|
}
|
|
|
|
|
projectedFields[target.ObservedAtField] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
if len(target.UpsertKeys) < 1 || len(target.UpsertKeys) > 8 {
|
|
|
|
|
violations = append(violations, prefix+".upsertKeys must contain between 1 and 8 fields")
|
|
|
|
|
}
|
|
|
|
|
for _, key := range target.UpsertKeys {
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(key) {
|
|
|
|
|
violations = append(violations, prefix+".upsertKeys contains an invalid field")
|
|
|
|
|
}
|
|
|
|
|
if _, exists := projectedFields[key]; !exists {
|
|
|
|
|
violations = append(violations, prefix+".upsertKeys field "+key+" is not projected")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
violations = append(violations, duplicateViolations(prefix+".upsertKeys", target.UpsertKeys)...)
|
|
|
|
|
return violations
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func validateGameClientBridgeLogProjection(prefix string, projection domain.GameClientBridgeLogProjectionDeclaration) []string {
|
|
|
|
|
var violations []string
|
|
|
|
|
if len(projection.StreamKeys) < 1 || len(projection.StreamKeys) > 64 {
|
|
|
|
@@ -852,6 +1038,21 @@ func validateGameClientBridgeLogProjectionTarget(prefix string, target domain.Ga
|
|
|
|
|
}
|
|
|
|
|
projectedFields[destination] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
if len(target.HashMappings) > 64 {
|
|
|
|
|
violations = append(violations, prefix+".hashMappings contains too many fields")
|
|
|
|
|
}
|
|
|
|
|
for destination, capture := range target.HashMappings {
|
|
|
|
|
if !gameClientBridgeFieldPattern.MatchString(destination) || !gameClientBridgeCaptureNamePattern.MatchString(capture) {
|
|
|
|
|
violations = append(violations, prefix+".hashMappings contains an invalid field or capture")
|
|
|
|
|
}
|
|
|
|
|
if _, exists := captures[capture]; !exists {
|
|
|
|
|
violations = append(violations, prefix+".hashMappings references undeclared capture "+capture)
|
|
|
|
|
}
|
|
|
|
|
if _, exists := projectedFields[destination]; exists {
|
|
|
|
|
violations = append(violations, prefix+" declares field "+destination+" more than once")
|
|
|
|
|
}
|
|
|
|
|
projectedFields[destination] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
if len(target.FixedValues) > 64 {
|
|
|
|
|
violations = append(violations, prefix+".fixedValues contains too many fields")
|
|
|
|
|
}
|
|
|
|
|