Skip to content

Commit

Permalink
add uniqueness check as well
Browse files Browse the repository at this point in the history
  • Loading branch information
VisualBean committed Jan 23, 2025
1 parent 5822895 commit 1b1498c
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/LEGO.AsyncAPI/Resource.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/LEGO.AsyncAPI/Resource.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Validation_ChannelsMustBeUnique" xml:space="preserve">
<value>Channel signature '{0}' MUST be unique.</value>
</data>
<data name="Validation_EmailMustBeEmailFormat" xml:space="preserve">
<value>The string '{0}' MUST be an email address.</value>
</data>
Expand Down
30 changes: 29 additions & 1 deletion src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace LEGO.AsyncAPI.Validation.Rules
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using LEGO.AsyncAPI.Models;
Expand All @@ -12,6 +13,7 @@ namespace LEGO.AsyncAPI.Validation.Rules
public static class AsyncApiDocumentRules
{
private static TimeSpan RegexTimeout = TimeSpan.FromSeconds(1);

/// <summary>
/// The key regex.
/// </summary>
Expand All @@ -35,22 +37,31 @@ public static class AsyncApiDocumentRules
context.Enter("channels");
try
{
// MUST have at least 1 channel
if (document.Channels == null || !document.Channels.Keys.Any())
{
context.CreateError(
nameof(DocumentRequiredFields),
string.Format(Resource.Validation_FieldRequired, "channels", "document"));
return;
}

var hashSet = new HashSet<string>();
foreach (var key in document.Channels.Keys)
{
// Uri-template
if (!ChannelKeyUriTemplateRegex.IsMatch(key))
{
context.CreateError(
"ChannelKeys",
string.Format(Resource.Validation_KeyMustMatchRegularExpr, key, "channels", KeyRegex.ToString()));
}

// Unique channel keys
var pathSignature = GetKeySignature(key);
if (!hashSet.Add(pathSignature))
{
context.CreateError("ChannelKey", string.Format(Resource.Validation_ChannelsMustBeUnique, pathSignature));
}
}
}
finally
Expand All @@ -59,6 +70,23 @@ public static class AsyncApiDocumentRules
}
});

private static string GetKeySignature(string path)
{
for (int openBrace = path.IndexOf('{'); openBrace > -1; openBrace = path.IndexOf('{', openBrace + 2))
{
int closeBrace = path.IndexOf('}', openBrace);

if (closeBrace < 0)
{
return path;
}

path = path.Substring(0, openBrace + 1) + path.Substring(closeBrace);
}

return path;
}

public static ValidationRule<AsyncApiDocument> KeyMustBeRegularExpression =>
new ValidationRule<AsyncApiDocument>(
(context, document) =>
Expand Down
36 changes: 36 additions & 0 deletions test/LEGO.AsyncAPI.Tests/Validation/ValidationRuleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,42 @@ public void ChannelKey_WithInvalidParameter_DiagnosticsError(string channelKey)
diagnostic.Errors.First().Pointer.Should().Be("#/channels");
}

[Test]
public void ChannelKey_WithNonUniqueKey_DiagnosticsError()
{
var input =
"""
asyncapi: 2.6.0
info:
title: Chat Application
version: 1.0.0
servers:
testing:
url: test.mosquitto.org:1883
protocol: mqtt
description: Test broker
channels:
chat/{personId}:
publish:
operationId: onMessageReceieved
message:
name: text
payload:
type: string
chat/{personIdentity}:
publish:
operationId: onMessageReceieved
message:
name: text
payload:
type: string
""";

var document = new AsyncApiStringReader().Read(input, out var diagnostic);
diagnostic.Errors.First().Message.Should().Be("Channel signature 'chat/{}' MUST be unique.");
diagnostic.Errors.First().Pointer.Should().Be("#/channels");
}

[Test]
[TestCase("chat")]
[TestCase("/some/chat/{personId}")]
Expand Down

0 comments on commit 1b1498c

Please sign in to comment.