Skip to content

Commit

Permalink
Merge pull request kgrzybek#85 from PiotrWachulec/feature/PaymentsEma…
Browse files Browse the repository at this point in the history
…ilNotification

Added sending confirmation email after subscription creation or renewal
  • Loading branch information
kgrzybek authored Jul 20, 2020
2 parents 83ac07e + 2e1d3a0 commit 65098b0
Show file tree
Hide file tree
Showing 15 changed files with 249 additions and 9 deletions.
3 changes: 2 additions & 1 deletion src/API/CompanyName.MyMeetings.API/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ private IServiceProvider CreateAutofacServiceProvider(IServiceCollection service
PaymentsStartup.Initialize(
this._configuration[MeetingsConnectionString],
executionContextAccessor,
_logger,
_logger,
emailsConfiguration,
null);

return new AutofacServiceProvider(container);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private static void ConfigureCompositionRoot(

containerBuilder.RegisterModule(new ProcessingModule());
containerBuilder.RegisterModule(new EventsBusModule(eventsBus));
containerBuilder.RegisterModule(new MediatorModule());
containerBuilder.RegisterModule(new MediatorModule());
containerBuilder.RegisterModule(new AuthenticationModule());
containerBuilder.RegisterModule(new OutboxModule());
containerBuilder.RegisterModule(new EmailModule(emailsConfiguration));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Threading;
using System.Threading.Tasks;
using CompanyName.MyMeetings.BuildingBlocks.Application.Data;
using CompanyName.MyMeetings.BuildingBlocks.Infrastructure;
using CompanyName.MyMeetings.Modules.Payments.Application.Configuration.Queries;
using Dapper;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
using CompanyName.MyMeetings.BuildingBlocks.Application.Data;
using Dapper;

namespace CompanyName.MyMeetings.Modules.Payments.Application.Payers.GetPayerEmail
{
public static class PayerEmailProvider
{
public static async Task<string> GetPayerEmail(Guid payerId, ISqlConnectionFactory sqlConnectionFactory)
{
var connection = sqlConnectionFactory.GetOpenConnection();

const string sql = "SELECT " +
"[Payer].[Email] " +
"FROM [payments].[Payers] AS [Payer] " +
"WHERE [Payer].[Id] = @PayerId";

return await connection.QuerySingleAsync<string>(sql, new
{
payerId
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using CompanyName.MyMeetings.BuildingBlocks.Application.Data;
using CompanyName.MyMeetings.Modules.Payments.Application.Configuration.Commands;
using CompanyName.MyMeetings.Modules.Payments.Application.Payers.GetPayer;
using CompanyName.MyMeetings.Modules.Payments.Application.Payers.GetPayerEmail;
using CompanyName.MyMeetings.Modules.Payments.Application.Subscriptions.SendSubscriptionCreationConfirmationEmail;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
using Dapper;
using MediatR;

namespace CompanyName.MyMeetings.Modules.Payments.Application.Subscriptions.CreateSubscription
{
public class SubscriptionCreatedEnqueueEmailConfirmationHandler : INotificationHandler<SubscriptionCreatedNotification>
{
private readonly ICommandsScheduler _commandsScheduler;
private readonly ISqlConnectionFactory _sqlConnectionFactory;

public SubscriptionCreatedEnqueueEmailConfirmationHandler(
ICommandsScheduler commandsScheduler,
ISqlConnectionFactory sqlConnectionFactory)
{
_commandsScheduler = commandsScheduler;
_sqlConnectionFactory = sqlConnectionFactory;
}

public async Task Handle(SubscriptionCreatedNotification notification, CancellationToken cancellationToken)
{
var payerEmail = await PayerEmailProvider.GetPayerEmail(
notification.DomainEvent.PayerId,
_sqlConnectionFactory);

await _commandsScheduler.EnqueueAsync(new SendSubscriptionCreationConfirmationEmailCommand(
Guid.NewGuid(),
new SubscriptionId(notification.DomainEvent.SubscriptionId),
payerEmail));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
using CompanyName.MyMeetings.BuildingBlocks.Infrastructure.EventBus;
using CompanyName.MyMeetings.Modules.Payments.IntegrationEvents;
Expand All @@ -17,7 +16,7 @@ public SubscriptionCreatedNotificationHandler(IEventsBus eventsBus)
}

public Task Handle(SubscriptionCreatedNotification notification, CancellationToken cancellationToken)
{
{
_eventsBus.Publish(new SubscriptionExpirationDateChangedIntegrationEvent(notification.Id,
notification.DomainEvent.OccurredOn,
notification.DomainEvent.PayerId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using CompanyName.MyMeetings.BuildingBlocks.Application.Data;
using CompanyName.MyMeetings.Modules.Payments.Application.Configuration.Commands;
using CompanyName.MyMeetings.Modules.Payments.Application.Payers.GetPayer;
using CompanyName.MyMeetings.Modules.Payments.Application.Payers.GetPayerEmail;
using CompanyName.MyMeetings.Modules.Payments.Application.Subscriptions.SendSubscriptionRenewalConfirmationEmail;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
using Dapper;
using MediatR;

namespace CompanyName.MyMeetings.Modules.Payments.Application.Subscriptions.RenewSubscription
{
public class SubscriptionRenewedEnqueueEmailConfirmationHandler : INotificationHandler<SubscriptionRenewedNotification>
{
private readonly ICommandsScheduler _commandsScheduler;
private readonly ISqlConnectionFactory _sqlConnectionFactory;

public SubscriptionRenewedEnqueueEmailConfirmationHandler(
ICommandsScheduler commandsScheduler,
ISqlConnectionFactory sqlConnectionFactory)
{
_commandsScheduler = commandsScheduler;
_sqlConnectionFactory = sqlConnectionFactory;
}

public async Task Handle(SubscriptionRenewedNotification notification, CancellationToken cancellationToken)
{
var payerEmail = await PayerEmailProvider.GetPayerEmail(
notification.DomainEvent.PayerId,
_sqlConnectionFactory);

await _commandsScheduler.EnqueueAsync(new SendSubscriptionRenewalConfirmationEmailCommand(
Guid.NewGuid(),
new SubscriptionId(notification.DomainEvent.SubscriptionId),
payerEmail));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using CompanyName.MyMeetings.Modules.Payments.Application.Configuration.Commands;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
using Newtonsoft.Json;

namespace CompanyName.MyMeetings.Modules.Payments.Application.Subscriptions.SendSubscriptionCreationConfirmationEmail
{
public class SendSubscriptionCreationConfirmationEmailCommand : InternalCommandBase
{
internal SubscriptionId SubscriptionId { get; }

internal string Email { get; }

[JsonConstructor]
public SendSubscriptionCreationConfirmationEmailCommand(
Guid id,
SubscriptionId subscriptionId,
string email)
: base(id)
{
SubscriptionId = subscriptionId;
Email = email;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Threading;
using System.Threading.Tasks;
using CompanyName.MyMeetings.BuildingBlocks.Application.Emails;
using CompanyName.MyMeetings.Modules.Payments.Application.Configuration.Commands;
using MediatR;

namespace CompanyName.MyMeetings.Modules.Payments.Application.Subscriptions.SendSubscriptionCreationConfirmationEmail
{
public class SendSubscriptionCreationConfirmationEmailCommandHandler : ICommandHandler<SendSubscriptionCreationConfirmationEmailCommand>
{
private readonly IEmailSender _emailSender;

public SendSubscriptionCreationConfirmationEmailCommandHandler(IEmailSender emailSender)
{
_emailSender = emailSender;
}

public Task<Unit> Handle(SendSubscriptionCreationConfirmationEmailCommand request, CancellationToken cancellationToken)
{
var emailMessage = new EmailMessage(request.Email, "MyMeetings - Subscription purchased",
$"Subscription {request.SubscriptionId.Value} was successfully paid and created with ❤ for you!");

_emailSender.SendEmail(emailMessage);
return Unit.Task;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using CompanyName.MyMeetings.Modules.Payments.Application.Configuration.Commands;
using CompanyName.MyMeetings.Modules.Payments.Domain.Subscriptions;
using Newtonsoft.Json;

namespace CompanyName.MyMeetings.Modules.Payments.Application.Subscriptions.SendSubscriptionRenewalConfirmationEmail
{
public class SendSubscriptionRenewalConfirmationEmailCommand : InternalCommandBase
{
internal SubscriptionId SubscriptionId { get; }

internal string Email { get; }

[JsonConstructor]
public SendSubscriptionRenewalConfirmationEmailCommand(
Guid id,
SubscriptionId subscriptionId,
string email)
: base(id)
{
SubscriptionId = subscriptionId;
Email = email;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Threading;
using System.Threading.Tasks;
using CompanyName.MyMeetings.BuildingBlocks.Application.Emails;
using CompanyName.MyMeetings.Modules.Payments.Application.Configuration.Commands;
using MediatR;

namespace CompanyName.MyMeetings.Modules.Payments.Application.Subscriptions.SendSubscriptionRenewalConfirmationEmail
{
public class SendSubscriptionRenewalConfirmationEmailCommandHandler : ICommandHandler<SendSubscriptionRenewalConfirmationEmailCommand>
{
private readonly IEmailSender _emailSender;

public SendSubscriptionRenewalConfirmationEmailCommandHandler(IEmailSender emailSender)
{
_emailSender = emailSender;
}

public Task<Unit> Handle(SendSubscriptionRenewalConfirmationEmailCommand request, CancellationToken cancellationToken)
{
var emailMessage = new EmailMessage(request.Email, "MyMeetings - Subscription renewed",
$"Subscription {request.SubscriptionId.Value} was successfully paid and renewed with ❤ for you!");

_emailSender.SendEmail(emailMessage);
return Unit.Task;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Autofac;
using CompanyName.MyMeetings.BuildingBlocks.Application.Emails;
using CompanyName.MyMeetings.BuildingBlocks.Infrastructure.Emails;

namespace CompanyName.MyMeetings.Modules.Payments.Infrastructure.Configuration.Email
{
internal class EmailModule : Module
{
private readonly EmailsConfiguration _configuration;

public EmailModule(EmailsConfiguration configuration)
{
_configuration = configuration;
}

protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<EmailSender>()
.As<IEmailSender>()
.WithParameter("configuration", _configuration)
.InstancePerLifetimeScope();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using Autofac;
using CompanyName.MyMeetings.BuildingBlocks.Application;
using CompanyName.MyMeetings.BuildingBlocks.Infrastructure.Emails;
using CompanyName.MyMeetings.BuildingBlocks.Infrastructure.EventBus;
using CompanyName.MyMeetings.Modules.Payments.Infrastructure.AggregateStore;
using CompanyName.MyMeetings.Modules.Payments.Infrastructure.Configuration.Authentication;
using CompanyName.MyMeetings.Modules.Payments.Infrastructure.Configuration.DataAccess;
using CompanyName.MyMeetings.Modules.Payments.Infrastructure.Configuration.Email;
using CompanyName.MyMeetings.Modules.Payments.Infrastructure.Configuration.EventsBus;
using CompanyName.MyMeetings.Modules.Payments.Infrastructure.Configuration.Logging;
using CompanyName.MyMeetings.Modules.Payments.Infrastructure.Configuration.Mediation;
Expand All @@ -25,12 +27,13 @@ public static void Initialize(
string connectionString,
IExecutionContextAccessor executionContextAccessor,
ILogger logger,
EmailsConfiguration emailsConfiguration,
IEventsBus eventsBus,
bool runQuartz = true)
{
var moduleLogger = logger.ForContext("Module", "Payments");

ConfigureCompositionRoot(connectionString, executionContextAccessor, moduleLogger, eventsBus, runQuartz);
ConfigureCompositionRoot(connectionString, executionContextAccessor, moduleLogger, emailsConfiguration, eventsBus, runQuartz);

if (runQuartz)
{
Expand All @@ -44,6 +47,7 @@ private static void ConfigureCompositionRoot(
string connectionString,
IExecutionContextAccessor executionContextAccessor,
ILogger logger,
EmailsConfiguration emailsConfiguration,
IEventsBus eventsBus,
bool runQuartz = true)
{
Expand All @@ -55,6 +59,7 @@ private static void ConfigureCompositionRoot(
containerBuilder.RegisterModule(new DataAccessModule(connectionString, loggerFactory));

containerBuilder.RegisterModule(new ProcessingModule());
containerBuilder.RegisterModule(new EmailModule(emailsConfiguration));
containerBuilder.RegisterModule(new EventsBusModule(eventsBus));
containerBuilder.RegisterModule(new MediatorModule());
containerBuilder.RegisterModule(new AuthenticationModule());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class TestBase

protected IEmailSender EmailSender;

protected EmailsConfiguration EmailsConfiguration;

protected EventsBusMock EventsBus;

protected ExecutionContextMock ExecutionContext;
Expand All @@ -53,13 +55,15 @@ public async Task BeforeEachTest()

Logger = Substitute.For<ILogger>();
EmailSender = Substitute.For<IEmailSender>();
EmailsConfiguration = new EmailsConfiguration("[email protected]");
EventsBus = new EventsBusMock();
ExecutionContext = new ExecutionContextMock(Guid.NewGuid());

PaymentsStartup.Initialize(
ConnectionString,
ExecutionContext,
Logger,
EmailsConfiguration,
EventsBus,
true);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
using System.Threading;
using System.Threading.Tasks;
using CompanyName.MyMeetings.BuildingBlocks.Application.Emails;
using CompanyName.MyMeetings.BuildingBlocks.Infrastructure.Emails;
using CompanyName.MyMeetings.Modules.UserAccess.Application.Configuration.Commands;
using MediatR;

namespace CompanyName.MyMeetings.Modules.UserAccess.Application.UserRegistrations.SendUserRegistrationConfirmationEmail
{
internal class SendUserRegistrationConfirmationEmailCommandHandler : ICommandHandler<SendUserRegistrationConfirmationEmailCommand>
{
private IEmailSender _emailSender;
private readonly IEmailSender _emailSender;

public SendUserRegistrationConfirmationEmailCommandHandler(IEmailSender emailSender)
{
Expand Down

0 comments on commit 65098b0

Please sign in to comment.