Skip to content

Commit

Permalink
Send email when reminder is due
Browse files Browse the repository at this point in the history
  • Loading branch information
amantinband committed Dec 17, 2023
1 parent fa79af1 commit 973e7fd
Show file tree
Hide file tree
Showing 33 changed files with 318 additions and 51 deletions.
6 changes: 3 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"rest-client.environmentVariables": {
"$shared": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiQW1pY2hhaSIsImZhbWlseV9uYW1lIjoiTWFudGluYmFuZCIsImVtYWlsIjoiYW1pY2hhaUBtYW50aW5iYW5kLmNvbSIsImlkIjoiYmFlOTNiZjUtOWUzYy00N2IzLWFhY2UtMzAzNDY1M2I2YmIyIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiQWRtaW4iLCJwZXJtaXNzaW9ucyI6WyJzZXQ6cmVtaW5kZXIiLCJnZXQ6cmVtaW5kZXIiLCJkaXNtaXNzOnJlbWluZGVyIiwiY3JlYXRlOnN1YnNjcmlwdGlvbiJdLCJleHAiOjE3MDIyMzk3OTIsImlzcyI6IlJlbWluZGVyU2VydmljZSIsImF1ZCI6IlJlbWluZGVyU2VydmljZSJ9.tevx5jtchFL9XJ_6QcaOXQCe9xaN9lx1rfYB7smS2W4",
"userId": "bae93bf5-9e3c-47b3-aace-3034653b6bb2",
"subscriptionId": "b6d1bc56-6dd7-45f1-907b-aa4be2e49c45",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiTGlvciIsImZhbWlseV9uYW1lIjoiRGFnYW4iLCJlbWFpbCI6Imxpb3JsaW9yZEBnbWFpbC5jb20iLCJpZCI6ImFhZTkzYmY1LTllM2MtNDdiMy1hYWNlLTMwMzQ2NTNiNmJiMiIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluIiwicGVybWlzc2lvbnMiOlsic2V0OnJlbWluZGVyIiwiZ2V0OnJlbWluZGVyIiwiZGlzbWlzczpyZW1pbmRlciIsImRlbGV0ZTpyZW1pbmRlciIsImNyZWF0ZTpzdWJzY3JpcHRpb24iLCJkZWxldGU6c3Vic2NyaXB0aW9uIiwiZ2V0OnN1YnNjcmlwdGlvbiJdLCJleHAiOjE3MDI3NjE0ODcsImlzcyI6IlJlbWluZGVyU2VydmljZSIsImF1ZCI6IlJlbWluZGVyU2VydmljZSJ9.IAMTLGboIQCcpPL-rCOWvt4vQ7z7dDw5lFK5znFsSAI",
"userId": "aae93bf5-9e3c-47b3-aace-3034653b6bb2",
"subscriptionId": "09c76854-c629-4c90-96d1-17a52595a765",
"reminderId": "1b306b10-a161-46fa-9285-0b180e37a319"
},
"dev": {
Expand Down
2 changes: 2 additions & 0 deletions Directory.Packages.Props
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
<PackageVersion Include="MediatR" Version="12.2.0" />
<PackageVersion Include="FluentValidation" Version="11.8.1" />
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageVersion Include="FluentEmail.Core" Version="3.0.2" />
<PackageVersion Include="FluentEmail.Smtp" Version="3.0.2" />
<!-- Analyzers -->
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
<!-- Tests -->
Expand Down
4 changes: 2 additions & 2 deletions requests/Reminders/CreateReminder.http
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ Content-Type: application/json
Authorization: Bearer {{token}}

{
"text": "let's do it",
"dateTime": "2025-02-26"
"text": "Tell my husband I love him",
"dateTime": "2023-12-16T22:20:09"
}
7 changes: 5 additions & 2 deletions requests/Subscriptions/CreateSubscription.http
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ Content-Type: application/json
Authorization: Bearer {{token}}

{
"SubscriptionType": "Basic"
// "SubscriptionType": "Pro"
"FirstName": "Lior",
"LastName": "Dagan",
"Email": "[email protected]",
// "SubscriptionType": "Basic"
"SubscriptionType": "Pro"
}
8 changes: 4 additions & 4 deletions requests/Tokens/GenerateToken.http
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ POST {{host}}/tokens/generate
Content-Type: application/json

{
"Id": "bae93bf5-9e3c-47b3-aace-3034653b6bb2",
"FirstName": "Amichai",
"LastName": "Mantinband",
"Email": "amichai@mantinband.com",
"Id": "aae93bf5-9e3c-47b3-aace-3034653b6bb2",
"FirstName": "Lior",
"LastName": "Dagan",
"Email": "lior@dagan.com",
"Permissions": [
"set:reminder",
"get:reminder",
Expand Down
46 changes: 24 additions & 22 deletions src/CleanArchitecture.Api/CleanArchitecture.Api.csproj
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
<IsPackable>true</IsPackable>
<UserSecretsId>36d3aabc-1075-47d7-9b3b-c1feebf8f3bd</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Contracts\CleanArchitecture.Contracts.csproj" />
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
</ItemGroup>

</Project>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CleanArchitecture.Contracts\CleanArchitecture.Contracts.csproj" />
<ProjectReference Include="..\CleanArchitecture.Application\CleanArchitecture.Application.csproj" />
<ProjectReference Include="..\CleanArchitecture.Infrastructure\CleanArchitecture.Infrastructure.csproj" />
</ItemGroup>

</Project>
Binary file modified src/CleanArchitecture.Api/CleanArchitecture.db
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ public async Task<IActionResult> CreateSubscription(Guid userId, CreateSubscript
detail: "Invalid plan type");
}

var command = new CreateSubscriptionCommand(userId, subscriptionType);
var command = new CreateSubscriptionCommand(
userId,
request.FirstName,
request.LastName,
request.Email,
subscriptionType);

var result = await _mediator.Send(command);

Expand Down
1 change: 1 addition & 0 deletions src/CleanArchitecture.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

var app = builder.Build();
{
app.UseExceptionHandler();
app.AddInfrastructureMiddleware();

if (app.Environment.IsDevelopment())
Expand Down
10 changes: 10 additions & 0 deletions src/CleanArchitecture.Api/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,15 @@
"TokenExpirationInMinutes": 60,
"Issuer": "ReminderService",
"Audience": "ReminderService"
},
"EmailSettings": {
"EnableEmailNotifications": false,
"DefaultFromEmail": "",
"SmtpSettings": {
"Server": "",
"Port": 0,
"Username": "",
"Password": ""
}
}
}
9 changes: 9 additions & 0 deletions src/CleanArchitecture.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,14 @@
"TokenExpirationInMinutes": 0,
"Issuer": "",
"Audience": ""
},
"EmailSettings": {
"SmtpSettings": {
"server": "",
"port": 0,
"username": "",
"password": ""
},
"defaultFromEmail": ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@
namespace CleanArchitecture.Application.Subscriptions.Commands.CreateSubscription;

[Authorize(Permissions = Permission.Subscription.Create, Policies = Policy.SelfOrAdmin)]
public record CreateSubscriptionCommand(Guid UserId, SubscriptionType SubscriptionType)
public record CreateSubscriptionCommand(
Guid UserId,
string FirstName,
string LastName,
string Email,
SubscriptionType SubscriptionType)
: IAuthorizeableRequest<ErrorOr<SubscriptionResult>>;
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public async Task<ErrorOr<SubscriptionResult>> Handle(CreateSubscriptionCommand

var user = new User(
request.UserId,
request.FirstName,
request.LastName,
request.Email,
subscription);

await _usersRepository.AddAsync(user, cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using FluentValidation;

namespace CleanArchitecture.Application.Subscriptions.Commands.CreateSubscription;

public class CreateSubscriptionCommandValidator : AbstractValidator<CreateSubscriptionCommand>
{
public CreateSubscriptionCommandValidator()
{
RuleFor(x => x.FirstName)
.MinimumLength(2)
.MaximumLength(10000);

RuleFor(x => x.LastName)
.MinimumLength(2)
.MaximumLength(10000);

RuleFor(x => x.Email).EmailAddress();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@

namespace CleanArchitecture.Contracts.Subscriptions;

public record CreateSubscriptionRequest(SubscriptionType? SubscriptionType);
public record CreateSubscriptionRequest(
string FirstName,
string LastName,
string Email,
SubscriptionType SubscriptionType);
12 changes: 12 additions & 0 deletions src/CleanArchitecture.Domain/Users/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,24 @@ public class User : Entity

public Subscription Subscription { get; private set; } = null!;

public string Email { get; } = null!;

public string FirstName { get; } = null!;

public string LastName { get; } = null!;

public User(
Guid id,
string firstName,
string lastName,
string email,
Subscription subscription,
Calendar? calendar = null)
: base(id)
{
FirstName = firstName;
LastName = lastName;
Email = email;
Subscription = subscription;
_calendar = calendar ?? Calendar.Empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="FluentEmail.Core" />
<PackageReference Include="FluentEmail.Smtp" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using CleanArchitecture.Application.Common.Interfaces;
using CleanArchitecture.Application.Common.Security.Request;
using CleanArchitecture.Infrastructure.Security.CurrentUserProvider;
using CleanArchitecture.Infrastructure.Common.Security.CurrentUserProvider;
using CleanArchitecture.Infrastructure.Security.PolicyEnforcer;

using ErrorOr;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using CleanArchitecture.Infrastructure.Common.Security.CurrentUserProvider;

namespace CleanArchitecture.Infrastructure.Security.CurrentUserProvider;
namespace CleanArchitecture.Infrastructure.Common.Security.CurrentUserProvider;

public interface ICurrentUserProvider
{
Expand Down
43 changes: 43 additions & 0 deletions src/CleanArchitecture.Infrastructure/DependencyInjection.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
using System.Net;
using System.Net.Mail;
using System.Text;

using CleanArchitecture.Application.Common.Interfaces;
using CleanArchitecture.Infrastructure.Common;
using CleanArchitecture.Infrastructure.Common.Security.CurrentUserProvider;
using CleanArchitecture.Infrastructure.Reminders.BackgroundServices;
using CleanArchitecture.Infrastructure.Reminders.Persistence;
using CleanArchitecture.Infrastructure.Security;
using CleanArchitecture.Infrastructure.Security.CurrentUserProvider;
Expand All @@ -10,6 +14,10 @@
using CleanArchitecture.Infrastructure.Services;
using CleanArchitecture.Infrastructure.Users.Persistence;

using FluentEmail.Core;

using FluentValidation;

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
Expand All @@ -26,13 +34,48 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi
services
.AddHttpContextAccessor()
.AddServices()
.AddBackgroundServices(configuration)
.AddAuthentication(configuration)
.AddAuthorization()
.AddPersistence();

return services;
}

private static IServiceCollection AddBackgroundServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddEmailNotifications(configuration);

return services;
}

private static IServiceCollection AddEmailNotifications(
this IServiceCollection services,
IConfiguration configuration)
{
EmailSettings emailSettings = new();
configuration.Bind(EmailSettings.Section, emailSettings);

if (!emailSettings.EnableEmailNotifications)
{
return services;
}

services.AddHostedService<ReminderEmailBackgroundService>();

services
.AddFluentEmail(emailSettings.DefaultFromEmail)
.AddSmtpSender(new SmtpClient(emailSettings.SmtpSettings.Server)
{
Port = emailSettings.SmtpSettings.Port,
Credentials = new NetworkCredential(
emailSettings.SmtpSettings.Username,
emailSettings.SmtpSettings.Password),
});

return services;
}

private static IServiceCollection AddServices(this IServiceCollection services)
{
services.AddSingleton<IDateTimeProvider, SystemDateTimeProvider>();
Expand Down

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

Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ protected override void Up(MigrationBuilder migrationBuilder)
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Subscription_SubscriptionType = table.Column<string>(type: "TEXT", nullable: false),
SubscriptionId = table.Column<Guid>(type: "TEXT", nullable: false),
Email = table.Column<string>(type: "TEXT", nullable: false),
FirstName = table.Column<string>(type: "TEXT", nullable: false),
LastName = table.Column<string>(type: "TEXT", nullable: false),
ReminderIds = table.Column<string>(type: "TEXT", nullable: false),
CalendarDictionary = table.Column<string>(type: "TEXT", nullable: true)
},
Expand Down
Loading

0 comments on commit 973e7fd

Please sign in to comment.