diff --git a/crates/wick/flow-graph-interpreter/tests/test/mod.rs b/crates/wick/flow-graph-interpreter/tests/test/mod.rs index 351ee251b..713e7caf9 100644 --- a/crates/wick/flow-graph-interpreter/tests/test/mod.rs +++ b/crates/wick/flow-graph-interpreter/tests/test/mod.rs @@ -38,7 +38,7 @@ pub(crate) async fn base_setup( use tokio_stream::StreamExt; use wick_packet::{Entity, Invocation}; let options = Some(InterpreterOptions { ..Default::default() }); - let mut def = wick_config::WickConfiguration::load_from_file_sync(manifest)?; + let mut def = wick_config::WickConfiguration::fetch(manifest, Default::default()).await?; def.set_root_config(component_config); let mut def = def.finish()?.try_component_config()?; let network = from_def(&mut def)?; diff --git a/crates/wick/wick-config/src/v1/conversions/types.rs b/crates/wick/wick-config/src/v1/conversions/types.rs index c9d5dabc7..546d55c0f 100644 --- a/crates/wick/wick-config/src/v1/conversions/types.rs +++ b/crates/wick/wick-config/src/v1/conversions/types.rs @@ -131,13 +131,11 @@ impl TryFrom for wick::Field { type Error = ManifestError; fn try_from(value: v1::Field) -> std::result::Result { - Ok(Self { - name: value.name, - description: value.description, - ty: value.ty.try_into()?, - default: None, - required: true, - }) + Ok(Self::new_with_description( + value.name, + value.ty.try_into()?, + value.description, + )) } } diff --git a/crates/wick/wick-interface-types/src/field.rs b/crates/wick/wick-interface-types/src/field.rs index 46e8a1f6a..0e2a83394 100644 --- a/crates/wick/wick-interface-types/src/field.rs +++ b/crates/wick/wick-interface-types/src/field.rs @@ -32,12 +32,16 @@ pub struct Field { impl Field { pub fn new(name: impl AsRef, ty: Type) -> Self { + Self::new_with_description(name, ty, None) + } + + pub fn new_with_description(name: impl AsRef, ty: Type, desc: Option) -> Self { Self { name: name.as_ref().to_owned(), - description: None, + description: desc, #[cfg(feature = "value")] default: None, - required: true, + required: !matches!(ty, Type::Optional { .. }), ty, } } diff --git a/crates/wick/wick-packet/src/validation.rs b/crates/wick/wick-packet/src/validation.rs index a5ee8b337..f0958ffb3 100644 --- a/crates/wick/wick-packet/src/validation.rs +++ b/crates/wick/wick-packet/src/validation.rs @@ -1,9 +1,14 @@ use tracing::warn; -use wick_interface_types::Field; +use wick_interface_types::{Field, Type}; use crate::{Error, RuntimeConfig}; pub fn expect_configuration_matches(name: &str, config: Option<&RuntimeConfig>, fields: &[Field]) -> Result<(), Error> { + let fields = fields + .iter() + .filter(|f| f.required() || !matches!(f.ty(), Type::Optional { .. })) + .collect::>(); + if config.is_none() { if fields.is_empty() { return Ok(()); @@ -47,6 +52,9 @@ mod test { #[rstest::rstest] #[case(json!({"required_field": "value"}),vec![Field::new("required_field", Type::String)])] + #[case(json!({"optional_field": "value"}),vec![Field::new("optional_field", Type::Optional { ty: Box::new(Type::String)})])] + #[case(json!({"optional_field": serde_json::Value::Null}),vec![Field::new("optional_field", Type::Optional { ty: Box::new(Type::String)})])] + #[case(json!({}),vec![Field::new("optional_field", Type::Optional { ty: Box::new(Type::String)})])] #[case(json!({}),vec![])] fn config_validation_positive(#[case] config: serde_json::Value, #[case] fields: Vec) -> Result<()> { diff --git a/crates/wick/wick-runtime/tests/manifests/v1/component-context-vars-passthrough-child.yaml b/crates/wick/wick-runtime/tests/manifests/v1/component-context-vars-passthrough-child.yaml new file mode 100644 index 000000000..5eeecbc3c --- /dev/null +++ b/crates/wick/wick-runtime/tests/manifests/v1/component-context-vars-passthrough-child.yaml @@ -0,0 +1,28 @@ +--- +name: 'test' +kind: wick/component@v1 +metadata: + version: '0.0.2' +component: + kind: wick/component/composite@v1 + with: + - name: required + type: string + - name: optional + type: string? + operations: + - name: test + with: + - name: required + type: string + - name: optional + type: string? + uses: + - name: SENDER + operation: core::sender + with: + output: 'root_required: {{ ctx.root_config.required }}, root_optional: {{ ctx.root_config.optional | default: "" }}, required: {{ ctx.config.required }}, optional: {{ ctx.config.optional | default: "" }}' + flow: + - <>.input -> wick::string::concat[a].left + - SENDER.output -> a.right + - a.output -> <> diff --git a/crates/wick/wick-runtime/tests/manifests/v1/component-context-vars-passthrough.yaml b/crates/wick/wick-runtime/tests/manifests/v1/component-context-vars-passthrough.yaml new file mode 100644 index 000000000..968881db6 --- /dev/null +++ b/crates/wick/wick-runtime/tests/manifests/v1/component-context-vars-passthrough.yaml @@ -0,0 +1,36 @@ +--- +name: 'test' +kind: wick/component@v1 +metadata: + version: '0.0.2' +import: + - name: child + component: + kind: wick/component/manifest@v1 + ref: ./component-context-vars-passthrough-child.yaml + with: + required: '{{ ctx.root_config.required }}' + optional: '{% if ctx.root_config contains "optional" %}{{ ctx.root_config.optional | output }}{% endif %}{% unless ctx.root_config contains "optional" %}{{ nil | output }}{% endunless %}' +component: + kind: wick/component/composite@v1 + with: + - name: required + type: string + - name: optional + type: string? + operations: + - name: test + with: + - name: required + type: string + - name: optional + type: string? + uses: + - name: CHILD_OP + operation: child::test + with: + required: '{{ ctx.config.required }}' + optional: '{% if ctx.config contains "optional" %}{{ ctx.config.optional | output }}{% endif %}{% unless ctx.config contains "optional" %}{{ nil | output }}{% endunless %}' + flow: + - <>.input -> CHILD_OP.input + - CHILD_OP.output -> <>.output diff --git a/crates/wick/wick-runtime/tests/utils/mod.rs b/crates/wick/wick-runtime/tests/utils/mod.rs index efe9add49..628d414cb 100644 --- a/crates/wick/wick-runtime/tests/utils/mod.rs +++ b/crates/wick/wick-runtime/tests/utils/mod.rs @@ -25,7 +25,7 @@ pub async fn common_test( target: &str, mut expected: Vec, ) -> anyhow::Result<()> { - base_test(path, stream, Entity::local(target), expected, None).await + base_test(path, stream, Entity::local(target), expected, None, None).await } #[allow(unused)] @@ -34,9 +34,10 @@ pub async fn test_with_config( stream: PacketStream, target: &str, mut expected: Vec, - config: RuntimeConfig, + root_config: Option, + config: Option, ) -> anyhow::Result<()> { - base_test(path, stream, Entity::local(target), expected, Some(config)).await + base_test(path, stream, Entity::local(target), expected, root_config, config).await } #[allow(unused)] @@ -45,10 +46,11 @@ pub async fn base_test( stream: PacketStream, target: Entity, mut expected: Vec, + root_config: Option, config: Option, ) -> anyhow::Result<()> { let cwd = std::env::current_dir()?; - let (engine, _) = init_engine_from_yaml(path, config).await?; + let (engine, _) = init_engine_from_yaml(path, root_config).await?; let inherent = InherentData::new(1, 1000); let target = if target.component_id() == Entity::LOCAL { @@ -60,7 +62,7 @@ pub async fn base_test( let result = engine .invoke( Invocation::test("simple schematic", target, stream, Some(inherent))?, - Default::default(), + config, ) .await?; diff --git a/crates/wick/wick-runtime/tests/v1_components.rs b/crates/wick/wick-runtime/tests/v1_components.rs index 3415ce25b..5c6ab146d 100644 --- a/crates/wick/wick-runtime/tests/v1_components.rs +++ b/crates/wick/wick-runtime/tests/v1_components.rs @@ -1,6 +1,9 @@ mod utils; +use std::collections::HashMap; + +use serde_json::json; use utils::*; -use wick_packet::{packet_stream, Packet}; +use wick_packet::{packet_stream, packets, Packet, RuntimeConfig}; type Result = anyhow::Result; @@ -35,3 +38,78 @@ async fn composite_inherit() -> Result<()> { ) .await } + +#[test_logger::test(tokio::test)] +async fn test_context_passthrough() -> Result<()> { + test_context_passthrough_base( + RuntimeConfig::from(HashMap::from([ + ("required".to_owned(), json!("required field")), + ("optional".to_owned(), json!("optional field")), + ])), + RuntimeConfig::from(HashMap::from([ + ("required".to_owned(), json!("required field")), + ("optional".to_owned(), json!("optional field")), + ])), + vec![ + Packet::encode("output", "[from input]root_required: required field, root_optional: optional field, required: required field, optional: optional field"), + Packet::done("output"), + ], + ) + .await?; + + Ok(()) +} + +#[test_logger::test(tokio::test)] +async fn test_context_passthrough_root_config_opt() -> Result<()> { + test_context_passthrough_base( + RuntimeConfig::from(HashMap::from([("required".to_owned(), json!("required field"))])), + RuntimeConfig::from(HashMap::from([ + ("required".to_owned(), json!("required field")), + ("optional".to_owned(), json!("optional field")), + ])), + vec![ + Packet::encode("output", "[from input]root_required: required field, root_optional: , required: required field, optional: optional field"), + Packet::done("output"), + ], + ) + .await?; + + Ok(()) +} + +#[test_logger::test(tokio::test)] +async fn test_context_passthrough_op_config_opt() -> Result<()> { + test_context_passthrough_base( + RuntimeConfig::from(HashMap::from([ + ("required".to_owned(), json!("required field")), + ("optional".to_owned(), json!("optional field")), + ])), + RuntimeConfig::from(HashMap::from([("required".to_owned(), json!("required field"))])), + vec![ + Packet::encode("output", "[from input]root_required: required field, root_optional: optional field, required: required field, optional: "), + Packet::done("output"), + ], + ) + .await?; + + Ok(()) +} + +async fn test_context_passthrough_base( + root_config: RuntimeConfig, + config: RuntimeConfig, + expected: Vec, +) -> Result<()> { + test_with_config( + "./tests/manifests/v1/component-context-vars-passthrough.yaml", + packets!(("input", "[from input]")).into(), + "test", + expected, + Some(root_config), + Some(config), + ) + .await?; + + Ok(()) +} diff --git a/crates/wick/wick-runtime/tests/wasm.rs b/crates/wick/wick-runtime/tests/wasm.rs index 8a0f212e2..51adf1b95 100644 --- a/crates/wick/wick-runtime/tests/wasm.rs +++ b/crates/wick/wick-runtime/tests/wasm.rs @@ -23,7 +23,8 @@ async fn good_wasm_component_v1() -> Result<()> { packet_stream!(("left", 10), ("right", 1001)), "add", vec![Packet::encode("output", 1011), Packet::done("output")], - json!({"default_err":"custom error"}).try_into()?, + Some(json!({"default_err":"custom error"}).try_into()?), + None, ) .await }