From dd9cb5cc6bacc4f71d7e38e0cdd92821798c3f39 Mon Sep 17 00:00:00 2001 From: Neal Date: Wed, 31 Jul 2024 15:13:40 -0700 Subject: [PATCH 1/3] update to use local schema for draft-07 --- .../json_schema_org/draft-07.json | 172 ++++++++++++++++++ crates/tbdex/src/json_schemas/mod.rs | 6 +- 2 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 crates/tbdex/src/json_schemas/json_schema_org/draft-07.json diff --git a/crates/tbdex/src/json_schemas/json_schema_org/draft-07.json b/crates/tbdex/src/json_schemas/json_schema_org/draft-07.json new file mode 100644 index 00000000..bfb8b8a7 --- /dev/null +++ b/crates/tbdex/src/json_schemas/json_schema_org/draft-07.json @@ -0,0 +1,172 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://json-schema.org/draft-07/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": true + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "if": { "$ref": "#" }, + "then": { "$ref": "#" }, + "else": { "$ref": "#" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": true +} \ No newline at end of file diff --git a/crates/tbdex/src/json_schemas/mod.rs b/crates/tbdex/src/json_schemas/mod.rs index ff985f58..dd356694 100644 --- a/crates/tbdex/src/json_schemas/mod.rs +++ b/crates/tbdex/src/json_schemas/mod.rs @@ -36,6 +36,10 @@ impl LocalSchemaResolver { "https://tbdex.dev/definitions.json".to_string(), serde_json::from_str(&DEFINITIONS_JSON_SCHEMA.replace("\\#", "#")).unwrap(), ); + schemas.insert( + "http://json-schema.org/draft-07/schema#".to_string(), + serde_json::from_str(include_str!("json_schema_org/draft-07.json")).unwrap(), + ); LocalSchemaResolver { schemas } } } @@ -100,4 +104,4 @@ pub fn validate(schema: &serde_json::Value, value: &T) -> Result<( } Ok(()) -} +} \ No newline at end of file From 96dfec42bd9b8b7eec6049019d093c96238eab85 Mon Sep 17 00:00:00 2001 From: Neal Date: Thu, 1 Aug 2024 10:31:42 -0700 Subject: [PATCH 2/3] update --- crates/tbdex/build.rs | 6 +- crates/tbdex/src/json_schemas/generated.rs | 173 ++++++++++++++++++ .../json_schema_org/draft-07.json | 172 ----------------- crates/tbdex/src/json_schemas/mod.rs | 87 ++++++++- 4 files changed, 264 insertions(+), 174 deletions(-) delete mode 100644 crates/tbdex/src/json_schemas/json_schema_org/draft-07.json diff --git a/crates/tbdex/build.rs b/crates/tbdex/build.rs index d6315e33..7ff6e46f 100644 --- a/crates/tbdex/build.rs +++ b/crates/tbdex/build.rs @@ -5,6 +5,7 @@ use std::path::Path; fn write_json_schemas() -> Result<(), Box> { let commit_hash = "7d2fdd03c9405b920b056ab7c7c776a858dc3591"; let schemas = vec![ + // Tbdex Schemas ("DEFINITIONS_JSON_SCHEMA", format!("https://raw.githubusercontent.com/TBD54566975/tbdex/{}/hosted/json-schemas/definitions.json", commit_hash)), ("RESOURCE_JSON_SCHEMA", format!("https://raw.githubusercontent.com/TBD54566975/tbdex/{}/hosted/json-schemas/resource.schema.json", commit_hash)), ("BALANCE_DATA_JSON_SCHEMA", format!("https://raw.githubusercontent.com/TBD54566975/tbdex/{}/hosted/json-schemas/balance.schema.json", commit_hash)), @@ -18,7 +19,10 @@ fn write_json_schemas() -> Result<(), Box> { ("CANCEL_DATA_JSON_SCHEMA", format!("https://raw.githubusercontent.com/TBD54566975/tbdex/{}/hosted/json-schemas/cancel.schema.json", commit_hash)), ("ORDER_STATUS_DATA_JSON_SCHEMA", format!("https://raw.githubusercontent.com/TBD54566975/tbdex/{}/hosted/json-schemas/orderstatus.schema.json", commit_hash)), ("CLOSE_DATA_JSON_SCHEMA", format!("https://raw.githubusercontent.com/TBD54566975/tbdex/{}/hosted/json-schemas/close.schema.json", commit_hash)), - ]; + + // Json schema + ("DRAFT_07_JSON_SCHEMA", "https://json-schema.org/draft-07/schema".to_string()), + ]; let dest_path = Path::new("src/json_schemas").join("generated.rs"); diff --git a/crates/tbdex/src/json_schemas/generated.rs b/crates/tbdex/src/json_schemas/generated.rs index c47fd880..83d45514 100644 --- a/crates/tbdex/src/json_schemas/generated.rs +++ b/crates/tbdex/src/json_schemas/generated.rs @@ -563,3 +563,176 @@ pub const CLOSE_DATA_JSON_SCHEMA: &str = r#"{ } } }"#; +pub const DRAFT_07_JSON_SCHEMA: &str = r#"{ + "$schema": "http://json-schema.org/draft-07/schema\#", + "$id": "http://json-schema.org/draft-07/schema\#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "\#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "\#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": true, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": true + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "\#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "\#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "\#" }, + "items": { + "anyOf": [ + { "$ref": "\#" }, + { "$ref": "\#/definitions/schemaArray" } + ], + "default": true + }, + "maxItems": { "$ref": "\#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "\#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "\#" }, + "maxProperties": { "$ref": "\#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "\#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "\#/definitions/stringArray" }, + "additionalProperties": { "$ref": "\#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "\#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "\#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "\#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "\#" }, + { "$ref": "\#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "\#" }, + "const": true, + "enum": { + "type": "array", + "items": true, + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "\#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "\#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "if": { "$ref": "\#" }, + "then": { "$ref": "\#" }, + "else": { "$ref": "\#" }, + "allOf": { "$ref": "\#/definitions/schemaArray" }, + "anyOf": { "$ref": "\#/definitions/schemaArray" }, + "oneOf": { "$ref": "\#/definitions/schemaArray" }, + "not": { "$ref": "\#" } + }, + "default": true +} +"#; diff --git a/crates/tbdex/src/json_schemas/json_schema_org/draft-07.json b/crates/tbdex/src/json_schemas/json_schema_org/draft-07.json deleted file mode 100644 index bfb8b8a7..00000000 --- a/crates/tbdex/src/json_schemas/json_schema_org/draft-07.json +++ /dev/null @@ -1,172 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "http://json-schema.org/draft-07/schema#", - "title": "Core schema meta-schema", - "definitions": { - "schemaArray": { - "type": "array", - "minItems": 1, - "items": { "$ref": "#" } - }, - "nonNegativeInteger": { - "type": "integer", - "minimum": 0 - }, - "nonNegativeIntegerDefault0": { - "allOf": [ - { "$ref": "#/definitions/nonNegativeInteger" }, - { "default": 0 } - ] - }, - "simpleTypes": { - "enum": [ - "array", - "boolean", - "integer", - "null", - "number", - "object", - "string" - ] - }, - "stringArray": { - "type": "array", - "items": { "type": "string" }, - "uniqueItems": true, - "default": [] - } - }, - "type": ["object", "boolean"], - "properties": { - "$id": { - "type": "string", - "format": "uri-reference" - }, - "$schema": { - "type": "string", - "format": "uri" - }, - "$ref": { - "type": "string", - "format": "uri-reference" - }, - "$comment": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "default": true, - "readOnly": { - "type": "boolean", - "default": false - }, - "writeOnly": { - "type": "boolean", - "default": false - }, - "examples": { - "type": "array", - "items": true - }, - "multipleOf": { - "type": "number", - "exclusiveMinimum": 0 - }, - "maximum": { - "type": "number" - }, - "exclusiveMaximum": { - "type": "number" - }, - "minimum": { - "type": "number" - }, - "exclusiveMinimum": { - "type": "number" - }, - "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, - "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "pattern": { - "type": "string", - "format": "regex" - }, - "additionalItems": { "$ref": "#" }, - "items": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/schemaArray" } - ], - "default": true - }, - "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, - "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "uniqueItems": { - "type": "boolean", - "default": false - }, - "contains": { "$ref": "#" }, - "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, - "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, - "required": { "$ref": "#/definitions/stringArray" }, - "additionalProperties": { "$ref": "#" }, - "definitions": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "properties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "patternProperties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "propertyNames": { "format": "regex" }, - "default": {} - }, - "dependencies": { - "type": "object", - "additionalProperties": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/stringArray" } - ] - } - }, - "propertyNames": { "$ref": "#" }, - "const": true, - "enum": { - "type": "array", - "items": true, - "minItems": 1, - "uniqueItems": true - }, - "type": { - "anyOf": [ - { "$ref": "#/definitions/simpleTypes" }, - { - "type": "array", - "items": { "$ref": "#/definitions/simpleTypes" }, - "minItems": 1, - "uniqueItems": true - } - ] - }, - "format": { "type": "string" }, - "contentMediaType": { "type": "string" }, - "contentEncoding": { "type": "string" }, - "if": { "$ref": "#" }, - "then": { "$ref": "#" }, - "else": { "$ref": "#" }, - "allOf": { "$ref": "#/definitions/schemaArray" }, - "anyOf": { "$ref": "#/definitions/schemaArray" }, - "oneOf": { "$ref": "#/definitions/schemaArray" }, - "not": { "$ref": "#" } - }, - "default": true -} \ No newline at end of file diff --git a/crates/tbdex/src/json_schemas/mod.rs b/crates/tbdex/src/json_schemas/mod.rs index dd356694..d020f6e7 100644 --- a/crates/tbdex/src/json_schemas/mod.rs +++ b/crates/tbdex/src/json_schemas/mod.rs @@ -6,6 +6,7 @@ use reqwest::blocking::get; use serde::Serialize; use serde_json::Error as SerdeJsonError; use std::{collections::HashMap, sync::Arc}; +use crate::json_schemas::generated::DRAFT_07_JSON_SCHEMA; #[derive(thiserror::Error, Debug, Clone, PartialEq)] pub enum JsonSchemaError { @@ -38,7 +39,7 @@ impl LocalSchemaResolver { ); schemas.insert( "http://json-schema.org/draft-07/schema#".to_string(), - serde_json::from_str(include_str!("json_schema_org/draft-07.json")).unwrap(), + serde_json::from_str(&DRAFT_07_JSON_SCHEMA.replace("\\#", "#")).unwrap(), ); LocalSchemaResolver { schemas } } @@ -104,4 +105,88 @@ pub fn validate(schema: &serde_json::Value, value: &T) -> Result<( } Ok(()) +} + +#[cfg(test)] +mod json_schemas_test { + use reqwest::Url; + use super::*; + use serde_json::json; + + #[test] + fn test_validate_json_schema() { + // Sample JSON data + let sample_json_data = json!({ + "name": "John Doe", + "age": 30, + "email": "john.doe@example.com" + }); + + // Sample JSON schema + let sample_json_schema = r#" + { + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "age": { + "type": "integer", + "minimum": 0 + }, + "email": { + "type": "string", + "format": "email" + } + }, + "required": ["name", "age", "email"] + } + "#; + + // Validate the sample JSON data against the schema + let schema: serde_json::Value = serde_json::from_str(sample_json_schema).unwrap(); + let result = crate::json_schemas::validate(&schema, &sample_json_data); + + // Check that the validation was successful + assert!(result.is_ok()); + } + + #[test] + fn test_local_schema_resolver_local() { + // Create a local schema resolver + let resolver = LocalSchemaResolver::new(); + + // Create a URL that matches one of the schemas in the resolver + let url = Url::parse("https://tbdex.dev/definitions.json").unwrap(); + + // Resolve the schema + let resolved_schema = resolver.resolve(&json!({}), &url, "").unwrap(); + + // Ensure the resolved schema is correct + let expected_schema: serde_json::Value = serde_json::from_str(&DEFINITIONS_JSON_SCHEMA.replace("\\#", "#")).unwrap(); + assert_eq!( + resolved_schema.as_ref(), + &expected_schema + ); + } + + #[test] + fn test_local_draft_07_schema_resolver_local() { + // Create a local schema resolver + let resolver = LocalSchemaResolver::new(); + + // Create a URL that matches one of the schemas in the resolver + let url = Url::parse("http://json-schema.org/draft-07/schema#").unwrap(); + + // Resolve the schema + let resolved_schema = resolver.resolve(&json!({}), &url, "").unwrap(); + + // Ensure the resolved schema is correct + let expected_schema: serde_json::Value = serde_json::from_str(&DRAFT_07_JSON_SCHEMA.replace("\\#", "#")).unwrap(); + assert_eq!( + resolved_schema.as_ref(), + &expected_schema + ); + } } \ No newline at end of file From 9bccd5c923febb69d31abe1a10c18bc50d5458f5 Mon Sep 17 00:00:00 2001 From: Neal Date: Thu, 1 Aug 2024 10:51:22 -0700 Subject: [PATCH 3/3] simplify unit test --- crates/tbdex/src/json_schemas/mod.rs | 31 +++++++--------------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/crates/tbdex/src/json_schemas/mod.rs b/crates/tbdex/src/json_schemas/mod.rs index d020f6e7..f6d5ed39 100644 --- a/crates/tbdex/src/json_schemas/mod.rs +++ b/crates/tbdex/src/json_schemas/mod.rs @@ -115,41 +115,24 @@ mod json_schemas_test { #[test] fn test_validate_json_schema() { - // Sample JSON data - let sample_json_data = json!({ + let data = json!({ "name": "John Doe", "age": 30, "email": "john.doe@example.com" }); - // Sample JSON schema - let sample_json_schema = r#" - { + let schema = json!({ "$schema": "https://json-schema.org/draft-07/schema#", "type": "object", "properties": { - "name": { - "type": "string" - }, - "age": { - "type": "integer", - "minimum": 0 - }, - "email": { - "type": "string", - "format": "email" - } + "name": { "type": "string" }, + "age": { "type": "integer", "minimum": 0 }, + "email": { "type": "string", "format": "email" } }, "required": ["name", "age", "email"] - } - "#; - - // Validate the sample JSON data against the schema - let schema: serde_json::Value = serde_json::from_str(sample_json_schema).unwrap(); - let result = crate::json_schemas::validate(&schema, &sample_json_data); + }); - // Check that the validation was successful - assert!(result.is_ok()); + assert!(crate::json_schemas::validate(&schema, &data).is_ok()); } #[test]