From 1418ce8d8eb8dd94c153d2c1cd87b9e3df34eecf Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Thu, 18 Jul 2024 15:28:28 -0400 Subject: [PATCH] rf(metaschema): Validate object definitions that are JSON schema (#1869) --- .pre-commit-config.yaml | 12 + src/metaschema.json | 346 ++++++++---------- src/schema/objects/metadata.yaml | 10 +- .../bidsschematools/render/tables.py | 4 +- 4 files changed, 167 insertions(+), 205 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5e1ed3b234..886c93bce6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,16 @@ repos: - id: check-ast - id: check-added-large-files - id: check-case-conflict + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.29.0 + hooks: + - id: check-dependabot + - id: check-github-workflows + args: ["--verbose"] + - id: check-metaschema + files: src/metaschema.json + - id: check-readthedocs + files: readthedocs.yml - repo: https://github.com/psf/black rev: 24.4.2 hooks: @@ -71,6 +81,8 @@ repos: - types-PyYAML - types-tabulate - types-jsonschema + - jsonschema + - httpx args: ["tools/schemacode/bidsschematools"] pass_filenames: false - repo: https://github.com/koalaman/shellcheck-precommit diff --git a/src/metaschema.json b/src/metaschema.json index 7e7baaf0ee..dd1a3fcb91 100644 --- a/src/metaschema.json +++ b/src/metaschema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema#", "type": "object", "properties": { "meta": { @@ -52,7 +52,7 @@ }, "context": { "description": "The context object is itself JSON schema", - "$ref": "http://json-schema.org/draft-07/schema#" + "$ref": "https://json-schema.org/draft/2020-12/schema#" }, "expression_tests": { "type": "array", @@ -84,44 +84,19 @@ "type": "object", "patternProperties": { "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "type": { - "$ref": "http://json-schema.org/draft-07/schema#/properties/type" - }, - "format": { - "type": "string" - }, - "pattern": { - "type": "string" - }, - "unit": { - "type": "string" - }, - "enum": { - "type": "array" - }, - "anyOf": { - "$ref": "http://json-schema.org/draft-07/schema#/properties/anyOf" - }, - "maximum": { - "type": "number" - }, - "minimum": { - "type": "number" + "allOf": [ + { "$ref": "#/definitions/schemaTerm" }, + { "$ref": "#/definitions/generalTerm" }, + { "$ref": "#/definitions/nameValueTerm" }, + { + "type": "object", + "properties": { + "format": { "$ref": "#/definitions/formats" }, + "unit": { "type": "string" } + } } - }, - "required": ["name", "display_name"], - "additionalProperties": false + ], + "unevaluatedProperties": false } }, "additionalProperties": false @@ -130,17 +105,8 @@ "type": "object", "patternProperties": { "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "display_name": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "required": ["display_name", "description"], - "additionalProperties": false + "$ref": "#/definitions/generalTerm", + "unevaluatedProperties": false }, "additionalProperties": false } @@ -149,20 +115,11 @@ "type": "object", "patternProperties": { "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "value": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "required": ["value", "display_name", "description"], - "additionalProperties": false + "allOf": [ + { "$ref": "#/definitions/generalTerm" }, + { "$ref": "#/definitions/valueTerm" } + ], + "unevaluatedProperties": false } }, "additionalProperties": false @@ -171,35 +128,19 @@ "type": "object", "patternProperties": { "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "type": { - "$ref": "http://json-schema.org/draft-07/schema#/properties/type" - }, - "format": { - "type": "string" - }, - "enum": { - "type": "array" + "allOf": [ + { "$ref": "#/definitions/schemaTerm" }, + { "$ref": "#/definitions/generalTerm" }, + { "$ref": "#/definitions/nameValueTerm" }, + { + "type": "object", + "properties": { + "format": { "$ref": "#/definitions/formats" } + }, + "required": ["format"] } - }, - "required": [ - "name", - "display_name", - "description", - "type", - "format" ], - "additionalProperties": false + "unevaluatedProperties": false } }, "additionalProperties": false @@ -207,30 +148,18 @@ "enums": { "type": "object", "patternProperties": { - "^[a-zA-Z0-9_-]+$": { - "type": "object", - "properties": { - "type": { - "$ref": "http://json-schema.org/draft-07/schema#/properties/type" - }, - "enum": { - "type": "array" - }, - "value": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "tags": { - "type": "array" - } - }, - "additionalProperties": false - } + "^[a-zA-Z0-9][a-zA-Z0-9_-]*$": { + "allOf": [ + { "$ref": "#/definitions/generalTerm" }, + { "$ref": "#/definitions/valueTerm" }, + { "properties": { "tags": { "type": "array" } } } + ] + }, + "^_[a-zA-Z0-9_-]+$": { + "$ref": "https://json-schema.org/draft/2020-12/schema#", + "required": ["type", "enum"] + }, + "unevaluatedProperties": false }, "additionalProperties": false }, @@ -238,20 +167,11 @@ "type": "object", "patternProperties": { "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "value": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "required": ["display_name", "description", "value"], - "additionalProperties": false + "allOf": [ + { "$ref": "#/definitions/generalTerm" }, + { "$ref": "#/definitions/valueTerm" } + ], + "unevaluatedProperties": false } }, "additionalProperties": false @@ -260,66 +180,56 @@ "type": "object", "patternProperties": { "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "display_name": { - "type": "string" - }, - "file_type": { - "type": "string" - }, - "description": { - "type": "string" + "allOf": [ + { "$ref": "#/definitions/generalTerm" }, + { + "properties": { "file_type": { "type": "string" } }, + "required": ["file_type"] } - }, - "required": ["display_name", "file_type", "description"], - "additionalProperties": false + ], + "unevaluatedProperties": false } }, "additionalProperties": false }, "formats": { "type": "object", - "patternProperties": { - "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "display_name": { - "type": "string" - }, - "description": { - "type": "string" + "propertyNames": { "$ref": "#/definitions/formats" }, + "additionalProperties": { + "allOf": [ + { "$ref": "#/definitions/generalTerm" }, + { + "type": "object", + "properties": { + "pattern": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/pattern" + } }, - "pattern": { - "format": "regex" - } - }, - "required": ["display_name", "description", "pattern"], - "additionalProperties": false - } - }, - "additionalProperties": false + "required": ["pattern"] + } + ], + "unevaluatedProperties": false + } }, "metadata": { "type": "object", "patternProperties": { "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "type": { - "$ref": "http://json-schema.org/draft-07/schema#/properties/type" + "allOf": [ + { "$ref": "#/definitions/schemaTerm" }, + { "$ref": "#/definitions/generalTerm" }, + { "$ref": "#/definitions/nameValueTerm" }, + { + "type": "object", + "properties": { + "recommended": { + "$ref": "https://json-schema.org/draft/2020-12/meta/validation#/properties/required" + }, + "unit": { "type": "string" } + } } - }, - "required": ["name", "display_name", "description"] + ], + "unevaluatedProperties": false } }, "additionalProperties": false @@ -328,17 +238,8 @@ "type": "object", "patternProperties": { "^[a-z]+$": { - "type": "object", - "properties": { - "display_name": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "required": ["display_name", "description"], - "additionalProperties": false + "$ref": "#/definitions/generalTerm", + "unevaluatedProperties": false } }, "additionalProperties": false @@ -347,19 +248,22 @@ "type": "object", "patternProperties": { "^[a-zA-Z0-9_]+$": { - "type": "object", - "properties": { - "value": { - "type": "string" - }, - "display_name": { - "type": "string" - }, - "description": { - "type": "string" + "allOf": [ + { "$ref": "#/definitions/generalTerm" }, + { "$ref": "#/definitions/valueTerm" }, + { + "type": "object", + "properties": { + "unit": { "type": "string" }, + "anyOf": { + "$ref": "https://json-schema.org/draft/2020-12/meta/applicator#/properties/anyOf" + }, + "maxValue": { "type": "number" }, + "minValue": { "type": "number" } + } } - }, - "required": ["value", "display_name", "description"] + ], + "unevaluatedProperties": false }, "additionalProperties": false } @@ -605,6 +509,52 @@ "required": ["meta", "objects", "rules", "bids_version", "schema_version"], "additionalProperties": false, "definitions": { + "formats": { + "$comment": "Formats whose patterns are defined in BIDS schema", + "type": "string", + "enum": [ + "index", + "label", + "boolean", + "integer", + "number", + "string", + "hed_version", + "bids_uri", + "dataset_relative", + "date", + "datetime", + "file_relative", + "participant_relative", + "rrid", + "stimuli_relative", + "time", + "unit", + "uri" + ] + }, + "generalTerm": { + "type": "object", + "properties": { + "display_name": { "type": "string" }, + "description": { "type": "string" } + }, + "required": ["display_name", "description"] + }, + "schemaTerm": { + "$ref": "https://json-schema.org/draft/2020-12/schema#", + "anyOf": [{ "required": ["type"] }, { "required": ["anyOf"] }] + }, + "nameValueTerm": { + "type": "object", + "properties": { "name": { "type": "string" } }, + "required": ["name"] + }, + "valueTerm": { + "type": "object", + "properties": { "value": { "type": "string" } }, + "required": ["value"] + }, "entities": { "type": "object", "patternProperties": { diff --git a/src/schema/objects/metadata.yaml b/src/schema/objects/metadata.yaml index 70926904fc..5614a37c3f 100644 --- a/src/schema/objects/metadata.yaml +++ b/src/schema/objects/metadata.yaml @@ -1104,8 +1104,8 @@ GeneratedBy: minItems: 1 items: type: object - required_fields: [Name] - recommended_fields: [Version] + required: [Name] + recommended: [Version] properties: Name: name: Name @@ -1136,7 +1136,7 @@ GeneratedBy: used to produce the dataset. Valid keys in this object include `Type`, `Tag` and [`URI`][uri] with [string][] values. type: object - recommended_fields: + recommended: - Type - Tag - URI @@ -1170,7 +1170,7 @@ Genetics: description: | An object containing information about the genetics descriptor. type: object - required_fields: [Dataset] + required: [Dataset] properties: Dataset: name: Dataset @@ -3353,7 +3353,7 @@ StimulusPresentation: Object containing key-value pairs related to the software used to present the stimuli during the experiment. type: object - recommended_fields: + recommended: - OperatingSystem - ScreenDistance - ScreenRefreshRate diff --git a/tools/schemacode/bidsschematools/render/tables.py b/tools/schemacode/bidsschematools/render/tables.py index b32e982e52..9366ae5725 100644 --- a/tools/schemacode/bidsschematools/render/tables.py +++ b/tools/schemacode/bidsschematools/render/tables.py @@ -528,8 +528,8 @@ def make_subobject_table( The tabulated table as a Markdown string. """ obj = schema.objects[object_name] - required_fields = set(obj.get("required_fields", ())) - recommended_fields = set(obj.get("recommended_fields", ())) + required_fields = set(obj.get("required", ())) + recommended_fields = set(obj.get("recommended", ())) field_info = {} for field in obj.properties: