diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/mod.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/mod.rs index deaaa7e84313..56b05972ecf5 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/mod.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/mod.rs @@ -21,6 +21,7 @@ mod prisma_18517; mod prisma_20799; mod prisma_21182; mod prisma_21369; +mod prisma_21454; mod prisma_21901; mod prisma_22298; mod prisma_5952; diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_21454.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_21454.rs new file mode 100644 index 000000000000..15c52256964a --- /dev/null +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_21454.rs @@ -0,0 +1,74 @@ +use query_engine_tests::*; + +#[test_suite(schema(schema), capabilities(Json))] +mod prisma_21454 { + + fn schema() -> String { + let schema = indoc! { + r#" + model Model { + #id(id, String, @id) + json Json + } + "# + }; + + schema.to_owned() + } + + #[connector_test] + async fn dollar_type_in_json(runner: Runner) -> TestResult<()> { + let res = runner + .query_json( + r#"{ + "modelName": "Model", + "action": "createOne", + "query": { + "selection": { "json": true }, + "arguments": { + "data": { + "id": "123", + "json": { "$type": "Raw", "value": {"$type": "Something" } } + } + } + } + }"#, + ) + .await?; + + res.assert_success(); + + insta::assert_snapshot!(res.to_string(), @r###"{"data":{"createOneModel":{"json":{"$type":"Json","value":"{\"$type\":\"Something\"}"}}}}"###); + + Ok(()) + } + + #[connector_test] + async fn nested_dollar_type_in_json(runner: Runner) -> TestResult<()> { + let res = runner + .query_json( + r#"{ + "modelName": "Model", + "action": "createOne", + "query": { + "selection": { "json": true }, + "arguments": { + "data": { + "id": "123", + "json": { + "something": { "$type": "Raw", "value": {"$type": "Something" } } + } + } + } + } + }"#, + ) + .await?; + + res.assert_success(); + + insta::assert_snapshot!(res.to_string(), @r###"{"data":{"createOneModel":{"json":{"$type":"Json","value":"{\"something\":{\"$type\":\"Something\"}}"}}}}"###); + + Ok(()) + } +} diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/data_types/json.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/data_types/json.rs index 5a2ddc350d06..0321836833e1 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/data_types/json.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/data_types/json.rs @@ -94,7 +94,7 @@ mod json { insta::assert_snapshot!( res, - @r###"{"data":{"findManyTestModel":[{"json":null},{"json":"null"}]}}"### + @r###"{"data":{"findManyTestModel":[{"json":null},{"json":null}]}}"### ); } query_engine_tests::EngineProtocol::Json => { diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/json_adapter/request.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/json_adapter/request.rs index b9354056b692..172003ceafe3 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/json_adapter/request.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/json_adapter/request.rs @@ -228,7 +228,7 @@ impl<'a, 'b> FieldTypeInferrer<'a, 'b> { None => InferredType::Unknown, } } - ArgumentValue::FieldRef(_) => unreachable!(), + ArgumentValue::FieldRef(_) | ArgumentValue::Raw(_) => unreachable!(), } } diff --git a/query-engine/core/src/constants.rs b/query-engine/core/src/constants.rs index abf320a2969c..f6a9eb403646 100644 --- a/query-engine/core/src/constants.rs +++ b/query-engine/core/src/constants.rs @@ -11,6 +11,7 @@ pub mod custom_types { pub const JSON: &str = "Json"; pub const ENUM: &str = "Enum"; pub const FIELD_REF: &str = "FieldRef"; + pub const RAW: &str = "Raw"; pub fn make_object(typ: &str, value: PrismaValue) -> PrismaValue { PrismaValue::Object(vec![make_type_pair(typ), make_value_pair(value)]) diff --git a/query-engine/core/src/query_document/argument_value.rs b/query-engine/core/src/query_document/argument_value.rs index 7629ea73c9fb..628e520a7125 100644 --- a/query-engine/core/src/query_document/argument_value.rs +++ b/query-engine/core/src/query_document/argument_value.rs @@ -14,6 +14,7 @@ pub enum ArgumentValue { Scalar(PrismaValue), Object(ArgumentValueObject), List(Vec), + Raw(serde_json::Value), FieldRef(ArgumentValueObject), } @@ -46,6 +47,10 @@ impl ArgumentValue { Self::Scalar(PrismaValue::Json(str)) } + pub fn raw(value: serde_json::Value) -> Self { + Self::Raw(value) + } + pub fn bytes(bytes: Vec) -> Self { Self::Scalar(PrismaValue::Bytes(bytes)) } @@ -76,6 +81,7 @@ impl ArgumentValue { pub(crate) fn should_be_parsed_as_json(&self) -> bool { match self { ArgumentValue::Object(_) => true, + ArgumentValue::Raw(_) => true, ArgumentValue::List(l) => l.iter().all(|v| v.should_be_parsed_as_json()), ArgumentValue::Scalar(pv) => !matches!(pv, PrismaValue::Enum(_) | PrismaValue::Json(_)), ArgumentValue::FieldRef(_) => false, diff --git a/query-engine/core/src/query_document/parser.rs b/query-engine/core/src/query_document/parser.rs index 79f30e1bd8b7..88f87773aaa8 100644 --- a/query-engine/core/src/query_document/parser.rs +++ b/query-engine/core/src/query_document/parser.rs @@ -871,6 +871,7 @@ pub(crate) mod conversions { format!("({})", itertools::join(v.iter().map(argument_value_to_type_name), ", ")) } ArgumentValue::FieldRef(_) => "FieldRef".to_string(), + ArgumentValue::Raw(_) => "JSON".to_string(), } } diff --git a/query-engine/request-handlers/src/protocols/json/protocol_adapter.rs b/query-engine/request-handlers/src/protocols/json/protocol_adapter.rs index 09ceeae20c0e..c3a566869d18 100644 --- a/query-engine/request-handlers/src/protocols/json/protocol_adapter.rs +++ b/query-engine/request-handlers/src/protocols/json/protocol_adapter.rs @@ -199,6 +199,11 @@ impl<'a> JsonProtocolAdapter<'a> { .map(ArgumentValue::float) .map_err(|_| build_err()) } + + Some(custom_types::RAW) => { + let value = obj.get(custom_types::VALUE).ok_or_else(build_err)?; + Ok(ArgumentValue::raw(value.clone())) + } Some(custom_types::BYTES) => { let value = obj .get(custom_types::VALUE) @@ -1288,10 +1293,10 @@ mod tests { assert_debug_snapshot!(operation.arguments()[0].1, @r###" Object( { - "x": Scalar( - Json( - "{ \"$type\": \"foo\", \"value\": \"bar\" }", - ), + "x": Json( + RawJson { + value: "{ \"$type\": \"foo\", \"value\": \"bar\" }", + }, ), }, )