├── README.md
├── EventReminder.Contracts
├── EventReminder.Contracts.csproj
├── Authentication
│ ├── LoginRequest.cs
│ ├── TokenResponse.cs
│ └── RegisterRequest.cs
├── Users
│ ├── ChangePasswordRequest.cs
│ ├── SendFriendshipRequestRequest.cs
│ ├── UpdateUserRequest.cs
│ └── UserResponse.cs
├── GroupEvents
│ ├── InviteFriendToGroupEventRequest.cs
│ ├── UpdateGroupEventRequest.cs
│ ├── CreateGroupEventRequest.cs
│ ├── GroupEventResponse.cs
│ └── DetailedGroupEventResponse.cs
├── PersonalEvents
│ ├── UpdatePersonalEventRequest.cs
│ ├── CreatePersonalEventRequest.cs
│ ├── PersonalEventResponse.cs
│ └── DetailedPersonalEventResponse.cs
├── Emails
│ ├── WelcomeEmail.cs
│ ├── PasswordChangedEmail.cs
│ ├── MailRequest.cs
│ ├── NotificationEmail.cs
│ ├── FriendshipRequestAcceptedEmail.cs
│ ├── FriendshipRequestSentEmail.cs
│ ├── AttendeeCreatedEmail.cs
│ ├── InvitationSentEmail.cs
│ └── GroupEventCancelledEmail.cs
├── Invitations
│ └── InvitationResponse.cs
├── Friendships
│ └── FriendshipResponse.cs
├── FriendshipRequests
│ └── FriendshipRequestResponse.cs
├── Attendees
│ └── AttendeeListResponse.cs
└── Common
│ └── PagedList.cs
├── EventReminder.Application
├── Abstractions
│ ├── Messaging
│ │ ├── IEvent.cs
│ │ ├── IIntegrationEvent.cs
│ │ ├── IQuery.cs
│ │ ├── ICommand.cs
│ │ ├── IEventHandler.cs
│ │ ├── IDomainEventHandler.cs
│ │ ├── IQueryHandler.cs
│ │ ├── ICommandHandler.cs
│ │ └── IIntegrationEventPublisher.cs
│ ├── Common
│ │ └── IDateTime.cs
│ ├── Authentication
│ │ ├── IUserIdentifierProvider.cs
│ │ └── IJwtProvider.cs
│ ├── Cryptography
│ │ └── IPasswordHasher.cs
│ ├── Emails
│ │ └── IEmailService.cs
│ └── Data
│ │ └── IUnitOfWork.cs
├── GroupEvents
│ ├── CancelGroupEvent
│ │ ├── CancelGroupEventCommandValidator.cs
│ │ └── CancelGroupEventCommand.cs
│ ├── GetGroupEventById
│ │ └── GetGroupEventByIdQuery.cs
│ ├── InviteFriendToGroupEvent
│ │ ├── InviteFriendToGroupEventCommandValidator.cs
│ │ └── InviteFriendToGroupEventCommand.cs
│ ├── UpdateGroupEvent
│ │ ├── UpdateGroupEventCommandValidator.cs
│ │ └── UpdateGroupEventCommand.cs
│ ├── CreateGroupEvent
│ │ └── CreateGroupEventCommandValidator.cs
│ ├── GroupEventCancelled
│ │ ├── GroupEventCancelledIntegrationEvent.cs
│ │ └── PublishIntegrationEventOnGroupEventCancelledDomainEventHandler.cs
│ ├── Get10MostRecentAttendingGroupEvents
│ │ └── Get10MostRecentAttendingGroupEventsQuery.cs
│ └── GroupEventNameChanged
│ │ └── PublishIntegrationEventOnGroupEventNameChangedDomainEventHandler.cs
├── Invitations
│ ├── AcceptInvitation
│ │ ├── AcceptInvitationCommandValidator.cs
│ │ └── AcceptInvitationCommand.cs
│ ├── RejectInvitation
│ │ ├── RejectInvitationCommandValidator.cs
│ │ └── RejectInvitationCommand.cs
│ ├── GetSentInvitations
│ │ └── GetSentInvitationsQuery.cs
│ ├── GetInvitationById
│ │ └── GetInvitationByIdQuery.cs
│ ├── GetPendingInvitations
│ │ └── GetPendingInvitationsQuery.cs
│ ├── InvitationSent
│ │ ├── InvitationSentIntegrationEvent.cs
│ │ └── PublishIntegrationEventOnInvitationSentDomainEventHandler.cs
│ ├── InvitationAccepted
│ │ ├── InvitationAcceptedIntegrationEvent.cs
│ │ └── PublishIntegrationEventOnInvitationAcceptedDomainEventHandler.cs
│ └── InvitationRejected
│ │ ├── InvitationRejectedIntegrationEvent.cs
│ │ └── PublishIntegrationEventOnInvitationRejectedDomainEventHandler.cs
├── Attendees
│ ├── AttendeeCreated
│ │ ├── AttendeeCreatedEvent.cs
│ │ ├── AttendeeCreatedIntegrationEvent.cs
│ │ └── PublishIntegrationEventOnAttendeeCreatedEventHandler.cs
│ └── GetAttendeesForEventId
│ │ └── GetAttendeesForGroupEventIdQuery.cs
├── PersonalEvents
│ ├── CancelPersonalEvent
│ │ ├── CancelPersonalEventCommandValidator.cs
│ │ └── CancelPersonalEventCommand.cs
│ ├── GetPersonalEventById
│ │ └── GetPersonalEventByIdQuery.cs
│ ├── UpdatePersonalEvent
│ │ ├── UpdatePersonalEventCommandValidator.cs
│ │ └── UpdatePersonalEventCommand.cs
│ └── CreatePersonalEvent
│ │ └── CreatePersonalEventCommandValidator.cs
├── Authentication
│ └── Login
│ │ ├── LoginCommandValidator.cs
│ │ └── LoginCommand.cs
├── EventReminder.Application.csproj
├── Users
│ ├── GetUserById
│ │ └── GetUserByIdQuery.cs
│ ├── ChangePassword
│ │ ├── ChangePasswordCommandValidator.cs
│ │ ├── ChangePasswordCommand.cs
│ │ ├── UserPasswordChangedIntegrationEvent.cs
│ │ └── PublishIntegrationEventOnUserPasswordChangedDomainEventHandler.cs
│ ├── SendFriendshipRequest
│ │ ├── SendFriendshipRequestCommandValidator.cs
│ │ └── SendFriendshipRequestCommand.cs
│ ├── UpdateUser
│ │ ├── UpdateUserCommandValidator.cs
│ │ └── UpdateUserCommand.cs
│ └── CreateUser
│ │ ├── UserCreatedIntegrationEvent.cs
│ │ ├── CreateUserCommandValidator.cs
│ │ └── PublishIntegrationEventOnUserCreatedDomainEventHandler.cs
├── FriendshipRequests
│ ├── AcceptFriendshipRequest
│ │ ├── AcceptFriendshipRequestCommandValidator.cs
│ │ └── AcceptFriendshipRequestCommand.cs
│ ├── RejectFriendshipRequest
│ │ ├── RejectFriendshipRequestCommandValidator.cs
│ │ └── RejectFriendshipRequestCommand.cs
│ ├── GetSentFriendshipRequests
│ │ └── GetSentFriendshipRequestsQuery.cs
│ ├── GetPendingFriendshipRequests
│ │ └── GetPendingFriendshipRequestsQuery.cs
│ ├── GetFriendshipRequestById
│ │ └── GetFriendshipRequestByIdQuery.cs
│ ├── FriendshipRequestSent
│ │ └── FriendshipRequestSentIntegrationEvent.cs
│ └── FriendshipRequestAccepted
│ │ └── FriendshipRequestAcceptedIntegrationEvent.cs
├── Friendships
│ ├── RemoveFriendship
│ │ ├── RemoveFriendshipCommandValidator.cs
│ │ └── RemoveFriendshipCommand.cs
│ ├── GetFriendship
│ │ └── GetFriendshipQuery.cs
│ └── GetFriendshipsForUserId
│ │ └── GetFriendshipsForUserIdQuery.cs
├── DependencyInjection.cs
└── Core
│ ├── Exceptions
│ └── ValidationException.cs
│ └── Extensions
│ └── FluentValidationExtensions.cs
├── EventReminder.Domain
├── Core
│ ├── Events
│ │ └── IDomainEvent.cs
│ ├── Abstractions
│ │ ├── IAuditableEntity.cs
│ │ └── ISoftDeletableEntity.cs
│ ├── Exceptions
│ │ └── DomainException.cs
│ └── Primitives
│ │ ├── Error.cs
│ │ └── Result
│ │ └── ResultT.cs
├── EventReminder.Domain.csproj
├── Events
│ ├── EventType.cs
│ ├── DomainEvents
│ │ ├── GroupEventCreatedDomainEvent.cs
│ │ ├── GroupEventCancelledDomainEvent.cs
│ │ ├── PersonalEventCancelledDomainEvent.cs
│ │ ├── PersonalEventCreatedDomainEvent.cs
│ │ ├── PersonalEventDateAndTimeChangedDomainEvent.cs
│ │ ├── GroupEventNameChangedDomainEvent.cs
│ │ └── GroupEventDateAndTimeChangedDomainEvent.cs
│ ├── Category.cs
│ ├── IGroupEventRepository.cs
│ └── IPersonalEventRepository.cs
├── Users
│ ├── IPasswordHashChecker.cs
│ ├── DomainEvents
│ │ ├── UserCreatedDomainEvent.cs
│ │ ├── UserNameChangedDomainEvent.cs
│ │ └── UserPasswordChangedDomainEvent.cs
│ └── IUserRepository.cs
├── Invitations
│ └── DomainEvents
│ │ ├── InvitationSentDomainEvent.cs
│ │ ├── InvitationRejectedDomainEvent.cs
│ │ └── InvitationAcceptedDomainEvent.cs
└── Friendships
│ ├── DomainEvents
│ ├── FriendshipRemovedDomainEvent.cs
│ ├── FriendshipRequestSentDomainEvent.cs
│ ├── FriendshipRequestRejectedDomainEvent.cs
│ └── FriendshipRequestAcceptedDomainEvent.cs
│ ├── IFriendshipRepository.cs
│ └── IFriendshipRequestRepository.cs
├── launchSettings.json
├── .dockerignore
├── EventReminder.Services.Api
├── Program.cs
├── appsettings.json
├── Contracts
│ └── ApiErrorResponse.cs
├── appsettings.Development.json
├── Controllers
│ └── AttendeesController.cs
├── Properties
│ └── launchSettings.json
├── Dockerfile
└── EventReminder.Services.Api.csproj
├── EventReminder.Infrastructure
├── Common
│ └── MachineDateTime.cs
├── EventReminder.Infrastructure.csproj
├── Emails
│ └── Settings
│ │ └── MailSettings.cs
├── Messaging
│ └── Settings
│ │ └── MessageBrokerSettings.cs
└── Authentication
│ ├── UserIdentifierProvider.cs
│ └── Settings
│ └── JwtSettings.cs
├── EventReminder.BackgroundTasks
├── EventReminder.BackgroundTasks.csproj
├── Services
│ ├── IIntegrationEventConsumer.cs
│ ├── IGroupEventNotificationsProducer.cs
│ ├── IPersonalEventNotificationsProducer.cs
│ ├── IntegrationEventConsumer.cs
│ └── IEmailNotificationsConsumer.cs
├── Abstractions
│ └── Messaging
│ │ └── IIntegrationEventHandler.cs
└── Settings
│ └── BackgroundTaskSettings.cs
├── EventReminder.Services.Notifications
├── Program.cs
├── appsettings.json
├── appsettings.Development.json
├── EventReminder.Services.Notifications.csproj
├── Properties
│ └── launchSettings.json
├── Startup.cs
└── Dockerfile
├── EventReminder.Persistence
├── EventReminder.Persistence.csproj
├── Specifications
│ ├── UnprocessedAttendeeSpecification.cs
│ ├── UnProcessedPersonalEventSpecification.cs
│ ├── UserWithEmailSpecification.cs
│ ├── PendingInvitationSpecification.cs
│ ├── GroupEventForAttendeesSpecification.cs
│ ├── FriendshipSpecification.cs
│ ├── Specification.cs
│ ├── PendingFriendshipRequestSpecification.cs
│ └── NotificationWithinTimeFrameSpecification.cs
├── Configurations
│ ├── PersonalEventConfiguration.cs
│ └── FriendshipConfiguration.cs
├── Infrastructure
│ └── ConnectionString.cs
└── Repositories
│ ├── FriendshipRepository.cs
│ ├── FriendshipRequestRepository.cs
│ ├── UserRepository.cs
│ ├── GroupEventRepository.cs
│ └── PersonalEventRepository.cs
├── docker-compose.override.yml
├── docker-compose.dcproj
├── LICENSE
└── docker-compose.yml
/README.md:
--------------------------------------------------------------------------------
1 | # event-reminder
2 | .NET Core Web API for seamless event organization with configurable notification systems.
3 |
4 | The end!
5 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/EventReminder.Contracts.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Messaging/IEvent.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace EventReminder.Application.Abstractions.Messaging
4 | {
5 | ///
6 | /// Represents the event interface.
7 | ///
8 | public interface IEvent : INotification
9 | {
10 | }
11 | }
--------------------------------------------------------------------------------
/EventReminder.Domain/Core/Events/IDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace EventReminder.Domain.Core.Events
4 | {
5 | ///
6 | /// Represents the interface for an event that is raised within the domain.
7 | ///
8 | public interface IDomainEvent : INotification
9 | {
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Docker Compose": {
4 | "commandName": "DockerCompose",
5 | "commandVersion": "1.0",
6 | "serviceActions": {
7 | "eventreminder.services.api": "StartDebugging",
8 | "eventreminder.services.notifications": "StartDebugging"
9 | }
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/EventReminder.Domain/EventReminder.Domain.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Messaging/IIntegrationEvent.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace EventReminder.Application.Abstractions.Messaging
4 | {
5 | ///
6 | /// Represents the marker interface for an integration event.
7 | ///
8 | public interface IIntegrationEvent : INotification
9 | {
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Messaging/IQuery.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace EventReminder.Application.Abstractions.Messaging
4 | {
5 | ///
6 | /// Represents the query interface.
7 | ///
8 | /// The query response type.
9 | public interface IQuery : IRequest
10 | {
11 | }
12 | }
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Messaging/ICommand.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace EventReminder.Application.Abstractions.Messaging
4 | {
5 | ///
6 | /// Represents the command interface.
7 | ///
8 | /// The command response type.
9 | public interface ICommand : IRequest
10 | {
11 | }
12 | }
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.classpath
2 | **/.dockerignore
3 | **/.env
4 | **/.git
5 | **/.gitignore
6 | **/.project
7 | **/.settings
8 | **/.toolstarget
9 | **/.vs
10 | **/.vscode
11 | **/*.*proj.user
12 | **/*.dbmdl
13 | **/*.jfm
14 | **/azds.yaml
15 | **/bin
16 | **/charts
17 | **/docker-compose*
18 | **/Dockerfile*
19 | **/node_modules
20 | **/npm-debug.log
21 | **/obj
22 | **/secrets.dev.yaml
23 | **/values.dev.yaml
24 | LICENSE
25 | README.md
--------------------------------------------------------------------------------
/EventReminder.Services.Api/Program.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Services.Api
2 | {
3 | public class Program
4 | {
5 | public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
6 |
7 | public static IHostBuilder CreateHostBuilder(string[] args) =>
8 | Host.CreateDefaultBuilder(args)
9 | .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup());
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Messaging/IEventHandler.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace EventReminder.Application.Abstractions.Messaging
4 | {
5 | ///
6 | /// Represents the event handler interface.
7 | ///
8 | /// The event type.
9 | public interface IEventHandler : INotificationHandler
10 | where TEvent : INotification
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/EventReminder.Domain/Events/EventType.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Domain.Events
2 | {
3 | ///
4 | /// Represents the event type.
5 | ///
6 | public enum EventType
7 | {
8 | ///
9 | /// Personal event.
10 | ///
11 | PersonalEvent = 0,
12 |
13 | ///
14 | /// Group event.
15 | ///
16 | GroupEvent = 1
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/EventReminder.Infrastructure/Common/MachineDateTime.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Common;
3 |
4 | namespace EventReminder.Infrastructure.Common
5 | {
6 | ///
7 | /// Represents the machine date time service.
8 | ///
9 | internal sealed class MachineDateTime : IDateTime
10 | {
11 | ///
12 | public DateTime UtcNow => DateTime.UtcNow;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Common/IDateTime.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Application.Abstractions.Common
4 | {
5 | ///
6 | /// Represents the interface for getting the current date and time.
7 | ///
8 | public interface IDateTime
9 | {
10 | ///
11 | /// Gets the current date and time in UTC format.
12 | ///
13 | DateTime UtcNow { get; }
14 | }
15 | }
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Authentication/IUserIdentifierProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Application.Abstractions.Authentication
4 | {
5 | ///
6 | /// Represents the user identifier provider interface.
7 | ///
8 | public interface IUserIdentifierProvider
9 | {
10 | ///
11 | /// Gets the authenticated user identifier.
12 | ///
13 | Guid UserId { get; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/EventReminder.BackgroundTasks/EventReminder.BackgroundTasks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Messaging/IDomainEventHandler.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using MediatR;
3 |
4 | namespace EventReminder.Application.Abstractions.Messaging
5 | {
6 | ///
7 | /// Represents a domain event handler interface.
8 | ///
9 | /// The domain event type.
10 | public interface IDomainEventHandler : INotificationHandler
11 | where TDomainEvent : IDomainEvent
12 | {
13 | }
14 | }
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Messaging/IQueryHandler.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace EventReminder.Application.Abstractions.Messaging
4 | {
5 | ///
6 | /// Represents the query interface.
7 | ///
8 | /// The query type.
9 | /// The query response type.
10 | public interface IQueryHandler : IRequestHandler
11 | where TQuery : IQuery
12 | {
13 | }
14 | }
--------------------------------------------------------------------------------
/EventReminder.Contracts/Authentication/LoginRequest.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Contracts.Authentication
2 | {
3 | ///
4 | /// Represents the login request.
5 | ///
6 | public sealed class LoginRequest
7 | {
8 | ///
9 | /// Gets or sets the email.
10 | ///
11 | public string Email { get; set; }
12 |
13 | ///
14 | /// Gets or sets the password
15 | ///
16 | public string Password { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/EventReminder.Services.Notifications/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Hosting;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace EventReminder.Services.Notifications
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
9 |
10 | public static IHostBuilder CreateHostBuilder(string[] args) =>
11 | Host.CreateDefaultBuilder(args)
12 | .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup());
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/EventReminder.BackgroundTasks/Services/IIntegrationEventConsumer.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Abstractions.Messaging;
2 |
3 | namespace EventReminder.BackgroundTasks.Services
4 | {
5 | ///
6 | /// Represents the integration event consumer interface.
7 | ///
8 | internal interface IIntegrationEventConsumer
9 | {
10 | ///
11 | /// Consumes the incoming the specified integration event.
12 | ///
13 | void Consume(IIntegrationEvent integrationEvent);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Messaging/ICommandHandler.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace EventReminder.Application.Abstractions.Messaging
4 | {
5 | ///
6 | /// Represents the command handler interface.
7 | ///
8 | /// The command type.
9 | /// The command response type.
10 | public interface ICommandHandler : IRequestHandler
11 | where TCommand : ICommand
12 | {
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Users/ChangePasswordRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.Users
4 | {
5 | ///
6 | /// Represents the change password request.
7 | ///
8 | public sealed class ChangePasswordRequest
9 | {
10 | ///
11 | /// Gets or sets the user identifier.
12 | ///
13 | public Guid UserId { get; set; }
14 |
15 | ///
16 | /// Gets or sets the new password.
17 | ///
18 | public string Password { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Authentication/IJwtProvider.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Users;
2 |
3 | namespace EventReminder.Application.Abstractions.Authentication
4 | {
5 | ///
6 | /// Represents the JWT provider interface.
7 | ///
8 | public interface IJwtProvider
9 | {
10 | ///
11 | /// Creates the JWT for the specified user.
12 | ///
13 | /// The user.
14 | /// The JWT for the specified user.
15 | string Create(User user);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/EventReminder.BackgroundTasks/Abstractions/Messaging/IIntegrationEventHandler.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Abstractions.Messaging;
2 | using MediatR;
3 |
4 | namespace EventReminder.BackgroundTasks.Abstractions.Messaging
5 | {
6 | ///
7 | /// Represents the integration event handler.
8 | ///
9 | /// The integration event type.
10 | public interface IIntegrationEventHandler : INotificationHandler
11 | where TIntegrationEvent : IIntegrationEvent
12 | {
13 | }
14 | }
--------------------------------------------------------------------------------
/EventReminder.Persistence/EventReminder.Persistence.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Specifications/UnprocessedAttendeeSpecification.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using EventReminder.Domain.Events;
4 |
5 | namespace EventReminder.Persistence.Specifications
6 | {
7 | ///
8 | /// Represents the specification for determining the unprocessed attendee.
9 | ///
10 | internal sealed class UnprocessedAttendeeSpecification : Specification
11 | {
12 | ///
13 | internal override Expression> ToExpression() => attendee => !attendee.Processed;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Users/SendFriendshipRequestRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.Users
4 | {
5 | ///
6 | /// Represents the send friendship request request.
7 | ///
8 | public sealed class SendFriendshipRequestRequest
9 | {
10 | ///
11 | /// Gets or sets the user identifier.
12 | ///
13 | public Guid UserId { get; set; }
14 |
15 | ///
16 | /// Gets or sets the friend identifier.
17 | ///
18 | public Guid FriendId { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/EventReminder.Services.Api/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "ConnectionStrings": {
11 | "EventReminderDb": ""
12 | },
13 | "Jwt": {
14 | "Issuer": "",
15 | "Audience": "",
16 | "SecurityKey": "",
17 | "TokenExpirationInMinutes": 0
18 | },
19 | "MessageBroker": {
20 | "HostName": "",
21 | "Port": 0,
22 | "UserName": "",
23 | "Password": "",
24 | "QueueName": ""
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Cryptography/IPasswordHasher.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Users;
2 |
3 | namespace EventReminder.Application.Abstractions.Cryptography
4 | {
5 | ///
6 | /// Represents the password hasher interface.
7 | ///
8 | public interface IPasswordHasher
9 | {
10 | ///
11 | /// Hashes the specified password.
12 | ///
13 | /// The password to be hashed.
14 | /// The password hash.
15 | string HashPassword(Password password);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Core/Abstractions/IAuditableEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Domain.Core.Abstractions
4 | {
5 | ///
6 | /// Represents the marker interface for auditable entities.
7 | ///
8 | public interface IAuditableEntity
9 | {
10 | ///
11 | /// Gets the created on date and time in UTC format.
12 | ///
13 | DateTime CreatedOnUtc { get; }
14 |
15 | ///
16 | /// Gets the modified on date and time in UTC format.
17 | ///
18 | DateTime? ModifiedOnUtc { get; }
19 | }
20 | }
--------------------------------------------------------------------------------
/EventReminder.Contracts/GroupEvents/InviteFriendToGroupEventRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.GroupEvents
4 | {
5 | ///
6 | /// Represents the invite friend to group event request.
7 | ///
8 | public sealed class InviteFriendToGroupEventRequest
9 | {
10 | ///
11 | /// Gets or sets the event identifier.
12 | ///
13 | public Guid GroupEventId { get; set; }
14 |
15 | ///
16 | /// Gets or sets the friend identifier.
17 | ///
18 | public Guid FriendId { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Specifications/UnProcessedPersonalEventSpecification.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using EventReminder.Domain.Events;
4 |
5 | namespace EventReminder.Persistence.Specifications
6 | {
7 | ///
8 | /// Represents the specification for determining the unprocessed personal event.
9 | ///
10 | internal sealed class UnProcessedPersonalEventSpecification : Specification
11 | {
12 | ///
13 | internal override Expression> ToExpression() => personalEvent => !personalEvent.Processed;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Messaging/IIntegrationEventPublisher.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Application.Abstractions.Messaging
2 | {
3 | ///
4 | /// Represents the integration event publisher interface.
5 | ///
6 | public interface IIntegrationEventPublisher
7 | {
8 | ///
9 | /// Publishes the specified integration event to the message queue.
10 | ///
11 | /// The integration event.
12 | /// The completed task.
13 | void Publish(IIntegrationEvent integrationEvent);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Authentication/TokenResponse.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Contracts.Authentication
2 | {
3 | ///
4 | /// Represents the token response.
5 | ///
6 | public sealed class TokenResponse
7 | {
8 | ///
9 | /// Initializes a new instance of the class.
10 | ///
11 | /// The token value.
12 | public TokenResponse(string token) => Token = token;
13 |
14 | ///
15 | /// Gets the token.
16 | ///
17 | public string Token { get; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/EventReminder.Infrastructure/EventReminder.Infrastructure.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Core/Abstractions/ISoftDeletableEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Domain.Core.Abstractions
4 | {
5 | ///
6 | /// Represents the marker interface for soft-deletable entities.
7 | ///
8 | public interface ISoftDeletableEntity
9 | {
10 | ///
11 | /// Gets the date and time in UTC format the entity was deleted on.
12 | ///
13 | DateTime? DeletedOnUtc { get; }
14 |
15 | ///
16 | /// Gets a value indicating whether the entity has been deleted.
17 | ///
18 | bool Deleted { get; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Emails/IEmailService.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using EventReminder.Contracts.Emails;
3 |
4 | namespace EventReminder.Application.Abstractions.Emails
5 | {
6 | ///
7 | /// Represents the email service interface.
8 | ///
9 | public interface IEmailService
10 | {
11 | ///
12 | /// Sends the email with the content based on the specified mail request.
13 | ///
14 | /// The mail request.
15 | /// The completed task.
16 | Task SendEmailAsync(MailRequest mailRequest);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Users/IPasswordHashChecker.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Domain.Users
2 | {
3 | ///
4 | /// Represents the password hash checker interfaces.
5 | ///
6 | public interface IPasswordHashChecker
7 | {
8 | ///
9 | /// Checks if the specified password hash and the provided password hash match.
10 | ///
11 | /// The password hash.
12 | /// The provided password.
13 | /// True if the password hashes match, otherwise false.
14 | bool HashesMatch(string passwordHash, string providedPassword);
15 | }
16 | }
--------------------------------------------------------------------------------
/EventReminder.Contracts/Users/UpdateUserRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.Users
4 | {
5 | ///
6 | /// Represents the update user request.
7 | ///
8 | public sealed class UpdateUserRequest
9 | {
10 | ///
11 | /// Gets or sets the user identifier.
12 | ///
13 | public Guid UserId { get; set; }
14 |
15 | ///
16 | /// Gets or sets the first name.
17 | ///
18 | public string FirstName { get; set; }
19 |
20 | ///
21 | /// Gets or sets the last name.
22 | ///
23 | public string LastName { get; set; }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/GroupEvents/UpdateGroupEventRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.GroupEvents
4 | {
5 | ///
6 | /// Represents the update group event request.
7 | ///
8 | public sealed class UpdateGroupEventRequest
9 | {
10 | ///
11 | /// Gets or sets the user identifier.
12 | ///
13 | public Guid GroupEventId { get; set; }
14 |
15 | ///
16 | /// Gets or sets the name.
17 | ///
18 | public string Name { get; set; }
19 |
20 | ///
21 | /// Gets or sets the date and time.
22 | ///
23 | public DateTime DateTimeUtc { get; set; }
24 | }
25 | }
--------------------------------------------------------------------------------
/EventReminder.Persistence/Configurations/PersonalEventConfiguration.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Events;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace EventReminder.Persistence.Configurations
6 | {
7 | ///
8 | /// Represents the configuration for the entity.
9 | ///
10 | internal sealed class PersonalEventConfiguration : IEntityTypeConfiguration
11 | {
12 | ///
13 | public void Configure(EntityTypeBuilder builder) =>
14 | builder.Property(personalEvent => personalEvent.Processed)
15 | .IsRequired()
16 | .HasDefaultValue(false);
17 | }
18 | }
--------------------------------------------------------------------------------
/EventReminder.Services.Api/Contracts/ApiErrorResponse.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Primitives;
2 |
3 | namespace EventReminder.Services.Api.Contracts
4 | {
5 | ///
6 | /// Represents API an error response.
7 | ///
8 | public class ApiErrorResponse
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The enumerable collection of errors.
14 | public ApiErrorResponse(IReadOnlyCollection errors) => Errors = errors;
15 |
16 | ///
17 | /// Gets the errors.
18 | ///
19 | public IReadOnlyCollection Errors { get; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/PersonalEvents/UpdatePersonalEventRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.PersonalEvents
4 | {
5 | ///
6 | /// Represents the update personal event request.
7 | ///
8 | public sealed class UpdatePersonalEventRequest
9 | {
10 | ///
11 | /// Gets or sets the personal identifier.
12 | ///
13 | public Guid PersonalEventId { get; set; }
14 |
15 | ///
16 | /// Gets or sets the name.
17 | ///
18 | public string Name { get; set; }
19 |
20 | ///
21 | /// Gets or sets the date and time.
22 | ///
23 | public DateTime DateTimeUtc { get; set; }
24 | }
25 | }
--------------------------------------------------------------------------------
/EventReminder.Domain/Users/DomainEvents/UserCreatedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using EventReminder.Domain.Users;
3 |
4 | namespace EventReminder.Domain.Users.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when a user is created.
8 | ///
9 | public sealed class UserCreatedDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The user.
15 | internal UserCreatedDomainEvent(User user) => User = user;
16 |
17 | ///
18 | /// Gets the user.
19 | ///
20 | public User User { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.BackgroundTasks/Services/IGroupEventNotificationsProducer.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace EventReminder.BackgroundTasks.Services
5 | {
6 | ///
7 | /// Represents the group events notifications producer interface.
8 | ///
9 | internal interface IGroupEventNotificationsProducer
10 | {
11 | ///
12 | /// Produces group event notifications for the specified batch size.
13 | ///
14 | /// The batch size.
15 | /// The cancellation token.
16 | /// The completed task.
17 | Task ProduceAsync(int batchSize, CancellationToken cancellationToken = default);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/EventReminder.BackgroundTasks/Services/IPersonalEventNotificationsProducer.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace EventReminder.BackgroundTasks.Services
5 | {
6 | ///
7 | /// Represents the personal event notifications producer interface.
8 | ///
9 | internal interface IPersonalEventNotificationsProducer
10 | {
11 | ///
12 | /// Produces personal event notifications for the specified batch size.
13 | ///
14 | /// The batch size.
15 | /// The cancellation token.
16 | /// The completed task.
17 | Task ProduceAsync(int batchSize, CancellationToken cancellationToken = default);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Users/DomainEvents/UserNameChangedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using EventReminder.Domain.Users;
3 |
4 | namespace EventReminder.Domain.Users.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when a users first and last name is changed.
8 | ///
9 | public sealed class UserNameChangedDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The user.
15 | internal UserNameChangedDomainEvent(User user) => User = user;
16 |
17 | ///
18 | /// Gets the user.
19 | ///
20 | public User User { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docker-compose.override.yml:
--------------------------------------------------------------------------------
1 | version: '3.4'
2 |
3 | services:
4 | eventreminder.services.api:
5 | environment:
6 | - ASPNETCORE_ENVIRONMENT=Development
7 | - ASPNETCORE_HTTP_PORTS=8080
8 | - ASPNETCORE_HTTPS_PORTS=8081
9 | ports:
10 | - "8080"
11 | - "8081"
12 | volumes:
13 | - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
14 | - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
15 |
16 | eventreminder.services.notifications:
17 | environment:
18 | - ASPNETCORE_ENVIRONMENT=Development
19 | - ASPNETCORE_HTTP_PORTS=8080
20 | - ASPNETCORE_HTTPS_PORTS=8081
21 | ports:
22 | - "8080"
23 | - "8081"
24 | volumes:
25 | - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
26 | - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
27 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Core/Exceptions/DomainException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Domain.Core.Primitives;
3 |
4 | namespace EventReminder.Domain.Core.Exceptions
5 | {
6 | ///
7 | /// Represents an exception that occurred in the domain.
8 | ///
9 | public class DomainException : Exception
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The error containing the information about what happened.
15 | public DomainException(Error error)
16 | : base(error.Message)
17 | => Error = error;
18 |
19 | ///
20 | /// Gets the error.
21 | ///
22 | public Error Error { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Users/DomainEvents/UserPasswordChangedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using EventReminder.Domain.Users;
3 |
4 | namespace EventReminder.Domain.Users.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when a users password is changed.
8 | ///
9 | public sealed class UserPasswordChangedDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The user.
15 | internal UserPasswordChangedDomainEvent(User user) => User = user;
16 |
17 | ///
18 | /// Gets the user.
19 | ///
20 | public User User { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Events/DomainEvents/GroupEventCreatedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 |
3 | namespace EventReminder.Domain.Events.DomainEvents
4 | {
5 | ///
6 | /// Represents the event that is raised when a group event is created.
7 | ///
8 | public sealed class GroupEventCreatedDomainEvent : IDomainEvent
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The group event.
14 | internal GroupEventCreatedDomainEvent(GroupEvent groupEvent) => GroupEvent = groupEvent;
15 |
16 | ///
17 | /// Gets the group event.
18 | ///
19 | public GroupEvent GroupEvent { get; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Events/DomainEvents/GroupEventCancelledDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 |
3 | namespace EventReminder.Domain.Events.DomainEvents
4 | {
5 | ///
6 | /// Represents the event that is raised when a group event is cancelled.
7 | ///
8 | public sealed class GroupEventCancelledDomainEvent : IDomainEvent
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The group event.
14 | internal GroupEventCancelledDomainEvent(GroupEvent groupEvent) => GroupEvent = groupEvent;
15 |
16 | ///
17 | /// Gets the group event.
18 | ///
19 | public GroupEvent GroupEvent { get; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Authentication/RegisterRequest.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Contracts.Authentication
2 | {
3 | ///
4 | /// Represents the register request.
5 | ///
6 | public sealed class RegisterRequest
7 | {
8 | ///
9 | /// Gets or sets the first name.
10 | ///
11 | public string FirstName { get; set; }
12 |
13 | ///
14 | /// Gets or sets the last name.
15 | ///
16 | public string LastName { get; set; }
17 |
18 | ///
19 | /// Gets or sets the email.
20 | ///
21 | public string Email { get; set; }
22 |
23 | ///
24 | /// Gets or sets the password.
25 | ///
26 | public string Password { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/EventReminder.Application/GroupEvents/CancelGroupEvent/CancelGroupEventCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.GroupEvents.CancelGroupEvent
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class CancelGroupEventCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public CancelGroupEventCommandValidator() =>
16 | RuleFor(x => x.GroupEventId).NotEmpty().WithError(ValidationErrors.CancelGroupEvent.GroupEventIdIsRequired);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/EventReminder.Application/Invitations/AcceptInvitation/AcceptInvitationCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.Invitations.AcceptInvitation
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class AcceptInvitationCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public AcceptInvitationCommandValidator() =>
16 | RuleFor(x => x.InvitationId).NotEmpty().WithError(ValidationErrors.AcceptInvitation.InvitationIdIsRequired);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/EventReminder.Application/Invitations/RejectInvitation/RejectInvitationCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.Invitations.RejectInvitation
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class RejectInvitationCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public RejectInvitationCommandValidator() =>
16 | RuleFor(x => x.InvitationId).NotEmpty().WithError(ValidationErrors.RejectInvitation.InvitationIdIsRequired);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/EventReminder.Application/Attendees/AttendeeCreated/AttendeeCreatedEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 |
4 | namespace EventReminder.Application.Attendees.AttendeeCreated
5 | {
6 | ///
7 | /// Represents the event that is published when an attendee is created.
8 | ///
9 | public sealed class AttendeeCreatedEvent : IEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The attendee identifier.
15 | public AttendeeCreatedEvent(Guid attendeeId) => AttendeeId = attendeeId;
16 |
17 | ///
18 | /// Gets the attendee identifier.
19 | ///
20 | public Guid AttendeeId { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Services.Api/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Information",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "ConnectionStrings": {
11 | "EventReminderDb": "Server=event-reminder-db;Database=EventReminder;User=sa;Password=Strong_password_123!;TrustServerCertificate=True"
12 | },
13 | "Jwt": {
14 | "Issuer": "EventReminder",
15 | "Audience": "event-reminder.com",
16 | "SecurityKey": "secret-key-that-also-needs-to-be-at-least-16-characters-long",
17 | "TokenExpirationInMinutes": 60
18 | },
19 | "MessageBroker": {
20 | "HostName": "event-reminder-mq",
21 | "Port": 5672,
22 | "UserName": "guest",
23 | "Password": "guest",
24 | "QueueName": "event-reminder-queue"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EventReminder.Services.Notifications/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "ConnectionStrings": {
11 | "EventReminderDb": ""
12 | },
13 | "Mail": {
14 | "SenderDisplayName": "",
15 | "SenderEmail": "",
16 | "SmtpPassword": "",
17 | "SmtpServer": "",
18 | "SmtpPort": 0
19 | },
20 | "BackgroundTasks": {
21 | "AllowedNotificationTimeDiscrepancyInMinutes": 0,
22 | "AttendeesBatchSize": 0,
23 | "PersonalEventsBatchSize": 0,
24 | "NotificationsBatchSize": 0,
25 | "SleepTimeInMilliseconds": 0
26 | },
27 | "MessageBroker": {
28 | "HostName": "",
29 | "Port": 0,
30 | "UserName": "",
31 | "Password": "",
32 | "QueueName": ""
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Invitations/DomainEvents/InvitationSentDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using EventReminder.Domain.Invitations;
3 |
4 | namespace EventReminder.Domain.Invitations.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when an invitation is sent.
8 | ///
9 | public sealed class InvitationSentDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The invitation.
15 | internal InvitationSentDomainEvent(Invitation invitation) => Invitation = invitation;
16 |
17 | ///
18 | /// Gets the invitation.
19 | ///
20 | public Invitation Invitation { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docker-compose.dcproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2.1
5 | Linux
6 | 2a1eaf84-04ad-4c2f-8faf-41f4c577a2bc
7 | LaunchBrowser
8 | {Scheme}://localhost:{ServicePort}/swagger
9 | eventreminder.services.api
10 | event-reminder
11 |
12 |
13 |
14 | docker-compose.yml
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Events/DomainEvents/PersonalEventCancelledDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 |
3 | namespace EventReminder.Domain.Events.DomainEvents
4 | {
5 | ///
6 | /// Represents the event that is raised when a personal event is cancelled.
7 | ///
8 | public sealed class PersonalEventCancelledDomainEvent : IDomainEvent
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The personal event.
14 | internal PersonalEventCancelledDomainEvent(PersonalEvent personalEvent) => PersonalEvent = personalEvent;
15 |
16 | ///
17 | /// Gets the personal event.
18 | ///
19 | public PersonalEvent PersonalEvent { get; }
20 | }
21 | }
--------------------------------------------------------------------------------
/EventReminder.Application/PersonalEvents/CancelPersonalEvent/CancelPersonalEventCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.PersonalEvents.CancelPersonalEvent
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class CancelGroupEventCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public CancelGroupEventCommandValidator() =>
16 | RuleFor(x => x.PersonalEventId).NotEmpty().WithError(ValidationErrors.CancelPersonalEvent.PersonalEventIdIsRequired);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/EventReminder.Application/Authentication/Login/LoginCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.Authentication.Login
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class LoginCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public LoginCommandValidator()
16 | {
17 | RuleFor(x => x.Email).NotEmpty().WithError(ValidationErrors.Login.EmailIsRequired);
18 |
19 | RuleFor(x => x.Password).NotEmpty().WithError(ValidationErrors.Login.PasswordIsRequired);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Application/EventReminder.Application.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Events/DomainEvents/PersonalEventCreatedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 |
3 | namespace EventReminder.Domain.Events.DomainEvents
4 | {
5 | ///
6 | /// Represents the event that is a raised when a new personal event is created.
7 | ///
8 | public sealed class PersonalEventCreatedDomainEvent : IDomainEvent
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The personal event.
14 | internal PersonalEventCreatedDomainEvent(PersonalEvent personalEvent) => PersonalEvent = personalEvent;
15 |
16 | ///
17 | /// Gets the personal event.
18 | ///
19 | public PersonalEvent PersonalEvent { get; }
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Friendships/DomainEvents/FriendshipRemovedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using EventReminder.Domain.Friendships;
3 |
4 | namespace EventReminder.Domain.Friendships.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when a friendship is removed.
8 | ///
9 | public sealed class FriendshipRemovedDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The friendship.
15 | internal FriendshipRemovedDomainEvent(Friendship friendship) => Friendship = friendship;
16 |
17 | ///
18 | /// Gets the friendship.
19 | ///
20 | public Friendship Friendship { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Invitations/DomainEvents/InvitationRejectedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using EventReminder.Domain.Invitations;
3 |
4 | namespace EventReminder.Domain.Invitations.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when an invitation is rejected.
8 | ///
9 | public sealed class InvitationRejectedDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The invitation.
15 | internal InvitationRejectedDomainEvent(Invitation invitation) => Invitation = invitation;
16 |
17 | ///
18 | /// Gets the invitation.
19 | ///
20 | public Invitation Invitation { get; }
21 | }
22 | }
--------------------------------------------------------------------------------
/EventReminder.Services.Notifications/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Information",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "ConnectionStrings": {
11 | "EventReminderDb": "Server=event-reminder-db;Database=EventReminder;User=sa;Password=Strong_password_123!;TrustServerCertificate=True"
12 | },
13 | "BackgroundTasks": {
14 | "AllowedNotificationTimeDiscrepancyInMinutes": 1,
15 | "AttendeesBatchSize": 50,
16 | "PersonalEventsBatchSize": 100,
17 | "NotificationsBatchSize": 200,
18 | "SleepTimeInMilliseconds": 5000
19 | },
20 | "MessageBroker": {
21 | "HostName": "event-reminder-mq",
22 | "Port": 5672,
23 | "UserName": "guest",
24 | "Password": "guest",
25 | "QueueName": "event-reminder-queue"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Invitations/DomainEvents/InvitationAcceptedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using EventReminder.Domain.Invitations;
3 |
4 | namespace EventReminder.Domain.Invitations.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when an invitation is accepted.
8 | ///
9 | public sealed class InvitationAcceptedDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The invitation.
15 | internal InvitationAcceptedDomainEvent(Invitation invitation) => Invitation = invitation;
16 |
17 | ///
18 | /// Gets the invitation.
19 | ///
20 | public Invitation Invitation { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.BackgroundTasks/Services/IntegrationEventConsumer.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Abstractions.Messaging;
2 | using MediatR;
3 |
4 | namespace EventReminder.BackgroundTasks.Services
5 | {
6 | ///
7 | /// Represents the integration event consumer.
8 | ///
9 | internal sealed class IntegrationEventConsumer : IIntegrationEventConsumer
10 | {
11 | private readonly IMediator _mediator;
12 |
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The mediator.
17 | public IntegrationEventConsumer(IMediator mediator) => _mediator = mediator;
18 |
19 | ///
20 | public void Consume(IIntegrationEvent integrationEvent) => _mediator.Publish(integrationEvent).GetAwaiter().GetResult();
21 | }
22 | }
--------------------------------------------------------------------------------
/EventReminder.Application/Users/GetUserById/GetUserByIdQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.Users;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.Users.GetUserById
7 | {
8 | ///
9 | /// Represents the query for getting a user by identifier.
10 | ///
11 | public sealed class GetUserByIdQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The user identifier.
17 | public GetUserByIdQuery(Guid userId) => UserId = userId;
18 |
19 | ///
20 | /// Gets the user identifier.
21 | ///
22 | public Guid UserId { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Emails/WelcomeEmail.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Contracts.Emails
2 | {
3 | ///
4 | /// Represents the welcome email.
5 | ///
6 | public sealed class WelcomeEmail
7 | {
8 | ///
9 | /// Initializes a new instance of the class.
10 | ///
11 | /// The email receiver.
12 | /// The name.
13 | public WelcomeEmail(string emailTo, string name)
14 | {
15 | EmailTo = emailTo;
16 | Name = name;
17 | }
18 |
19 | ///
20 | /// Gets the email receiver.
21 | ///
22 | public string EmailTo { get; }
23 |
24 | ///
25 | /// Gets the name.
26 | ///
27 | public string Name { get; }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/GroupEvents/CreateGroupEventRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.GroupEvents
4 | {
5 | ///
6 | /// Represents the create group event request.
7 | ///
8 | public sealed class CreateGroupEventRequest
9 | {
10 | ///
11 | /// Gets or sets the user identifier.
12 | ///
13 | public Guid UserId { get; set; }
14 |
15 | ///
16 | /// Gets or sets the name.
17 | ///
18 | public string Name { get; set; }
19 |
20 | ///
21 | /// Gets or sets the category identifier.
22 | ///
23 | public int CategoryId { get; set; }
24 |
25 | ///
26 | /// Gets or sets the date and time.
27 | ///
28 | public DateTime DateTimeUtc { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Specifications/UserWithEmailSpecification.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using EventReminder.Domain.Users;
4 |
5 | namespace EventReminder.Persistence.Specifications
6 | {
7 | ///
8 | /// Represents the specification for determining the user with email.
9 | ///
10 | internal sealed class UserWithEmailSpecification : Specification
11 | {
12 | private readonly Email _email;
13 |
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// The email.
18 | internal UserWithEmailSpecification(Email email) => _email = email;
19 |
20 | ///
21 | internal override Expression> ToExpression() => user => user.Email.Value == _email;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/PersonalEvents/CreatePersonalEventRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.PersonalEvents
4 | {
5 | ///
6 | /// Represents the create personal event request.
7 | ///
8 | public sealed class CreatePersonalEventRequest
9 | {
10 | ///
11 | /// Gets or sets the user identifier.
12 | ///
13 | public Guid UserId { get; set; }
14 |
15 | ///
16 | /// Gets or sets the name.
17 | ///
18 | public string Name { get; set; }
19 |
20 | ///
21 | /// Gets or sets the category identifier.
22 | ///
23 | public int CategoryId { get; set; }
24 |
25 | ///
26 | /// Gets or sets the date and time.
27 | ///
28 | public DateTime DateTimeUtc { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/EventReminder.Application/Invitations/AcceptInvitation/AcceptInvitationCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.Invitations.AcceptInvitation
6 | {
7 | ///
8 | /// Represents the accept invitation command.
9 | ///
10 | public sealed class AcceptInvitationCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The invitation identifier.
16 | public AcceptInvitationCommand(Guid invitationId) => InvitationId = invitationId;
17 |
18 | ///
19 | /// Gets the invitation identifier.
20 | ///
21 | public Guid InvitationId { get; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EventReminder.Application/Invitations/RejectInvitation/RejectInvitationCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.Invitations.RejectInvitation
6 | {
7 | ///
8 | /// Represents the reject invitation command.
9 | ///
10 | public sealed class RejectInvitationCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The invitation identifier.
16 | public RejectInvitationCommand(Guid invitationId) => InvitationId = invitationId;
17 |
18 | ///
19 | /// Gets the invitation identifier.
20 | ///
21 | public Guid InvitationId { get; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EventReminder.Application/GroupEvents/CancelGroupEvent/CancelGroupEventCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.GroupEvents.CancelGroupEvent
6 | {
7 | ///
8 | /// Represents the cancel group event command.
9 | ///
10 | public sealed class CancelGroupEventCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The group event identifier.
16 | public CancelGroupEventCommand(Guid groupEventId) => GroupEventId = groupEventId;
17 |
18 | ///
19 | /// Gets the group event identifier.
20 | ///
21 | public Guid GroupEventId { get; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Emails/PasswordChangedEmail.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Contracts.Emails
2 | {
3 | ///
4 | /// Represents the password changed email.
5 | ///
6 | public sealed class PasswordChangedEmail
7 | {
8 | ///
9 | /// Initializes a new instance of the class.
10 | ///
11 | /// The email receiver.
12 | /// The name.
13 | public PasswordChangedEmail(string emailTo, string name)
14 | {
15 | EmailTo = emailTo;
16 | Name = name;
17 | }
18 |
19 | ///
20 | /// Gets the email receiver.
21 | ///
22 | public string EmailTo { get; }
23 |
24 | ///
25 | /// Gets the name.
26 | ///
27 | public string Name { get; }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Events/DomainEvents/PersonalEventDateAndTimeChangedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 |
3 | namespace EventReminder.Domain.Events.DomainEvents
4 | {
5 | ///
6 | /// Represents the event that is raised when the date and time of a personal event is changed.
7 | ///
8 | public sealed class PersonalEventDateAndTimeChangedDomainEvent : IDomainEvent
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The personal event.
14 | internal PersonalEventDateAndTimeChangedDomainEvent(PersonalEvent personalEvent) => PersonalEvent = personalEvent;
15 |
16 | ///
17 | /// Gets the personal event.
18 | ///
19 | public PersonalEvent PersonalEvent { get; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/EventReminder.Services.Notifications/EventReminder.Services.Notifications.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | 2a4c9a5e-4350-4b81-b69b-a9bb9c541adc
7 | Linux
8 | ..\docker-compose.dcproj
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/EventReminder.Application/Users/ChangePassword/ChangePasswordCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.Users.ChangePassword
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class ChangePasswordCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public ChangePasswordCommandValidator()
16 | {
17 | RuleFor(x => x.UserId).NotEmpty().WithError(ValidationErrors.ChangePassword.UserIdIsRequired);
18 |
19 | RuleFor(x => x.Password).NotEmpty().WithError(ValidationErrors.ChangePassword.PasswordIsRequired);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Infrastructure/ConnectionString.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Persistence.Infrastructure
2 | {
3 | ///
4 | /// Represents a connection string.
5 | ///
6 | public sealed class ConnectionString
7 | {
8 | ///
9 | /// The connection strings key.
10 | ///
11 | public const string SettingsKey = "EventReminderDb";
12 |
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The connection string value.
17 | public ConnectionString(string value) => Value = value;
18 |
19 | ///
20 | /// Gets the connection string value.
21 | ///
22 | public string Value { get; }
23 |
24 | public static implicit operator string(ConnectionString connectionString) => connectionString.Value;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Friendships/DomainEvents/FriendshipRequestSentDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using EventReminder.Domain.Friendships;
3 |
4 | namespace EventReminder.Domain.Friendships.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when a friendship request is sent.
8 | ///
9 | public sealed class FriendshipRequestSentDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The friendship request.
15 | internal FriendshipRequestSentDomainEvent(FriendshipRequest friendshipRequest) => FriendshipRequest = friendshipRequest;
16 |
17 | ///
18 | /// Gets the friendship request.
19 | ///
20 | public FriendshipRequest FriendshipRequest { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Application/PersonalEvents/CancelPersonalEvent/CancelPersonalEventCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.PersonalEvents.CancelPersonalEvent
6 | {
7 | ///
8 | /// Represents the cancel personal event command.
9 | ///
10 | public sealed class CancelPersonalEventCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The personal event identifier.
16 | public CancelPersonalEventCommand(Guid personalEventId) => PersonalEventId = personalEventId;
17 |
18 | ///
19 | /// Gets the personal event identifier.
20 | ///
21 | public Guid PersonalEventId { get; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EventReminder.Application/Attendees/AttendeeCreated/AttendeeCreatedIntegrationEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 |
4 | namespace EventReminder.Application.Attendees.AttendeeCreated
5 | {
6 | ///
7 | /// Represents the event that is raised when an attendee is created.
8 | ///
9 | public sealed class AttendeeCreatedIntegrationEvent : IIntegrationEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The attendee created event.
15 | internal AttendeeCreatedIntegrationEvent(AttendeeCreatedEvent attendeeCreatedEvent) =>
16 | AttendeeId = attendeeCreatedEvent.AttendeeId;
17 |
18 | ///
19 | /// Gets the attendee identifier.
20 | ///
21 | public Guid AttendeeId { get; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EventReminder.Application/FriendshipRequests/AcceptFriendshipRequest/AcceptFriendshipRequestCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.FriendshipRequests.AcceptFriendshipRequest
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class AcceptFriendshipRequestCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public AcceptFriendshipRequestCommandValidator() =>
16 | RuleFor(x => x.FriendshipRequestId)
17 | .NotEmpty()
18 | .WithError(ValidationErrors.AcceptFriendshipRequest.FriendshipRequestIdIsRequired);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/EventReminder.Application/FriendshipRequests/RejectFriendshipRequest/RejectFriendshipRequestCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.FriendshipRequests.RejectFriendshipRequest
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class RejectFriendshipRequestCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public RejectFriendshipRequestCommandValidator() =>
16 | RuleFor(x => x.FriendshipRequestId)
17 | .NotEmpty()
18 | .WithError(ValidationErrors.RejectFriendshipRequest.FriendshipRequestIdIsRequired);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Friendships/DomainEvents/FriendshipRequestRejectedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using EventReminder.Domain.Friendships;
3 |
4 | namespace EventReminder.Domain.Friendships.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when a friendship request is rejected.
8 | ///
9 | public sealed class FriendshipRequestRejectedDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The friendship request.
15 | internal FriendshipRequestRejectedDomainEvent(FriendshipRequest friendshipRequest) => FriendshipRequest = friendshipRequest;
16 |
17 | ///
18 | /// Gets the friendship request.
19 | ///
20 | public FriendshipRequest FriendshipRequest { get; }
21 | }
22 | }
--------------------------------------------------------------------------------
/EventReminder.Application/Friendships/RemoveFriendship/RemoveFriendshipCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.Friendships.RemoveFriendship
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class RemoveFriendshipCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public RemoveFriendshipCommandValidator()
16 | {
17 | RuleFor(x => x.UserId).NotEmpty().WithError(ValidationErrors.RemoveFriendship.UserIdIsRequired);
18 |
19 | RuleFor(x => x.FriendId).NotEmpty().WithError(ValidationErrors.RemoveFriendship.FriendIdIsRequired);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Friendships/DomainEvents/FriendshipRequestAcceptedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 | using EventReminder.Domain.Friendships;
3 |
4 | namespace EventReminder.Domain.Friendships.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when a friendship request is accepted.
8 | ///
9 | public sealed class FriendshipRequestAcceptedDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The friendship request.
15 | internal FriendshipRequestAcceptedDomainEvent(FriendshipRequest friendshipRequest) => FriendshipRequest = friendshipRequest;
16 |
17 | ///
18 | /// Gets the friendship request.
19 | ///
20 | public FriendshipRequest FriendshipRequest { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Application/Invitations/GetSentInvitations/GetSentInvitationsQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.Invitations;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.Invitations.GetSentInvitations
7 | {
8 | ///
9 | /// Represents the query for getting the sent invitations for the user identifier.
10 | ///
11 | public sealed class GetSentInvitationsQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The user identifier provider.
17 | public GetSentInvitationsQuery(Guid userId) => UserId = userId;
18 |
19 | ///
20 | /// Gets the user identifier.
21 | ///
22 | public Guid UserId { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Application/Invitations/GetInvitationById/GetInvitationByIdQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.Invitations;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.Invitations.GetInvitationById
7 | {
8 | ///
9 | /// Represents the query for getting the invitation by the identifier.
10 | ///
11 | public sealed class GetInvitationByIdQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The invitation identifier.
17 | public GetInvitationByIdQuery(Guid invitationId) => InvitationId = invitationId;
18 |
19 | ///
20 | /// Gets the invitation identifier.
21 | ///
22 | public Guid InvitationId { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Application/GroupEvents/GetGroupEventById/GetGroupEventByIdQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.GroupEvents;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.GroupEvents.GetGroupEventById
7 | {
8 | ///
9 | /// Represents the query for getting the group event by identifier.
10 | ///
11 | public sealed class GetGroupEventByIdQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The group event identifier.
17 | public GetGroupEventByIdQuery(Guid groupEventId) => GroupEventId = groupEventId;
18 |
19 | ///
20 | /// Gets the group event identifier.
21 | ///
22 | public Guid GroupEventId { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Application/Users/SendFriendshipRequest/SendFriendshipRequestCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.Users.SendFriendshipRequest
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class SendFriendshipRequestCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public SendFriendshipRequestCommandValidator()
16 | {
17 | RuleFor(x => x.UserId).NotEmpty().WithError(ValidationErrors.SendFriendshipRequest.UserIdIsRequired);
18 |
19 | RuleFor(x => x.FriendId).NotEmpty().WithError(ValidationErrors.SendFriendshipRequest.FriendIdIsRequired);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.BackgroundTasks/Services/IEmailNotificationsConsumer.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace EventReminder.BackgroundTasks.Services
5 | {
6 | ///
7 | /// Represents the email notifications consumer interface.
8 | ///
9 | internal interface IEmailNotificationsConsumer
10 | {
11 | ///
12 | /// Consumes the event notifications for the specified batch size.
13 | ///
14 | /// The batch size.
15 | ///
16 | /// The allowed discrepancy in minutes between the current time and the notification time.
17 | ///
18 | /// The cancellation token.
19 | /// The completed task.
20 | Task ConsumeAsync(int batchSize, int allowedNotificationTimeDiscrepancyInMinutes, CancellationToken cancellationToken = default);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Application/Invitations/GetPendingInvitations/GetPendingInvitationsQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.Invitations;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.Invitations.GetPendingInvitations
7 | {
8 | ///
9 | /// Represents the query for getting the pending invitations for the user identifier.
10 | ///
11 | public sealed class GetPendingInvitationsQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The user identifier provider.
17 | public GetPendingInvitationsQuery(Guid userId) => UserId = userId;
18 |
19 | ///
20 | /// Gets the user identifier.
21 | ///
22 | public Guid UserId { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Application/Users/UpdateUser/UpdateUserCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.Users.UpdateUser
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class UpdateUserCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public UpdateUserCommandValidator()
16 | {
17 | RuleFor(x => x.UserId).NotEmpty().WithError(ValidationErrors.UpdateUser.UserIdIsRequired);
18 |
19 | RuleFor(x => x.FirstName).NotEmpty().WithError(ValidationErrors.UpdateUser.FirstNameIsRequired);
20 |
21 | RuleFor(x => x.LastName).NotEmpty().WithError(ValidationErrors.UpdateUser.LastNameIsRequired);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Application/FriendshipRequests/AcceptFriendshipRequest/AcceptFriendshipRequestCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.FriendshipRequests.AcceptFriendshipRequest
6 | {
7 | ///
8 | /// Represents the accept friendship request command.
9 | ///
10 | public sealed class AcceptFriendshipRequestCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The friendship request identifier.
16 | public AcceptFriendshipRequestCommand(Guid friendshipRequestId) => FriendshipRequestId = friendshipRequestId;
17 |
18 | ///
19 | /// Gets the friendship request identifier.
20 | ///
21 | public Guid FriendshipRequestId { get; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EventReminder.Application/FriendshipRequests/RejectFriendshipRequest/RejectFriendshipRequestCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.FriendshipRequests.RejectFriendshipRequest
6 | {
7 | ///
8 | /// Represents the reject friendship request command.
9 | ///
10 | public sealed class RejectFriendshipRequestCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The friendship request identifier.
16 | public RejectFriendshipRequestCommand(Guid friendshipRequestId) => FriendshipRequestId = friendshipRequestId;
17 |
18 | ///
19 | /// Gets the friendship request identifier.
20 | ///
21 | public Guid FriendshipRequestId { get; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EventReminder.Application/Attendees/GetAttendeesForEventId/GetAttendeesForGroupEventIdQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.Attendees;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.Attendees.GetAttendeesForEventId
7 | {
8 | ///
9 | /// Represents the query for getting group event attendees.
10 | ///
11 | public sealed class GetAttendeesForGroupEventIdQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The group event identifier.
17 | public GetAttendeesForGroupEventIdQuery(Guid groupEventId) => GroupEventId = groupEventId;
18 |
19 | ///
20 | /// Gets the group event identifier.
21 | ///
22 | public Guid GroupEventId { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Repositories/FriendshipRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using EventReminder.Application.Abstractions.Data;
3 | using EventReminder.Domain.Friendships;
4 | using EventReminder.Domain.Users;
5 | using EventReminder.Persistence.Specifications;
6 |
7 | namespace EventReminder.Persistence.Repositories
8 | {
9 | ///
10 | /// Represents the friendship repository.
11 | ///
12 | internal sealed class FriendshipRepository : GenericRepository,IFriendshipRepository
13 | {
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// The database context.
18 | public FriendshipRepository(IDbContext dbContext)
19 | : base(dbContext)
20 | {
21 | }
22 |
23 | ///
24 | public async Task CheckIfFriendsAsync(User user, User friend) => await AnyAsync(new FriendshipSpecification(user, friend));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EventReminder.Application/PersonalEvents/GetPersonalEventById/GetPersonalEventByIdQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.PersonalEvents;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.PersonalEvents.GetPersonalEventById
7 | {
8 | ///
9 | /// Represents the query for getting the personal event by identifier.
10 | ///
11 | public sealed class GetPersonalEventByIdQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The personal event identifier.
17 | public GetPersonalEventByIdQuery(Guid personalEventId) => PersonalEventId = personalEventId;
18 |
19 | ///
20 | /// Gets the personal event identifier.
21 | ///
22 | public Guid PersonalEventId { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Application/GroupEvents/InviteFriendToGroupEvent/InviteFriendToGroupEventCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.GroupEvents.InviteFriendToGroupEvent
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class InviteFriendToGroupEventCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public InviteFriendToGroupEventCommandValidator()
16 | {
17 | RuleFor(x => x.GroupEventId).NotEmpty().WithError(ValidationErrors.InviteFriendToGroupEvent.GroupEventIdIsRequired);
18 |
19 | RuleFor(x => x.FriendId).NotEmpty().WithError(ValidationErrors.InviteFriendToGroupEvent.FriendIdIsRequired);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/EventReminder.Application/FriendshipRequests/GetSentFriendshipRequests/GetSentFriendshipRequestsQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.FriendshipRequests;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.FriendshipRequests.GetSentFriendshipRequests
7 | {
8 | ///
9 | /// Represents the query for getting the sent friendship requests for the user identifier.
10 | ///
11 | public sealed class GetSentFriendshipRequestsQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The user identifier provider.
17 | public GetSentFriendshipRequestsQuery(Guid userId) => UserId = userId;
18 |
19 | ///
20 | /// Gets the user identifier.
21 | ///
22 | public Guid UserId { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Application/GroupEvents/UpdateGroupEvent/UpdateGroupEventCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.GroupEvents.UpdateGroupEvent
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class UpdateGroupEventCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the
14 | ///
15 | public UpdateGroupEventCommandValidator()
16 | {
17 | RuleFor(x => x.GroupEventId).NotEmpty().WithError(ValidationErrors.UpdateGroupEvent.GroupEventIdIsRequired);
18 |
19 | RuleFor(x => x.Name).NotEmpty().WithError(ValidationErrors.UpdateGroupEvent.NameIsRequired);
20 |
21 | RuleFor(x => x.DateTimeUtc).NotEmpty().WithError(ValidationErrors.UpdateGroupEvent.DateAndTimeIsRequired);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Infrastructure/Emails/Settings/MailSettings.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Infrastructure.Emails.Settings
2 | {
3 | ///
4 | /// Represents the mail settings.
5 | ///
6 | public class MailSettings
7 | {
8 | public const string SettingsKey = "Mail";
9 |
10 | ///
11 | /// Gets or sets the email sender display name.
12 | ///
13 | public string SenderDisplayName { get; set; }
14 |
15 | ///
16 | /// Gets or sets the email sender.
17 | ///
18 | public string SenderEmail { get; set; }
19 |
20 | ///
21 | /// Gets or sets the SMTP password.
22 | ///
23 | public string SmtpPassword { get; set; }
24 |
25 | ///
26 | /// Gets or sets the SMTP server.
27 | ///
28 | public string SmtpServer { get; set; }
29 |
30 | ///
31 | /// Gets or sets the SMTP port.
32 | ///
33 | public int SmtpPort { get; set; }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/EventReminder.Infrastructure/Messaging/Settings/MessageBrokerSettings.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Infrastructure.Messaging.Settings
2 | {
3 | ///
4 | /// Represents the message broker settings.
5 | ///
6 | public sealed class MessageBrokerSettings
7 | {
8 | public const string SettingsKey = "MessageBroker";
9 |
10 | ///
11 | /// Gets or sets the host name.
12 | ///
13 | public string HostName { get; set; }
14 |
15 | ///
16 | /// Gets or sets the port.
17 | ///
18 | public int Port { get; set; }
19 |
20 | ///
21 | /// Gets or sets the user name.
22 | ///
23 | public string UserName { get; set; }
24 |
25 | ///
26 | /// Gets or sets the password.
27 | ///
28 | public string Password { get; set; }
29 |
30 | ///
31 | /// Gets or sets the queue name.
32 | ///
33 | public string QueueName { get; set; }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/EventReminder.Application/FriendshipRequests/GetPendingFriendshipRequests/GetPendingFriendshipRequestsQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.FriendshipRequests;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.FriendshipRequests.GetPendingFriendshipRequests
7 | {
8 | ///
9 | /// Represents the query for getting the pending friendship requests for the user identifier.
10 | ///
11 | public sealed class GetPendingFriendshipRequestsQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The user identifier provider.
17 | public GetPendingFriendshipRequestsQuery(Guid userId) => UserId = userId;
18 |
19 | ///
20 | /// Gets the user identifier.
21 | ///
22 | public Guid UserId { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Invitations/InvitationResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.Invitations
4 | {
5 | ///
6 | /// Represents the invitation response.
7 | ///
8 | public sealed class InvitationResponse
9 | {
10 | ///
11 | /// Gets or sets the identifier.
12 | ///
13 | public Guid Id { get; set; }
14 |
15 | ///
16 | /// Gets or sets the friend name.
17 | ///
18 | public string FriendName { get; set; }
19 |
20 | ///
21 | /// Gets or sets the event name.
22 | ///
23 | public string EventName { get; set; }
24 |
25 | ///
26 | /// Gets or sets the event date and time in UTC format.
27 | ///
28 | public DateTime EventDateTimeUtc { get; set; }
29 |
30 | ///
31 | /// Gets or sets the created on date and time in UTC format.
32 | ///
33 | public DateTime CreatedOnUtc { get; set; }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/EventReminder.Application/Abstractions/Data/IUnitOfWork.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Microsoft.EntityFrameworkCore.Storage;
4 |
5 | namespace EventReminder.Application.Abstractions.Data
6 | {
7 | ///
8 | /// Represents the unit of work interface.
9 | ///
10 | public interface IUnitOfWork
11 | {
12 | ///
13 | /// Saves all of the pending changes in the unit of work.
14 | ///
15 | /// The cancellation token.
16 | /// The number of entities that have been saved.
17 | Task SaveChangesAsync(CancellationToken cancellationToken = default);
18 |
19 | ///
20 | /// Begins a transaction on the current unit of work.
21 | ///
22 | /// The cancellation token.
23 | /// The new database context transaction.
24 | Task BeginTransactionAsync(CancellationToken cancellationToken = default);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EventReminder.Application/PersonalEvents/UpdatePersonalEvent/UpdatePersonalEventCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.PersonalEvents.UpdatePersonalEvent
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class UpdatePersonalEventCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the
14 | ///
15 | public UpdatePersonalEventCommandValidator()
16 | {
17 | RuleFor(x => x.PersonalEventId).NotEmpty().WithError(ValidationErrors.UpdatePersonalEvent.GroupEventIdIsRequired);
18 |
19 | RuleFor(x => x.Name).NotEmpty().WithError(ValidationErrors.UpdatePersonalEvent.NameIsRequired);
20 |
21 | RuleFor(x => x.DateTimeUtc).NotEmpty().WithError(ValidationErrors.UpdatePersonalEvent.DateAndTimeIsRequired);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Services.Notifications/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:6000",
7 | "sslPort": 6001
8 | }
9 | },
10 | "$schema": "http://json.schemastore.org/launchsettings.json",
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": false,
15 | "environmentVariables": {
16 | "ASPNETCORE_ENVIRONMENT": "Development"
17 | }
18 | },
19 | "EventReminder.Services.Notifications": {
20 | "commandName": "Project",
21 | "launchBrowser": false,
22 | "environmentVariables": {
23 | "ASPNETCORE_ENVIRONMENT": "Development"
24 | },
25 | "applicationUrl": "https://localhost:6001;http://localhost:6000"
26 | },
27 | "Docker": {
28 | "commandName": "Docker",
29 | "launchBrowser": false,
30 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/weatherforecast",
31 | "publishAllPorts": true,
32 | "useSSL": true
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Milan Jovanović
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Specifications/PendingInvitationSpecification.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using EventReminder.Domain.Events;
4 | using EventReminder.Domain.Invitations;
5 | using EventReminder.Domain.Users;
6 |
7 | namespace EventReminder.Persistence.Specifications
8 | {
9 | ///
10 | /// Represents the specification for determining the pending invitation.
11 | ///
12 | internal sealed class PendingInvitationSpecification : Specification
13 | {
14 | private readonly Guid _groupEventId;
15 | private readonly Guid _userId;
16 |
17 | internal PendingInvitationSpecification(GroupEvent groupEvent, User user)
18 | {
19 | _groupEventId = groupEvent.Id;
20 | _userId = user.Id;
21 | }
22 |
23 | ///
24 | internal override Expression> ToExpression() =>
25 | invitation => invitation.CompletedOnUtc == null &&
26 | invitation.EventId == _groupEventId &&
27 | invitation.UserId == _userId;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/EventReminder.Application/Users/CreateUser/UserCreatedIntegrationEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Users.DomainEvents;
4 | using Newtonsoft.Json;
5 |
6 | namespace EventReminder.Application.Users.CreateUser
7 | {
8 | ///
9 | /// Represents the integration event that is raised when a user is created.
10 | ///
11 | public sealed class UserCreatedIntegrationEvent : IIntegrationEvent
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The user created domain event.
17 | internal UserCreatedIntegrationEvent(UserCreatedDomainEvent userCreatedDomainEvent) => UserId = userCreatedDomainEvent.User.Id;
18 |
19 | [JsonConstructor]
20 | private UserCreatedIntegrationEvent(Guid userId) => UserId = userId;
21 |
22 | ///
23 | /// Gets the user identifier.
24 | ///
25 | public Guid UserId { get; }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/EventReminder.Application/Authentication/Login/LoginCommand.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Abstractions.Messaging;
2 | using EventReminder.Contracts.Authentication;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.Authentication.Login
6 | {
7 | ///
8 | /// Represents the login command.
9 | ///
10 | public sealed class LoginCommand : ICommand>
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The email.
16 | /// The password.
17 | public LoginCommand(string email, string password)
18 | {
19 | Email = email;
20 | Password = password;
21 | }
22 |
23 | ///
24 | /// Gets the email.
25 | ///
26 | public string Email { get; }
27 |
28 | ///
29 | /// Gets the password.
30 | ///
31 | public string Password { get; }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/EventReminder.Application/DependencyInjection.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Behaviors;
2 | using FluentValidation;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using System.Reflection;
5 |
6 | namespace EventReminder.Application
7 | {
8 | public static class DependencyInjection
9 | {
10 | ///
11 | /// Registers the necessary services with the DI framework.
12 | ///
13 | /// The service collection.
14 | /// The same service collection.
15 | public static IServiceCollection AddApplication(this IServiceCollection services)
16 | {
17 | services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
18 |
19 | services.AddMediatR(cfg =>
20 | {
21 | cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
22 |
23 | cfg.AddOpenBehavior(typeof(ValidationBehaviour<,>));
24 |
25 | cfg.AddOpenBehavior(typeof(TransactionBehaviour<,>));
26 | });
27 |
28 | return services;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/EventReminder.Application/FriendshipRequests/GetFriendshipRequestById/GetFriendshipRequestByIdQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.FriendshipRequests;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.FriendshipRequests.GetFriendshipRequestById
7 | {
8 | ///
9 | /// Represents the query for getting the friendship request by the identifier.
10 | ///
11 | public sealed class GetFriendshipRequestByIdQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The friendship request identifier.
17 | public GetFriendshipRequestByIdQuery(Guid friendshipRequestId) => FriendshipRequestId = friendshipRequestId;
18 |
19 | ///
20 | /// Gets the friendship request identifier.
21 | ///
22 | public Guid FriendshipRequestId { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Emails/MailRequest.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Contracts.Emails
2 | {
3 | ///
4 | /// Represents the mail request.
5 | ///
6 | public sealed class MailRequest
7 | {
8 | ///
9 | /// Initializes a new instance of the class.
10 | ///
11 | /// The email receiver.
12 | /// The subject.
13 | /// The body.
14 | public MailRequest(string emailTo, string subject, string body)
15 | {
16 | EmailTo = emailTo;
17 | Subject = subject;
18 | Body = body;
19 | }
20 |
21 | ///
22 | /// Gets the email receiver.
23 | ///
24 | public string EmailTo { get; }
25 |
26 | ///
27 | /// Gets the subject.
28 | ///
29 | public string Subject { get; }
30 |
31 | ///
32 | /// Gets the body.
33 | ///
34 | public string Body { get; }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/EventReminder.Services.Api/Controllers/AttendeesController.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Attendees.GetAttendeesForEventId;
2 | using EventReminder.Contracts.Attendees;
3 | using EventReminder.Domain.Core.Primitives.Maybe;
4 | using EventReminder.Services.Api.Contracts;
5 | using EventReminder.Services.Api.Infrastructure;
6 | using MediatR;
7 | using Microsoft.AspNetCore.Mvc;
8 |
9 | namespace EventReminder.Services.Api.Controllers
10 | {
11 | public sealed class AttendeesController : ApiController
12 | {
13 | public AttendeesController(IMediator mediator)
14 | : base(mediator)
15 | {
16 | }
17 |
18 | [HttpGet(ApiRoutes.Attendees.Get)]
19 | [ProducesResponseType(typeof(AttendeeListResponse), StatusCodes.Status200OK)]
20 | [ProducesResponseType(StatusCodes.Status404NotFound)]
21 | public async Task Get(Guid groupEventId) =>
22 | await Maybe
23 | .From(new GetAttendeesForGroupEventIdQuery(groupEventId))
24 | .Bind(query => Mediator.Send(query))
25 | .Match(Ok, NotFound);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/EventReminder.Application/Users/CreateUser/CreateUserCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.Users.CreateUser
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class CreateUserCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public CreateUserCommandValidator()
16 | {
17 | RuleFor(x => x.FirstName).NotEmpty().WithError(ValidationErrors.CreateUser.FirstNameIsRequired);
18 |
19 | RuleFor(x => x.LastName).NotEmpty().WithError(ValidationErrors.CreateUser.LastNameIsRequired);
20 |
21 | RuleFor(x => x.Email).NotEmpty().WithError(ValidationErrors.CreateUser.EmailIsRequired);
22 |
23 | RuleFor(x => x.Password).NotEmpty().WithError(ValidationErrors.CreateUser.PasswordIsRequired);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EventReminder.Services.Api/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5000",
7 | "sslPort": 5001
8 | }
9 | },
10 | "$schema": "http://json.schemastore.org/launchsettings.json",
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "swagger",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "EventReminder.Api": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "swagger",
24 | "environmentVariables": {
25 | "ASPNETCORE_ENVIRONMENT": "Development"
26 | },
27 | "applicationUrl": "https://localhost:5001;http://localhost:5000"
28 | },
29 | "Docker": {
30 | "commandName": "Docker",
31 | "launchBrowser": false,
32 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
33 | "publishAllPorts": true,
34 | "useSSL": true
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/EventReminder.Infrastructure/Authentication/UserIdentifierProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Claims;
3 | using EventReminder.Application.Abstractions.Authentication;
4 | using Microsoft.AspNetCore.Http;
5 |
6 | namespace EventReminder.Infrastructure.Authentication
7 | {
8 | ///
9 | /// Represents the user identifier provider.
10 | ///
11 | internal sealed class UserIdentifierProvider : IUserIdentifierProvider
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The HTTP context accessor.
17 | public UserIdentifierProvider(IHttpContextAccessor httpContextAccessor)
18 | {
19 | string userIdClaim = httpContextAccessor.HttpContext?.User?.FindFirstValue("userId")
20 | ?? throw new ArgumentException("The user identifier claim is required.", nameof(httpContextAccessor));
21 |
22 | UserId = new Guid(userIdClaim);
23 | }
24 |
25 | ///
26 | public Guid UserId { get; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Repositories/FriendshipRequestRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using EventReminder.Application.Abstractions.Data;
3 | using EventReminder.Domain.Friendships;
4 | using EventReminder.Domain.Users;
5 | using EventReminder.Persistence.Specifications;
6 |
7 | namespace EventReminder.Persistence.Repositories
8 | {
9 | ///
10 | /// Represents the friendship request repository.
11 | ///
12 | internal sealed class FriendshipRequestRepository : GenericRepository, IFriendshipRequestRepository
13 | {
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// The database context.
18 | public FriendshipRequestRepository(IDbContext dbContext)
19 | : base(dbContext)
20 | {
21 | }
22 |
23 | ///
24 | public async Task CheckForPendingFriendshipRequestAsync(User user, User friend) =>
25 | await AnyAsync(new PendingFriendshipRequestSpecification(user, friend));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/EventReminder.Application/Users/ChangePassword/ChangePasswordCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.Users.ChangePassword
6 | {
7 | ///
8 | /// Represents the change password command.
9 | ///
10 | public sealed class ChangePasswordCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The user identifier.
16 | /// The new password.
17 | public ChangePasswordCommand(Guid userId, string password)
18 | {
19 | UserId = userId;
20 | Password = password;
21 | }
22 |
23 | ///
24 | /// Gets the user identifier.
25 | ///
26 | public Guid UserId { get; }
27 |
28 | ///
29 | /// Gets the new password.
30 | ///
31 | public string Password { get; }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Events/DomainEvents/GroupEventNameChangedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Events;
2 |
3 | namespace EventReminder.Domain.Events.DomainEvents
4 | {
5 | ///
6 | /// Represents the event that is raised when the name of a group event is changed.
7 | ///
8 | public sealed class GroupEventNameChangedDomainEvent : IDomainEvent
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The group event.
14 | /// The previous name.
15 | internal GroupEventNameChangedDomainEvent(GroupEvent groupEvent, string previousName)
16 | {
17 | GroupEvent = groupEvent;
18 | PreviousName = previousName;
19 | }
20 |
21 | ///
22 | /// Gets the group event.
23 | ///
24 | public GroupEvent GroupEvent { get; }
25 |
26 | ///
27 | /// Gets the previous name.
28 | ///
29 | public string PreviousName { get; }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Repositories/UserRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using EventReminder.Application.Abstractions.Data;
3 | using EventReminder.Domain.Core.Primitives.Maybe;
4 | using EventReminder.Domain.Users;
5 | using EventReminder.Persistence.Specifications;
6 |
7 | namespace EventReminder.Persistence.Repositories
8 | {
9 | ///
10 | /// Represents the user repository.
11 | ///
12 | internal sealed class UserRepository : GenericRepository, IUserRepository
13 | {
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// The database context.
18 | public UserRepository(IDbContext dbContext)
19 | : base(dbContext)
20 | {
21 | }
22 |
23 | ///
24 | public async Task> GetByEmailAsync(Email email) => await FirstOrDefaultAsync(new UserWithEmailSpecification(email));
25 |
26 | ///
27 | public async Task IsEmailUniqueAsync(Email email) => !await AnyAsync(new UserWithEmailSpecification(email));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Specifications/GroupEventForAttendeesSpecification.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using EventReminder.Domain.Events;
6 |
7 | namespace EventReminder.Persistence.Specifications
8 | {
9 | ///
10 | /// Represents the specification for determining the group event the attendees will be attending.
11 | ///
12 | internal sealed class GroupEventForAttendeesSpecification : Specification
13 | {
14 | private readonly Guid[] _groupEventIds;
15 |
16 | ///
17 | /// Initializes a new instance of the class.
18 | ///
19 | /// The attendees.
20 | internal GroupEventForAttendeesSpecification(IReadOnlyCollection attendees) =>
21 | _groupEventIds = attendees.Select(attendee => attendee.EventId).Distinct().ToArray();
22 |
23 | ///
24 | internal override Expression> ToExpression() => groupEvent => _groupEventIds.Contains(groupEvent.Id);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EventReminder.Application/Friendships/RemoveFriendship/RemoveFriendshipCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.Friendships.RemoveFriendship
6 | {
7 | ///
8 | /// Represents the remove friendship command.
9 | ///
10 | public sealed class RemoveFriendshipCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The user identifier.
16 | /// The friend identifier.
17 | public RemoveFriendshipCommand(Guid userId, Guid friendId)
18 | {
19 | UserId = userId;
20 | FriendId = friendId;
21 | }
22 |
23 | ///
24 | /// Gets the user identifier.
25 | ///
26 | public Guid UserId { get; }
27 |
28 | ///
29 | /// Gets the friend identifier.
30 | ///
31 | public Guid FriendId { get; }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/EventReminder.Application/Core/Exceptions/ValidationException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using EventReminder.Domain.Core.Primitives;
5 | using FluentValidation.Results;
6 |
7 | namespace EventReminder.Application.Core.Exceptions
8 | {
9 | ///
10 | /// Represents an exception that occurs when a validation fails.
11 | ///
12 | public sealed class ValidationException : Exception
13 | {
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// The collection of validation failures.
18 | public ValidationException(IEnumerable failures)
19 | : base("One or more validation failures has occurred.") =>
20 | Errors = failures
21 | .Distinct()
22 | .Select(failure => new Error(failure.ErrorCode, failure.ErrorMessage))
23 | .ToList();
24 |
25 | ///
26 | /// Gets the validation errors.
27 | ///
28 | public IReadOnlyCollection Errors { get; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Emails/NotificationEmail.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Contracts.Emails
2 | {
3 | ///
4 | /// Represents the notification email.
5 | ///
6 | public sealed class NotificationEmail
7 | {
8 | ///
9 | /// Initializes a new instance of the class.
10 | ///
11 | /// The email receiver.
12 | /// The email subject.
13 | /// The email body.
14 | public NotificationEmail(string emailTo, string subject, string body)
15 | {
16 | EmailTo = emailTo;
17 | Subject = subject;
18 | Body = body;
19 | }
20 |
21 | ///
22 | /// Gets the email receiver.
23 | ///
24 | public string EmailTo { get; }
25 |
26 | ///
27 | /// Gets the email subject.
28 | ///
29 | public string Subject { get; }
30 |
31 | ///
32 | /// Gets the email body.
33 | ///
34 | public string Body { get; }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Specifications/FriendshipSpecification.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using EventReminder.Domain.Friendships;
4 | using EventReminder.Domain.Users;
5 |
6 | namespace EventReminder.Persistence.Specifications
7 | {
8 | ///
9 | /// Represents the specification for determining the friendship of two users.
10 | ///
11 | internal sealed class FriendshipSpecification : Specification
12 | {
13 | private readonly Guid _userId;
14 | private readonly Guid _friendId;
15 |
16 | ///
17 | /// Initializes a new instance of the class.
18 | ///
19 | /// The user.
20 | /// The friend.
21 | internal FriendshipSpecification(User user, User friend)
22 | {
23 | _userId = user.Id;
24 | _friendId = friend.Id;
25 | }
26 |
27 | ///
28 | internal override Expression> ToExpression() =>
29 | friendship => friendship.UserId == _userId && friendship.FriendId == _friendId;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/EventReminder.Application/Users/SendFriendshipRequest/SendFriendshipRequestCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.Users.SendFriendshipRequest
6 | {
7 | ///
8 | /// Represents the send friendship request command.
9 | ///
10 | public sealed class SendFriendshipRequestCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The user identifier.
16 | /// The friend identifier.
17 | public SendFriendshipRequestCommand(Guid userId, Guid friendId)
18 | {
19 | UserId = userId;
20 | FriendId = friendId;
21 | }
22 |
23 | ///
24 | /// Gets the user identifier.
25 | ///
26 | public Guid UserId { get; }
27 |
28 | ///
29 | /// Gets the friend identifier.
30 | ///
31 | public Guid FriendId { get; }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Events/Category.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Domain.Core.Primitives;
2 |
3 | namespace EventReminder.Domain.Events
4 | {
5 | ///
6 | /// Represents the category enumeration.
7 | ///
8 | public sealed class Category : Enumeration
9 | {
10 | public static readonly Category None = new Category(0, "None");
11 | public static readonly Category Concert = new Category(1, "Concert");
12 |
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The value.
17 | /// The name.
18 | private Category(int value, string name)
19 | : base(value, name)
20 | {
21 | }
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | /// The value.
27 | ///
28 | /// Required by EF Core.
29 | ///
30 | private Category(int value)
31 | : base(value, FromValue(value).Value.Name)
32 | {
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/GroupEvents/GroupEventResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.GroupEvents
4 | {
5 | ///
6 | /// Represents the group event response.
7 | ///
8 | public sealed class GroupEventResponse
9 | {
10 | ///
11 | /// Gets or sets the identifier.
12 | ///
13 | public Guid Id { get; set; }
14 |
15 | ///
16 | /// Gets or sets the name.
17 | ///
18 | public string Name { get; set; }
19 |
20 | ///
21 | /// Gets or sets the category identifier.
22 | ///
23 | public int CategoryId { get; set; }
24 |
25 | ///
26 | /// Gets or sets the category.
27 | ///
28 | public string Category { get; set; }
29 |
30 | ///
31 | /// Gets or sets the date and time in UTC format.
32 | ///
33 | public DateTime DateTimeUtc { get; set; }
34 |
35 | ///
36 | /// Gets or sets the created on date and time in UTC format.
37 | ///
38 | public DateTime CreatedOnUtc { get; set; }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/EventReminder.Application/GroupEvents/CreateGroupEvent/CreateGroupEventCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.GroupEvents.CreateGroupEvent
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class CreateGroupEventCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public CreateGroupEventCommandValidator()
16 | {
17 | RuleFor(x => x.UserId).NotEmpty().WithError(ValidationErrors.CreateGroupEvent.UserIdIsRequired);
18 |
19 | RuleFor(x => x.Name).NotEmpty().WithError(ValidationErrors.CreateGroupEvent.NameIsRequired);
20 |
21 | RuleFor(x => x.CategoryId).NotEmpty().WithError(ValidationErrors.CreateGroupEvent.CategoryIdIsRequired);
22 |
23 | RuleFor(x => x.DateTimeUtc).NotEmpty().WithError(ValidationErrors.CreateGroupEvent.DateAndTimeIsRequired);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EventReminder.Application/Users/ChangePassword/UserPasswordChangedIntegrationEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Users.DomainEvents;
4 | using Newtonsoft.Json;
5 |
6 | namespace EventReminder.Application.Users.ChangePassword
7 | {
8 | ///
9 | /// Represents the integration event that is raised when a user's password is changed.
10 | ///
11 | public sealed class UserPasswordChangedIntegrationEvent : IIntegrationEvent
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The user password changed domain event.
17 | internal UserPasswordChangedIntegrationEvent(UserPasswordChangedDomainEvent userPasswordChangedDomainEvent) =>
18 | UserId = userPasswordChangedDomainEvent.User.Id;
19 |
20 | [JsonConstructor]
21 | private UserPasswordChangedIntegrationEvent(Guid userId) => UserId = userId;
22 |
23 | ///
24 | /// Gets the user identifier.
25 | ///
26 | public Guid UserId { get; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/PersonalEvents/PersonalEventResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.PersonalEvents
4 | {
5 | ///
6 | /// Represents the personal event response.
7 | ///
8 | public sealed class PersonalEventResponse
9 | {
10 | ///
11 | /// Gets or sets the identifier.
12 | ///
13 | public Guid Id { get; set; }
14 |
15 | ///
16 | /// Gets or sets the name.
17 | ///
18 | public string Name { get; set; }
19 |
20 | ///
21 | /// Gets or sets the category identifier.
22 | ///
23 | public int CategoryId { get; set; }
24 |
25 | ///
26 | /// Gets or sets the category.
27 | ///
28 | public string Category { get; set; }
29 |
30 | ///
31 | /// Gets or sets the date and time in UTC format.
32 | ///
33 | public DateTime DateTimeUtc { get; set; }
34 |
35 | ///
36 | /// Gets or sets the created on date and time in UTC format.
37 | ///
38 | public DateTime CreatedOnUtc { get; set; }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/EventReminder.Application/Invitations/InvitationSent/InvitationSentIntegrationEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Invitations.DomainEvents;
4 | using Newtonsoft.Json;
5 |
6 | namespace EventReminder.Application.Invitations.InvitationSent
7 | {
8 | ///
9 | /// Represents the integration event that is raised when an invitation is sent.
10 | ///
11 | public sealed class InvitationSentIntegrationEvent : IIntegrationEvent
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The invitation sent domain event.
17 | internal InvitationSentIntegrationEvent(InvitationSentDomainEvent invitationSentDomainEvent) =>
18 | InvitationId = invitationSentDomainEvent.Invitation.Id;
19 |
20 | [JsonConstructor]
21 | private InvitationSentIntegrationEvent(Guid invitationId) => InvitationId = invitationId;
22 |
23 | ///
24 | /// Gets the invitation identifier.
25 | ///
26 | public Guid InvitationId { get; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/EventReminder.BackgroundTasks/Settings/BackgroundTaskSettings.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.BackgroundTasks.Settings
2 | {
3 | ///
4 | /// Represents the background task settings.
5 | ///
6 | public class BackgroundTaskSettings
7 | {
8 | public const string SettingsKey = "BackgroundTasks";
9 |
10 | ///
11 | /// Gets or sets the allowed notification time discrepancy in minutes.
12 | ///
13 | public int AllowedNotificationTimeDiscrepancyInMinutes { get; set; }
14 |
15 | ///
16 | /// Gets or sets the attendees batch size.
17 | ///
18 | public int AttendeesBatchSize { get; set; }
19 |
20 | ///
21 | /// Gets or sets the personal events batch size.
22 | ///
23 | public int PersonalEventsBatchSize { get; set; }
24 |
25 | ///
26 | /// Gets or sets the notifications batch size.
27 | ///
28 | public int NotificationsBatchSize { get; set; }
29 |
30 | ///
31 | /// Gets or sets the sleep time in milliseconds.
32 | ///
33 | public int SleepTimeInMilliseconds { get; set; }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Emails/FriendshipRequestAcceptedEmail.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Contracts.Emails
2 | {
3 | ///
4 | /// Represents the friendship request accepted email.
5 | ///
6 | public sealed class FriendshipRequestAcceptedEmail
7 | {
8 | ///
9 | /// Initializes a new instance of the class.
10 | ///
11 | /// The email receiver.
12 | /// The name.
13 | /// The friend name.
14 | public FriendshipRequestAcceptedEmail(string emailTo, string name, string friendName)
15 | {
16 | EmailTo = emailTo;
17 | Name = name;
18 | FriendName = friendName;
19 | }
20 |
21 | ///
22 | /// Gets the email receiver.
23 | ///
24 | public string EmailTo { get; }
25 |
26 | ///
27 | /// Gets the name.
28 | ///
29 | public string Name { get; }
30 |
31 | ///
32 | /// Gets the friend name.
33 | ///
34 | public string FriendName { get; }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Friendships/IFriendshipRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using EventReminder.Domain.Users;
3 |
4 | namespace EventReminder.Domain.Friendships
5 | {
6 | ///
7 | /// Represents the friendship repository interface.
8 | ///
9 | public interface IFriendshipRepository
10 | {
11 | ///
12 | /// Checks if the specified users are friends.
13 | ///
14 | /// The user.
15 | /// The friend.
16 | /// True if the specified users are friends, otherwise false.
17 | Task CheckIfFriendsAsync(User user, User friend);
18 |
19 | ///
20 | /// Inserts the specified friendship to the database.
21 | ///
22 | /// The friendship to be inserted to the database.
23 | void Insert(Friendship friendship);
24 |
25 | ///
26 | /// Removes the specified friendship from the database.
27 | ///
28 | /// The friendship to be removed from the database.
29 | void Remove(Friendship friendship);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/EventReminder.Application/PersonalEvents/CreatePersonalEvent/CreatePersonalEventCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using EventReminder.Application.Core.Errors;
2 | using EventReminder.Application.Core.Extensions;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.PersonalEvents.CreatePersonalEvent
6 | {
7 | ///
8 | /// Represents the validator.
9 | ///
10 | public sealed class CreateGroupEventCommandValidator : AbstractValidator
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public CreateGroupEventCommandValidator()
16 | {
17 | RuleFor(x => x.UserId).NotEmpty().WithError(ValidationErrors.CreatePersonalEvent.UserIdIsRequired);
18 |
19 | RuleFor(x => x.Name).NotEmpty().WithError(ValidationErrors.CreatePersonalEvent.NameIsRequired);
20 |
21 | RuleFor(x => x.CategoryId).NotEmpty().WithError(ValidationErrors.CreatePersonalEvent.CategoryIdIsRequired);
22 |
23 | RuleFor(x => x.DateTimeUtc).NotEmpty().WithError(ValidationErrors.CreatePersonalEvent.DateAndTimeIsRequired);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EventReminder.Application/Friendships/GetFriendship/GetFriendshipQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Contracts.Friendships;
4 | using EventReminder.Domain.Core.Primitives.Maybe;
5 |
6 | namespace EventReminder.Application.Friendships.GetFriendship
7 | {
8 | ///
9 | /// Represents the query for getting the friendship for the user and friend identifiers.
10 | ///
11 | public sealed class GetFriendshipQuery : IQuery>
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The user identifier.
17 | /// The friend identifier.
18 | public GetFriendshipQuery(Guid userId, Guid friendId)
19 | {
20 | UserId = userId;
21 | FriendId = friendId;
22 | }
23 |
24 | ///
25 | /// Gets the user identifier.
26 | ///
27 | public Guid UserId { get; }
28 |
29 | ///
30 | /// Gets the friend identifier.
31 | ///
32 | public Guid FriendId { get; }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/EventReminder.Application/Core/Extensions/FluentValidationExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Domain.Core.Primitives;
3 | using FluentValidation;
4 |
5 | namespace EventReminder.Application.Core.Extensions
6 | {
7 | ///
8 | /// Contains extension methods for fluent validations.
9 | ///
10 | public static class FluentValidationExtensions
11 | {
12 | ///
13 | /// Specifies a custom error to use if validation fails.
14 | ///
15 | /// The type being validated.
16 | /// The property being validated.
17 | /// The current rule.
18 | /// The error to use.
19 | /// The same rule builder.
20 | public static IRuleBuilderOptions WithError(
21 | this IRuleBuilderOptions rule, Error error)
22 | {
23 | if (error is null)
24 | {
25 | throw new ArgumentNullException(nameof(error), "The error is required");
26 | }
27 |
28 | return rule.WithErrorCode(error.Code).WithMessage(error.Message);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/EventReminder.Application/GroupEvents/GroupEventCancelled/GroupEventCancelledIntegrationEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Events.DomainEvents;
4 | using Newtonsoft.Json;
5 |
6 | namespace EventReminder.Application.GroupEvents.GroupEventCancelled
7 | {
8 | ///
9 | /// Represents the event that is raised when a group event is cancelled.
10 | ///
11 | public sealed class GroupEventCancelledIntegrationEvent : IIntegrationEvent
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The group event cancelled domain event.
17 | internal GroupEventCancelledIntegrationEvent(GroupEventCancelledDomainEvent groupEventCancelledDomainEvent) =>
18 | GroupEventId = groupEventCancelledDomainEvent.GroupEvent.Id;
19 |
20 | [JsonConstructor]
21 | private GroupEventCancelledIntegrationEvent(Guid groupEventId) => GroupEventId = groupEventId;
22 |
23 | ///
24 | /// Gets the group event identifier.
25 | ///
26 | public Guid GroupEventId { get; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/EventReminder.Application/GroupEvents/InviteFriendToGroupEvent/InviteFriendToGroupEventCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.GroupEvents.InviteFriendToGroupEvent
6 | {
7 | ///
8 | /// Represents the invite friend to group event command.
9 | ///
10 | public sealed class InviteFriendToGroupEventCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The group event identifier.
16 | /// The friend identifier.
17 | public InviteFriendToGroupEventCommand(Guid groupEventId, Guid friendId)
18 | {
19 | GroupEventId = groupEventId;
20 | FriendId = friendId;
21 | }
22 |
23 | ///
24 | /// Gets the group event identifier.
25 | ///
26 | public Guid GroupEventId { get; }
27 |
28 | ///
29 | /// Gets the friend identifier.
30 | ///
31 | public Guid FriendId { get; }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/EventReminder.Application/Invitations/InvitationAccepted/InvitationAcceptedIntegrationEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Invitations.DomainEvents;
4 | using Newtonsoft.Json;
5 |
6 | namespace EventReminder.Application.Invitations.InvitationAccepted
7 | {
8 | ///
9 | /// Represents the integration event that is raised when an invitation is accepted.
10 | ///
11 | public sealed class InvitationAcceptedIntegrationEvent : IIntegrationEvent
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The invitation accepted domain event.
17 | internal InvitationAcceptedIntegrationEvent(InvitationAcceptedDomainEvent invitationAcceptedDomainEvent) =>
18 | InvitationId = invitationAcceptedDomainEvent.Invitation.Id;
19 |
20 | [JsonConstructor]
21 | private InvitationAcceptedIntegrationEvent(Guid invitationId) => InvitationId = invitationId;
22 |
23 | ///
24 | /// Gets the invitation identifier.
25 | ///
26 | public Guid InvitationId { get; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/EventReminder.Application/Invitations/InvitationRejected/InvitationRejectedIntegrationEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Invitations.DomainEvents;
4 | using Newtonsoft.Json;
5 |
6 | namespace EventReminder.Application.Invitations.InvitationRejected
7 | {
8 | ///
9 | /// Represents the integration event that is raised when an invitation is rejected.
10 | ///
11 | public sealed class InvitationRejectedIntegrationEvent : IIntegrationEvent
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The invitation rejected domain event.
17 | internal InvitationRejectedIntegrationEvent(InvitationRejectedDomainEvent invitationRejectedDomainEvent) =>
18 | InvitationId = invitationRejectedDomainEvent.Invitation.Id;
19 |
20 | [JsonConstructor]
21 | private InvitationRejectedIntegrationEvent(Guid invitationId) => InvitationId = invitationId;
22 |
23 | ///
24 | /// Gets the invitation identifier.
25 | ///
26 | public Guid InvitationId { get; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/EventReminder.Infrastructure/Authentication/Settings/JwtSettings.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace EventReminder.Infrastructure.Authentication.Settings
3 | {
4 | ///
5 | /// Represents the JWT configuration settings.
6 | ///
7 | public class JwtSettings
8 | {
9 | public const string SettingsKey = "Jwt";
10 |
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | public JwtSettings()
15 | {
16 | Issuer = string.Empty;
17 | Audience = string.Empty;
18 | SecurityKey = string.Empty;
19 | }
20 |
21 | ///
22 | /// Gets or sets the issuer.
23 | ///
24 | public string Issuer { get; set; }
25 |
26 | ///
27 | /// Gets or sets the audience.
28 | ///
29 | public string Audience { get; set; }
30 |
31 | ///
32 | /// Gets or sets the security key.
33 | ///
34 | public string SecurityKey { get; set; }
35 |
36 | ///
37 | /// Gets or sets the token expiration time in minutes.
38 | ///
39 | public int TokenExpirationInMinutes { get; set; }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Specifications/Specification.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 | using EventReminder.Domain.Core.Primitives;
4 |
5 | namespace EventReminder.Persistence.Specifications
6 | {
7 | ///
8 | /// Represents the abstract base class for specifications.
9 | ///
10 | /// The entity type.
11 | internal abstract class Specification
12 | where TEntity : Entity
13 | {
14 | ///
15 | /// Converts the specification to an expression predicate.
16 | ///
17 | /// The expression predicate.
18 | internal abstract Expression> ToExpression();
19 |
20 | ///
21 | /// Checks if the specified entity satisfies this specification.
22 | ///
23 | /// The entity.
24 | /// True if the entity satisfies the specification, otherwise false.
25 | internal bool IsSatisfiedBy(TEntity entity) => ToExpression().Compile()(entity);
26 |
27 | public static implicit operator Expression>(Specification specification) =>
28 | specification.ToExpression();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/EventReminder.Application/GroupEvents/Get10MostRecentAttendingGroupEvents/Get10MostRecentAttendingGroupEventsQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using EventReminder.Application.Abstractions.Messaging;
4 | using EventReminder.Contracts.GroupEvents;
5 | using EventReminder.Domain.Core.Primitives.Maybe;
6 |
7 | namespace EventReminder.Application.GroupEvents.Get10MostRecentAttendingGroupEvents
8 | {
9 | ///
10 | /// Represents the query for getting the 10 most recent group event the user is attending.
11 | ///
12 | public sealed class Get10MostRecentAttendingGroupEventsQuery : IQuery>>
13 | {
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// The user identifier.
18 | public Get10MostRecentAttendingGroupEventsQuery(Guid userId) => UserId = userId;
19 |
20 | ///
21 | /// Gets the user identifier.
22 | ///
23 | public Guid UserId { get; }
24 |
25 | ///
26 | /// Gets the number of group events to take.
27 | ///
28 | public int NumberOfGroupEventsToTake => 10;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Repositories/GroupEventRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using EventReminder.Application.Abstractions.Data;
6 | using EventReminder.Domain.Events;
7 | using EventReminder.Persistence.Specifications;
8 | using Microsoft.EntityFrameworkCore;
9 |
10 | namespace EventReminder.Persistence.Repositories
11 | {
12 | ///
13 | /// Represents the group event repository.
14 | ///
15 | internal sealed class GroupEventRepository : GenericRepository, IGroupEventRepository
16 | {
17 | ///
18 | /// Initializes a new instance of the class.
19 | ///
20 | /// The database context.
21 | public GroupEventRepository(IDbContext dbContext)
22 | : base(dbContext)
23 | {
24 | }
25 |
26 | ///
27 | public async Task> GetForAttendeesAsync(IReadOnlyCollection attendees) =>
28 | attendees.Any()
29 | ? await DbContext.Set().Where(new GroupEventForAttendeesSpecification(attendees)).ToArrayAsync()
30 | : Array.Empty();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Emails/FriendshipRequestSentEmail.cs:
--------------------------------------------------------------------------------
1 | namespace EventReminder.Contracts.Emails
2 | {
3 | ///
4 | /// Represents the friendship request sent email.
5 | ///
6 | public sealed class FriendshipRequestSentEmail
7 | {
8 | ///
9 | /// Initializes a new instance of the class.
10 | ///
11 | /// The email receiver.
12 | /// The name.
13 | /// THe name of the user who sent the friendship request.
14 | public FriendshipRequestSentEmail(string emailTo, string name, string userWhoSentRequest)
15 | {
16 | EmailTo = emailTo;
17 | Name = name;
18 | UserWhoSentRequest = userWhoSentRequest;
19 | }
20 |
21 | ///
22 | /// Gets the email receiver.
23 | ///
24 | public string EmailTo { get; }
25 |
26 | ///
27 | /// Gets the name.
28 | ///
29 | public string Name { get; }
30 |
31 | ///
32 | /// Gets the name of the user who sent the friendship request.
33 | ///
34 | public string UserWhoSentRequest { get; }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/EventReminder.Persistence/Repositories/PersonalEventRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using EventReminder.Application.Abstractions.Data;
5 | using EventReminder.Domain.Events;
6 | using EventReminder.Persistence.Specifications;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace EventReminder.Persistence.Repositories
10 | {
11 | ///
12 | /// Represents the attendee repository.
13 | ///
14 | internal sealed class PersonalEventRepository : GenericRepository, IPersonalEventRepository
15 | {
16 | ///
17 | /// Initializes a new instance of the class.
18 | ///
19 | /// The database context.
20 | public PersonalEventRepository(IDbContext dbContext)
21 | : base(dbContext)
22 | {
23 | }
24 |
25 | ///
26 | public async Task> GetUnprocessedAsync(int take) =>
27 | await DbContext.Set()
28 | .Where(new UnProcessedPersonalEventSpecification())
29 | .OrderBy(personalEvent => personalEvent.CreatedOnUtc)
30 | .Take(take)
31 | .ToArrayAsync();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Events/DomainEvents/GroupEventDateAndTimeChangedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Domain.Core.Events;
3 |
4 | namespace EventReminder.Domain.Events.DomainEvents
5 | {
6 | ///
7 | /// Represents the event that is raised when the date and time of a group event is changed.
8 | ///
9 | public sealed class GroupEventDateAndTimeChangedDomainEvent : IDomainEvent
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The group event.
15 | /// The previous date and time of the group event in UTC format.
16 | internal GroupEventDateAndTimeChangedDomainEvent(GroupEvent groupEvent, DateTime previousDateAndTimeUtc)
17 | {
18 | GroupEvent = groupEvent;
19 | PreviousDateAndTimeUtc = previousDateAndTimeUtc;
20 | }
21 |
22 | ///
23 | /// Gets the group event.
24 | ///
25 | public GroupEvent GroupEvent { get; }
26 |
27 | ///
28 | /// Gets the previous date and time of the group event in UTC format.
29 | ///
30 | public DateTime PreviousDateAndTimeUtc { get; }
31 | }
32 | }
--------------------------------------------------------------------------------
/EventReminder.Contracts/Friendships/FriendshipResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.Friendships
4 | {
5 | ///
6 | /// Represents the friendship response.
7 | ///
8 | public sealed class FriendshipResponse
9 | {
10 | ///
11 | /// Gets or sets the user identifier.
12 | ///
13 | public Guid UserId { get; set; }
14 |
15 | ///
16 | /// Gets or sets the user email.
17 | ///
18 | public string UserEmail { get; set; }
19 |
20 | ///
21 | /// Gets or sets the user name.
22 | ///
23 | public string UserName { get; set; }
24 |
25 | ///
26 | /// Gets or sets the friend identifier.
27 | ///
28 | public Guid FriendId { get; set; }
29 |
30 | ///
31 | /// Gets or sets the friend email.
32 | ///
33 | public string FriendEmail { get; set; }
34 |
35 | ///
36 | /// Gets or sets the friend name.
37 | ///
38 | public string FriendName { get; set; }
39 |
40 | ///
41 | /// Gets or sets the created on date and time in UTC format.
42 | ///
43 | public DateTime CreatedOnUtc { get; set; }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/EventReminder.Application/Users/UpdateUser/UpdateUserCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Core.Primitives.Result;
4 |
5 | namespace EventReminder.Application.Users.UpdateUser
6 | {
7 | ///
8 | /// Represents the update user command.
9 | ///
10 | public sealed class UpdateUserCommand : ICommand
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// The user identifier.
16 | /// The first name.
17 | /// The last name.
18 | public UpdateUserCommand(Guid userId, string firstName, string lastName)
19 | {
20 | UserId = userId;
21 | FirstName = firstName;
22 | LastName = lastName;
23 | }
24 |
25 | ///
26 | /// Gets the user identifier.
27 | ///
28 | public Guid UserId { get; }
29 |
30 | ///
31 | /// Gets the first name.
32 | ///
33 | public string FirstName { get; }
34 |
35 | ///
36 | /// Gets the last name.
37 | ///
38 | public string LastName { get; }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/EventReminder.Services.Api/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
2 | USER app
3 | WORKDIR /app
4 | EXPOSE 8080
5 | EXPOSE 8081
6 |
7 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
8 | ARG BUILD_CONFIGURATION=Release
9 | WORKDIR /src
10 | COPY ["EventReminder.Services.Api/EventReminder.Services.Api.csproj", "EventReminder.Services.Api/"]
11 | COPY ["EventReminder.Infrastructure/EventReminder.Infrastructure.csproj", "EventReminder.Infrastructure/"]
12 | COPY ["EventReminder.Application/EventReminder.Application.csproj", "EventReminder.Application/"]
13 | COPY ["EventReminder.Contracts/EventReminder.Contracts.csproj", "EventReminder.Contracts/"]
14 | COPY ["EventReminder.Domain/EventReminder.Domain.csproj", "EventReminder.Domain/"]
15 | COPY ["EventReminder.Persistence/EventReminder.Persistence.csproj", "EventReminder.Persistence/"]
16 | RUN dotnet restore "./EventReminder.Services.Api/EventReminder.Services.Api.csproj"
17 | COPY . .
18 | WORKDIR "/src/EventReminder.Services.Api"
19 | RUN dotnet build "./EventReminder.Services.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build
20 |
21 | FROM build AS publish
22 | ARG BUILD_CONFIGURATION=Release
23 | RUN dotnet publish "./EventReminder.Services.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
24 |
25 | FROM base AS final
26 | WORKDIR /app
27 | COPY --from=publish /app/publish .
28 | ENTRYPOINT ["dotnet", "EventReminder.Services.Api.dll"]
--------------------------------------------------------------------------------
/EventReminder.Application/FriendshipRequests/FriendshipRequestSent/FriendshipRequestSentIntegrationEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EventReminder.Application.Abstractions.Messaging;
3 | using EventReminder.Domain.Friendships.DomainEvents;
4 | using Newtonsoft.Json;
5 |
6 | namespace EventReminder.Application.FriendshipRequests.FriendshipRequestSent
7 | {
8 | ///
9 | /// Represents the integration event that is raised when a friendship request is sent.
10 | ///
11 | public sealed class FriendshipRequestSentIntegrationEvent : IIntegrationEvent
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The friendship request sent domain event.
17 | internal FriendshipRequestSentIntegrationEvent(FriendshipRequestSentDomainEvent friendshipRequestSentDomainEvent) =>
18 | FriendshipRequestId = friendshipRequestSentDomainEvent.FriendshipRequest.Id;
19 |
20 | [JsonConstructor]
21 | private FriendshipRequestSentIntegrationEvent(Guid friendshipRequestId) => FriendshipRequestId = friendshipRequestId;
22 |
23 | ///
24 | /// Gets the friendship request identifier.
25 | ///
26 | public Guid FriendshipRequestId { get; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/EventReminder.Application/Attendees/AttendeeCreated/PublishIntegrationEventOnAttendeeCreatedEventHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using EventReminder.Application.Abstractions.Messaging;
4 |
5 | namespace EventReminder.Application.Attendees.AttendeeCreated
6 | {
7 | ///
8 | /// Represents the handler.
9 | ///
10 | internal sealed class PublishIntegrationEventOnAttendeeCreatedEventHandler : IEventHandler
11 | {
12 | private readonly IIntegrationEventPublisher _integrationEventPublisher;
13 |
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// The integration event publisher.
18 | public PublishIntegrationEventOnAttendeeCreatedEventHandler(IIntegrationEventPublisher integrationEventPublisher) =>
19 | _integrationEventPublisher = integrationEventPublisher;
20 |
21 | ///
22 | public async Task Handle(AttendeeCreatedEvent notification, CancellationToken cancellationToken)
23 | {
24 | _integrationEventPublisher.Publish(new AttendeeCreatedIntegrationEvent(notification));
25 |
26 | await Task.CompletedTask;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/FriendshipRequests/FriendshipRequestResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.FriendshipRequests
4 | {
5 | ///
6 | /// Represents the friendship request response.
7 | ///
8 | public sealed class FriendshipRequestResponse
9 | {
10 | ///
11 | /// Gets or sets the user identifier.
12 | ///
13 | public Guid UserId { get; set; }
14 |
15 | ///
16 | /// Gets or sets the user email.
17 | ///
18 | public string UserEmail { get; set; }
19 |
20 | ///
21 | /// Gets or sets the user name.
22 | ///
23 | public string UserName { get; set; }
24 |
25 | ///
26 | /// Gets or sets the friend identifier.
27 | ///
28 | public Guid FriendId { get; set; }
29 |
30 | ///
31 | /// Gets or sets the friend email.
32 | ///
33 | public string FriendEmail { get; set; }
34 |
35 | ///
36 | /// Gets or sets the friend name.
37 | ///
38 | public string FriendName { get; set; }
39 |
40 | ///
41 | /// Gets or sets the created on date and time in UTC format.
42 | ///
43 | public DateTime CreatedOnUtc { get; set; }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/PersonalEvents/DetailedPersonalEventResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.PersonalEvents
4 | {
5 | ///
6 | /// Represents the personal event response.
7 | ///
8 | public sealed class DetailedPersonalEventResponse
9 | {
10 | ///
11 | /// Gets or sets the identifier.
12 | ///
13 | public Guid Id { get; set; }
14 |
15 | ///
16 | /// Gets or sets the name.
17 | ///
18 | public string Name { get; set; }
19 |
20 | ///
21 | /// Gets or sets the category identifier.
22 | ///
23 | public int CategoryId { get; set; }
24 |
25 | ///
26 | /// Gets or sets the category.
27 | ///
28 | public string Category { get; set; }
29 |
30 | ///
31 | /// Gets or sets the created by name.
32 | ///
33 | public string CreatedBy { get; set; }
34 |
35 | ///
36 | /// Gets or sets the date and time in UTC format.
37 | ///
38 | public DateTime DateTimeUtc { get; set; }
39 |
40 | ///
41 | /// Gets or sets the created on date and time in UTC format.
42 | ///
43 | public DateTime CreatedOnUtc { get; set; }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/EventReminder.Contracts/Attendees/AttendeeListResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EventReminder.Contracts.Attendees
4 | {
5 | ///
6 | /// Represents the attendee list response.
7 | ///
8 | public sealed class AttendeeListResponse
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The attendees.
14 | public AttendeeListResponse(AttendeeModel[] attendees) => Attendees = attendees;
15 |
16 | ///
17 | /// Gets the attendees.
18 | ///
19 | public AttendeeModel[] Attendees { get; }
20 |
21 | ///
22 | /// Represents the attendee model.
23 | ///
24 | public sealed class AttendeeModel
25 | {
26 | ///
27 | /// Gets or sets the user identifier.
28 | ///
29 | public Guid UserId { get; set; }
30 |
31 | ///
32 | /// Gets or sets the name.
33 | ///
34 | public string Name { get; set; }
35 |
36 | ///
37 | /// Gets or sets the created on date and time in UTC format.
38 | ///
39 | public DateTime CreatedOnUtc { get; set; }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/EventReminder.Application/FriendshipRequests/FriendshipRequestAccepted/FriendshipRequestAcceptedIntegrationEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using EventReminder.Domain.Friendships.DomainEvents;
4 | using EventReminder.Application.Abstractions.Messaging;
5 |
6 | namespace EventReminder.Application.FriendshipRequests.FriendshipRequestAccepted
7 | {
8 | ///
9 | /// Represents the integration event that is raised when a friendship request is accepted.
10 | ///
11 | public sealed class FriendshipRequestAcceptedIntegrationEvent : IIntegrationEvent
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// The friendship request accepted domain event.
17 | internal FriendshipRequestAcceptedIntegrationEvent(FriendshipRequestAcceptedDomainEvent friendshipRequestAcceptedDomainEvent) =>
18 | FriendshipRequestId = friendshipRequestAcceptedDomainEvent.FriendshipRequest.Id;
19 |
20 | [JsonConstructor]
21 | private FriendshipRequestAcceptedIntegrationEvent(Guid friendshipRequestId) => FriendshipRequestId = friendshipRequestId;
22 |
23 | ///
24 | /// Gets the friendship request identifier.
25 | ///
26 | public Guid FriendshipRequestId { get; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/EventReminder.Domain/Core/Primitives/Error.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace EventReminder.Domain.Core.Primitives
4 | {
5 | ///
6 | /// Represents a concrete domain error.
7 | ///
8 | public sealed class Error : ValueObject
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// The error code.
14 | /// The error message.
15 | public Error(string code, string message)
16 | {
17 | Code = code;
18 | Message = message;
19 | }
20 |
21 | ///
22 | /// Gets the error code.
23 | ///
24 | public string Code { get; }
25 |
26 | ///
27 | /// Gets the error message.
28 | ///
29 | public string Message { get; }
30 |
31 | public static implicit operator string(Error error) => error?.Code ?? string.Empty;
32 |
33 | ///
34 | protected override IEnumerable