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

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


The following commit(s) were added to refs/heads/main by this push:
     new 3160e011e5 AVRO-4173: [js] Fix namespace inheritance for nested types 
in schema parsing (#3466)
3160e011e5 is described below

commit 3160e011e5c852a5ee5e8e42f27c7a3fd24d0454
Author: tyler-blip <[email protected]>
AuthorDate: Mon Oct 13 02:13:35 2025 -0400

    AVRO-4173: [js] Fix namespace inheritance for nested types in schema 
parsing (#3466)
    
    * AVRO-4173: [JS] Fix namespace inheritance for nested types
    
    * AVRO-4173: [JS] Refactor getOpts to prevent shared state
    
    * Simplify registry and logicalTypes initialization
    
    * Revert "Simplify registry and logicalTypes initialization"
    
    This reverts commit 28fb6c0fec5077b7f0a5fc36c983fb7e1c50b18b.
    
    ---------
    
    Co-authored-by: Martin Grigorov <[email protected]>
    Co-authored-by: Martin Tzvetanov Grigorov <[email protected]>
---
 lang/js/lib/schemas.js       |  21 ++++--
 lang/js/test/test_schemas.js | 161 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 178 insertions(+), 4 deletions(-)

diff --git a/lang/js/lib/schemas.js b/lang/js/lib/schemas.js
index cbf9e9600a..d99eedb8a1 100644
--- a/lang/js/lib/schemas.js
+++ b/lang/js/lib/schemas.js
@@ -2029,10 +2029,23 @@ function getOpts(attrs, opts) {
     throw new Error('invalid type: null (did you mean "null"?)');
   }
   opts = opts || {};
-  opts.registry = opts.registry || {};
-  opts.namespace = attrs.namespace || opts.namespace;
-  opts.logicalTypes = opts.logicalTypes || {};
-  return opts;
+  
+  // Ensure registry and logicalTypes exist and are preserved by reference so 
+  // type definitions can accumulate across recursive calls.
+  if (!opts.registry) {
+    opts.registry = {};
+  }
+  if (!opts.logicalTypes) {
+    opts.logicalTypes = {};
+  }
+  
+  return {
+    registry: opts.registry,  // Preserve same object reference
+    namespace: attrs.namespace || opts.namespace,
+    logicalTypes: opts.logicalTypes,  // Preserve same object reference
+    typeHook: opts.typeHook,
+    assertLogicalTypes: opts.assertLogicalTypes
+  };
 }
 
 /**
diff --git a/lang/js/test/test_schemas.js b/lang/js/test/test_schemas.js
index 0cc0996b89..d8bbcd116b 100644
--- a/lang/js/test/test_schemas.js
+++ b/lang/js/test/test_schemas.js
@@ -2188,6 +2188,167 @@ describe('types', function () {
       assert.equal(type._fields[1]._type._name, 'all.Alien');
     });
 
+    it('namespace inheritance', function () {
+      // When nested types don't specify a namespace, they should inherit the 
parent's 
+      // namespace, but other nested types with explicit namespaces shouldn't 
corrupt 
+      // the inherited context.
+      var schema = {
+        type: 'record',
+        name: 'Parent',
+        namespace: 'parent.ns',
+        fields: [
+          {
+            name: 'child_field',
+            type: {
+              type: 'record',
+              name: 'Child', // No namespace - should inherit 'parent.ns'
+              fields: [{name: 'value', type: 'int'}]
+            }
+          },
+          {
+            name: 'other_field', 
+            type: {
+              type: 'record',
+              name: 'Other',
+              namespace: 'different.ns', // Different namespace
+              fields: [{name: 'data', type: 'int'}]
+            }
+          },
+          {
+            name: 'reference_field',
+            type: 'Child' // Should resolve to 'parent.ns.Child'
+          }
+        ]
+      };
+      
+      var type = createType(schema);
+      assert.equal(type.getName(), 'parent.ns.Parent');
+      
+      var fields = type.getFields();
+      assert.equal(fields.length, 3);
+      
+      // Test all field types to demonstrate the fix works for all namespace 
scenarios
+      assert.equal(fields[0].getType().getName(), 'parent.ns.Child');    // 
Inherited namespace
+      assert.equal(fields[1].getType().getName(), 'different.ns.Other'); // 
Explicit namespace
+      assert.equal(fields[2].getType().getName(), 'parent.ns.Child');    // 
Reference resolution
+      
+      // Verify the schema works for serialization
+      var testData = {
+        child_field: { value: 42 },
+        other_field: { data: 123 },
+        reference_field: { value: 99 }
+      };
+      assert(type.isValid(testData));
+      var buf = type.toBuffer(testData);
+      assert.deepEqual(type.fromBuffer(buf), testData);
+    });
+
+    it('deep namespace inheritance', function () {
+      // Test namespace inheritance across multiple levels of nesting with 
various
+      // namespace changes to ensure the fix works robustly in complex 
scenarios.
+      var schema = {
+        type: 'record',
+        name: 'Root',
+        namespace: 'level1',
+        fields: [
+          {
+            name: 'level2_inherited',
+            type: {
+              type: 'record',
+              name: 'Level2Inherited', // Inherits 'level1'
+              fields: [
+                {
+                  name: 'level3_inherited',
+                  type: {
+                    type: 'record', 
+                    name: 'Level3Inherited', // Also inherits 'level1'
+                    fields: [{name: 'deep_value', type: 'int'}]
+                  }
+                },
+                {
+                  name: 'level3_override',
+                  type: {
+                    type: 'record',
+                    name: 'Level3Override',
+                    namespace: 'level3.ns', // Changes namespace context
+                    fields: [{name: 'override_value', type: 'string'}]
+                  }
+                }
+              ]
+            }
+          },
+          {
+            name: 'level2_different',
+            type: {
+              type: 'record',
+              name: 'Level2Different',
+              namespace: 'level2.ns', // Different namespace
+              fields: [
+                {
+                  name: 'nested_inherited',
+                  type: {
+                    type: 'record',
+                    name: 'NestedInherited', // Should inherit 'level2.ns'
+                    fields: [{name: 'nested_data', type: 'double'}]
+                  }
+                }
+              ]
+            }
+          },
+          {
+            name: 'ref_level2_inherited',
+            type: 'Level2Inherited' // Should resolve to 
'level1.Level2Inherited'
+          },
+          {
+            name: 'ref_level3_inherited', 
+            type: 'Level3Inherited' // Should resolve to 
'level1.Level3Inherited'
+          }
+        ]
+      };
+      
+      var type = createType(schema);
+      assert.equal(type.getName(), 'level1.Root');
+      
+      var fields = type.getFields();
+      assert.equal(fields.length, 4);
+      
+      // Verify deep inheritance worked correctly
+      assert.equal(fields[0].getType().getName(), 'level1.Level2Inherited');
+      assert.equal(fields[1].getType().getName(), 'level2.ns.Level2Different');
+      
+      // Critical tests: references should resolve to correct namespaces
+      assert.equal(fields[2].getType().getName(), 'level1.Level2Inherited');
+      assert.equal(fields[3].getType().getName(), 'level1.Level3Inherited');
+      
+      // Verify nested types have correct namespaces
+      var level2Fields = fields[0].getType().getFields();
+      assert.equal(level2Fields[0].getType().getName(), 
'level1.Level3Inherited');
+      assert.equal(level2Fields[1].getType().getName(), 
'level3.ns.Level3Override');
+      
+      var level2DiffFields = fields[1].getType().getFields();
+      assert.equal(level2DiffFields[0].getType().getName(), 
'level2.ns.NestedInherited');
+      
+      // Test serialization works correctly
+      var testData = {
+        level2_inherited: {
+          level3_inherited: { deep_value: 42 },
+          level3_override: { override_value: 'test' }
+        },
+        level2_different: {
+          nested_inherited: { nested_data: 3.14 }
+        },
+        ref_level2_inherited: {
+          level3_inherited: { deep_value: 99 },
+          level3_override: { override_value: 'ref' }
+        },
+        ref_level3_inherited: { deep_value: 123 }
+      };
+      
+      assert(type.isValid(testData));
+      var buf = type.toBuffer(testData);
+      assert.deepEqual(type.fromBuffer(buf), testData);
+    });
+
     it('wrapped primitive', function () {
       var type = createType({
         type: 'record',

Reply via email to