Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add the validate utils #32

Merged
merged 12 commits into from
Jul 20, 2023
142 changes: 142 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,64 @@ fn jsonschema_is_valid(schema: Json) -> bool {
}
}

#[pg_extern(immutable, strict)]
fn validate_json_schema(schema: Json, instance: Json) -> bool {
Copy link
Collaborator

@olirice olirice Jul 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the linked issue I thought the goal was to produce functions that return an array of strings that described and errors that are present.

These two functions have identical signatures and behavior to json_matches_schema and jsonb_matches_schema but have raise notices that list the errors.

If that is the goal, why not update the existing two functions? e.g. is it a performance problem? if so, how big is the penalty?

If it is something like performance, an optional argument to the existing functions like on_error_raise that takes one of nothing (default), notice, or exception would be preferred to adding new functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there is performance problem.
From the doc, the jsonschema::is_valid is much faster than validate function.

Anyhow, I removed the new functions, and updated the existing functions as recommended.
Please re-check it. @olirice

let compiled = match jsonschema::JSONSchema::compile(&schema.0) {
Ok(c) => c,
Err(e) => {
// Only call notice! for a non empty instance_path
if e.instance_path.last().is_some() {
notice!(
"Invalid JSON schema at path: {}",
e.instance_path.to_string()
);
}
return false;
}
};

let is_valid = match compiled.validate(&instance.0) {
Ok(_) => true,
Err(e) => {
let _ = e
.into_iter()
.for_each(|e| notice!("Invalid instance {} at {}", e.instance, e.instance_path));
false
}
};

is_valid
}

#[pg_extern(immutable, strict)]
fn validate_jsonb_schema(schema: Json, instance: JsonB) -> bool {
let compiled = match jsonschema::JSONSchema::compile(&schema.0) {
Ok(c) => c,
Err(e) => {
// Only call notice! for a non empty instance_path
if e.instance_path.last().is_some() {
notice!(
"Invalid JSON schema at path: {}",
e.instance_path.to_string()
);
}
return false;
}
};

let is_valid = match compiled.validate(&instance.0) {
Ok(_) => true,
Err(e) => {
let _ = e
.into_iter()
.for_each(|e| notice!("Invalid instance {} at {}", e.instance, e.instance_path));
false
}
};

is_valid
}

#[pg_schema]
#[cfg(any(test, feature = "pg_test"))]
mod tests {
Expand Down Expand Up @@ -132,6 +190,90 @@ mod tests {
"type": "obj"
}))));
}

#[pg_test]
fn test_json_validates_schema_rs() {
let max_length: i32 = 5;
assert!(crate::validate_json_schema(
Json(json!({ "maxLength": max_length })),
Json(json!("foo")),
));
}

#[pg_test]
fn test_json_not_validates_schema_rs() {
let max_length: i32 = 5;
assert!(!crate::validate_json_schema(
Json(json!({ "maxLength": max_length })),
Json(json!("foobar")),
));
}

#[pg_test]
fn test_jsonb_validates_schema_rs() {
let max_length: i32 = 5;
assert!(crate::validate_jsonb_schema(
Json(json!({ "maxLength": max_length })),
JsonB(json!("foo")),
));
}

#[pg_test]
fn test_jsonb_not_validates_schema_rs() {
let max_length: i32 = 5;
assert!(!crate::validate_jsonb_schema(
Json(json!({ "maxLength": max_length })),
JsonB(json!("foobar")),
));
}

#[pg_test]
fn test_json_validates_schema_spi() {
let result = Spi::get_one::<bool>(
r#"
select validate_json_schema('{"type": "object"}', '{}')
"#,
)
.unwrap()
.unwrap();
assert!(result);
}

#[pg_test]
fn test_json_not_validates_schema_spi() {
let result = Spi::get_one::<bool>(
r#"
select validate_json_schema('{"type": "object"}', '1')
"#,
)
.unwrap()
.unwrap();
assert!(!result);
}

#[pg_test]
fn test_jsonb_validates_schema_spi() {
let result = Spi::get_one::<bool>(
r#"
select validate_jsonb_schema('{"type": "object"}', '{}')
"#,
)
.unwrap()
.unwrap();
assert!(result);
}

#[pg_test]
fn test_jsonb_not_validates_schema_spi() {
let result = Spi::get_one::<bool>(
r#"
select validate_jsonb_schema('{"type": "object"}', '1')
"#,
)
.unwrap()
.unwrap();
assert!(!result);
}
}

#[cfg(test)]
Expand Down