Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e55786e3e | |||
| 6520413069 | |||
| b97879ff61 | |||
| ea0b139f87 | |||
| dccaa0a46e | |||
| 441597e604 |
49
Cargo.lock
generated
49
Cargo.lock
generated
@ -362,6 +362,15 @@ version = "0.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codepage"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48f68d061bc2828ae826206326e61251aca94c1e4a5305cf52d9138639c918b4"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -418,6 +427,15 @@ version = "1.15.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encoding_rs"
|
||||||
|
version = "0.8.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enum-map"
|
name = "enum-map"
|
||||||
version = "2.7.3"
|
version = "2.7.3"
|
||||||
@ -1106,9 +1124,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pgrx"
|
name = "pgrx"
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e1b41219b12cfcaa5d58f946a7ff1e7ddf0a4f7f930a7cdab612916e8a12c64"
|
checksum = "bab5bc1d60d3bc3c966d307a3c7313b1ebfb49a0ec183be3f1a057df0bcc9988"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-traits",
|
"atomic-traits",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
@ -1130,9 +1148,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pgrx-bindgen"
|
name = "pgrx-bindgen"
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6afcef51e801bb18662716f1c524cedfb7943844593171734fe4d3a94c9afa12"
|
checksum = "9804b74c211a9edd550cd974718f8cc407dec50d8e9cafb906e0b042ba434af0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cc",
|
"cc",
|
||||||
@ -1149,9 +1167,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pgrx-macros"
|
name = "pgrx-macros"
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "729af3e6954d2f76230d700efd8606121f13f71f800e5c76173add2c02097948"
|
checksum = "f230769493bf567f137de23264d604d267dd72b8a77c596528e43cf423c6208e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pgrx-sql-entity-graph",
|
"pgrx-sql-entity-graph",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -1161,11 +1179,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pgrx-pg-config"
|
name = "pgrx-pg-config"
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "116e33a329f3fac976b5f3150f14f2612735dfc56a15cb0a0800f25a3bd90aa7"
|
checksum = "49b64c071c2a46a19ab4521120a25b02b598f4abf6e9b4b1769a7922edeee3de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
|
"codepage",
|
||||||
|
"encoding_rs",
|
||||||
"eyre",
|
"eyre",
|
||||||
"home",
|
"home",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
@ -1175,13 +1195,14 @@ dependencies = [
|
|||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"toml",
|
"toml",
|
||||||
"url",
|
"url",
|
||||||
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pgrx-pg-sys"
|
name = "pgrx-pg-sys"
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd074044513f1f7fc63fd1ed0117ad0fbe690ef1b445f6d72b92e611b3846490"
|
checksum = "fcbfa98ec7a90252d13a78ac666541173dbb01a2fc1ba20131db6490c0711125"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cee-scape",
|
"cee-scape",
|
||||||
"libc",
|
"libc",
|
||||||
@ -1194,9 +1215,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pgrx-sql-entity-graph"
|
name = "pgrx-sql-entity-graph"
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0eb73c4b916d4abb422fff66c2606c46bf4b99136209306836e89766a8d49cd"
|
checksum = "e79bbf5a33cff6cfdc6dda3a976cd931c995eaa2c073a7c59b8f8fe8f6faa073"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"eyre",
|
"eyre",
|
||||||
@ -1210,9 +1231,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pgrx-tests"
|
name = "pgrx-tests"
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aba4e9a97fd148c9f65cf0c56d33a4cde4deb9941f5c0d914a39148e8148a7a6"
|
checksum = "9791c709882f3af9545bcca71670fdd82768f67a428b416b6210eae3773dbd0d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap-cargo",
|
"clap-cargo",
|
||||||
"eyre",
|
"eyre",
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "jspg"
|
name = "jspg"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pgrx = "0.14.0"
|
pgrx = "0.15.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
boon = "0.6.1"
|
boon = "0.6.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pgrx-tests = "0.14.0"
|
pgrx-tests = "0.15.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "lib"]
|
crate-type = ["cdylib", "lib"]
|
||||||
|
|||||||
4
flow
4
flow
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Flows
|
# Flows
|
||||||
source ./flows/base
|
source ./flows/base
|
||||||
@ -11,7 +11,7 @@ source ./flows/rust
|
|||||||
POSTGRES_VERSION="17"
|
POSTGRES_VERSION="17"
|
||||||
POSTGRES_CONFIG_PATH="/opt/homebrew/opt/postgresql@${POSTGRES_VERSION}/bin/pg_config"
|
POSTGRES_CONFIG_PATH="/opt/homebrew/opt/postgresql@${POSTGRES_VERSION}/bin/pg_config"
|
||||||
DEPENDENCIES+=(icu4c pkg-config "postgresql@${POSTGRES_VERSION}")
|
DEPENDENCIES+=(icu4c pkg-config "postgresql@${POSTGRES_VERSION}")
|
||||||
CARGO_DEPENDENCIES=(cargo-pgrx==0.14.0)
|
CARGO_DEPENDENCIES=(cargo-pgrx==0.15.0)
|
||||||
GITEA_ORGANIZATION="cellular"
|
GITEA_ORGANIZATION="cellular"
|
||||||
GITEA_REPOSITORY="jspg"
|
GITEA_REPOSITORY="jspg"
|
||||||
|
|
||||||
|
|||||||
267
src/lib.rs
267
src/lib.rs
@ -31,65 +31,225 @@ lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[pg_extern(strict)]
|
#[pg_extern(strict)]
|
||||||
fn cache_json_schema(schema_id: &str, schema: JsonB, strict: bool) -> JsonB {
|
fn cache_json_schemas(types: JsonB, puncs: JsonB) -> JsonB {
|
||||||
let mut cache = SCHEMA_CACHE.write().unwrap();
|
let mut cache = SCHEMA_CACHE.write().unwrap();
|
||||||
let mut schema_value: Value = schema.0;
|
let types_value: Value = types.0;
|
||||||
let schema_path = format!("urn:{}", schema_id);
|
let puncs_value: Value = puncs.0;
|
||||||
|
|
||||||
// Apply strict validation to all objects in the schema if requested
|
// Clear existing cache
|
||||||
if strict {
|
*cache = BoonCache {
|
||||||
apply_strict_validation(&mut schema_value);
|
schemas: Schemas::new(),
|
||||||
}
|
id_to_index: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
// Create the boon compiler and enable format assertions
|
// Create the boon compiler and enable format assertions
|
||||||
let mut compiler = Compiler::new();
|
let mut compiler = Compiler::new();
|
||||||
compiler.enable_format_assertions();
|
compiler.enable_format_assertions();
|
||||||
|
|
||||||
// Use schema_path when adding the resource
|
let mut errors = Vec::new();
|
||||||
if let Err(e) = compiler.add_resource(&schema_path, schema_value.clone()) {
|
|
||||||
return JsonB(json!({
|
// Track all schema IDs for compilation
|
||||||
"errors": [{
|
let mut all_schema_ids = Vec::new();
|
||||||
"code": "SCHEMA_RESOURCE_ADD_FAILED",
|
|
||||||
"message": format!("Failed to add schema resource '{}'", schema_id),
|
// Phase 1: Add all type schemas as resources (these are referenced by puncs)
|
||||||
"details": {
|
// Types are never strict - they're reusable building blocks
|
||||||
"schema": schema_id,
|
if let Some(types_array) = types_value.as_array() {
|
||||||
"cause": format!("{}", e)
|
for type_row in types_array {
|
||||||
|
if let Some(type_obj) = type_row.as_object() {
|
||||||
|
if let (Some(type_name), Some(schemas_raw)) = (
|
||||||
|
type_obj.get("name").and_then(|v| v.as_str()),
|
||||||
|
type_obj.get("schemas")
|
||||||
|
) {
|
||||||
|
// Parse the schemas JSONB field
|
||||||
|
if let Some(schemas_array) = schemas_raw.as_array() {
|
||||||
|
for schema_def in schemas_array {
|
||||||
|
if let Some(schema_id) = schema_def.get("$id").and_then(|v| v.as_str()) {
|
||||||
|
if let Err(e) = add_schema_resource(&mut compiler, schema_id, schema_def.clone(), false, &mut errors) {
|
||||||
|
errors.push(json!({
|
||||||
|
"code": "TYPE_SCHEMA_RESOURCE_FAILED",
|
||||||
|
"message": format!("Failed to add schema resource '{}' for type '{}'", schema_id, type_name),
|
||||||
|
"details": {
|
||||||
|
"type_name": type_name,
|
||||||
|
"schema_id": schema_id,
|
||||||
|
"cause": format!("{}", e)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
all_schema_ids.push(schema_id.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}]
|
}
|
||||||
}));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use schema_path when compiling
|
// Phase 2: Add all punc schemas as resources (these may reference type schemas)
|
||||||
match compiler.compile(&schema_path, &mut cache.schemas) {
|
// Each punc gets strict validation based on its public field
|
||||||
Ok(sch_index) => {
|
if let Some(puncs_array) = puncs_value.as_array() {
|
||||||
// Store the index using the original schema_id as the key
|
for punc_row in puncs_array {
|
||||||
cache.id_to_index.insert(schema_id.to_string(), sch_index);
|
if let Some(punc_obj) = punc_row.as_object() {
|
||||||
JsonB(json!({ "response": "success" }))
|
if let Some(punc_name) = punc_obj.get("name").and_then(|v| v.as_str()) {
|
||||||
}
|
// Get the strict setting for this specific punc (public = strict)
|
||||||
Err(e) => {
|
let punc_strict = punc_obj.get("public")
|
||||||
let errors = match &e {
|
.and_then(|v| v.as_bool())
|
||||||
CompileError::ValidationError { url: _url, src } => {
|
.unwrap_or(false);
|
||||||
// Collect leaf errors from the meta-schema validation failure
|
|
||||||
let mut error_list = Vec::new();
|
// Add punc local schemas as resources (from schemas field) - use $id directly (universal)
|
||||||
collect_errors(src, &mut error_list);
|
if let Some(schemas_raw) = punc_obj.get("schemas") {
|
||||||
// Filter and format errors properly - no instance for schema compilation
|
if let Some(schemas_array) = schemas_raw.as_array() {
|
||||||
format_errors(error_list, &schema_value, schema_id)
|
for schema_def in schemas_array {
|
||||||
}
|
if let Some(schema_id) = schema_def.get("$id").and_then(|v| v.as_str()) {
|
||||||
_ => {
|
if let Err(e) = add_schema_resource(&mut compiler, schema_id, schema_def.clone(), punc_strict, &mut errors) {
|
||||||
// Other compilation errors
|
errors.push(json!({
|
||||||
vec![json!({
|
"code": "PUNC_LOCAL_SCHEMA_RESOURCE_FAILED",
|
||||||
"code": "SCHEMA_COMPILATION_FAILED",
|
"message": format!("Failed to add local schema resource '{}' for punc '{}'", schema_id, punc_name),
|
||||||
"message": format!("Schema '{}' compilation failed", schema_id),
|
"details": {
|
||||||
"details": {
|
"punc_name": punc_name,
|
||||||
"schema": schema_id,
|
"schema_id": schema_id,
|
||||||
"cause": format!("{:?}", e)
|
"cause": format!("{}", e)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
all_schema_ids.push(schema_id.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})]
|
}
|
||||||
|
|
||||||
|
// Add request schema as resource if present - use {punc_name}.request
|
||||||
|
if let Some(request_schema) = punc_obj.get("request") {
|
||||||
|
if !request_schema.is_null() {
|
||||||
|
let request_schema_id = format!("{}.request", punc_name);
|
||||||
|
if let Err(e) = add_schema_resource(&mut compiler, &request_schema_id, request_schema.clone(), punc_strict, &mut errors) {
|
||||||
|
errors.push(json!({
|
||||||
|
"code": "PUNC_REQUEST_SCHEMA_RESOURCE_FAILED",
|
||||||
|
"message": format!("Failed to add request schema resource for punc '{}'", punc_name),
|
||||||
|
"details": {
|
||||||
|
"punc_name": punc_name,
|
||||||
|
"schema_id": request_schema_id,
|
||||||
|
"cause": format!("{}", e)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
all_schema_ids.push(request_schema_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add response schema as resource if present - use {punc_name}.response
|
||||||
|
if let Some(response_schema) = punc_obj.get("response") {
|
||||||
|
if !response_schema.is_null() {
|
||||||
|
let response_schema_id = format!("{}.response", punc_name);
|
||||||
|
if let Err(e) = add_schema_resource(&mut compiler, &response_schema_id, response_schema.clone(), punc_strict, &mut errors) {
|
||||||
|
errors.push(json!({
|
||||||
|
"code": "PUNC_RESPONSE_SCHEMA_RESOURCE_FAILED",
|
||||||
|
"message": format!("Failed to add response schema resource for punc '{}'", punc_name),
|
||||||
|
"details": {
|
||||||
|
"punc_name": punc_name,
|
||||||
|
"schema_id": response_schema_id,
|
||||||
|
"cause": format!("{}", e)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
all_schema_ids.push(response_schema_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
JsonB(json!({ "errors": errors }))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 3: Compile all schemas now that all resources are added
|
||||||
|
if !errors.is_empty() {
|
||||||
|
// If we had errors adding resources, don't attempt compilation
|
||||||
|
return JsonB(json!({ "errors": errors }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(_) = compile_all_schemas(&mut compiler, &mut cache, &all_schema_ids, &mut errors) {
|
||||||
|
// compile_all_schemas already adds errors to the errors vector
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.is_empty() {
|
||||||
|
JsonB(json!({ "response": "success" }))
|
||||||
|
} else {
|
||||||
|
JsonB(json!({ "errors": errors }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to add a schema resource (without compiling)
|
||||||
|
fn add_schema_resource(
|
||||||
|
compiler: &mut Compiler,
|
||||||
|
schema_id: &str,
|
||||||
|
mut schema_value: Value,
|
||||||
|
strict: bool,
|
||||||
|
errors: &mut Vec<Value>
|
||||||
|
) -> Result<(), String> {
|
||||||
|
// Apply strict validation to all objects in the schema if requested
|
||||||
|
if strict {
|
||||||
|
apply_strict_validation(&mut schema_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use schema_id directly - simple IDs like "entity", "user", "punc.request"
|
||||||
|
if let Err(e) = compiler.add_resource(schema_id, schema_value.clone()) {
|
||||||
|
errors.push(json!({
|
||||||
|
"code": "SCHEMA_RESOURCE_FAILED",
|
||||||
|
"message": format!("Failed to add schema resource '{}'", schema_id),
|
||||||
|
"details": {
|
||||||
|
"schema": schema_id,
|
||||||
|
"cause": format!("{}", e)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return Err(format!("Failed to add schema resource: {}", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to compile all added resources
|
||||||
|
fn compile_all_schemas(
|
||||||
|
compiler: &mut Compiler,
|
||||||
|
cache: &mut BoonCache,
|
||||||
|
schema_ids: &[String],
|
||||||
|
errors: &mut Vec<Value>
|
||||||
|
) -> Result<(), String> {
|
||||||
|
for schema_id in schema_ids {
|
||||||
|
match compiler.compile(schema_id, &mut cache.schemas) {
|
||||||
|
Ok(sch_index) => {
|
||||||
|
// Store the index using the original schema_id as the key
|
||||||
|
cache.id_to_index.insert(schema_id.to_string(), sch_index);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
match &e {
|
||||||
|
CompileError::ValidationError { url: _url, src } => {
|
||||||
|
// Collect leaf errors from the meta-schema validation failure
|
||||||
|
let mut error_list = Vec::new();
|
||||||
|
collect_errors(src, &mut error_list);
|
||||||
|
// Get schema value for error formatting - we'll need to reconstruct or store it
|
||||||
|
let schema_value = json!({}); // Placeholder - we don't have the original value here
|
||||||
|
let formatted_errors = format_errors(error_list, &schema_value, schema_id);
|
||||||
|
errors.extend(formatted_errors);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Other compilation errors
|
||||||
|
errors.push(json!({
|
||||||
|
"code": "SCHEMA_COMPILATION_FAILED",
|
||||||
|
"message": format!("Schema '{}' compilation failed", schema_id),
|
||||||
|
"details": {
|
||||||
|
"schema": schema_id,
|
||||||
|
"cause": format!("{:?}", e)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return Err(format!("Schema compilation failed: {:?}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to apply strict validation to a schema
|
// Helper function to apply strict validation to a schema
|
||||||
@ -135,7 +295,7 @@ fn apply_strict_validation_recursive(schema: &mut Value, inside_conditional: boo
|
|||||||
#[pg_extern(strict, parallel_safe)]
|
#[pg_extern(strict, parallel_safe)]
|
||||||
fn validate_json_schema(schema_id: &str, instance: JsonB) -> JsonB {
|
fn validate_json_schema(schema_id: &str, instance: JsonB) -> JsonB {
|
||||||
let cache = SCHEMA_CACHE.read().unwrap();
|
let cache = SCHEMA_CACHE.read().unwrap();
|
||||||
// Lookup uses the original schema_id
|
// Lookup uses the original schema_id - schemas should always be available after bulk caching
|
||||||
match cache.id_to_index.get(schema_id) {
|
match cache.id_to_index.get(schema_id) {
|
||||||
None => JsonB(json!({
|
None => JsonB(json!({
|
||||||
"errors": [{
|
"errors": [{
|
||||||
@ -143,7 +303,7 @@ fn validate_json_schema(schema_id: &str, instance: JsonB) -> JsonB {
|
|||||||
"message": format!("Schema '{}' not found in cache", schema_id),
|
"message": format!("Schema '{}' not found in cache", schema_id),
|
||||||
"details": {
|
"details": {
|
||||||
"schema": schema_id,
|
"schema": schema_id,
|
||||||
"cause": "Schema must be cached before validation"
|
"cause": "Schema was not found in bulk cache - ensure cache_json_schemas was called"
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
})),
|
})),
|
||||||
@ -157,7 +317,11 @@ fn validate_json_schema(schema_id: &str, instance: JsonB) -> JsonB {
|
|||||||
let errors = format_errors(error_list, &instance_value, schema_id);
|
let errors = format_errors(error_list, &instance_value, schema_id);
|
||||||
// Filter out FALSE_SCHEMA errors if there are other validation errors
|
// Filter out FALSE_SCHEMA errors if there are other validation errors
|
||||||
let filtered_errors = filter_false_schema_errors(errors);
|
let filtered_errors = filter_false_schema_errors(errors);
|
||||||
JsonB(json!({ "errors": filtered_errors }))
|
if filtered_errors.is_empty() {
|
||||||
|
JsonB(json!({ "response": "success" }))
|
||||||
|
} else {
|
||||||
|
JsonB(json!({ "errors": filtered_errors }))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -486,6 +650,13 @@ fn handle_additional_items_error(base_path: &str, got: usize) -> Vec<Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_format_error(base_path: &str, want: &str, got: &Cow<Value>, err: &Box<dyn std::error::Error>) -> Vec<Error> {
|
fn handle_format_error(base_path: &str, want: &str, got: &Cow<Value>, err: &Box<dyn std::error::Error>) -> Vec<Error> {
|
||||||
|
// If the value is an empty string, skip format validation.
|
||||||
|
if let Value::String(s) = got.as_ref() {
|
||||||
|
if s.is_empty() {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vec![Error {
|
vec![Error {
|
||||||
path: base_path.to_string(),
|
path: base_path.to_string(),
|
||||||
code: "FORMAT_INVALID".to_string(),
|
code: "FORMAT_INVALID".to_string(),
|
||||||
|
|||||||
2146
src/tests.rs
2146
src/tests.rs
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user