Skip to content

Commit e653de6

Browse files
committed
refactor: Prevent duplicate schema serialization errors in AsyncAPI components
The serialization logic in AsyncApiComponents.cs is updated to remove the explicit distinct filtering when writing schemas for JSON and MultiFormat types. This change addresses a regression where inlining recursive schemas reached by multiple references previously caused an error ("An item with the same key has already been added"). This change is supported by updating the serialization logic in src/ByteBard.AsyncAPI/Models/AsyncApiComponents.cs to remove the distinct filtering logic for AsyncApiJsonSchemaReference and AsyncApiMultiFormatSchemaReference when writing schemas. A corresponding regression test in test/ByteBard.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs has been added to verify that serializing components with recursive references reached multiple times no longer throws an error.
1 parent 70e73db commit e653de6

2 files changed

Lines changed: 42 additions & 7 deletions

File tree

‎src/ByteBard.AsyncAPI/Models/AsyncApiComponents.cs‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ namespace ByteBard.AsyncAPI.Models
22
{
33
using System;
44
using System.Collections.Generic;
5-
using System.Linq;
65
using ByteBard.AsyncAPI.Models.Interfaces;
76
using ByteBard.AsyncAPI.Writers;
87

@@ -129,9 +128,6 @@ public void SerializeV2(IAsyncApiWriter writer)
129128
writer.WriteStartObject();
130129
if (loops.TryGetValue(typeof(AsyncApiJsonSchemaReference), out List<object> schemas))
131130
{
132-
var asyncApiSchemas = schemas.Cast<AsyncApiJsonSchemaReference>().Distinct().ToList()
133-
.ToDictionary<AsyncApiJsonSchemaReference, string>(k => k.Reference.FragmentId);
134-
135131
writer.WriteOptionalMap(
136132
AsyncApiConstants.Schemas,
137133
this.Schemas,
@@ -395,9 +391,6 @@ public void SerializeV3(IAsyncApiWriter writer)
395391
writer.WriteStartObject();
396392
if (loops.TryGetValue(typeof(AsyncApiMultiFormatSchemaReference), out List<object> schemas))
397393
{
398-
var asyncApiSchemas = schemas.Cast<AsyncApiMultiFormatSchemaReference>().Distinct().ToList()
399-
.ToDictionary<AsyncApiMultiFormatSchemaReference, string>(k => k.Reference.FragmentId);
400-
401394
writer.WriteOptionalMap(
402395
AsyncApiConstants.Schemas,
403396
this.Schemas,

‎test/ByteBard.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,5 +712,47 @@ public void V2_Serialize_WithNot_DoesNotWriteElse()
712712

713713
Assert.True(!yaml.Contains("else:"));
714714
}
715+
716+
/// <summary>
717+
/// Regression test.
718+
/// Bug: Inlining a recursive schema that is reached by more than one reference threw
719+
/// "An item with the same key has already been added" when writing components.
720+
/// </summary>
721+
[Test]
722+
public void V2_Serialize_WithInlinedReferences_RecursiveSchemaReachedTwice_DoesNotThrow()
723+
{
724+
var yaml =
725+
"""
726+
asyncapi: 2.6.0
727+
info:
728+
title: test
729+
version: 1.0.0
730+
channels:
731+
first:
732+
publish:
733+
message:
734+
payload:
735+
$ref: '#/components/schemas/Node'
736+
components:
737+
schemas:
738+
Node:
739+
type: object
740+
properties:
741+
next:
742+
$ref: '#/components/schemas/Node'
743+
prev:
744+
$ref: '#/components/schemas/Node'
745+
""";
746+
var document = new AsyncApiStringReader().Read(yaml, out var diagnostic);
747+
diagnostic.Errors.Should().BeEmpty();
748+
749+
var outputString = new StringWriter();
750+
var writer = new AsyncApiYamlWriter(outputString, new AsyncApiWriterSettings { ReferenceInline = ReferenceInlineSetting.InlineReferences });
751+
752+
var act = () => document.SerializeV2(writer);
753+
754+
act.Should().NotThrow();
755+
outputString.ToString().Replace("\r\n", "\n").Should().Contain("components:\n schemas:\n Node:");
756+
}
715757
}
716758
}

0 commit comments

Comments
 (0)