diff --git a/.githooks/commit-msg b/.githooks/commit-msg index 8d68861..d4cdbe2 100755 --- a/.githooks/commit-msg +++ b/.githooks/commit-msg @@ -15,9 +15,10 @@ if echo "$commit_msg" | grep -qE "^Merge "; then exit 0 fi -# Conventional commit pattern: type(optional scope): description +# Conventional commit pattern: type(optional scope)(optional !): description # Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert, deps -pattern="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert|deps)(\(.+\))?: .+" +# The optional ! before the colon indicates a breaking change. +pattern="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert|deps)(\(.+\))?!?: .+" if ! echo "$commit_msg" | head -1 | grep -qE "$pattern"; then echo "" @@ -25,6 +26,8 @@ if ! echo "$commit_msg" | head -1 | grep -qE "$pattern"; then echo "" echo "Expected format: : " echo " (): " + echo " !: " + echo " ()!: " echo "" echo "Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert, deps" echo "" @@ -33,8 +36,11 @@ if ! echo "$commit_msg" | head -1 | grep -qE "$pattern"; then echo " fix: resolve validation error" echo " docs: update README" echo " chore(deps): update dependencies" + echo " fix!: change Service Bus message format" + echo " feat(api)!: remove deprecated endpoint" echo "" echo "Note: There must be a space after the colon." + echo " Use ! before the colon to indicate a breaking change." echo "" echo "Your commit message was:" echo " $(head -1 "$commit_msg_file")" diff --git a/src/AzureEventGridSimulator.Tests/UnitTests/Subscribers/Delivery/ServiceBusEventDeliveryServiceTests.cs b/src/AzureEventGridSimulator.Tests/UnitTests/Subscribers/Delivery/ServiceBusEventDeliveryServiceTests.cs index b7c65ff..a0af4bc 100644 --- a/src/AzureEventGridSimulator.Tests/UnitTests/Subscribers/Delivery/ServiceBusEventDeliveryServiceTests.cs +++ b/src/AzureEventGridSimulator.Tests/UnitTests/Subscribers/Delivery/ServiceBusEventDeliveryServiceTests.cs @@ -184,7 +184,7 @@ EventSchema schema } [Fact] - public void GivenEventGridFormatter_WhenSerializing_ThenReturnsJson() + public void GivenEventGridFormatter_WhenSerializing_ThenReturnsJsonArray() { var formatter = _formatterFactory.GetFormatter(EventSchema.EventGridSchema); var evt = CreateTestEvent(); @@ -193,10 +193,12 @@ public void GivenEventGridFormatter_WhenSerializing_ThenReturnsJson() json.ShouldNotBeNullOrEmpty(); json.ShouldContain("test-event-id"); + json.TrimStart().ShouldStartWith("["); + json.TrimEnd().ShouldEndWith("]"); } [Fact] - public void GivenCloudEventFormatter_WhenSerializing_ThenReturnsJson() + public void GivenCloudEventFormatter_WhenSerializing_ThenReturnsJsonArray() { var formatter = _formatterFactory.GetFormatter(EventSchema.CloudEventV1_0); var evt = CreateTestEvent(); @@ -205,6 +207,8 @@ public void GivenCloudEventFormatter_WhenSerializing_ThenReturnsJson() json.ShouldNotBeNullOrEmpty(); json.ShouldContain("test-event-id"); + json.TrimStart().ShouldStartWith("["); + json.TrimEnd().ShouldEndWith("]"); } [Fact] @@ -248,4 +252,34 @@ public void GivenPropertyResolver_WhenResolvingDynamicProperty_ThenReturnsEventV resolved["Subject"].ShouldBe("test/subject"); } + + [Fact] + public void GivenCloudEventFormatter_WhenSerializingSingle_ThenReturnsJsonWithoutArray() + { + var formatter = _formatterFactory.GetFormatter(EventSchema.CloudEventV1_0); + var evt = CreateTestEvent(); + + var json = formatter.SerializeSingle(evt); + + json.ShouldNotBeNullOrEmpty(); + json.ShouldContain("test-event-id"); + // Verify it's NOT an array (doesn't start with '[') + json.TrimStart().ShouldStartWith("{"); + json.TrimEnd().ShouldEndWith("}"); + } + + [Fact] + public void GivenEventGridFormatter_WhenSerializingSingle_ThenReturnsJsonWithoutArray() + { + var formatter = _formatterFactory.GetFormatter(EventSchema.EventGridSchema); + var evt = CreateTestEvent(); + + var json = formatter.SerializeSingle(evt); + + json.ShouldNotBeNullOrEmpty(); + json.ShouldContain("test-event-id"); + // Verify it's NOT an array (doesn't start with '[') + json.TrimStart().ShouldStartWith("{"); + json.TrimEnd().ShouldEndWith("}"); + } } diff --git a/src/AzureEventGridSimulator/Domain/Services/CloudEventSchemaFormatter.cs b/src/AzureEventGridSimulator/Domain/Services/CloudEventSchemaFormatter.cs index 3c54575..1974df9 100644 --- a/src/AzureEventGridSimulator/Domain/Services/CloudEventSchemaFormatter.cs +++ b/src/AzureEventGridSimulator/Domain/Services/CloudEventSchemaFormatter.cs @@ -33,6 +33,13 @@ public string Serialize(SimulatorEvent evt) return JsonSerializer.Serialize(new[] { cloudEvent }, _serializerOptions); } + /// + public string SerializeSingle(SimulatorEvent evt) + { + var cloudEvent = ConvertToCloudEvent(evt); + return JsonSerializer.Serialize(cloudEvent, _serializerOptions); + } + /// public string SerializeArray(IEnumerable events) { diff --git a/src/AzureEventGridSimulator/Domain/Services/Delivery/ServiceBusEventDeliveryService.cs b/src/AzureEventGridSimulator/Domain/Services/Delivery/ServiceBusEventDeliveryService.cs index 31481fd..15e81f6 100644 --- a/src/AzureEventGridSimulator/Domain/Services/Delivery/ServiceBusEventDeliveryService.cs +++ b/src/AzureEventGridSimulator/Domain/Services/Delivery/ServiceBusEventDeliveryService.cs @@ -58,8 +58,8 @@ CancellationToken cancellationToken subscription.DeliverySchema ?? delivery.Topic.OutputSchema ?? delivery.InputSchema; var formatter = formatterFactory.GetFormatter(deliverySchema); - // Serialize the event - var json = formatter.Serialize(delivery.Event); + // Serialize as a single event (matches Azure Event Grid to Service Bus behavior) + var json = formatter.SerializeSingle(delivery.Event); // Get or create the sender var sender = GetOrCreateSender(subscription); @@ -171,8 +171,8 @@ EventSchema inputSchema var deliverySchema = subscription.DeliverySchema ?? topic.OutputSchema ?? inputSchema; var formatter = formatterFactory.GetFormatter(deliverySchema); - // Serialize the event - var json = formatter.Serialize(evt); + // Serialize as a single event (matches Azure Event Grid to Service Bus behavior) + var json = formatter.SerializeSingle(evt); // Get or create the sender var sender = GetOrCreateSender(subscription); diff --git a/src/AzureEventGridSimulator/Domain/Services/EventGridSchemaFormatter.cs b/src/AzureEventGridSimulator/Domain/Services/EventGridSchemaFormatter.cs index f3377f7..8ac4272 100644 --- a/src/AzureEventGridSimulator/Domain/Services/EventGridSchemaFormatter.cs +++ b/src/AzureEventGridSimulator/Domain/Services/EventGridSchemaFormatter.cs @@ -21,6 +21,13 @@ public string Serialize(SimulatorEvent evt) return JsonSerializer.Serialize(new[] { eventGridEvent }); } + /// + public string SerializeSingle(SimulatorEvent evt) + { + var eventGridEvent = ConvertToEventGridEvent(evt); + return JsonSerializer.Serialize(eventGridEvent); + } + /// public string SerializeArray(IEnumerable events) { diff --git a/src/AzureEventGridSimulator/Domain/Services/IEventSchemaFormatter.cs b/src/AzureEventGridSimulator/Domain/Services/IEventSchemaFormatter.cs index 7bb4e30..3cbf2ff 100644 --- a/src/AzureEventGridSimulator/Domain/Services/IEventSchemaFormatter.cs +++ b/src/AzureEventGridSimulator/Domain/Services/IEventSchemaFormatter.cs @@ -19,6 +19,7 @@ public interface IEventSchemaFormatter /// /// Serializes an event to JSON for delivery. + /// By default, wraps single events in an array for Azure Event Grid compatibility. /// /// /// The event to serialize. @@ -28,6 +29,18 @@ public interface IEventSchemaFormatter /// string Serialize(SimulatorEvent evt); + /// + /// Serializes a single event to JSON without array wrapper. + /// Used for Service Bus delivery which doesn't use array format. + /// + /// + /// The event to serialize. + /// + /// + /// The JSON representation of the single event. + /// + string SerializeSingle(SimulatorEvent evt); + /// /// Serializes multiple events to JSON for delivery. /// diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index cc396f7..89b5caf 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -34,7 +34,7 @@ - + @@ -44,6 +44,6 @@ - + diff --git a/wiki b/wiki index 8638568..bf6f87f 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 8638568a117a71446e81eb7a927be343c0f0f963 +Subproject commit bf6f87fc527d6c9c6bc1db5cd61bd0b443cd83cf