implemented type match checking for types on schema id instead of type const
This commit is contained in:
61
src/lib.rs
61
src/lib.rs
@ -9,7 +9,7 @@ use std::borrow::Cow;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::{collections::HashMap, sync::RwLock};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum SchemaType {
|
||||
Enum,
|
||||
Type,
|
||||
@ -20,6 +20,7 @@ enum SchemaType {
|
||||
struct BoonCache {
|
||||
schemas: Schemas,
|
||||
id_to_index: HashMap<String, SchemaIndex>,
|
||||
id_to_type: HashMap<String, SchemaType>,
|
||||
}
|
||||
|
||||
// Structure to hold error information without lifetimes
|
||||
@ -35,6 +36,7 @@ lazy_static! {
|
||||
static ref SCHEMA_CACHE: RwLock<BoonCache> = RwLock::new(BoonCache {
|
||||
schemas: Schemas::new(),
|
||||
id_to_index: HashMap::new(),
|
||||
id_to_type: HashMap::new(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -49,6 +51,7 @@ fn cache_json_schemas(enums: JsonB, types: JsonB, puncs: JsonB) -> JsonB {
|
||||
*cache = BoonCache {
|
||||
schemas: Schemas::new(),
|
||||
id_to_index: HashMap::new(),
|
||||
id_to_type: HashMap::new(),
|
||||
};
|
||||
|
||||
// Create the boon compiler and enable format assertions
|
||||
@ -85,6 +88,7 @@ fn cache_json_schemas(enums: JsonB, types: JsonB, puncs: JsonB) -> JsonB {
|
||||
}));
|
||||
} else {
|
||||
all_schema_ids.push(schema_id.to_string());
|
||||
cache.id_to_type.insert(schema_id.to_string(), SchemaType::Enum);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,6 +123,7 @@ fn cache_json_schemas(enums: JsonB, types: JsonB, puncs: JsonB) -> JsonB {
|
||||
}));
|
||||
} else {
|
||||
all_schema_ids.push(schema_id.to_string());
|
||||
cache.id_to_type.insert(schema_id.to_string(), SchemaType::Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,6 +171,7 @@ fn cache_json_schemas(enums: JsonB, types: JsonB, puncs: JsonB) -> JsonB {
|
||||
}));
|
||||
} else {
|
||||
all_schema_ids.push(schema_id.to_string());
|
||||
cache.id_to_type.insert(schema_id.to_string(), schema_type_for_def);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -322,6 +328,47 @@ fn apply_strict_validation_recursive(schema: &mut Value, inside_conditional: boo
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_type_against_schema_id(instance: &Value, schema_id: &str) -> JsonB {
|
||||
let expected_type = schema_id.split('.').next().unwrap_or(schema_id);
|
||||
|
||||
if let Some(actual_type) = instance.get("type").and_then(|v| v.as_str()) {
|
||||
if actual_type == expected_type {
|
||||
return JsonB(json!({ "response": "success" }));
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach here, validation failed. Now we build the specific error.
|
||||
let (message, cause, context) =
|
||||
if let Some(actual_type) = instance.get("type").and_then(|v| v.as_str()) {
|
||||
// This handles the case where the type is a string but doesn't match.
|
||||
(
|
||||
format!("Instance type '{}' does not match expected type '{}' derived from schema ID", actual_type, expected_type),
|
||||
json!({ "expected": expected_type, "actual": actual_type }),
|
||||
json!(actual_type)
|
||||
)
|
||||
} else {
|
||||
// This handles the case where 'type' is missing or not a string.
|
||||
(
|
||||
"Instance 'type' property is missing or not a string".to_string(),
|
||||
json!("The 'type' property must be a string and is required for this validation."),
|
||||
instance.get("type").unwrap_or(&Value::Null).clone()
|
||||
)
|
||||
};
|
||||
|
||||
JsonB(json!({
|
||||
"errors": [{
|
||||
"code": "TYPE_MISMATCH",
|
||||
"message": message,
|
||||
"details": {
|
||||
"path": "/type",
|
||||
"context": context,
|
||||
"cause": cause,
|
||||
"schema": schema_id
|
||||
}
|
||||
}]
|
||||
}))
|
||||
}
|
||||
|
||||
#[pg_extern(strict, parallel_safe)]
|
||||
fn validate_json_schema(schema_id: &str, instance: JsonB) -> JsonB {
|
||||
let cache = SCHEMA_CACHE.read().unwrap();
|
||||
@ -340,7 +387,16 @@ fn validate_json_schema(schema_id: &str, instance: JsonB) -> JsonB {
|
||||
Some(sch_index) => {
|
||||
let instance_value: Value = instance.0;
|
||||
match cache.schemas.validate(&instance_value, *sch_index) {
|
||||
Ok(_) => JsonB(json!({ "response": "success" })),
|
||||
Ok(_) => {
|
||||
// After standard validation, perform custom type check if it's a Type schema
|
||||
if let Some(&schema_type) = cache.id_to_type.get(schema_id) {
|
||||
if schema_type == SchemaType::Type {
|
||||
return validate_type_against_schema_id(&instance_value, schema_id);
|
||||
}
|
||||
}
|
||||
// For non-Type schemas, or if type not found (shouldn't happen), success.
|
||||
JsonB(json!({ "response": "success" }))
|
||||
}
|
||||
Err(validation_error) => {
|
||||
let mut error_list = Vec::new();
|
||||
collect_errors(&validation_error, &mut error_list);
|
||||
@ -961,6 +1017,7 @@ fn clear_json_schemas() -> JsonB {
|
||||
*cache = BoonCache {
|
||||
schemas: Schemas::new(),
|
||||
id_to_index: HashMap::new(),
|
||||
id_to_type: HashMap::new(),
|
||||
};
|
||||
JsonB(json!({ "response": "success" }))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user