From e2d596b974846e90eeaba01209d2f86868741901 Mon Sep 17 00:00:00 2001 From: Victor Moreno Date: Thu, 8 Jan 2026 22:38:15 +0000 Subject: [PATCH 1/3] expose json schema with resolved types functionality in cli --- cedar-policy-cli/CHANGELOG.md | 1 + cedar-policy-cli/src/lib.rs | 41 ++++ cedar-policy-cli/tests/sample.rs | 181 ++++++++++++++++++ .../src/validator/json_schema.rs | 15 +- cedar-policy/src/api.rs | 44 +++++ cedar-policy/src/ffi/convert.rs | 56 ++---- 6 files changed, 294 insertions(+), 44 deletions(-) diff --git a/cedar-policy-cli/CHANGELOG.md b/cedar-policy-cli/CHANGELOG.md index dea7b65d0..f443d65a4 100644 --- a/cedar-policy-cli/CHANGELOG.md +++ b/cedar-policy-cli/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to Cedar CLI tool will be documented in this file. Changes to the Cedar language, which are likely to affect users of the CLI, are documented separately in the [primary changelog](../cedar-policy/CHANGELOG.md). ## Unreleased +- Added a new option when converting cedar schemas to JSON that allows the caller to get back a JSON schema where all types are resolved to commontype or entity (never entityOrCommon). ## 4.8.2 diff --git a/cedar-policy-cli/src/lib.rs b/cedar-policy-cli/src/lib.rs index b03b43022..8cc309030 100644 --- a/cedar-policy-cli/src/lib.rs +++ b/cedar-policy-cli/src/lib.rs @@ -170,6 +170,8 @@ pub enum SchemaTranslationDirection { JsonToCedar, /// Cedar schema syntax -> JSON CedarToJson, + /// Cedar schema syntax -> JSON with all types resolved to entity or common + CedarToJsonWithResolvedTypes, } #[derive(Debug, Default, Clone, Copy, ValueEnum)] @@ -1251,10 +1253,49 @@ fn translate_schema_to_json(cedar_src: impl AsRef) -> Result { Ok(output) } +fn translate_schema_to_json_with_resolved_types(cedar_src: impl AsRef) -> Result { + match cedar_policy::schema_str_to_resolved_fragment(cedar_src.as_ref()) { + Ok((fully_resolved_fragment, warnings)) => { + // Output warnings to stderr + for warning in &warnings { + eprintln!("{warning:?}"); + } + + // Serialize to JSON with pretty formatting + let json_value = serde_json::to_value(&fully_resolved_fragment) + .into_diagnostic() + .wrap_err("Failed to serialize resolved schema to JSON")?; + + serde_json::to_string_pretty(&json_value).into_diagnostic() + } + Err(reports) => { + // Output errors to stderr + for report in &reports { + eprintln!("{report:?}"); + } + + // Check if this is a parsing error by looking at the error messages + let has_parse_error = reports.iter().any(|r| { + let error_str = format!("{r:?}"); + error_str.contains("parse") || error_str.contains("unexpected token") + }); + + if has_parse_error { + Err(miette::miette!("Failed to parse Cedar schema")) + } else { + Err(miette::miette!("Failed to resolve schema types")) + } + } + } +} + fn translate_schema_inner(args: &TranslateSchemaArgs) -> Result { let translate = match args.direction { SchemaTranslationDirection::JsonToCedar => translate_schema_to_cedar, SchemaTranslationDirection::CedarToJson => translate_schema_to_json, + SchemaTranslationDirection::CedarToJsonWithResolvedTypes => { + translate_schema_to_json_with_resolved_types + } }; read_from_file_or_stdin(args.input_file.as_ref(), "schema").and_then(translate) } diff --git a/cedar-policy-cli/tests/sample.rs b/cedar-policy-cli/tests/sample.rs index 32a1b360a..c417ad6b3 100644 --- a/cedar-policy-cli/tests/sample.rs +++ b/cedar-policy-cli/tests/sample.rs @@ -1274,6 +1274,187 @@ fn test_translate_schema() { .code(0); } +#[test] +fn test_translate_schema_with_resolved_types() { + let cedar_filename = "sample-data/tiny_sandboxes/translate-schema/tinytodo.cedarschema"; + + // Test cedar -> json with resolved types + let output = cargo::cargo_bin_cmd!("cedar") + .arg("translate-schema") + .arg("--direction") + .arg("cedar-to-json-with-resolved-types") + .arg("-s") + .arg(cedar_filename) + .assert() + .code(0); + + let json_output = + std::str::from_utf8(&output.get_output().stdout).expect("output should be decodable"); + + // Parse the JSON to ensure it's valid + let parsed_json: serde_json::Value = + serde_json::from_str(json_output).expect("output should be valid JSON"); + + // Verify that the output contains resolved types (no "EntityOrCommon" strings) + let json_str = serde_json::to_string(&parsed_json).unwrap(); + assert!( + !json_str.contains("EntityOrCommon"), + "Output should not contain unresolved EntityOrCommon types: {}", + json_str + ); + + // Verify that the output contains expected entity types + assert!( + json_str.contains("List"), + "Output should contain List entity type" + ); + assert!( + json_str.contains("User"), + "Output should contain User entity type" + ); + assert!( + json_str.contains("Team"), + "Output should contain Team entity type" + ); + assert!( + json_str.contains("Application"), + "Output should contain Application entity type" + ); +} +#[test] + +fn test_translate_schema_with_resolved_types_stdin() { + // Test with stdin input + let stdin_output = cargo::cargo_bin_cmd!("cedar") + .arg("translate-schema") + .arg("--direction") + .arg("cedar-to-json-with-resolved-types") + .write_stdin("entity User; action \"view\";") + .assert() + .code(0); + + let stdin_json = std::str::from_utf8(&stdin_output.get_output().stdout) + .expect("stdin output should be decodable"); + + // Parse and verify stdin JSON + let stdin_parsed: serde_json::Value = + serde_json::from_str(stdin_json).expect("stdin output should be valid JSON"); + + let stdin_json_str = serde_json::to_string(&stdin_parsed).unwrap(); + assert!( + !stdin_json_str.contains("EntityOrCommon"), + "Stdin output should not contain unresolved EntityOrCommon types" + ); + assert!( + stdin_json_str.contains("User"), + "Stdin output should contain User entity type" + ); +} + +#[test] +fn test_translate_schema_with_resolved_types_invalid_input() { + // Test with invalid Cedar schema syntax + let invalid_output = cargo::cargo_bin_cmd!("cedar") + .arg("translate-schema") + .arg("--direction") + .arg("cedar-to-json-with-resolved-types") + .write_stdin("invalid cedar syntax {") + .assert() + .code(1); // Should fail with non-zero exit code + + let stderr = std::str::from_utf8(&invalid_output.get_output().stderr) + .expect("stderr should be decodable"); + + // Should contain error message about parsing failure + assert!( + stderr.contains("Failed to parse Cedar schema") || stderr.contains("parse"), + "Error message should indicate parsing failure: {}", + stderr + ); +} + +#[test] +fn test_translate_schema_with_resolved_types_warnings() { + // Create a schema that generates warnings (Shadowing a primitive type with an entity) + let schema_with_warnings = r#" + entity String; + "#; + + let output = cargo::cargo_bin_cmd!("cedar") + .arg("translate-schema") + .arg("--direction") + .arg("cedar-to-json-with-resolved-types") + .write_stdin(schema_with_warnings) + .assert() + .code(0); // Should succeed despite warnings + + let json_output = + std::str::from_utf8(&output.get_output().stdout).expect("output should be decodable"); + + // Should produce valid JSON + let _parsed_json: serde_json::Value = + serde_json::from_str(json_output).expect("output should be valid JSON"); + + // Warnings should be output to stderr (if any) + let _stderr = + std::str::from_utf8(&output.get_output().stderr).expect("stderr should be decodable"); + + assert!(_stderr.contains("The name `String` shadows a builtin Cedar name")); +} + +#[test] +fn test_translate_schema_with_resolved_types_file_errors() { + // Test with non-existent input file + let nonexistent_output = cargo::cargo_bin_cmd!("cedar") + .arg("translate-schema") + .arg("--direction") + .arg("cedar-to-json-with-resolved-types") + .arg("-s") + .arg("nonexistent-file.cedarschema") + .assert() + .code(1); // Should fail with non-zero exit code + + let stderr = std::str::from_utf8(&nonexistent_output.get_output().stderr) + .expect("stderr should be decodable"); + + // Should contain error message about file not found + assert!( + stderr.contains("nonexistent-file.cedarschema") || stderr.contains("No such file"), + "Error message should indicate file not found: {}", + stderr + ); +} + +#[test] +fn test_translate_schema_with_resolved_types_unresolvable_references() { + // Test with schema that has unresolvable type references + let unresolvable_schema = r#" + entity User = { "manager": Manager }; + // Manager entity type is not defined + action "view"; + "#; + + let output = cargo::cargo_bin_cmd!("cedar") + .arg("translate-schema") + .arg("--direction") + .arg("cedar-to-json-with-resolved-types") + .write_stdin(unresolvable_schema) + .assert() + .code(1); // Should fail with non-zero exit code + + let stderr = + std::str::from_utf8(&output.get_output().stderr).expect("stderr should be decodable"); + + // Should contain error message about unresolvable references + assert!( + stderr.contains("Failed to resolve schema types") + || stderr.contains("Manager") + || stderr.contains("resolve"), + "Error message should indicate unresolvable type reference: {}", + stderr + ); +} + #[rstest] fn visualize_entities_parses_as_dot( #[files("sample-data/**/entities.json")] diff --git a/cedar-policy-core/src/validator/json_schema.rs b/cedar-policy-core/src/validator/json_schema.rs index acd9e0b80..4f0e433bf 100644 --- a/cedar-policy-core/src/validator/json_schema.rs +++ b/cedar-policy-core/src/validator/json_schema.rs @@ -198,7 +198,8 @@ impl Fragment { } /// Convert this `Fragment` to a `Fragment` where all the - /// entity or common type references have been resolved + /// entity or common type references have been resolved. If the fragment contains any type + /// references that are not defined, this will return Err. pub fn to_internal_name_fragment_with_resolved_types( &self, ) -> std::result::Result, SchemaError> { @@ -218,7 +219,12 @@ impl Fragment { .collect(); for tyname in &primitives_as_internal_names { + // Add __cedar-prefixed primitives as commontypes all_defs.mark_as_defined_as_common_type(tyname.qualify_with(Some(&cedar_namespace))); + // Add aliases for primitive types in the empty namespace (so "String" resolves to "__cedar::String") + if !all_defs.is_defined_as_common(tyname) && !all_defs.is_defined_as_entity(tyname) { + all_defs.mark_as_defined_as_common_type(tyname.clone()); + } } // Add extension types in __cedar namespace and also without @@ -234,13 +240,6 @@ impl Fragment { } } - // Add aliases for primitive types in the empty namespace (so "String" resolves to "__cedar::String") - for tyname in &primitives_as_internal_names { - if !all_defs.is_defined_as_common(tyname) && !all_defs.is_defined_as_entity(tyname) { - all_defs.mark_as_defined_as_common_type(tyname.clone()); - } - } - // Step 1: Convert Fragment to Fragment let conditional_fragment = Fragment( self.0 diff --git a/cedar-policy/src/api.rs b/cedar-policy/src/api.rs index 575db763b..050299146 100644 --- a/cedar-policy/src/api.rs +++ b/cedar-policy/src/api.rs @@ -2137,6 +2137,50 @@ impl Schema { } } +/// Convert a Cedar schema string to a resolved fragment with types resolved. +/// +/// This function resolves ambiguous "`EntityOrCommon`" types to their specific +/// Entity or `CommonType` classifications using the schema's type definitions. +/// This is primarily meant to be used when working with schemas programmatically, +/// for example when creating a schema building UI. +/// +/// Returns Ok((fragment, warnings)) on success, or Err(errors) on failure. +/// Fails if there are any types in the schema that are unresolved. +pub fn schema_str_to_resolved_fragment( + schema_str: &str, +) -> Result< + ( + json_schema::Fragment, + Vec, + ), + Vec, +> { + use cedar_policy_core::validator::cedar_schema::parser::parse_cedar_schema_fragment; + + let (json_schema_fragment, warnings) = + match parse_cedar_schema_fragment(schema_str, Extensions::all_available()) { + Ok((json_schema, warnings)) => (json_schema, warnings), + Err(e) => { + return Err(vec![miette::Report::new(e)]); + } + }; + + let warnings_as_reports: Vec = warnings.map(miette::Report::new).collect(); + + // Use the new method from json_schema.rs to get the resolved fragment + let fully_resolved_fragment = + match json_schema_fragment.to_internal_name_fragment_with_resolved_types() { + Ok(fragment) => fragment, + Err(e) => { + let mut errors = warnings_as_reports; + errors.push(miette::Report::new(e)); + return Err(errors); + } + }; + + Ok((fully_resolved_fragment, warnings_as_reports)) +} + /// Contains the result of policy validation. /// /// The result includes the list of issues found by validation and whether validation succeeds or fails. diff --git a/cedar-policy/src/ffi/convert.rs b/cedar-policy/src/ffi/convert.rs index 87fdac6ea..948162469 100644 --- a/cedar-policy/src/ffi/convert.rs +++ b/cedar-policy/src/ffi/convert.rs @@ -21,9 +21,6 @@ use super::utils::JsonValueWithNoDuplicateKeys; use super::{DetailedError, Policy, Schema, Template}; use crate::api::{PolicySet, StringifiedPolicySet}; -use cedar_policy_core::{ - extensions::Extensions, validator::cedar_schema::parser::parse_cedar_schema_fragment, -}; use serde::{Deserialize, Serialize}; use std::str::FromStr; #[cfg(feature = "wasm")] @@ -191,41 +188,28 @@ pub fn schema_to_json(schema: Schema) -> SchemaToJsonAnswer { wasm_bindgen(js_name = "schemaToJsonWithResolvedTypes") )] pub fn schema_to_json_with_resolved_types(schema_str: &str) -> SchemaToJsonWithResolvedTypesAnswer { - let (json_schema_fragment, warnings) = - match parse_cedar_schema_fragment(schema_str, Extensions::all_available()) { - Ok((json_schema, warnings)) => (json_schema, warnings), - Err(e) => { - return SchemaToJsonWithResolvedTypesAnswer::Failure { - errors: vec![miette::Report::new(e).into()], - }; + match crate::api::schema_str_to_resolved_fragment(schema_str) { + Ok((fully_resolved_fragment, warnings)) => { + // Serialize the resolved Fragment to JSON + match serde_json::to_value(&fully_resolved_fragment) { + Ok(json) => SchemaToJsonWithResolvedTypesAnswer::Success { + json: json.into(), + warnings: warnings.into_iter().map(std::convert::Into::into).collect(), + }, + Err(e) => SchemaToJsonWithResolvedTypesAnswer::Failure { + errors: vec![DetailedError::from_str(&format!( + "JSON serialization failed: {e}" + )) + .unwrap_or_default()], + }, } - }; - - let warnings_as_detailed_errs: Vec = warnings.map(|w| (&w).into()).collect(); - - // Use the new method from json_schema.rs to get the resolved fragment - let fully_resolved_fragment = - match json_schema_fragment.to_internal_name_fragment_with_resolved_types() { - Ok(fragment) => fragment, - Err(e) => { - return SchemaToJsonWithResolvedTypesAnswer::Failure { - errors: vec![miette::Report::new(e).into()], - }; + } + Err(reports) => { + // Convert miette::Report to DetailedError + SchemaToJsonWithResolvedTypesAnswer::Failure { + errors: reports.into_iter().map(std::convert::Into::into).collect(), } - }; - - // Serialize the resolved Fragment to JSON - match serde_json::to_value(&fully_resolved_fragment) { - Ok(json) => SchemaToJsonWithResolvedTypesAnswer::Success { - json: json.into(), - warnings: warnings_as_detailed_errs, - }, - Err(e) => SchemaToJsonWithResolvedTypesAnswer::Failure { - errors: vec![ - DetailedError::from_str(&format!("JSON serialization failed: {e}")) - .unwrap_or_default(), - ], - }, + } } } From 0dad8737f137e6e20d2682fbe7727021d8366a1e Mon Sep 17 00:00:00 2001 From: Victor Moreno Date: Fri, 9 Jan 2026 19:37:49 +0000 Subject: [PATCH 2/3] refactor to hide core types --- cedar-policy-cli/src/lib.rs | 30 ++----- cedar-policy-cli/tests/sample.rs | 2 +- cedar-policy/src/api.rs | 133 +++++++++++++++++++++++++------ cedar-policy/src/ffi/convert.rs | 27 ++----- 4 files changed, 125 insertions(+), 67 deletions(-) diff --git a/cedar-policy-cli/src/lib.rs b/cedar-policy-cli/src/lib.rs index 8cc309030..1183f18ca 100644 --- a/cedar-policy-cli/src/lib.rs +++ b/cedar-policy-cli/src/lib.rs @@ -1254,37 +1254,19 @@ fn translate_schema_to_json(cedar_src: impl AsRef) -> Result { } fn translate_schema_to_json_with_resolved_types(cedar_src: impl AsRef) -> Result { - match cedar_policy::schema_str_to_resolved_fragment(cedar_src.as_ref()) { - Ok((fully_resolved_fragment, warnings)) => { + match cedar_policy::schema_str_to_json_with_resolved_types(cedar_src.as_ref()) { + Ok((json_value, warnings)) => { // Output warnings to stderr for warning in &warnings { - eprintln!("{warning:?}"); + eprintln!("{warning}"); } // Serialize to JSON with pretty formatting - let json_value = serde_json::to_value(&fully_resolved_fragment) - .into_diagnostic() - .wrap_err("Failed to serialize resolved schema to JSON")?; - serde_json::to_string_pretty(&json_value).into_diagnostic() } - Err(reports) => { - // Output errors to stderr - for report in &reports { - eprintln!("{report:?}"); - } - - // Check if this is a parsing error by looking at the error messages - let has_parse_error = reports.iter().any(|r| { - let error_str = format!("{r:?}"); - error_str.contains("parse") || error_str.contains("unexpected token") - }); - - if has_parse_error { - Err(miette::miette!("Failed to parse Cedar schema")) - } else { - Err(miette::miette!("Failed to resolve schema types")) - } + Err(error) => { + // Convert CedarSchemaError to miette::Report to preserve all diagnostic information + Err(miette::Report::new(error)) } } } diff --git a/cedar-policy-cli/tests/sample.rs b/cedar-policy-cli/tests/sample.rs index c417ad6b3..e6bdfbce6 100644 --- a/cedar-policy-cli/tests/sample.rs +++ b/cedar-policy-cli/tests/sample.rs @@ -1367,7 +1367,7 @@ fn test_translate_schema_with_resolved_types_invalid_input() { // Should contain error message about parsing failure assert!( - stderr.contains("Failed to parse Cedar schema") || stderr.contains("parse"), + stderr.contains("error parsing schema"), "Error message should indicate parsing failure: {}", stderr ); diff --git a/cedar-policy/src/api.rs b/cedar-policy/src/api.rs index 050299146..67d5c58a8 100644 --- a/cedar-policy/src/api.rs +++ b/cedar-policy/src/api.rs @@ -2137,53 +2137,140 @@ impl Schema { } } -/// Convert a Cedar schema string to a resolved fragment with types resolved. +/// Convert a Cedar schema string to JSON format with resolved types. /// /// This function resolves ambiguous "`EntityOrCommon`" types to their specific /// Entity or `CommonType` classifications using the schema's type definitions. /// This is primarily meant to be used when working with schemas programmatically, /// for example when creating a schema building UI. /// -/// Returns Ok((fragment, warnings)) on success, or Err(errors) on failure. +/// Returns `Ok((json_value`, warnings)) on success, or Err(error) on failure. /// Fails if there are any types in the schema that are unresolved. -pub fn schema_str_to_resolved_fragment( +pub fn schema_str_to_json_with_resolved_types( schema_str: &str, -) -> Result< - ( - json_schema::Fragment, - Vec, - ), - Vec, -> { - use cedar_policy_core::validator::cedar_schema::parser::parse_cedar_schema_fragment; - +) -> Result<(serde_json::Value, Vec), CedarSchemaError> { + // Parse the Cedar schema string into a fragment let (json_schema_fragment, warnings) = - match parse_cedar_schema_fragment(schema_str, Extensions::all_available()) { - Ok((json_schema, warnings)) => (json_schema, warnings), - Err(e) => { - return Err(vec![miette::Report::new(e)]); - } - }; + json_schema::Fragment::from_cedarschema_str(schema_str, Extensions::all_available()) + .map_err( + |e: cedar_policy_core::validator::CedarSchemaError| -> CedarSchemaError { + e.into() + }, + )?; - let warnings_as_reports: Vec = warnings.map(miette::Report::new).collect(); + let warnings_as_schema_warnings: Vec = warnings.collect(); // Use the new method from json_schema.rs to get the resolved fragment let fully_resolved_fragment = match json_schema_fragment.to_internal_name_fragment_with_resolved_types() { Ok(fragment) => fragment, Err(e) => { - let mut errors = warnings_as_reports; - errors.push(miette::Report::new(e)); - return Err(errors); + // SchemaError can be directly converted to CedarSchemaError + return Err(e.into()); } }; - Ok((fully_resolved_fragment, warnings_as_reports)) + // Serialize the resolved fragment to JSON + let json_value = serde_json::to_value(&fully_resolved_fragment).map_err(|e| { + let schema_error = SchemaError::JsonSerialization( + cedar_policy_core::validator::schema_errors::JsonSerializationError::from(e), + ); + CedarSchemaError::Schema(schema_error) + })?; + + Ok((json_value, warnings_as_schema_warnings)) } /// Contains the result of policy validation. /// /// The result includes the list of issues found by validation and whether validation succeeds or fails. + +#[cfg(test)] +mod test_schema_str_to_json_with_resolved_types { + use super::*; + + #[test] + fn test_unresolved_type_error() { + let schema_str = r#"entity User = { "name": MyName };"#; + + let result = schema_str_to_json_with_resolved_types(schema_str); + + // Should return an error because MyName is not defined + match result { + Ok(_) => panic!("Expected error but got success - MyName should not be resolved"), + Err(CedarSchemaError::Schema(SchemaError::TypeNotDefined(type_not_defined_error))) => { + // Verify that the error message contains information about the undefined type "MyName" + let error_message = format!("{}", type_not_defined_error); + assert!( + error_message.contains("MyName"), + "Expected error message to contain 'MyName', but got: {}", + error_message + ); + + // Verify it's specifically about failing to resolve types + assert!( + error_message.contains("failed to resolve type"), + "Expected error message to mention 'failed to resolve type', but got: {}", + error_message + ); + } + Err(CedarSchemaError::Schema(other_schema_error)) => { + panic!( + "Expected TypeNotDefined error, but got different SchemaError: {:?}", + other_schema_error + ); + } + Err(CedarSchemaError::Parse(parse_error)) => { + panic!( + "Expected TypeNotDefined error, but got parse error: {:?}", + parse_error + ); + } + Err(CedarSchemaError::Io(io_error)) => { + panic!( + "Expected TypeNotDefined error, but got IO error: {:?}", + io_error + ); + } + } + } + + #[test] + fn test_successful_resolution() { + let schema_str = r#" + type MyName = String; + entity User = { "name": MyName }; + "#; + + let result = schema_str_to_json_with_resolved_types(schema_str); + + match result { + Ok((json_value, warnings)) => { + // Verify we got a JSON value + assert!(json_value.is_object(), "Expected JSON object"); + + // Verify the JSON doesn't contain "EntityOrCommon" (should be resolved) + let json_str = serde_json::to_string(&json_value).unwrap(); + assert!( + !json_str.contains("EntityOrCommon"), + "JSON should not contain unresolved EntityOrCommon types: {}", + json_str + ); + + // Verify MyName is resolved to a reference to the common type + assert!( + json_str.contains("MyName"), + "JSON should contain resolved MyName type reference: {}", + json_str + ); + + // Should have no warnings for this simple valid schema + assert_eq!(warnings.len(), 0, "Expected no warnings for valid schema"); + } + Err(e) => panic!("Expected success but got error: {:?}", e), + } + } +} /// Validation succeeds if there are no fatal errors. There may still be /// non-fatal warnings present when validation passes. #[derive(Debug, Clone)] diff --git a/cedar-policy/src/ffi/convert.rs b/cedar-policy/src/ffi/convert.rs index 948162469..84561c593 100644 --- a/cedar-policy/src/ffi/convert.rs +++ b/cedar-policy/src/ffi/convert.rs @@ -188,26 +188,15 @@ pub fn schema_to_json(schema: Schema) -> SchemaToJsonAnswer { wasm_bindgen(js_name = "schemaToJsonWithResolvedTypes") )] pub fn schema_to_json_with_resolved_types(schema_str: &str) -> SchemaToJsonWithResolvedTypesAnswer { - match crate::api::schema_str_to_resolved_fragment(schema_str) { - Ok((fully_resolved_fragment, warnings)) => { - // Serialize the resolved Fragment to JSON - match serde_json::to_value(&fully_resolved_fragment) { - Ok(json) => SchemaToJsonWithResolvedTypesAnswer::Success { - json: json.into(), - warnings: warnings.into_iter().map(std::convert::Into::into).collect(), - }, - Err(e) => SchemaToJsonWithResolvedTypesAnswer::Failure { - errors: vec![DetailedError::from_str(&format!( - "JSON serialization failed: {e}" - )) - .unwrap_or_default()], - }, - } - } - Err(reports) => { - // Convert miette::Report to DetailedError + match crate::api::schema_str_to_json_with_resolved_types(schema_str) { + Ok((json_value, warnings)) => SchemaToJsonWithResolvedTypesAnswer::Success { + json: json_value.into(), + warnings: warnings.iter().map(std::convert::Into::into).collect(), + }, + Err(error) => { + // Convert CedarSchemaError to DetailedError SchemaToJsonWithResolvedTypesAnswer::Failure { - errors: reports.into_iter().map(std::convert::Into::into).collect(), + errors: vec![(&error).into()], } } } From 27c7eae32fb03b3501bc0b06408226ff74e06fa5 Mon Sep 17 00:00:00 2001 From: Victor Moreno Date: Mon, 12 Jan 2026 16:38:32 +0000 Subject: [PATCH 3/3] fix doc strings for json resolved types feature --- cedar-policy-cli/src/lib.rs | 5 ++++- cedar-policy/src/api.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cedar-policy-cli/src/lib.rs b/cedar-policy-cli/src/lib.rs index 1183f18ca..e51ab6e67 100644 --- a/cedar-policy-cli/src/lib.rs +++ b/cedar-policy-cli/src/lib.rs @@ -170,7 +170,10 @@ pub enum SchemaTranslationDirection { JsonToCedar, /// Cedar schema syntax -> JSON CedarToJson, - /// Cedar schema syntax -> JSON with all types resolved to entity or common + /// Cedar schema syntax -> JSON with all types resolved to entity or common. + /// + /// In contrast to `cedar-to-json`, this option requires that every type + /// referenced in the schema is also defined. CedarToJsonWithResolvedTypes, } diff --git a/cedar-policy/src/api.rs b/cedar-policy/src/api.rs index 67d5c58a8..aee7aabfb 100644 --- a/cedar-policy/src/api.rs +++ b/cedar-policy/src/api.rs @@ -2144,7 +2144,7 @@ impl Schema { /// This is primarily meant to be used when working with schemas programmatically, /// for example when creating a schema building UI. /// -/// Returns `Ok((json_value`, warnings)) on success, or Err(error) on failure. +/// Returns `Ok((json_value, warnings))` on success, or `Err(error)` on failure. /// Fails if there are any types in the schema that are unresolved. pub fn schema_str_to_json_with_resolved_types( schema_str: &str,