Global json adjustments

This commit is contained in:
2026-06-05 16:35:14 +02:00
parent c672a1ff33
commit dfdc47c2dc
3 changed files with 932 additions and 17 deletions
+188
View File
@@ -972,6 +972,9 @@ func validateGlobalJSONFile(path string, obj map[string]any, report *ValidationR
if _, ok := obj["overrides"]; ok {
validateOverridesFile(path, obj, report)
}
if _, ok := obj["defaults"]; ok {
validateGlobalDefaults(path, obj, report)
}
if rawPosition, ok := obj["position"]; ok {
position, ok := rawPosition.(string)
if !ok {
@@ -1032,6 +1035,191 @@ func validateGlobalJSONFile(path string, obj map[string]any, report *ValidationR
validateRowCollection(path, obj, rows, report)
}
func topdataColumnsForGlobalValidation(path string, obj map[string]any) ([]string, bool) {
basePath := filepath.Join(filepath.Dir(path), "base.json")
baseData, err := loadJSONObject(basePath)
if err != nil {
return nil, false
}
datasetName := filepath.ToSlash(filepath.Base(filepath.Dir(path)))
columns, err := parseColumns(baseData, datasetName)
if err != nil {
return nil, true
}
modulePaths, err := collectModulePaths(filepath.Join(filepath.Dir(path), "modules"))
if err == nil {
for _, modulePath := range modulePaths {
moduleData, err := loadJSONObject(modulePath)
if err != nil {
continue
}
columns, err = extendColumns(columns, moduleData, datasetName, modulePath)
if err != nil {
return columns, true
}
}
}
columns, err = extendColumns(columns, obj, datasetName, path)
if err != nil {
return columns, true
}
return columns, true
}
func validateGlobalDefaults(path string, obj map[string]any, report *ValidationReport) {
rawDefaults, ok := obj["defaults"]
if !ok {
return
}
defaults, ok := rawDefaults.([]any)
if !ok {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: "global defaults must be a JSON array",
})
return
}
columns, hasBaseDataset := topdataColumnsForGlobalValidation(path, obj)
if !hasBaseDataset {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: "global defaults require a sibling base.json dataset",
})
return
}
for index, rawDefault := range defaults {
defaultRule, ok := rawDefault.(map[string]any)
if !ok {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d must be an object", index),
})
continue
}
validateGlobalDefaultMatch(path, index, defaultRule["match"], report)
rawValues, ok := defaultRule["values"]
if !ok {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d values is required", index),
})
continue
}
values, ok := rawValues.(map[string]any)
if !ok {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d values must be an object", index),
})
continue
}
for field, value := range values {
if _, ok := canonicalColumn(columns, field); !ok {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d values.%s is not a known column", index, field),
})
}
validateGlobalDefaultValue(path, index, field, value, report)
}
}
}
func validateGlobalDefaultMatch(path string, index int, rawMatch any, report *ValidationReport) {
if rawMatch == nil {
return
}
if text, ok := rawMatch.(string); ok {
if strings.EqualFold(strings.TrimSpace(text), "all") {
return
}
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d match must be all or an object", index),
})
return
}
match, ok := rawMatch.(map[string]any)
if !ok {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d match must be all or an object", index),
})
return
}
for key := range match {
if key != "source" {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d match.%s is not supported", index, key),
})
}
}
source, ok := match["source"].(string)
if !ok || strings.TrimSpace(source) == "" {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d match.source is required", index),
})
return
}
switch strings.ToLower(strings.TrimSpace(source)) {
case string(nativeRowSourceBase), string(nativeRowSourceEntries), string(nativeRowSourceOverride):
default:
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d match.source is not supported", index),
})
}
}
func validateGlobalDefaultValue(path string, index int, field string, value any, report *ValidationReport) {
obj, ok := value.(map[string]any)
if !ok {
return
}
rawFormat, hasFormat := obj["format"]
if !hasFormat {
return
}
if len(obj) != 1 {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d values.%s format object must contain only format", index, field),
})
return
}
format, ok := rawFormat.(string)
if !ok {
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d values.%s format must be a string", index, field),
})
return
}
if err := validateTopDataRowFormat(format); err != nil {
message := strings.TrimPrefix(err.Error(), "default ")
report.Diagnostics = append(report.Diagnostics, Diagnostic{
Severity: SeverityError,
Path: path,
Message: fmt.Sprintf("global default %d values.%s %s", index, field, message),
})
}
}
func validateGlobalConditionList(path, label string, raw any, report *ValidationReport) {
if raw == nil {
return