diff --git a/Masa.Framework.sln b/Masa.Framework.sln index 866922a49..1e2c7aaff 100644 --- a/Masa.Framework.sln +++ b/Masa.Framework.sln @@ -625,6 +625,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Utils.DynamicsCrm.Core EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Utils.DynamicsCrm.EntityFrameworkCore", "src\Utils\DynamicsCrm\Masa.Utils.DynamicsCrm.EntityFrameworkCore\Masa.Utils.DynamicsCrm.EntityFrameworkCore.csproj", "{8A51A2A9-FBF4-40DC-AD89-AD3B9D3A50DC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Framework.EventApiTest", "test\Masa.Framework.EventApiTest\Masa.Framework.EventApiTest.csproj", "{345F5F33-8788-43F3-9005-B3EEBC007C72}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -2185,6 +2187,14 @@ Global {8A51A2A9-FBF4-40DC-AD89-AD3B9D3A50DC}.Release|Any CPU.Build.0 = Release|Any CPU {8A51A2A9-FBF4-40DC-AD89-AD3B9D3A50DC}.Release|x64.ActiveCfg = Release|Any CPU {8A51A2A9-FBF4-40DC-AD89-AD3B9D3A50DC}.Release|x64.Build.0 = Release|Any CPU + {345F5F33-8788-43F3-9005-B3EEBC007C72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {345F5F33-8788-43F3-9005-B3EEBC007C72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {345F5F33-8788-43F3-9005-B3EEBC007C72}.Debug|x64.ActiveCfg = Debug|Any CPU + {345F5F33-8788-43F3-9005-B3EEBC007C72}.Debug|x64.Build.0 = Debug|Any CPU + {345F5F33-8788-43F3-9005-B3EEBC007C72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {345F5F33-8788-43F3-9005-B3EEBC007C72}.Release|Any CPU.Build.0 = Release|Any CPU + {345F5F33-8788-43F3-9005-B3EEBC007C72}.Release|x64.ActiveCfg = Release|Any CPU + {345F5F33-8788-43F3-9005-B3EEBC007C72}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2493,6 +2503,7 @@ Global {64B54122-44F1-4379-9422-953EF706A3A6} = {5944A182-13B8-4DA6-AEE2-0A01E64A9648} {83310F46-E1C7-4438-B32A-9F6F7EA13FCF} = {64B54122-44F1-4379-9422-953EF706A3A6} {8A51A2A9-FBF4-40DC-AD89-AD3B9D3A50DC} = {64B54122-44F1-4379-9422-953EF706A3A6} + {345F5F33-8788-43F3-9005-B3EEBC007C72} = {E747043D-81E2-4A89-8B5B-1258ED45F941} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {40383055-CC50-4600-AD9A-53C14F620D03} diff --git a/src/BuildingBlocks/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Logs/IIntegrationEventLogService.cs b/src/BuildingBlocks/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Logs/IIntegrationEventLogService.cs index 62cbccb11..e1104ceb6 100644 --- a/src/BuildingBlocks/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Logs/IIntegrationEventLogService.cs +++ b/src/BuildingBlocks/Dispatcher/Masa.BuildingBlocks.Dispatcher.IntegrationEvents/Logs/IIntegrationEventLogService.cs @@ -37,10 +37,16 @@ Task SaveEventAsync( Task MarkEventAsPublishedAsync(Guid eventId, CancellationToken cancellationToken = default); + Task BulkMarkEventAsPublishedAsync(IEnumerable eventIds, CancellationToken cancellationToken = default); + Task MarkEventAsInProgressAsync(Guid eventId, int minimumRetryInterval, CancellationToken cancellationToken = default); + Task BulkMarkEventAsInProgressAsync(IEnumerable eventIds, int minimumRetryInterval, CancellationToken cancellationToken = default); + Task MarkEventAsFailedAsync(Guid eventId, CancellationToken cancellationToken = default); + Task BulkMarkEventAsFailedAsync(IEnumerable eventIds, CancellationToken cancellationToken = default); + /// /// Delete successfully published and expired data /// diff --git a/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/Publisher.cs b/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/Publisher.cs index a9839ec31..b66eaa941 100644 --- a/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/Publisher.cs +++ b/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/Publisher.cs @@ -67,4 +67,59 @@ public async Task PublishAsync( @event); } } + + public async Task BulkPublishAsync( + string topicName, List<(T @event, IntegrationEventExpand? eventMessageExpand)> @events, + CancellationToken stoppingToken = default) + { + + _logger?.LogDebug("-----BulkPublishEvent Integration event publishing is in progress from {AppId} with DaprAppId as '{DaprAppId}'", _appId, + _daprAppId); + + if (!@events.Any()) + return; + + MasaArgumentException.ThrowIfNullOrWhiteSpace(_daprAppId); + + var masaCloudEvents = new List>(); + var waitEvents = new List(); + + @events.ForEach(item => + { + if (item.eventMessageExpand is { Isolation.Count: > 0 }) + { + var eventMessage = new IntegrationEventMessage(item.@event, item.eventMessageExpand); + var masaCloudEvent = new MasaCloudEvent(eventMessage) + { + Source = new Uri(_daprAppId, UriKind.RelativeOrAbsolute) + }; + + masaCloudEvents.Add(masaCloudEvent); + } + else + { + waitEvents.Add(item.@event); + } + }); + + if (masaCloudEvents.Any()) + { + await DaprClient.PublishEventAsync(_pubSubName, topicName, masaCloudEvents, stoppingToken); + _logger?.LogDebug( + "-----BulkPublishEvent Publishing integration event from {AppId} succeeded with DaprAppId is {DaprAppId} and Event is {Event}", + _appId, + _daprAppId, + masaCloudEvents); + } + + if (waitEvents.Any()) + { + await DaprClient.BulkPublishEventAsync(_pubSubName, topicName, @events.ToList(), cancellationToken: stoppingToken); + _logger?.LogDebug( + "-----BulkPublishEvent Publishing integration event from {AppId} succeeded with DaprAppId is {DaprAppId} and Event is {Event}", + _appId, + _daprAppId, + @events); + } + } } diff --git a/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EFCore/IntegrationEventLogService.cs b/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EFCore/IntegrationEventLogService.cs index 7caff061d..867c9e966 100644 --- a/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EFCore/IntegrationEventLogService.cs +++ b/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EFCore/IntegrationEventLogService.cs @@ -106,6 +106,25 @@ public Task MarkEventAsPublishedAsync(Guid eventId, CancellationToken cancellati }, cancellationToken); } + public Task BulkMarkEventAsPublishedAsync(IEnumerable eventIds, CancellationToken cancellationToken = default) + { + return BulkUpdateEventStatus(eventIds, IntegrationEventStates.Published, eventLogs => + { + eventLogs.ForEach(eventLog => + { + if (eventLog.State != IntegrationEventStates.InProgress) + { + _logger?.LogWarning( + "Failed to modify the state of the local message table to {OptState}, the current State is {State}, Id: {Id}", + IntegrationEventStates.Published, eventLog.State, eventLog.Id); + throw new UserFriendlyException( + $"Failed to modify the state of the local message table to {IntegrationEventStates.Published}, the current State is {eventLog.State}, Id: {eventLog.Id}"); + } + }); + + }, cancellationToken); + } + public Task MarkEventAsInProgressAsync(Guid eventId, int minimumRetryInterval, CancellationToken cancellationToken = default) { return UpdateEventStatus(eventId, IntegrationEventStates.InProgress, eventLog => @@ -132,6 +151,35 @@ public Task MarkEventAsInProgressAsync(Guid eventId, int minimumRetryInterval, C }, cancellationToken); } + public Task BulkMarkEventAsInProgressAsync(IEnumerable eventIds, int minimumRetryInterval, CancellationToken cancellationToken = default) + { + return BulkUpdateEventStatus(eventIds, IntegrationEventStates.InProgress, eventLogs => + { + eventLogs.ForEach(eventLog => + { + if (eventLog.State is IntegrationEventStates.InProgress or IntegrationEventStates.PublishedFailed && + (eventLog.GetCurrentTime() - eventLog.ModificationTime).TotalSeconds < minimumRetryInterval) + { + _logger?.LogInformation( + "Failed to modify the state of the local message table to {OptState}, the current State is {State}, Id: {Id}, Multitasking execution error, waiting for the next retry", + IntegrationEventStates.InProgress, eventLog.State, eventLog.Id); + throw new UserFriendlyException( + $"Failed to modify the state of the local message table to {IntegrationEventStates.InProgress}, the current State is {eventLog.State}, Id: {eventLog.Id}, Multitasking execution error, waiting for the next retry"); + } + if (eventLog.State != IntegrationEventStates.NotPublished && + eventLog.State != IntegrationEventStates.InProgress && + eventLog.State != IntegrationEventStates.PublishedFailed) + { + _logger?.LogWarning( + "Failed to modify the state of the local message table to {OptState}, the current State is {State}, Id: {Id}", + IntegrationEventStates.InProgress, eventLog.State, eventLog.Id); + throw new UserFriendlyException( + $"Failed to modify the state of the local message table to {IntegrationEventStates.InProgress}, the current State is {eventLog.State}, Id: {eventLog.Id}"); + } + }); + }, cancellationToken); + } + public Task MarkEventAsFailedAsync(Guid eventId, CancellationToken cancellationToken = default) { return UpdateEventStatus(eventId, IntegrationEventStates.PublishedFailed, eventLog => @@ -147,6 +195,24 @@ public Task MarkEventAsFailedAsync(Guid eventId, CancellationToken cancellationT }, cancellationToken); } + public Task BulkMarkEventAsFailedAsync(IEnumerable eventIds, CancellationToken cancellationToken = default) + { + return BulkUpdateEventStatus(eventIds, IntegrationEventStates.PublishedFailed, eventLogs => + { + eventLogs.ForEach(eventLog => + { + if (eventLog.State != IntegrationEventStates.InProgress) + { + _logger?.LogWarning( + "Failed to modify the state of the local message table to {OptState}, the current State is {State}, Id: {Id}", + IntegrationEventStates.PublishedFailed, eventLog.State, eventLog.Id); + throw new UserFriendlyException( + $"Failed to modify the state of the local message table to {IntegrationEventStates.PublishedFailed}, the current State is {eventLog.State}, Id: {eventLog.Id}"); + } + }); + }, cancellationToken); + } + public async Task DeleteExpiresAsync(DateTime expiresAt, int batchCount, CancellationToken token = default) { var eventLogs = _eventLogContext.EventLogs.Where(e => e.ModificationTime < expiresAt && e.State == IntegrationEventStates.Published) @@ -164,6 +230,51 @@ public async Task DeleteExpiresAsync(DateTime expiresAt, int batchCount, Cancell } } + private async Task BulkUpdateEventStatus(IEnumerable eventIds, + IntegrationEventStates status, + Action>? action = null, + CancellationToken cancellationToken = default) + { + var eventLogEntrys = + await _eventLogContext.EventLogs.Where(e => eventIds.Contains(e.EventId)).ToListAsync(); + if (eventLogEntrys == null || !eventLogEntrys.Any()) + throw new ArgumentException( + $"The local message record does not exist, please confirm whether the local message record has been deleted or other reasons cause the local message record to not be inserted successfully In EventId: {eventIds}", + nameof(eventIds)); + + action?.Invoke(eventLogEntrys); + + var updateEventLogEntry = new List(); + foreach (var eventLogEntry in eventLogEntrys) + { + if (eventLogEntry.State == status) + { + continue; + } + + eventLogEntry.State = status; + eventLogEntry.ModificationTime = eventLogEntry.GetCurrentTime(); + + if (status == IntegrationEventStates.InProgress) + eventLogEntry.TimesSent++; + + updateEventLogEntry.Add(eventLogEntry); + } + + _eventLogContext.EventLogs.UpdateRange(updateEventLogEntry); + + try + { + await _eventLogContext.DbContext.SaveChangesAsync(cancellationToken); + } + catch (DbUpdateConcurrencyException ex) + { + throw new UserFriendlyException($"Concurrency conflict, update exception. {ex.Message}"); + } + + updateEventLogEntry.ForEach(CheckAndDetached); + } + private async Task UpdateEventStatus(Guid eventId, IntegrationEventStates status, Action? action = null, diff --git a/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/IPublisher.cs b/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/IPublisher.cs index 7c6c35bb5..5c3792c38 100644 --- a/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/IPublisher.cs +++ b/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/IPublisher.cs @@ -1,4 +1,4 @@ -// Copyright (c) MASA Stack All rights reserved. +// Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. namespace Masa.Contrib.Dispatcher.IntegrationEvents; @@ -10,4 +10,8 @@ Task PublishAsync( T @event, IntegrationEventExpand? eventMessageExpand, CancellationToken stoppingToken = default); + + Task BulkPublishAsync( + string topicName, List<(T @event, IntegrationEventExpand? eventMessageExpand)> @events, + CancellationToken stoppingToken = default); } diff --git a/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/Internal/LocalQueueProcessor.cs b/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/Internal/LocalQueueProcessor.cs index e0fcb6d4d..299032cc6 100644 --- a/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/Internal/LocalQueueProcessor.cs +++ b/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/Internal/LocalQueueProcessor.cs @@ -22,6 +22,9 @@ public static void SetLogger(IServiceCollection services) public void AddJobs(IntegrationEventLogItem items) => _retryEventLogs.TryAdd(items.EventId, items); + public void BulkAddJobs(List items) + => items.ForEach(item => _retryEventLogs.TryAdd(item.EventId, item)); + public void RemoveJobs(Guid eventId) => _retryEventLogs.TryRemove(eventId, out _); diff --git a/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/Processor/SendByDataProcessor.cs b/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/Processor/SendByDataProcessor.cs index fc45942a6..38f653c5b 100644 --- a/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/Processor/SendByDataProcessor.cs +++ b/src/Contrib/Dispatcher/IntegrationEvents/Masa.Contrib.Dispatcher.IntegrationEvents/Processor/SendByDataProcessor.cs @@ -35,7 +35,68 @@ await eventLogService.RetrieveEventLogsPendingToPublishAsync( _options.Value.BatchSize, stoppingToken); - if(!retrieveEventLogs.Any()) + if (!retrieveEventLogs.Any()) + return; + + var publisher = serviceProvider.GetRequiredService(); + var retrieveEventLogsGroupByTopic = retrieveEventLogs.GroupBy(eventLog => eventLog.Topic) + .Select(eventLog => new + { + TopicName = eventLog.Key, + Events = eventLog.Select(log => new { log.Event, log.EventExpand, log.EventId }).ToList(), + }).ToList(); + + foreach (var eventLog in retrieveEventLogsGroupByTopic) + { + var eventIds = eventLog.Events.Select(item => item.EventId); + var events = eventLog.Events.Select(item => (item.Event, item.EventExpand)).ToList(); + + try + { + await eventLogService.BulkMarkEventAsInProgressAsync(eventIds, _options.Value.MinimumRetryInterval, stoppingToken); + + _logger?.LogDebug("Publishing integration event {Event} to {TopicName}", + eventLog, + eventLog.TopicName); + + await publisher.BulkPublishAsync(eventLog.TopicName, events, stoppingToken); + + await eventLogService.BulkMarkEventAsPublishedAsync(eventIds, stoppingToken); + } + catch (UserFriendlyException) + { + //Update state due to multitasking contention, no processing required + } + catch (Exception ex) + { + _logger?.LogError(ex, + "Error Publishing integration event: {IntegrationEventId} from {AppId} - ({IntegrationEvent})", + eventIds, _masaAppConfigureOptions?.CurrentValue.AppId ?? string.Empty, eventLog); + await eventLogService.BulkMarkEventAsFailedAsync(eventIds, stoppingToken); + + var integrationEventLogItem = eventLog.Events.Select(item => + new IntegrationEventLogItem(item.EventId, eventLog.TopicName, item.Event, item.EventExpand)).ToList(); + + LocalQueueProcessor.Default.BulkAddJobs(integrationEventLogItem); + } + } + } + + [Obsolete] + private async Task ExecuteByObsoleteAsync(IServiceProvider serviceProvider, CancellationToken stoppingToken) + { + var unitOfWork = serviceProvider.GetService(); + if (unitOfWork != null) + unitOfWork.UseTransaction = false; + + var eventLogService = serviceProvider.GetRequiredService(); + + var retrieveEventLogs = + await eventLogService.RetrieveEventLogsPendingToPublishAsync( + _options.Value.BatchSize, + stoppingToken); + + if (!retrieveEventLogs.Any()) return; var publisher = serviceProvider.GetRequiredService(); @@ -65,7 +126,8 @@ await eventLogService.RetrieveEventLogsPendingToPublishAsync( eventLog.EventId, _masaAppConfigureOptions?.CurrentValue.AppId ?? string.Empty, eventLog); await eventLogService.MarkEventAsFailedAsync(eventLog.EventId, stoppingToken); - LocalQueueProcessor.Default.AddJobs(new IntegrationEventLogItem(eventLog.EventId, eventLog.Topic, eventLog.Event, eventLog.EventExpand)); + LocalQueueProcessor.Default.AddJobs(new IntegrationEventLogItem(eventLog.EventId, eventLog.Topic, eventLog.Event, + eventLog.EventExpand)); } } } diff --git a/src/Contrib/Dispatcher/IntegrationEvents/Tests/Masa.Contrib.Dispatcher.IntegrationEvents.Tests/Infrastructure/CustomIntegrationEventLogService.cs b/src/Contrib/Dispatcher/IntegrationEvents/Tests/Masa.Contrib.Dispatcher.IntegrationEvents.Tests/Infrastructure/CustomIntegrationEventLogService.cs index 9d354eb8d..c6fdfe995 100644 --- a/src/Contrib/Dispatcher/IntegrationEvents/Tests/Masa.Contrib.Dispatcher.IntegrationEvents.Tests/Infrastructure/CustomIntegrationEventLogService.cs +++ b/src/Contrib/Dispatcher/IntegrationEvents/Tests/Masa.Contrib.Dispatcher.IntegrationEvents.Tests/Infrastructure/CustomIntegrationEventLogService.cs @@ -1,4 +1,4 @@ -// Copyright (c) MASA Stack All rights reserved. +// Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. namespace Masa.Contrib.Dispatcher.IntegrationEvents.Tests.Infrastructure; @@ -47,4 +47,19 @@ public Task SaveEventAsync( { return Task.CompletedTask; } + + public Task BulkMarkEventAsPublishedAsync(IEnumerable eventIds, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + + public Task BulkMarkEventAsInProgressAsync(IEnumerable eventIds, int minimumRetryInterval, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + + public Task BulkMarkEventAsFailedAsync(IEnumerable eventIds, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } } diff --git a/src/Contrib/Dispatcher/Masa.Contrib.Dispatcher.Events/Internal/Options/DispatcherOptions.cs b/src/Contrib/Dispatcher/Masa.Contrib.Dispatcher.Events/Internal/Options/DispatcherOptions.cs index b2bcf02af..bd5f4e9c1 100644 --- a/src/Contrib/Dispatcher/Masa.Contrib.Dispatcher.Events/Internal/Options/DispatcherOptions.cs +++ b/src/Contrib/Dispatcher/Masa.Contrib.Dispatcher.Events/Internal/Options/DispatcherOptions.cs @@ -28,12 +28,12 @@ public DispatcherOptions(IServiceCollection services, Assembly[] assemblies) .Where(type => type.IsClass && typeof(IEvent).IsAssignableFrom(type)) .ToList(); - allEventTypes.AddRange(GetGenericTypeEventType(assemblies)); + allEventTypes.AddRange(GetGenericEventType(assemblies)); UnitOfWorkRelation = allEventTypes.ToDictionary(type => type, IsSupportUnitOfWork); } - private List GetGenericTypeEventType(Assembly[] assemblies) + private List GetGenericEventType(Assembly[] assemblies) { var methods = assemblies .SelectMany(assembly => assembly.GetTypes().SelectMany(method => method.GetMethods())) diff --git a/test/Masa.Framework.EventApiTest/Application/Command/RegisterUserCommand.cs b/test/Masa.Framework.EventApiTest/Application/Command/RegisterUserCommand.cs new file mode 100644 index 000000000..125728960 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Application/Command/RegisterUserCommand.cs @@ -0,0 +1,11 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Application.Command; + +public record RegisterUserCommand : BuildingBlocks.ReadWriteSplitting.Cqrs.Commands.Command +{ + public string Name { get; set; } + + public int Age { get; set; } +} diff --git a/test/Masa.Framework.EventApiTest/Application/Events/AddGoodsIntegrationEvent.cs b/test/Masa.Framework.EventApiTest/Application/Events/AddGoodsIntegrationEvent.cs new file mode 100644 index 000000000..b52043035 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Application/Events/AddGoodsIntegrationEvent.cs @@ -0,0 +1,17 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Application.Events; + +public record AddGoodsIntegrationEvent : IntegrationEvent +{ + public Guid Id { get; set; } + + public string Name { get; set; } + + public int Count { get; set; } + + public decimal Price { get; set; } + + public override string Topic { get; set; } = nameof(AddGoodsIntegrationEvent); +} diff --git a/test/Masa.Framework.EventApiTest/Application/Events/RegisterUserEvent.cs b/test/Masa.Framework.EventApiTest/Application/Events/RegisterUserEvent.cs new file mode 100644 index 000000000..febb72e6a --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Application/Events/RegisterUserEvent.cs @@ -0,0 +1,26 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +using Masa.BuildingBlocks.Ddd.Domain.Events; + +namespace Masa.Framework.IntegrationTests.EventBus.Application.Events; + +public record RegisterUserEvent : Event +{ + public string Name { get; set; } + + public int Age { get; set; } +} +public record RegisterUserDomainEvent : DomainCommand +{ + public string Name { get; set; } + + public int Age { get; set; } +} + +public record RegisterUserIntegrationDomainEvent : IntegrationDomainEvent +{ + public string Name { get; set; } + + public int Age { get; set; } +} diff --git a/test/Masa.Framework.EventApiTest/Application/Queries/CheckUserQuery.cs b/test/Masa.Framework.EventApiTest/Application/Queries/CheckUserQuery.cs new file mode 100644 index 000000000..e43964af6 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Application/Queries/CheckUserQuery.cs @@ -0,0 +1,11 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Application.Queries; + +public record CheckUserQuery : Query +{ + public string Name { get; set; } + + public override bool Result { get; set; } +} diff --git a/test/Masa.Framework.EventApiTest/Application/Queries/CheckUserQueryValidator.cs b/test/Masa.Framework.EventApiTest/Application/Queries/CheckUserQueryValidator.cs new file mode 100644 index 000000000..d7255d017 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Application/Queries/CheckUserQueryValidator.cs @@ -0,0 +1,12 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Application.Queries; + +public class CheckUserQueryValidator : AbstractValidator +{ + public CheckUserQueryValidator() + { + RuleFor(u => u.Name).NotNull().WithMessage("Name is required on CheckUserQuery"); + } +} diff --git a/test/Masa.Framework.EventApiTest/Application/Queries/UserAgeQuery.cs b/test/Masa.Framework.EventApiTest/Application/Queries/UserAgeQuery.cs new file mode 100644 index 000000000..172807102 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Application/Queries/UserAgeQuery.cs @@ -0,0 +1,11 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Application.Queries; + +public record UserAgeQuery : Query +{ + public string Name { get; set; } + + public override int Result { get; set; } +} diff --git a/test/Masa.Framework.EventApiTest/Application/Queries/UserAgeQueryValidator.cs b/test/Masa.Framework.EventApiTest/Application/Queries/UserAgeQueryValidator.cs new file mode 100644 index 000000000..6ed387503 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Application/Queries/UserAgeQueryValidator.cs @@ -0,0 +1,12 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Application.Queries; + +public class UserAgeQueryValidator: AbstractValidator +{ + public UserAgeQueryValidator() + { + RuleFor(u => u.Name).NotNull().WithMessage("Name is required on UserAgeQuery"); + } +} diff --git a/test/Masa.Framework.EventApiTest/Application/UserHandler.cs b/test/Masa.Framework.EventApiTest/Application/UserHandler.cs new file mode 100644 index 000000000..4195bb848 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Application/UserHandler.cs @@ -0,0 +1,89 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +using Masa.BuildingBlocks.Ddd.Domain.Events; + +namespace Masa.Framework.IntegrationTests.EventBus.Application; + +public class UserHandler +{ + private readonly IEventBus _eventBus; + private readonly IRepository _repository; + private readonly IUnitOfWork _unitOfWork; + + public UserHandler(IEventBus eventBus, IRepository repository, IUnitOfWork unitOfWork) + { + _eventBus = eventBus; + _repository = repository; + _unitOfWork = unitOfWork; + } + + [EventHandler] + public async Task RegisterUserByEventAsync(RegisterUserEvent @event) + { + await _repository.AddAsync(new User() + { + Name = @event.Name, + Age = @event.Age + }); + } + + [EventHandler] + public async Task RegisterUserByCommandAsync(RegisterUserCommand command) + { + var query = new CheckUserQuery() + { + Name = command.Name + }; + await _eventBus.PublishAsync(query); + if (query.Result) + throw new Exception($"User 【{command.Name}】 already exists"); + + await _repository.AddAsync(new User() + { + Name = command.Name, + Age = command.Age + }); + } + + [EventHandler] + public async Task UserExistAsync(UserAgeQuery query) + { + var checkUserQuery = new CheckUserQuery(); //Check whether the second verification can enter normally + await Assert.ThrowsExceptionAsync(async () => await _eventBus.PublishAsync(checkUserQuery), "Name is required on CheckUserQuery"); + if (!checkUserQuery.Result) + return; + + var user = await _repository.FindAsync(u => u.Name == query.Name); + query.Result = user!.Age; + } + + [EventHandler] + public async Task UserExistAsync(CheckUserQuery query) + { + var user = await _repository.FindAsync(u => u.Name == query.Name); + query.Result = user != null; + } + + [EventHandler] + public async Task UserEntityCreatedEventAsync(EntityCreatedDomainEvent command) + { + var userEntity = command.Entity; + if (userEntity is null) + { + throw new Exception($"User 【{nameof(UserEntityCreatedEventAsync)}】 already exists"); + } + } + + [EventHandler] + public async Task RegisterUserDomainEvent(RegisterUserDomainEvent command) + { + await Console.Out.WriteLineAsync($"User 【{nameof(RegisterUserDomainEvent)}】 already exists"); + } + + [EventHandler] + public async Task RegisterUserIntegrationDomainEvent(RegisterUserIntegrationDomainEvent command) + { + await Console.Out.WriteLineAsync($"User 【{nameof(RegisterUserIntegrationDomainEvent)}】 already exists"); + } +} diff --git a/test/Masa.Framework.EventApiTest/Controllers/WeatherForecastController.cs b/test/Masa.Framework.EventApiTest/Controllers/WeatherForecastController.cs new file mode 100644 index 000000000..38a496009 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Controllers/WeatherForecastController.cs @@ -0,0 +1,76 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +using Dapr; +using Microsoft.AspNetCore.Mvc; + +namespace Masa.Framework.EventApiTest.Controllers +{ + [ApiController] + [Route("[controller]/[action]")] + public class WeatherForecastController : ControllerBase + { + private readonly ILogger _logger; + private readonly IEventBus eventBus; + private readonly CustomDbContext dbContext; + private readonly IUnitOfWork unitOfWork; + private readonly Masa.Framework.EventApiTest.Domain.IUserRepository userRepository; + private readonly IIntegrationEventBus integrationEventBus; + + public WeatherForecastController(ILogger logger, IEventBus eventBus, CustomDbContext customDbContext, IUnitOfWork unitOfWork, Domain.IUserRepository userRepository, IIntegrationEventBus integrationEventBus) + { + _logger = logger; + this.eventBus = eventBus; + this.dbContext = customDbContext; + this.unitOfWork = unitOfWork; + this.userRepository = userRepository; + this.integrationEventBus = integrationEventBus; + } + + [HttpGet] + public async Task Add() + { + var user = new User + { + Age = 18, + Name = 1.ToString() + }; + //user.RegisterUserIntegrationDomainEvent(); + user.RegisterUserDomainEvent(); + + await userRepository.AddAsync(user); + await userRepository.UnitOfWork.SaveChangesAsync(); + await userRepository.UnitOfWork.CommitAsync(); + + //await dbContext.Set().AddAsync(user); + //await unitOfWork.SaveChangesAsync(); + //await unitOfWork.CommitAsync(); + var count = await userRepository.GetCountAsync(); + await Console.Out.WriteLineAsync(count.ToString()); + + return count; + } + + [HttpGet] + public async Task IntegrationEventBus() + { + await integrationEventBus.PublishAsync(new AddGoodsIntegrationEvent() + { + Name = "Apple", + Count = 1, + Id = Guid.NewGuid(), + Price = 9.9m, + }); + + return 1; + } + + [HttpPost] + [Topic("pubsub", nameof(AddGoodsIntegrationEvent))] + public async Task UnlockDeviceBindingStatusAsync(AddGoodsIntegrationEvent integrationEvent) + { + await Console.Out.WriteLineAsync("123"); + } + + } +} diff --git a/test/Masa.Framework.EventApiTest/Domain/Aggregate/User.cs b/test/Masa.Framework.EventApiTest/Domain/Aggregate/User.cs new file mode 100644 index 000000000..14078516e --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Domain/Aggregate/User.cs @@ -0,0 +1,27 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Domain.Aggregate; + +public class User : AggregateRoot +{ + public string Name { get; set; } + + public int Age { get; set; } + + public User() + { + Id = Guid.NewGuid(); + } + + public void RegisterUserIntegrationDomainEvent() { + + base.AddDomainEvent(new RegisterUserIntegrationDomainEvent()); + } + public void RegisterUserDomainEvent() { + + base.AddDomainEvent(new RegisterUserDomainEvent()); + } + + +} diff --git a/test/Masa.Framework.EventApiTest/Domain/IUserRepository.cs b/test/Masa.Framework.EventApiTest/Domain/IUserRepository.cs new file mode 100644 index 000000000..b0cced648 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Domain/IUserRepository.cs @@ -0,0 +1,9 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.EventApiTest.Domain +{ + public interface IUserRepository : IRepository + { + } +} diff --git a/test/Masa.Framework.EventApiTest/Infrastructure/CustomDbContext.cs b/test/Masa.Framework.EventApiTest/Infrastructure/CustomDbContext.cs new file mode 100644 index 000000000..077141377 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Infrastructure/CustomDbContext.cs @@ -0,0 +1,13 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Infrastructure; + +public class CustomDbContext : MasaDbContext +{ + public DbSet User { get; set; } + + public CustomDbContext(MasaDbContextOptions options) : base(options) + { + } +} diff --git a/test/Masa.Framework.EventApiTest/Infrastructure/Extensions/DefaultPublisher.cs b/test/Masa.Framework.EventApiTest/Infrastructure/Extensions/DefaultPublisher.cs new file mode 100644 index 000000000..c56c62e72 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Infrastructure/Extensions/DefaultPublisher.cs @@ -0,0 +1,18 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + + +namespace Masa.Framework.IntegrationTests.EventBus.Infrastructure.Extensions; + +public class DefaultPublisher : IPublisher +{ + public Task BulkPublishAsync(string topicName, List<(T @event, IntegrationEventExpand? eventMessageExpand)> events, CancellationToken stoppingToken = default) + { + return Task.CompletedTask; + } + + public Task PublishAsync(string topicName, T @event, IntegrationEventExpand? eventMessageExpand, CancellationToken stoppingToken = default) + { + return Task.CompletedTask; + } +} diff --git a/test/Masa.Framework.EventApiTest/Infrastructure/Extensions/DispatcherOptionsExtensions.cs b/test/Masa.Framework.EventApiTest/Infrastructure/Extensions/DispatcherOptionsExtensions.cs new file mode 100644 index 000000000..c49134f74 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Infrastructure/Extensions/DispatcherOptionsExtensions.cs @@ -0,0 +1,14 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Infrastructure.Extensions; + +public static class DispatcherOptionsExtensions +{ + public static Contrib.Dispatcher.IntegrationEvents.Options.IntegrationEventOptions UseTestPub( + this Contrib.Dispatcher.IntegrationEvents.Options.IntegrationEventOptions dispatcherOptions) + { + dispatcherOptions.Services.TryAddSingleton(); + return dispatcherOptions; + } +} diff --git a/test/Masa.Framework.EventApiTest/Infrastructure/Middleware/RecordEventMiddleware.cs b/test/Masa.Framework.EventApiTest/Infrastructure/Middleware/RecordEventMiddleware.cs new file mode 100644 index 000000000..60b045561 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Infrastructure/Middleware/RecordEventMiddleware.cs @@ -0,0 +1,18 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Infrastructure.Middleware; + +public class RecordEventMiddleware : EventMiddleware + where TEvent : IEvent +{ + public static int Time { get; set; } + + public override bool SupportRecursive => false; + + public override async Task HandleAsync(TEvent @event, EventHandlerDelegate next) + { + Time++; + await next(); + } +} diff --git a/test/Masa.Framework.EventApiTest/Infrastructure/Middleware/ValidatorEventMiddleware.cs b/test/Masa.Framework.EventApiTest/Infrastructure/Middleware/ValidatorEventMiddleware.cs new file mode 100644 index 000000000..f9a9b75a7 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Infrastructure/Middleware/ValidatorEventMiddleware.cs @@ -0,0 +1,31 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.IntegrationTests.EventBus.Infrastructure.Middleware; + +public class ValidatorEventMiddleware : EventMiddleware + where TEvent : notnull, IEvent +{ + private readonly IEnumerable> _validators; + + public ValidatorEventMiddleware(IEnumerable> validators) + { + _validators = validators; + } + + public override async Task HandleAsync(TEvent action, EventHandlerDelegate next) + { + var failures = _validators + .Select(v => v.Validate(action)) + .SelectMany(result => result.Errors) + .Where(error => error != null) + .ToList(); + + if (failures.Any()) + { + throw new ValidationException(failures.Select(x=>x.ErrorMessage).FirstOrDefault()); + } + + await next(); + } +} diff --git a/test/Masa.Framework.EventApiTest/Infrastructure/UserRepository.cs b/test/Masa.Framework.EventApiTest/Infrastructure/UserRepository.cs new file mode 100644 index 000000000..da7d18ef1 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Infrastructure/UserRepository.cs @@ -0,0 +1,13 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + + +namespace Masa.Framework.EventApiTest.Infrastructure +{ + public class UserRepository : Masa.Contrib.Ddd.Domain.Repository.EFCore.Repository, Masa.Framework.EventApiTest.Domain.IUserRepository + { + public UserRepository(CustomDbContext context, IUnitOfWork unitOfWork) : base(context, unitOfWork) + { + } + } +} diff --git a/test/Masa.Framework.EventApiTest/Masa.Framework.EventApiTest.csproj b/test/Masa.Framework.EventApiTest/Masa.Framework.EventApiTest.csproj new file mode 100644 index 000000000..93aceeafe --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Masa.Framework.EventApiTest.csproj @@ -0,0 +1,38 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + diff --git a/test/Masa.Framework.EventApiTest/Masa.Framework.EventApiTest.http b/test/Masa.Framework.EventApiTest/Masa.Framework.EventApiTest.http new file mode 100644 index 000000000..fcf285ea5 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Masa.Framework.EventApiTest.http @@ -0,0 +1,6 @@ +@Masa.Framework.EventApiTest_HostAddress = http://localhost:5021 + +GET {{Masa.Framework.EventApiTest_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/test/Masa.Framework.EventApiTest/Program.cs b/test/Masa.Framework.EventApiTest/Program.cs new file mode 100644 index 000000000..e67c8ff82 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Program.cs @@ -0,0 +1,55 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +using FluentValidation.AspNetCore; +using Masa.BuildingBlocks.Dispatcher.IntegrationEvents; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +var builder = WebApplication.CreateBuilder(args); + +if (builder.Environment.IsDevelopment()) +{ + builder.Services.AddDaprStarter(builder.Configuration.GetSection("DaprStarter")); +} + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Services.AddFluentValidation(options => +{ + options.RegisterValidatorsFromAssemblyContaining(); +}); +builder.Services.AddDomainEventBus(dispatcherOptions => +{ + dispatcherOptions + .UseIntegrationEventBus(option => option.UseDapr().UseEventLog())// + .UseEventBus(eventBusBuilder => eventBusBuilder.UseMiddleware(typeof(RecordEventMiddleware<>)).UseMiddleware(typeof(ValidatorEventMiddleware<>))) + .UseUoW(optionBuilder => + { + optionBuilder.UseSqlite($"data source=disabled-soft-delete-db-030e2f29-3398-493e-831a-0df85d3cba90").UseFilter(); + //optionBuilder.UseSqlite($"data source=disabled-soft-delete-db-{Guid.NewGuid()}").UseFilter(); + }) + .UseRepository(); +}); +//var dbContext = builder.Services.BuildServiceProvider().GetRequiredService(); +//dbContext.Database.EnsureCreated(); +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/test/Masa.Framework.EventApiTest/Properties/launchSettings.json b/test/Masa.Framework.EventApiTest/Properties/launchSettings.json new file mode 100644 index 000000000..4fb629230 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:33162", + "sslPort": 44300 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5021", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7269;http://localhost:5021", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/Masa.Framework.EventApiTest/WeatherForecast.cs b/test/Masa.Framework.EventApiTest/WeatherForecast.cs new file mode 100644 index 000000000..fb578f4a5 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/WeatherForecast.cs @@ -0,0 +1,16 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Framework.EventApiTest +{ + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } + } +} diff --git a/test/Masa.Framework.EventApiTest/_Imports.cs b/test/Masa.Framework.EventApiTest/_Imports.cs new file mode 100644 index 000000000..f7d877b0b --- /dev/null +++ b/test/Masa.Framework.EventApiTest/_Imports.cs @@ -0,0 +1,25 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +global using FluentValidation; +global using FluentValidation.AspNetCore; +global using Masa.BuildingBlocks.Data.UoW; +global using Masa.BuildingBlocks.Ddd.Domain.Entities; +global using Masa.BuildingBlocks.Ddd.Domain.Repositories; +global using Masa.BuildingBlocks.Dispatcher.Events; +global using Masa.BuildingBlocks.Dispatcher.IntegrationEvents; +global using Masa.BuildingBlocks.ReadWriteSplitting.Cqrs.Queries; +global using Masa.Contrib.Dispatcher.Events; +global using Masa.Contrib.Dispatcher.IntegrationEvents; +global using Masa.Framework.IntegrationTests.EventBus.Application.Command; +global using Masa.Framework.IntegrationTests.EventBus.Application.Events; +global using Masa.Framework.IntegrationTests.EventBus.Application.Queries; +global using Masa.Framework.IntegrationTests.EventBus.Domain.Aggregate; +global using Masa.Framework.IntegrationTests.EventBus.Infrastructure; +global using Masa.Framework.IntegrationTests.EventBus.Infrastructure.Extensions; +global using Masa.Framework.IntegrationTests.EventBus.Infrastructure.Middleware; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.VisualStudio.TestTools.UnitTesting; +global using System.Collections.Concurrent; diff --git a/test/Masa.Framework.EventApiTest/appsettings.Development.json b/test/Masa.Framework.EventApiTest/appsettings.Development.json new file mode 100644 index 000000000..a49f171a3 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/appsettings.Development.json @@ -0,0 +1,17 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + }, + "DaprStarter": { + "AppId": "loncloud-apis-aftersales", + "AppPort": 18900, + "MetricsPort": 18901, + "DaprGrpcPort": 18902, + "DaprHttpPort": 18903, + "CreateNoWindow": true, + "ExtendedParameter": " -placement-host-address localhost:6050 -components-path %USERPROFILE%\\.dapr\\components -log-level debug" + } + } +} diff --git a/test/Masa.Framework.EventApiTest/appsettings.json b/test/Masa.Framework.EventApiTest/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/test/Masa.Framework.EventApiTest/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-030e2f29-3398-493e-831a-0df85d3cba90 b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-030e2f29-3398-493e-831a-0df85d3cba90 new file mode 100644 index 000000000..15a78fed6 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-030e2f29-3398-493e-831a-0df85d3cba90 differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-030e2f29-3398-493e-831a-0df85d3cba90-shm b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-030e2f29-3398-493e-831a-0df85d3cba90-shm new file mode 100644 index 000000000..2617466be Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-030e2f29-3398-493e-831a-0df85d3cba90-shm differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-030e2f29-3398-493e-831a-0df85d3cba90-wal b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-030e2f29-3398-493e-831a-0df85d3cba90-wal new file mode 100644 index 000000000..6ff41c62e Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-030e2f29-3398-493e-831a-0df85d3cba90-wal differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-3c5ac305-031e-4528-b266-8dda3033996b b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-3c5ac305-031e-4528-b266-8dda3033996b new file mode 100644 index 000000000..e2a2455a1 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-3c5ac305-031e-4528-b266-8dda3033996b differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-3c5ac305-031e-4528-b266-8dda3033996b-shm b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-3c5ac305-031e-4528-b266-8dda3033996b-shm new file mode 100644 index 000000000..d89ea7b61 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-3c5ac305-031e-4528-b266-8dda3033996b-shm differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-3c5ac305-031e-4528-b266-8dda3033996b-wal b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-3c5ac305-031e-4528-b266-8dda3033996b-wal new file mode 100644 index 000000000..802677ef9 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-3c5ac305-031e-4528-b266-8dda3033996b-wal differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-771923ba-dbfc-4e66-a48e-3e3b88dab164 b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-771923ba-dbfc-4e66-a48e-3e3b88dab164 new file mode 100644 index 000000000..e2a2455a1 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-771923ba-dbfc-4e66-a48e-3e3b88dab164 differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-771923ba-dbfc-4e66-a48e-3e3b88dab164-shm b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-771923ba-dbfc-4e66-a48e-3e3b88dab164-shm new file mode 100644 index 000000000..9c12c8fcc Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-771923ba-dbfc-4e66-a48e-3e3b88dab164-shm differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-771923ba-dbfc-4e66-a48e-3e3b88dab164-wal b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-771923ba-dbfc-4e66-a48e-3e3b88dab164-wal new file mode 100644 index 000000000..24646f79d Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-771923ba-dbfc-4e66-a48e-3e3b88dab164-wal differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-8be0bdd5-e9d9-47a2-9469-2ad8cd398c5a b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-8be0bdd5-e9d9-47a2-9469-2ad8cd398c5a new file mode 100644 index 000000000..e2a2455a1 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-8be0bdd5-e9d9-47a2-9469-2ad8cd398c5a differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-8be0bdd5-e9d9-47a2-9469-2ad8cd398c5a-shm b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-8be0bdd5-e9d9-47a2-9469-2ad8cd398c5a-shm new file mode 100644 index 000000000..4fb1f7b37 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-8be0bdd5-e9d9-47a2-9469-2ad8cd398c5a-shm differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-8be0bdd5-e9d9-47a2-9469-2ad8cd398c5a-wal b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-8be0bdd5-e9d9-47a2-9469-2ad8cd398c5a-wal new file mode 100644 index 000000000..4f180185d Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-8be0bdd5-e9d9-47a2-9469-2ad8cd398c5a-wal differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-984c0369-48ed-42be-a99c-72107b56f152 b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-984c0369-48ed-42be-a99c-72107b56f152 new file mode 100644 index 000000000..e2a2455a1 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-984c0369-48ed-42be-a99c-72107b56f152 differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-984c0369-48ed-42be-a99c-72107b56f152-shm b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-984c0369-48ed-42be-a99c-72107b56f152-shm new file mode 100644 index 000000000..72efec166 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-984c0369-48ed-42be-a99c-72107b56f152-shm differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-984c0369-48ed-42be-a99c-72107b56f152-wal b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-984c0369-48ed-42be-a99c-72107b56f152-wal new file mode 100644 index 000000000..d8ecf9a7e Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-984c0369-48ed-42be-a99c-72107b56f152-wal differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-9ce2678b-23f0-45c2-a462-5cc133563f99 b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-9ce2678b-23f0-45c2-a462-5cc133563f99 new file mode 100644 index 000000000..e2a2455a1 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-9ce2678b-23f0-45c2-a462-5cc133563f99 differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-9ce2678b-23f0-45c2-a462-5cc133563f99-shm b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-9ce2678b-23f0-45c2-a462-5cc133563f99-shm new file mode 100644 index 000000000..79ca4236c Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-9ce2678b-23f0-45c2-a462-5cc133563f99-shm differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-9ce2678b-23f0-45c2-a462-5cc133563f99-wal b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-9ce2678b-23f0-45c2-a462-5cc133563f99-wal new file mode 100644 index 000000000..bb199cf20 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-9ce2678b-23f0-45c2-a462-5cc133563f99-wal differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-c06366dc-86f3-432f-88df-e4113c9a1cde b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-c06366dc-86f3-432f-88df-e4113c9a1cde new file mode 100644 index 000000000..e2a2455a1 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-c06366dc-86f3-432f-88df-e4113c9a1cde differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-c06366dc-86f3-432f-88df-e4113c9a1cde-shm b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-c06366dc-86f3-432f-88df-e4113c9a1cde-shm new file mode 100644 index 000000000..252241dfd Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-c06366dc-86f3-432f-88df-e4113c9a1cde-shm differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-c06366dc-86f3-432f-88df-e4113c9a1cde-wal b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-c06366dc-86f3-432f-88df-e4113c9a1cde-wal new file mode 100644 index 000000000..02efa57c0 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-c06366dc-86f3-432f-88df-e4113c9a1cde-wal differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2690ef2-e66f-48c1-a860-de70296ad11c b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2690ef2-e66f-48c1-a860-de70296ad11c new file mode 100644 index 000000000..e2a2455a1 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2690ef2-e66f-48c1-a860-de70296ad11c differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2690ef2-e66f-48c1-a860-de70296ad11c-shm b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2690ef2-e66f-48c1-a860-de70296ad11c-shm new file mode 100644 index 000000000..289388e02 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2690ef2-e66f-48c1-a860-de70296ad11c-shm differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2690ef2-e66f-48c1-a860-de70296ad11c-wal b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2690ef2-e66f-48c1-a860-de70296ad11c-wal new file mode 100644 index 000000000..17766cb14 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2690ef2-e66f-48c1-a860-de70296ad11c-wal differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2f00a26-ada5-4daf-87e6-25f27eaea770 b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2f00a26-ada5-4daf-87e6-25f27eaea770 new file mode 100644 index 000000000..e2a2455a1 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2f00a26-ada5-4daf-87e6-25f27eaea770 differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2f00a26-ada5-4daf-87e6-25f27eaea770-shm b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2f00a26-ada5-4daf-87e6-25f27eaea770-shm new file mode 100644 index 000000000..c10021964 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2f00a26-ada5-4daf-87e6-25f27eaea770-shm differ diff --git a/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2f00a26-ada5-4daf-87e6-25f27eaea770-wal b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2f00a26-ada5-4daf-87e6-25f27eaea770-wal new file mode 100644 index 000000000..a86385fb9 Binary files /dev/null and b/test/Masa.Framework.EventApiTest/disabled-soft-delete-db-f2f00a26-ada5-4daf-87e6-25f27eaea770-wal differ diff --git a/test/Masa.Framework.IntegrationTests.EventBus/Infrastructure/Extensions/DefaultPublisher.cs b/test/Masa.Framework.IntegrationTests.EventBus/Infrastructure/Extensions/DefaultPublisher.cs index 10a12c2a5..c56c62e72 100644 --- a/test/Masa.Framework.IntegrationTests.EventBus/Infrastructure/Extensions/DefaultPublisher.cs +++ b/test/Masa.Framework.IntegrationTests.EventBus/Infrastructure/Extensions/DefaultPublisher.cs @@ -1,10 +1,16 @@ -// Copyright (c) MASA Stack All rights reserved. +// Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. + namespace Masa.Framework.IntegrationTests.EventBus.Infrastructure.Extensions; public class DefaultPublisher : IPublisher { + public Task BulkPublishAsync(string topicName, List<(T @event, IntegrationEventExpand? eventMessageExpand)> events, CancellationToken stoppingToken = default) + { + return Task.CompletedTask; + } + public Task PublishAsync(string topicName, T @event, IntegrationEventExpand? eventMessageExpand, CancellationToken stoppingToken = default) { return Task.CompletedTask;