Skip to content

Commit 1b1498c

Browse files
committed
add uniqueness check as well
1 parent 5822895 commit 1b1498c

File tree

4 files changed

+77
-1
lines changed

4 files changed

+77
-1
lines changed

src/LEGO.AsyncAPI/Resource.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LEGO.AsyncAPI/Resource.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@
117117
<resheader name="writer">
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120+
<data name="Validation_ChannelsMustBeUnique" xml:space="preserve">
121+
<value>Channel signature '{0}' MUST be unique.</value>
122+
</data>
120123
<data name="Validation_EmailMustBeEmailFormat" xml:space="preserve">
121124
<value>The string '{0}' MUST be an email address.</value>
122125
</data>

src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace LEGO.AsyncAPI.Validation.Rules
44
{
55
using System;
6+
using System.Collections.Generic;
67
using System.Linq;
78
using System.Text.RegularExpressions;
89
using LEGO.AsyncAPI.Models;
@@ -12,6 +13,7 @@ namespace LEGO.AsyncAPI.Validation.Rules
1213
public static class AsyncApiDocumentRules
1314
{
1415
private static TimeSpan RegexTimeout = TimeSpan.FromSeconds(1);
16+
1517
/// <summary>
1618
/// The key regex.
1719
/// </summary>
@@ -35,22 +37,31 @@ public static class AsyncApiDocumentRules
3537
context.Enter("channels");
3638
try
3739
{
40+
// MUST have at least 1 channel
3841
if (document.Channels == null || !document.Channels.Keys.Any())
3942
{
4043
context.CreateError(
4144
nameof(DocumentRequiredFields),
4245
string.Format(Resource.Validation_FieldRequired, "channels", "document"));
4346
return;
4447
}
45-
48+
var hashSet = new HashSet<string>();
4649
foreach (var key in document.Channels.Keys)
4750
{
51+
// Uri-template
4852
if (!ChannelKeyUriTemplateRegex.IsMatch(key))
4953
{
5054
context.CreateError(
5155
"ChannelKeys",
5256
string.Format(Resource.Validation_KeyMustMatchRegularExpr, key, "channels", KeyRegex.ToString()));
5357
}
58+
59+
// Unique channel keys
60+
var pathSignature = GetKeySignature(key);
61+
if (!hashSet.Add(pathSignature))
62+
{
63+
context.CreateError("ChannelKey", string.Format(Resource.Validation_ChannelsMustBeUnique, pathSignature));
64+
}
5465
}
5566
}
5667
finally
@@ -59,6 +70,23 @@ public static class AsyncApiDocumentRules
5970
}
6071
});
6172

73+
private static string GetKeySignature(string path)
74+
{
75+
for (int openBrace = path.IndexOf('{'); openBrace > -1; openBrace = path.IndexOf('{', openBrace + 2))
76+
{
77+
int closeBrace = path.IndexOf('}', openBrace);
78+
79+
if (closeBrace < 0)
80+
{
81+
return path;
82+
}
83+
84+
path = path.Substring(0, openBrace + 1) + path.Substring(closeBrace);
85+
}
86+
87+
return path;
88+
}
89+
6290
public static ValidationRule<AsyncApiDocument> KeyMustBeRegularExpression =>
6391
new ValidationRule<AsyncApiDocument>(
6492
(context, document) =>

test/LEGO.AsyncAPI.Tests/Validation/ValidationRuleTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,42 @@ public void ChannelKey_WithInvalidParameter_DiagnosticsError(string channelKey)
4646
diagnostic.Errors.First().Pointer.Should().Be("#/channels");
4747
}
4848

49+
[Test]
50+
public void ChannelKey_WithNonUniqueKey_DiagnosticsError()
51+
{
52+
var input =
53+
"""
54+
asyncapi: 2.6.0
55+
info:
56+
title: Chat Application
57+
version: 1.0.0
58+
servers:
59+
testing:
60+
url: test.mosquitto.org:1883
61+
protocol: mqtt
62+
description: Test broker
63+
channels:
64+
chat/{personId}:
65+
publish:
66+
operationId: onMessageReceieved
67+
message:
68+
name: text
69+
payload:
70+
type: string
71+
chat/{personIdentity}:
72+
publish:
73+
operationId: onMessageReceieved
74+
message:
75+
name: text
76+
payload:
77+
type: string
78+
""";
79+
80+
var document = new AsyncApiStringReader().Read(input, out var diagnostic);
81+
diagnostic.Errors.First().Message.Should().Be("Channel signature 'chat/{}' MUST be unique.");
82+
diagnostic.Errors.First().Pointer.Should().Be("#/channels");
83+
}
84+
4985
[Test]
5086
[TestCase("chat")]
5187
[TestCase("/some/chat/{personId}")]

0 commit comments

Comments
 (0)