10func load_json(file_path:String) -> Dictionary:
11 var file = FileAccess.open(file_path, FileAccess.READ)
13 return {
"ok": false,
"data": {},
"issues": [_issue(
"missing_file", file_path,
"Unable to open JSON file.")]}
14 var parsed = JSON.parse_string(file.get_as_text())
15 if not parsed
is Dictionary:
16 return {
"ok": false,
"data": {},
"issues": [_issue(
"invalid_json", file_path,
"JSON root must be an object.")]}
17 return {
"ok": true,
"data": parsed,
"issues": []}
21func validate_records(records:Array, schema:Dictionary = {}, path:String =
"records") -> Array:
23 var id_field = str(schema.get(
"id_field",
"id"))
24 var required = schema.get(
"required", [])
if schema.get(
"required", [])
is Array
else []
26 for index
in range(records.size()):
27 var record = records[index]
28 var record_path =
"%s[%d]" % [path, index]
29 if not record
is Dictionary:
30 issues.append(_issue(
"invalid_record", record_path,
"Record must be an object."))
32 for field
in required:
33 if not record.has(str(field))
or str(record.get(str(field),
"")).strip_edges() ==
"":
34 issues.append(_issue(
"missing_field",
"%s.%s" % [record_path, str(field)],
"Required field is missing."))
35 var id = str(record.get(id_field,
""))
38 issues.append(_issue(
"duplicate_id", record_path,
"Duplicate %s '%s'." % [id_field, id]))
40 _validate_references(record, schema.get(
"references", []), record_path, issues)
45func validate_section(data:Dictionary, section:String, schema:Dictionary = {}) -> Array:
46 var records = data.get(section, [])
47 if not records
is Array:
48 return [_issue(
"invalid_section", section,
"Section must be an array.")]
49 return validate_records(records, schema, section)
53func summarize(issues:Array) -> Dictionary:
56 if issue
is Dictionary:
57 var type = str(issue.get(
"type",
"unknown"))
58 by_type[type] = int(by_type.get(type, 0)) + 1
60 "ok": issues.is_empty(),
61 "count": issues.size(),
67func validate_bundle(bundle:Dictionary) -> Dictionary:
69 for file_config
in bundle.get(
"files", []):
70 if not file_config
is Dictionary:
72 var path = str(file_config.get(
"path",
""))
73 var loaded = load_json(path)
74 all_issues.append_array(loaded.get(
"issues", []))
75 if not bool(loaded.get(
"ok", false)):
77 var data = loaded.get(
"data", {})
78 for section_config
in file_config.get(
"sections", []):
79 if not section_config
is Dictionary:
81 var section = str(section_config.get(
"name",
""))
82 all_issues.append_array(validate_section(data, section, section_config.get(
"schema", {})))
83 var summary = summarize(all_issues)
84 summary[
"issues"] = all_issues
88func _validate_references(record:Dictionary, references, record_path:String, issues:Array) -> void:
89 if not references
is Array:
91 for reference
in references:
92 if not reference
is Dictionary:
94 var field = str(reference.get(
"field",
""))
95 if field ==
"" or not record.has(field):
97 var allowed = reference.get(
"allowed", [])
98 if not allowed
is Array:
100 var values = record.get(field)
101 var list = values
if values
is Array
else [values]
104 if id !=
"" and not allowed.has(id):
105 issues.append(_issue(
"bad_reference",
"%s.%s" % [record_path, field],
"Unknown reference '%s'." % id))
108func _issue(type:String, path:String, message:String) -> Dictionary: