Skip to content

Commit ee29e69

Browse files
bieluclaude
andcommitted
fix: write 2.x channel servers as server names
AsyncAPI 2.x lists a channel's servers by name, but the 2.x writer emitted the reference fragment, so "#/servers/production" was written as "/servers/production". This also broke a 2.x read/write round trip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent d167365 commit ee29e69

2 files changed

Lines changed: 63 additions & 1 deletion

File tree

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public virtual void SerializeV2(IAsyncApiWriter writer)
8484
writer.WriteOptionalProperty(AsyncApiConstants.Description, this.Description);
8585

8686
// servers
87-
writer.WriteOptionalCollection(AsyncApiConstants.Servers, this.Servers.Select(s => s.Reference.FragmentId).ToList(), (w, s) => w.WriteValue(s));
87+
writer.WriteOptionalCollection(AsyncApiConstants.Servers, this.Servers.Select(GetV2ServerName).ToList(), (w, s) => w.WriteValue(s));
8888

8989
var operations = writer.Workspace.RootDocument?.Operations.Values.Where(operation => CheckOperationChannel(operation, writer)).ToList();
9090

@@ -166,5 +166,20 @@ private bool CheckOperationChannel(AsyncApiOperation operation, IAsyncApiWriter
166166
operation.Channel.Reference.Workspace = writer.Workspace;
167167
return operation.Channel.Equals(this);
168168
}
169+
170+
/// <summary>
171+
/// AsyncAPI 2.x lists channel servers by name, so a "#/servers/{name}" pointer is written as "{name}".
172+
/// </summary>
173+
private static string GetV2ServerName(AsyncApiServerReference server)
174+
{
175+
var fragmentId = server.Reference.FragmentId;
176+
if (fragmentId == null)
177+
{
178+
return server.Reference.Reference;
179+
}
180+
181+
var tokens = new JsonPointer(fragmentId).Tokens;
182+
return tokens.Length == 2 && tokens[0] == AsyncApiConstants.Servers ? tokens[1] : fragmentId;
183+
}
169184
}
170185
}

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,53 @@ public void V2_AsyncApiChannel_UpgradesAndNormalizesKey()
156156
reserialized.Should().BePlatformAgnosticEquivalentTo(yaml);
157157
}
158158

159+
[Test]
160+
public void V2_AsyncApiChannel_WithServers_SerializesServerNames()
161+
{
162+
var yaml =
163+
"""
164+
asyncapi: 2.6.0
165+
info:
166+
title: test spec
167+
version: 1.0.0
168+
servers:
169+
production:
170+
url: example.com
171+
protocol: kafka
172+
channels:
173+
mychannel:
174+
servers:
175+
- production
176+
""";
177+
178+
var document = new AsyncApiStringReader().Read(yaml, out var diag);
179+
180+
document.Channels.First().Value.Servers.Single().Reference.Reference.Should().Be("#/servers/production");
181+
182+
var reserialized = document.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0);
183+
184+
reserialized.Should().BePlatformAgnosticEquivalentTo(yaml);
185+
}
186+
187+
[Test]
188+
public void V2_AsyncApiChannel_WithServerReference_SerializesServerName()
189+
{
190+
var expected =
191+
"""
192+
servers:
193+
- production
194+
""";
195+
196+
var channel = new AsyncApiChannel
197+
{
198+
Servers = new List<AsyncApiServerReference> { new AsyncApiServerReference("#/servers/production") },
199+
};
200+
201+
var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0);
202+
203+
actual.Should().BePlatformAgnosticEquivalentTo(expected);
204+
}
205+
159206
[Test]
160207
public void V2_AsyncApiChannel_reserializes()
161208
{

0 commit comments

Comments
 (0)