Align runtime profiles with plugin-owned records
This commit is contained in:
@@ -166,7 +166,6 @@ func ValidateGamePlugin(plugin domain.GamePlugin) error {
|
||||
violations = append(violations, err.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.RequiredRunCapabilities, plugin.Pages, plugin.RuntimeProfiles)...)
|
||||
violations = append(violations, validatePluginCreateFields("createFields", plugin.CreateFields)...)
|
||||
violations = append(violations, validatePluginAssetFiles("lifecycleAssets", plugin.LifecycleAssets)...)
|
||||
@@ -237,7 +236,6 @@ func ValidateGamePluginManifestRegistration(registration domain.GamePluginManife
|
||||
violations = append(violations, err.Error())
|
||||
}
|
||||
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.Capabilities, manifest.Pages, manifest.RuntimeProfiles)...)
|
||||
violations = append(violations, validatePluginAssetFileDeclarations("manifest.assetFiles", manifest.AssetFiles)...)
|
||||
violations = append(violations, validatePluginAssetFiles("assetFiles", registration.AssetFiles)...)
|
||||
@@ -469,7 +467,7 @@ func ValidatePluginCreateInputs(fields []domain.PluginCreateField, inputs map[st
|
||||
|
||||
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.LifecycleProjections) == 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.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
|
||||
@@ -628,18 +626,6 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
violations = append(violations, prefix+" transport must be sqlite with remote.run.db.sqlite.query capability")
|
||||
}
|
||||
}
|
||||
logProjectionKeys := map[string]struct{}{}
|
||||
for index, projection := range bridge.LogProjections {
|
||||
prefix := fmt.Sprintf("%s.logProjections[%d]", field, index)
|
||||
if !clientManagerIdentifierPattern.MatchString(projection.Key) {
|
||||
violations = append(violations, prefix+".key is invalid")
|
||||
}
|
||||
if _, exists := logProjectionKeys[projection.Key]; exists {
|
||||
violations = append(violations, prefix+".key is duplicated")
|
||||
}
|
||||
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)
|
||||
@@ -662,10 +648,10 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
violations = append(violations, prefix+".key is duplicated")
|
||||
}
|
||||
dataPackKeys[dataPack.Key] = struct{}{}
|
||||
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")
|
||||
if dataPack.DatabaseUserVersion < 1 || len(dataPack.ConfigMapRefs) == 0 {
|
||||
violations = append(violations, prefix+" must declare a database version and config assets")
|
||||
}
|
||||
refs := append(domain.CopyStringSlice(dataPack.LogParserRefs), dataPack.ConfigMapRefs...)
|
||||
refs := domain.CopyStringSlice(dataPack.ConfigMapRefs)
|
||||
refs = append(refs, dataPack.DataRefs...)
|
||||
for _, ref := range refs {
|
||||
if !safeRelativeJSONRef(ref) {
|
||||
@@ -939,154 +925,6 @@ func validateGameClientBridgeBulkActivityTarget(prefix string, target domain.Gam
|
||||
return violations
|
||||
}
|
||||
|
||||
func validateGameClientBridgeLogProjection(prefix string, projection domain.GameClientBridgeLogProjectionDeclaration) []string {
|
||||
var violations []string
|
||||
if len(projection.StreamKeys) < 1 || len(projection.StreamKeys) > 64 {
|
||||
violations = append(violations, prefix+".streamKeys must contain between 1 and 64 streams")
|
||||
}
|
||||
for _, streamKey := range projection.StreamKeys {
|
||||
if !clientManagerIdentifierPattern.MatchString(streamKey) {
|
||||
violations = append(violations, prefix+".streamKeys contains an invalid stream key")
|
||||
}
|
||||
}
|
||||
violations = append(violations, duplicateViolations(prefix+".streamKeys", projection.StreamKeys)...)
|
||||
|
||||
captures := map[string]struct{}{}
|
||||
if len(projection.Steps) < 1 || len(projection.Steps) > 64 {
|
||||
violations = append(violations, prefix+".steps must contain between 1 and 64 patterns")
|
||||
}
|
||||
for index, step := range projection.Steps {
|
||||
stepPrefix := fmt.Sprintf("%s.steps[%d].pattern", prefix, index)
|
||||
if strings.TrimSpace(step.Pattern) == "" || len([]rune(step.Pattern)) > 16384 {
|
||||
violations = append(violations, stepPrefix+" is empty or too large")
|
||||
continue
|
||||
}
|
||||
compiled, err := regexp.Compile(step.Pattern)
|
||||
if err != nil {
|
||||
violations = append(violations, stepPrefix+" must be a valid regular expression")
|
||||
continue
|
||||
}
|
||||
for _, capture := range compiled.SubexpNames() {
|
||||
if capture != "" {
|
||||
captures[capture] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(projection.CorrelationFields) < 1 || len(projection.CorrelationFields) > 64 {
|
||||
violations = append(violations, prefix+".correlationFields must contain between 1 and 64 captures")
|
||||
}
|
||||
for _, field := range projection.CorrelationFields {
|
||||
if !gameClientBridgeCaptureNamePattern.MatchString(field) {
|
||||
violations = append(violations, prefix+".correlationFields contains an invalid capture name")
|
||||
continue
|
||||
}
|
||||
if _, exists := captures[field]; !exists {
|
||||
violations = append(violations, prefix+".correlationFields references undeclared capture "+field)
|
||||
}
|
||||
}
|
||||
violations = append(violations, duplicateViolations(prefix+".correlationFields", projection.CorrelationFields)...)
|
||||
if projection.MaxInterveningLines < 0 || projection.MaxInterveningLines > 100000 {
|
||||
violations = append(violations, prefix+".maxInterveningLines is invalid")
|
||||
}
|
||||
violations = append(violations, validateGameClientBridgeLogProjectionTarget(prefix+".target", projection.Target, captures)...)
|
||||
|
||||
if projection.Presence == nil {
|
||||
return violations
|
||||
}
|
||||
presence := projection.Presence
|
||||
if !gameClientBridgeFieldPattern.MatchString(presence.TimestampField) || !gameClientBridgeLogProjectionTargetDeclaresField(projection.Target, presence.TimestampField) {
|
||||
violations = append(violations, prefix+".presence.timestampField must reference a projected target field")
|
||||
}
|
||||
if presence.ActiveWindowSeconds < 1 || presence.ActiveWindowSeconds > 31536000 {
|
||||
violations = append(violations, prefix+".presence.activeWindowSeconds is invalid")
|
||||
}
|
||||
if presence.ActivityTarget != nil {
|
||||
violations = append(violations, validateGameClientBridgeLogProjectionTarget(prefix+".presence.activityTarget", *presence.ActivityTarget, captures)...)
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
func validateGameClientBridgeLogProjectionTarget(prefix string, target domain.GameClientBridgeLogProjectionTargetDeclaration, captures map[string]struct{}) []string {
|
||||
var violations []string
|
||||
if !gameClientBridgeCollectionPattern.MatchString(target.Collection) {
|
||||
violations = append(violations, prefix+".collection is invalid")
|
||||
}
|
||||
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 !gameClientBridgeLogProjectionTargetDeclaresField(target, key) {
|
||||
violations = append(violations, prefix+".upsertKeys field "+key+" is not projected")
|
||||
}
|
||||
}
|
||||
violations = append(violations, duplicateViolations(prefix+".upsertKeys", target.UpsertKeys)...)
|
||||
|
||||
if len(target.CaptureMappings) < 1 || len(target.CaptureMappings) > 64 {
|
||||
violations = append(violations, prefix+".captureMappings must contain between 1 and 64 mappings")
|
||||
}
|
||||
projectedFields := map[string]struct{}{}
|
||||
for destination, capture := range target.CaptureMappings {
|
||||
if !gameClientBridgeFieldPattern.MatchString(destination) || !gameClientBridgeCaptureNamePattern.MatchString(capture) {
|
||||
violations = append(violations, prefix+".captureMappings contains an invalid field or capture")
|
||||
}
|
||||
if _, exists := captures[capture]; !exists {
|
||||
violations = append(violations, prefix+".captureMappings references undeclared capture "+capture)
|
||||
}
|
||||
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")
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
func gameClientBridgeLogProjectionTargetDeclaresField(target domain.GameClientBridgeLogProjectionTargetDeclaration, field string) bool {
|
||||
if target.ObservedAtField == field {
|
||||
return true
|
||||
}
|
||||
if _, exists := target.CaptureMappings[field]; exists {
|
||||
return true
|
||||
}
|
||||
_, exists := target.FixedValues[field]
|
||||
return exists
|
||||
}
|
||||
|
||||
func validCompanionProofEnvironment(value string) bool {
|
||||
if len(value) < 3 || len(value) > 64 || value[0] < 'A' || value[0] > 'Z' {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user