You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by mg...@apache.org on 2022/03/22 08:04:48 UTC

[avro] branch branch-1.11 updated: AVRO-3452: Implement custom deserialization for Name that employs the special parsing of name and namespace (#1615)

This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch branch-1.11
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/branch-1.11 by this push:
     new a9a422f  AVRO-3452: Implement custom deserialization for Name that employs the special parsing of name and namespace (#1615)
a9a422f is described below

commit a9a422fd7b970c510aa55ee732b42110df24f4d2
Author: Martin Grigorov <ma...@users.noreply.github.com>
AuthorDate: Tue Mar 22 10:04:07 2022 +0200

    AVRO-3452: Implement custom deserialization for Name that employs the special parsing of name and namespace (#1615)
    
    * AVRO-3452: Implement custom deserialization for Name that employs the special parsing of name and namespace
    
    Signed-off-by: Martin Tzvetanov Grigorov <mg...@apache.org>
    
    * AVRO-3452: Move the use/import of serde::de::Error in a higher scope so that it could be used for .map_err() call too
    
    Signed-off-by: Martin Tzvetanov Grigorov <mg...@apache.org>
    (cherry picked from commit 8d0e53c3781e4fcfaac61e0bfd9c04b00115ad79)
---
 lang/rust/avro/src/schema.rs   | 23 +++++++++++++++++++--
 lang/rust/avro/tests/schema.rs | 47 +++++++++++++++++++++++++++++++++++-------
 2 files changed, 61 insertions(+), 9 deletions(-)

diff --git a/lang/rust/avro/src/schema.rs b/lang/rust/avro/src/schema.rs
index 9c0750b..ed98118 100644
--- a/lang/rust/avro/src/schema.rs
+++ b/lang/rust/avro/src/schema.rs
@@ -228,7 +228,7 @@ impl From<&types::Value> for SchemaKind {
 ///
 /// More information about schema names can be found in the
 /// [Avro specification](https://avro.apache.org/docs/current/spec.html#names)
-#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Eq)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
 pub struct Name {
     pub name: String,
     pub namespace: Namespace,
@@ -265,7 +265,7 @@ impl Name {
     }
 
     /// Parse a `serde_json::Value` into a `Name`.
-    fn parse(complex: &Map<String, Value>) -> AvroResult<Self> {
+    pub(crate) fn parse(complex: &Map<String, Value>) -> AvroResult<Self> {
         let (name, namespace_from_name) = complex
             .name()
             .map(|name| Name::get_name_and_namespace(name.as_str()).unwrap())
@@ -335,6 +335,25 @@ impl fmt::Display for Name {
     }
 }
 
+impl<'de> Deserialize<'de> for Name {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::de::Deserializer<'de>,
+    {
+        serde_json::Value::deserialize(deserializer).and_then(|value| {
+            use serde::de::Error;
+            if let Value::Object(json) = value {
+                Name::parse(&json).map_err(D::Error::custom)
+            } else {
+                Err(D::Error::custom(format!(
+                    "Expected a json object: {:?}",
+                    value
+                )))
+            }
+        })
+    }
+}
+
 pub(crate) struct ResolvedSchema<'s> {
     names_ref: NamesRef<'s>,
     root_schema: &'s Schema,
diff --git a/lang/rust/avro/tests/schema.rs b/lang/rust/avro/tests/schema.rs
index c404452..292d0aa 100644
--- a/lang/rust/avro/tests/schema.rs
+++ b/lang/rust/avro/tests/schema.rs
@@ -1077,9 +1077,9 @@ fn test_fullname_name_and_namespace_specified() {
 #[test]
 fn test_fullname_fullname_and_namespace_specified() {
     init();
-    let name: Name =
-        serde_json::from_str(r#"{"name": "a.b.c.d", "namespace": "o.a.h", "aliases": null}"#)
-            .unwrap();
+    let name: Name = serde_json::from_str(r#"{"name": "a.b.c.d", "namespace": "o.a.h"}"#).unwrap();
+    assert_eq!(&name.name, "d");
+    assert_eq!(name.namespace, Some("a.b.c".to_owned()));
     let fullname = name.fullname(None);
     assert_eq!("a.b.c.d", fullname);
 }
@@ -1087,8 +1087,9 @@ fn test_fullname_fullname_and_namespace_specified() {
 #[test]
 fn test_fullname_name_and_default_namespace_specified() {
     init();
-    let name: Name =
-        serde_json::from_str(r#"{"name": "a", "namespace": null, "aliases": null}"#).unwrap();
+    let name: Name = serde_json::from_str(r#"{"name": "a", "namespace": null}"#).unwrap();
+    assert_eq!(&name.name, "a");
+    assert_eq!(name.namespace, None);
     let fullname = name.fullname(Some("b.c.d".into()));
     assert_eq!("b.c.d.a", fullname);
 }
@@ -1096,18 +1097,48 @@ fn test_fullname_name_and_default_namespace_specified() {
 #[test]
 fn test_fullname_fullname_and_default_namespace_specified() {
     init();
-    let name: Name =
-        serde_json::from_str(r#"{"name": "a.b.c.d", "namespace": null, "aliases": null}"#).unwrap();
+    let name: Name = serde_json::from_str(r#"{"name": "a.b.c.d", "namespace": null}"#).unwrap();
+    assert_eq!(&name.name, "d");
+    assert_eq!(name.namespace, Some("a.b.c".to_owned()));
     let fullname = name.fullname(Some("o.a.h".into()));
     assert_eq!("a.b.c.d", fullname);
 }
 
 #[test]
+fn test_avro_3452_parsing_name_without_namespace() {
+    init();
+    let name: Name = serde_json::from_str(r#"{"name": "a.b.c.d"}"#).unwrap();
+    assert_eq!(&name.name, "d");
+    assert_eq!(name.namespace, Some("a.b.c".to_owned()));
+    let fullname = name.fullname(None);
+    assert_eq!("a.b.c.d", fullname);
+}
+
+#[test]
+fn test_avro_3452_parsing_name_with_leading_dot_without_namespace() {
+    init();
+    let name: Name = serde_json::from_str(r#"{"name": ".a"}"#).unwrap();
+    assert_eq!(&name.name, "a");
+    assert_eq!(name.namespace, None);
+    assert_eq!("a", name.fullname(None));
+}
+
+#[test]
+fn test_avro_3452_parse_json_without_name_field() {
+    init();
+    let result: serde_json::error::Result<Name> = serde_json::from_str(r#"{"unknown": "a"}"#);
+    assert!(&result.is_err());
+    assert_eq!(result.unwrap_err().to_string(), "No `name` field");
+}
+
+#[test]
 fn test_fullname_fullname_namespace_and_default_namespace_specified() {
     init();
     let name: Name =
         serde_json::from_str(r#"{"name": "a.b.c.d", "namespace": "o.a.a", "aliases": null}"#)
             .unwrap();
+    assert_eq!(&name.name, "d");
+    assert_eq!(name.namespace, Some("a.b.c".to_owned()));
     let fullname = name.fullname(Some("o.a.h".into()));
     assert_eq!("a.b.c.d", fullname);
 }
@@ -1117,6 +1148,8 @@ fn test_fullname_name_namespace_and_default_namespace_specified() {
     init();
     let name: Name =
         serde_json::from_str(r#"{"name": "a", "namespace": "o.a.a", "aliases": null}"#).unwrap();
+    assert_eq!(&name.name, "a");
+    assert_eq!(name.namespace, Some("o.a.a".to_owned()));
     let fullname = name.fullname(Some("o.a.h".into()));
     assert_eq!("o.a.a.a", fullname);
 }