├── 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 GetAtomicValues() 35 | { 36 | yield return Code; 37 | yield return Message; 38 | } 39 | 40 | /// 41 | /// Gets the empty error instance. 42 | /// 43 | internal static Error None => new Error(string.Empty, string.Empty); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /EventReminder.Domain/Events/IGroupEventRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using EventReminder.Domain.Core.Primitives.Maybe; 5 | 6 | namespace EventReminder.Domain.Events 7 | { 8 | /// 9 | /// Represents the group event repository interface. 10 | /// 11 | public interface IGroupEventRepository 12 | { 13 | /// 14 | /// Gets the group event with the specified identifier. 15 | /// 16 | /// The group event identifier. 17 | /// The maybe instance that may contain the group event with the specified identifier. 18 | Task> GetByIdAsync(Guid groupEventId); 19 | 20 | /// 21 | /// Gets the distinct group events for the specified attendees. 22 | /// 23 | /// The attendees to get the group events for. 24 | /// The readonly collection of group events with the specified identifiers. 25 | Task> GetForAttendeesAsync(IReadOnlyCollection attendees); 26 | 27 | /// 28 | /// Inserts the specified group event to the database. 29 | /// 30 | /// The group event to be inserted to the database. 31 | void Insert(GroupEvent groupEvent); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /EventReminder.Domain/Events/IPersonalEventRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using EventReminder.Domain.Core.Primitives.Maybe; 5 | 6 | namespace EventReminder.Domain.Events 7 | { 8 | /// 9 | /// Represents the personal event repository interface. 10 | /// 11 | public interface IPersonalEventRepository 12 | { 13 | /// 14 | /// Gets the personal event with the specified identifier. 15 | /// 16 | /// The personal event identifier. 17 | /// The maybe instance that may contain the personal event with the specified identifier. 18 | Task> GetByIdAsync(Guid personalEventId); 19 | 20 | /// 21 | /// Gets the specified number of unprocessed personal events, if they exist. 22 | /// 23 | /// The number of personal events to take. 24 | /// The specified number of unprocessed personal events, if they exist. 25 | Task> GetUnprocessedAsync(int take); 26 | 27 | /// 28 | /// Inserts the specified personal event to the database. 29 | /// 30 | /// The personal event to be inserted to the database. 31 | void Insert(PersonalEvent personalEvent); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /EventReminder.Persistence/Specifications/PendingFriendshipRequestSpecification.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 pending friendship request. 10 | /// 11 | internal sealed class PendingFriendshipRequestSpecification : 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 PendingFriendshipRequestSpecification(User user, User friend) 22 | { 23 | _userId = user.Id; 24 | _friendId = friend.Id; 25 | } 26 | 27 | /// 28 | internal override Expression> ToExpression() => 29 | friendshipRequest => (friendshipRequest.UserId == _userId || friendshipRequest.UserId == _friendId) && 30 | (friendshipRequest.FriendId == _userId || friendshipRequest.FriendId == _friendId) && 31 | friendshipRequest.CompletedOnUtc == null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /EventReminder.Contracts/Users/UserResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EventReminder.Contracts.Users 4 | { 5 | /// 6 | /// Represents the user response. 7 | /// 8 | public sealed class UserResponse 9 | { 10 | /// 11 | /// Gets or sets the identifier. 12 | /// 13 | public Guid Id { get; set; } 14 | 15 | /// 16 | /// Gets or sets the full name. 17 | /// 18 | public string FullName { get; set; } 19 | 20 | /// 21 | /// Gets or sets the first name. 22 | /// 23 | public string FirstName { get; set; } 24 | 25 | /// 26 | /// Gets or sets the last name. 27 | /// 28 | public string LastName { get; set; } 29 | 30 | /// 31 | /// Gets or sets the email. 32 | /// 33 | public string Email { 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 | /// Gets or sets the number of personal events. 42 | /// 43 | public int NumberOfPersonalEvents { get; set; } 44 | 45 | /// 46 | /// Gets or sets the number of friends. 47 | /// 48 | public int NumberOfFriends { get; set; } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /EventReminder.Persistence/Specifications/NotificationWithinTimeFrameSpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using EventReminder.Domain.Notifications; 4 | 5 | namespace EventReminder.Persistence.Specifications 6 | { 7 | /// 8 | /// Represents the specification for determining the notifications within a particular time-frame. 9 | /// 10 | internal sealed class NotificationWithinTimeFrameSpecification : Specification 11 | { 12 | private readonly DateTime _startTime; 13 | private readonly DateTime _endTime; 14 | 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | /// The current date and time in UTC format. 19 | /// The time-frame in minutes. 20 | internal NotificationWithinTimeFrameSpecification(DateTime utcNow, int timeFrameInMinutes) 21 | { 22 | _startTime = utcNow.AddMinutes(-timeFrameInMinutes); 23 | _endTime = utcNow.AddMinutes(timeFrameInMinutes); 24 | } 25 | 26 | /// 27 | internal override Expression> ToExpression() => 28 | notification => !notification.Sent && 29 | notification.DateTimeUtc >= _startTime && 30 | notification.DateTimeUtc <= _endTime; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EventReminder.Services.Notifications/Startup.cs: -------------------------------------------------------------------------------- 1 | using EventReminder.BackgroundTasks; 2 | using EventReminder.Infrastructure; 3 | using EventReminder.Persistence; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | 10 | namespace EventReminder.Services.Notifications 11 | { 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services 24 | .AddHttpContextAccessor() 25 | .AddInfrastructure(Configuration) 26 | .AddPersistence(Configuration) 27 | .AddBackgroundTasks(Configuration); 28 | 29 | services.AddControllers(); 30 | } 31 | 32 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 33 | { 34 | if (env.IsDevelopment()) 35 | { 36 | app.UseDeveloperExceptionPage(); 37 | } 38 | 39 | app.UseHttpsRedirection(); 40 | 41 | app.UseRouting(); 42 | 43 | app.UseAuthorization(); 44 | 45 | app.UseEndpoints(endpoints => endpoints.MapControllers()); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /EventReminder.Contracts/Emails/AttendeeCreatedEmail.cs: -------------------------------------------------------------------------------- 1 | namespace EventReminder.Contracts.Emails 2 | { 3 | /// 4 | /// Represents the attendee created email. 5 | /// 6 | public sealed class AttendeeCreatedEmail 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// The email receiver. 12 | /// The name. 13 | /// The event name. 14 | /// The event date and time. 15 | public AttendeeCreatedEmail(string emailTo, string name, string eventName, string eventDateAndTime) 16 | { 17 | EmailTo = emailTo; 18 | Name = name; 19 | EventName = eventName; 20 | EventDateAndTime = eventDateAndTime; 21 | } 22 | 23 | /// 24 | /// Gets the email receiver. 25 | /// 26 | public string EmailTo { get; } 27 | 28 | /// 29 | /// Gets the name. 30 | /// 31 | public string Name { get; } 32 | 33 | /// 34 | /// Gets the event name. 35 | /// 36 | public string EventName { get; } 37 | 38 | /// 39 | /// Gets the event date and time. 40 | /// 41 | public string EventDateAndTime { get; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /EventReminder.Application/Users/CreateUser/PublishIntegrationEventOnUserCreatedDomainEventHandler.cs: -------------------------------------------------------------------------------- 1 | using EventReminder.Application.Abstractions.Messaging; 2 | using EventReminder.Domain.Core.Events; 3 | using EventReminder.Domain.Users.DomainEvents; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventReminder.Application.Users.CreateUser 8 | { 9 | /// 10 | /// Represents the handler. 11 | /// 12 | internal sealed class PublishIntegrationEventOnUserCreatedDomainEventHandler 13 | : IDomainEventHandler 14 | { 15 | private readonly IIntegrationEventPublisher _integrationEventPublisher; 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The integration event publisher. 21 | public PublishIntegrationEventOnUserCreatedDomainEventHandler(IIntegrationEventPublisher integrationEventPublisher) => 22 | _integrationEventPublisher = integrationEventPublisher; 23 | 24 | /// 25 | public async Task Handle(UserCreatedDomainEvent notification, CancellationToken cancellationToken) 26 | { 27 | _integrationEventPublisher.Publish(new UserCreatedIntegrationEvent(notification)); 28 | 29 | await Task.CompletedTask; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EventReminder.Contracts/Emails/InvitationSentEmail.cs: -------------------------------------------------------------------------------- 1 | namespace EventReminder.Contracts.Emails 2 | { 3 | /// 4 | /// Represents the invitation sent email. 5 | /// 6 | public sealed class InvitationSentEmail 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// The email receiver. 12 | /// The name. 13 | /// The event name. 14 | /// The date and time of the event. 15 | public InvitationSentEmail(string emailTo, string name, string eventName, string eventDateAndTime) 16 | { 17 | EmailTo = emailTo; 18 | Name = name; 19 | EventName = eventName; 20 | EventDateAndTime = eventDateAndTime; 21 | } 22 | 23 | /// 24 | /// Gets the email receiver. 25 | /// 26 | public string EmailTo { get; } 27 | 28 | /// 29 | /// Gets the name. 30 | /// 31 | public string Name { get; } 32 | 33 | /// 34 | /// Gets the event name. 35 | /// 36 | public string EventName { get; } 37 | 38 | /// 39 | /// Gets the date and time of the event. 40 | /// 41 | public string EventDateAndTime { get; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /EventReminder.Persistence/Configurations/FriendshipConfiguration.cs: -------------------------------------------------------------------------------- 1 | using EventReminder.Domain.Friendships; 2 | using EventReminder.Domain.Users; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 5 | 6 | namespace EventReminder.Persistence.Configurations 7 | { 8 | /// 9 | /// Represents the configuration for the entity. 10 | /// 11 | internal sealed class FriendshipConfiguration : IEntityTypeConfiguration 12 | { 13 | /// 14 | public void Configure(EntityTypeBuilder builder) 15 | { 16 | builder.HasKey(friendship => new 17 | { 18 | friendship.UserId, 19 | friendship.FriendId 20 | }); 21 | 22 | builder.HasOne() 23 | .WithMany() 24 | .HasForeignKey(friendship => friendship.UserId) 25 | .IsRequired() 26 | .OnDelete(DeleteBehavior.NoAction); 27 | 28 | builder.HasOne() 29 | .WithMany() 30 | .HasForeignKey(friendship => friendship.FriendId) 31 | .IsRequired() 32 | .OnDelete(DeleteBehavior.NoAction); 33 | 34 | builder.Property(friendship => friendship.CreatedOnUtc).IsRequired(); 35 | 36 | builder.Property(friendship => friendship.ModifiedOnUtc); 37 | 38 | builder.Ignore(friendship => friendship.Id); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /EventReminder.Application/GroupEvents/UpdateGroupEvent/UpdateGroupEventCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EventReminder.Application.Abstractions.Messaging; 3 | using EventReminder.Domain.Core.Primitives.Result; 4 | 5 | namespace EventReminder.Application.GroupEvents.UpdateGroupEvent 6 | { 7 | /// 8 | /// Represents the update group event command. 9 | /// 10 | public sealed class UpdateGroupEventCommand : ICommand 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// The group event identifier. 16 | /// The event name. 17 | /// The date and time of the event in UTC format. 18 | public UpdateGroupEventCommand(Guid groupEventId, string name, DateTime dateTimeUtc) 19 | { 20 | GroupEventId = groupEventId; 21 | Name = name; 22 | DateTimeUtc = dateTimeUtc.ToUniversalTime(); 23 | } 24 | 25 | /// 26 | /// Gets the group event identifier. 27 | /// 28 | public Guid GroupEventId { get; } 29 | 30 | /// 31 | /// Gets the name. 32 | /// 33 | public string Name { get; } 34 | 35 | /// 36 | /// Gets the date and time in UTC format. 37 | /// 38 | public DateTime DateTimeUtc { get; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /EventReminder.Contracts/Emails/GroupEventCancelledEmail.cs: -------------------------------------------------------------------------------- 1 | namespace EventReminder.Contracts.Emails 2 | { 3 | /// 4 | /// Represents the group event cancelled email. 5 | /// 6 | public sealed class GroupEventCancelledEmail 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// The email receiver. 12 | /// The name. 13 | /// The event name. 14 | /// The date and time of the event. 15 | public GroupEventCancelledEmail(string emailTo, string name, string eventName, string eventDateAndTime) 16 | { 17 | EmailTo = emailTo; 18 | Name = name; 19 | EventName = eventName; 20 | EventDateAndTime = eventDateAndTime; 21 | } 22 | 23 | /// 24 | /// Gets the email receiver. 25 | /// 26 | public string EmailTo { get; } 27 | 28 | /// 29 | /// Gets the name. 30 | /// 31 | public string Name { get; } 32 | 33 | /// 34 | /// Gets the event name. 35 | /// 36 | public string EventName { get; } 37 | 38 | /// 39 | /// Gets the date and time of the event. 40 | /// 41 | public string EventDateAndTime { get; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /EventReminder.Application/Invitations/InvitationSent/PublishIntegrationEventOnInvitationSentDomainEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using EventReminder.Application.Abstractions.Messaging; 4 | using EventReminder.Domain.Core.Events; 5 | using EventReminder.Domain.Invitations.DomainEvents; 6 | 7 | namespace EventReminder.Application.Invitations.InvitationSent 8 | { 9 | /// 10 | /// Represents the handler. 11 | /// 12 | internal sealed class PublishIntegrationEventOnInvitationSentDomainEventHandler : IDomainEventHandler 13 | { 14 | private readonly IIntegrationEventPublisher _integrationEventPublisher; 15 | 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | /// The integration event publisher. 20 | public PublishIntegrationEventOnInvitationSentDomainEventHandler(IIntegrationEventPublisher integrationEventPublisher) => 21 | _integrationEventPublisher = integrationEventPublisher; 22 | 23 | /// 24 | public async Task Handle(InvitationSentDomainEvent notification, CancellationToken cancellationToken) 25 | { 26 | _integrationEventPublisher.Publish(new InvitationSentIntegrationEvent(notification)); 27 | 28 | await Task.CompletedTask; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | eventreminder.services.api: 5 | image: ${DOCKER_REGISTRY-}eventreminderservicesapi 6 | container_name: EventReminder.Api 7 | build: 8 | context: . 9 | dockerfile: EventReminder.Services.Api/Dockerfile 10 | restart: always 11 | ports: 12 | - 5000:8080 13 | - 5001:8081 14 | environment: 15 | ASPNETCORE_ENVIRONMENT: "Development" 16 | 17 | eventreminder.services.notifications: 18 | image: ${DOCKER_REGISTRY-}eventreminderservicesnotifications 19 | container_name: EventReminder.Notifications 20 | build: 21 | context: . 22 | dockerfile: EventReminder.Services.Notifications/Dockerfile 23 | restart: always 24 | ports: 25 | - 6000:8080 26 | - 6001:8081 27 | environment: 28 | ASPNETCORE_ENVIRONMENT: "Development" 29 | 30 | event-reminder-db: 31 | image: mcr.microsoft.com/mssql/server:2022-latest 32 | container_name: EventReminder.Db 33 | volumes: 34 | - ./.containers/database:/var/opt/mssql/data 35 | ports: 36 | - "1433:1433" 37 | environment: 38 | ACCEPT_EULA: "Y" 39 | SA_PASSWORD: "Strong_password_123!" 40 | 41 | event-reminder-queue: 42 | image: rabbitmq:management 43 | container_name: EventReminder.Queue 44 | hostname: event-reminder-mq 45 | volumes: 46 | - ./.containers/queue/data/:/var/lib/rabbitmq 47 | - ./.containers/queue/log/:/var/log/rabbitmq 48 | environment: 49 | RABBITMQ_DEFAULT_USER: guest 50 | RABBITMQ_DEFAULT_PASS: guest -------------------------------------------------------------------------------- /EventReminder.Contracts/GroupEvents/DetailedGroupEventResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EventReminder.Contracts.GroupEvents 4 | { 5 | /// 6 | /// Represents the group event response. 7 | /// 8 | public sealed class DetailedGroupEventResponse 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 number of attendees. 37 | /// 38 | public int NumberOfAttendees { get; set; } 39 | 40 | /// 41 | /// Gets or sets the date and time in UTC format. 42 | /// 43 | public DateTime DateTimeUtc { get; set; } 44 | 45 | /// 46 | /// Gets or sets the created on date and time in UTC format. 47 | /// 48 | public DateTime CreatedOnUtc { get; set; } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /EventReminder.Domain/Users/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using EventReminder.Domain.Core.Primitives.Maybe; 4 | 5 | namespace EventReminder.Domain.Users 6 | { 7 | /// 8 | /// Represents the user repository interface. 9 | /// 10 | public interface IUserRepository 11 | { 12 | /// 13 | /// Gets the user with the specified identifier. 14 | /// 15 | /// The user identifier. 16 | /// The maybe instance that may contain the user with the specified identifier. 17 | Task> GetByIdAsync(Guid userId); 18 | 19 | /// 20 | /// Gets the user with the specified email. 21 | /// 22 | /// The user email. 23 | /// The maybe instance that may contain the user with the specified email. 24 | Task> GetByEmailAsync(Email email); 25 | 26 | /// 27 | /// Checks if the specified email is unique. 28 | /// 29 | /// The email. 30 | /// True if the specified email is unique, otherwise false. 31 | Task IsEmailUniqueAsync(Email email); 32 | 33 | /// 34 | /// Inserts the specified user to the database. 35 | /// 36 | /// The user to be inserted to the database. 37 | void Insert(User user); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /EventReminder.Services.Api/EventReminder.Services.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | f19c00c1-c9d3-4702-9eab-19d0a4544efd 8 | Linux 9 | ..\docker-compose.dcproj 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /EventReminder.Application/Friendships/GetFriendshipsForUserId/GetFriendshipsForUserIdQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EventReminder.Application.Abstractions.Messaging; 3 | using EventReminder.Contracts.Common; 4 | using EventReminder.Contracts.Friendships; 5 | using EventReminder.Domain.Core.Primitives.Maybe; 6 | 7 | namespace EventReminder.Application.Friendships.GetFriendshipsForUserId 8 | { 9 | /// 10 | /// Represents the query for getting the friendships for the user identifier. 11 | /// 12 | public sealed class GetFriendshipsForUserIdQuery : IQuery>> 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// The user identifier. 18 | /// The current page. 19 | /// The page size. 20 | public GetFriendshipsForUserIdQuery(Guid userId, int page, int pageSize) 21 | { 22 | UserId = userId; 23 | Page = page; 24 | PageSize = pageSize; 25 | } 26 | 27 | /// 28 | /// Gets the user identifier. 29 | /// 30 | public Guid UserId { get; } 31 | 32 | /// 33 | /// Gets the current page. 34 | /// 35 | public int Page { get; } 36 | 37 | /// 38 | /// Gets the page size. 39 | /// 40 | public int PageSize { get; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /EventReminder.Application/PersonalEvents/UpdatePersonalEvent/UpdatePersonalEventCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EventReminder.Application.Abstractions.Messaging; 3 | using EventReminder.Domain.Core.Primitives.Result; 4 | 5 | namespace EventReminder.Application.PersonalEvents.UpdatePersonalEvent 6 | { 7 | /// 8 | /// Represents the update personal event command. 9 | /// 10 | public sealed class UpdatePersonalEventCommand : ICommand 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// The personal event identifier. 16 | /// The event name. 17 | /// The date and time of the event in UTC format. 18 | public UpdatePersonalEventCommand(Guid personalEventId, string name, DateTime dateTimeUtc) 19 | { 20 | PersonalEventId = personalEventId; 21 | Name = name; 22 | DateTimeUtc = dateTimeUtc.ToUniversalTime(); 23 | } 24 | 25 | /// 26 | /// Gets the personal event identifier. 27 | /// 28 | public Guid PersonalEventId { get; } 29 | 30 | /// 31 | /// Gets the name. 32 | /// 33 | public string Name { get; } 34 | 35 | /// 36 | /// Gets the date and time in UTC format. 37 | /// 38 | public DateTime DateTimeUtc { get; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /EventReminder.Application/Users/ChangePassword/PublishIntegrationEventOnUserPasswordChangedDomainEventHandler.cs: -------------------------------------------------------------------------------- 1 | using EventReminder.Application.Abstractions.Messaging; 2 | using EventReminder.Domain.Core.Events; 3 | using EventReminder.Domain.Users.DomainEvents; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventReminder.Application.Users.ChangePassword 8 | { 9 | /// 10 | /// Represents the handler. 11 | /// 12 | internal sealed class PublishIntegrationEventOnUserPasswordChangedDomainEventHandler 13 | : IDomainEventHandler 14 | { 15 | private readonly IIntegrationEventPublisher _integrationEventPublisher; 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The integration event publisher. 21 | public PublishIntegrationEventOnUserPasswordChangedDomainEventHandler(IIntegrationEventPublisher integrationEventPublisher) => 22 | _integrationEventPublisher = integrationEventPublisher; 23 | 24 | /// 25 | public async Task Handle(UserPasswordChangedDomainEvent notification, CancellationToken cancellationToken) 26 | { 27 | _integrationEventPublisher.Publish(new UserPasswordChangedIntegrationEvent(notification)); 28 | 29 | await Task.CompletedTask; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EventReminder.Domain/Friendships/IFriendshipRequestRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using EventReminder.Domain.Core.Primitives.Maybe; 4 | using EventReminder.Domain.Users; 5 | 6 | namespace EventReminder.Domain.Friendships 7 | { 8 | /// 9 | /// Represents the friendship request repository interface. 10 | /// 11 | public interface IFriendshipRequestRepository 12 | { 13 | /// 14 | /// Gets the friend request with the specified identifier. 15 | /// 16 | /// The friendship request identifier. 17 | /// The maybe instance that may contain the friendship request with the specified identifier. 18 | Task> GetByIdAsync(Guid friendshipRequestId); 19 | 20 | /// 21 | /// Checks if the specified users have a pending friendship request. 22 | /// 23 | /// The user. 24 | /// The friend. 25 | /// True if the specified users have a pending friendship request, otherwise false. 26 | Task CheckForPendingFriendshipRequestAsync(User user, User friend); 27 | 28 | /// 29 | /// Inserts the specified friendship request to the database. 30 | /// 31 | /// The friendship request to be inserted to the database. 32 | void Insert(FriendshipRequest friendshipRequest); 33 | } 34 | } -------------------------------------------------------------------------------- /EventReminder.Services.Notifications/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.Notifications/EventReminder.Services.Notifications.csproj", "EventReminder.Services.Notifications/"] 11 | COPY ["EventReminder.BackgroundTasks/EventReminder.BackgroundTasks.csproj", "EventReminder.BackgroundTasks/"] 12 | COPY ["EventReminder.Infrastructure/EventReminder.Infrastructure.csproj", "EventReminder.Infrastructure/"] 13 | COPY ["EventReminder.Application/EventReminder.Application.csproj", "EventReminder.Application/"] 14 | COPY ["EventReminder.Contracts/EventReminder.Contracts.csproj", "EventReminder.Contracts/"] 15 | COPY ["EventReminder.Domain/EventReminder.Domain.csproj", "EventReminder.Domain/"] 16 | COPY ["EventReminder.Persistence/EventReminder.Persistence.csproj", "EventReminder.Persistence/"] 17 | RUN dotnet restore "./EventReminder.Services.Notifications/EventReminder.Services.Notifications.csproj" 18 | COPY . . 19 | WORKDIR "/src/EventReminder.Services.Notifications" 20 | RUN dotnet build "./EventReminder.Services.Notifications.csproj" -c $BUILD_CONFIGURATION -o /app/build 21 | 22 | FROM build AS publish 23 | ARG BUILD_CONFIGURATION=Release 24 | RUN dotnet publish "./EventReminder.Services.Notifications.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false 25 | 26 | FROM base AS final 27 | WORKDIR /app 28 | COPY --from=publish /app/publish . 29 | ENTRYPOINT ["dotnet", "EventReminder.Services.Notifications.dll"] -------------------------------------------------------------------------------- /EventReminder.Application/Invitations/InvitationRejected/PublishIntegrationEventOnInvitationRejectedDomainEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using EventReminder.Application.Abstractions.Messaging; 4 | using EventReminder.Domain.Core.Events; 5 | using EventReminder.Domain.Invitations.DomainEvents; 6 | 7 | namespace EventReminder.Application.Invitations.InvitationRejected 8 | { 9 | /// 10 | /// Represents the handler. 11 | /// 12 | internal sealed class PublishIntegrationEventOnInvitationRejectedDomainEventHandler 13 | : IDomainEventHandler 14 | { 15 | private readonly IIntegrationEventPublisher _integrationEventPublisher; 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The integration event publisher. 21 | public PublishIntegrationEventOnInvitationRejectedDomainEventHandler(IIntegrationEventPublisher integrationEventPublisher) => 22 | _integrationEventPublisher = integrationEventPublisher; 23 | 24 | /// 25 | public async Task Handle(InvitationRejectedDomainEvent notification, CancellationToken cancellationToken) 26 | { 27 | _integrationEventPublisher.Publish(new InvitationRejectedIntegrationEvent(notification)); 28 | 29 | await Task.CompletedTask; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EventReminder.Application/GroupEvents/GroupEventCancelled/PublishIntegrationEventOnGroupEventCancelledDomainEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using EventReminder.Application.Abstractions.Messaging; 4 | using EventReminder.Domain.Core.Events; 5 | using EventReminder.Domain.Events.DomainEvents; 6 | 7 | namespace EventReminder.Application.GroupEvents.GroupEventCancelled 8 | { 9 | /// 10 | /// Represents the class. 11 | /// 12 | internal sealed class PublishIntegrationEventOnGroupEventCancelledDomainEventHandler 13 | : IDomainEventHandler 14 | { 15 | private readonly IIntegrationEventPublisher _integrationEventPublisher; 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The integration event publisher. 21 | public PublishIntegrationEventOnGroupEventCancelledDomainEventHandler(IIntegrationEventPublisher integrationEventPublisher) => 22 | _integrationEventPublisher = integrationEventPublisher; 23 | 24 | /// 25 | public async Task Handle(GroupEventCancelledDomainEvent notification, CancellationToken cancellationToken) 26 | { 27 | _integrationEventPublisher.Publish(new GroupEventCancelledIntegrationEvent(notification)); 28 | 29 | await Task.CompletedTask; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EventReminder.Application/Invitations/InvitationAccepted/PublishIntegrationEventOnInvitationAcceptedDomainEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using EventReminder.Application.Abstractions.Messaging; 4 | using EventReminder.Domain.Core.Events; 5 | using EventReminder.Domain.Invitations.DomainEvents; 6 | 7 | namespace EventReminder.Application.Invitations.InvitationAccepted 8 | { 9 | /// 10 | /// Represents the handler. 11 | /// 12 | internal sealed class PublishIntegrationEventOnInvitationAcceptedDomainEventHandler 13 | : IDomainEventHandler 14 | { 15 | private readonly IIntegrationEventPublisher _integrationEventPublisher; 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The integration event publisher. 21 | public PublishIntegrationEventOnInvitationAcceptedDomainEventHandler(IIntegrationEventPublisher integrationEventPublisher) => 22 | _integrationEventPublisher = integrationEventPublisher; 23 | 24 | /// 25 | public async Task Handle(InvitationAcceptedDomainEvent notification, CancellationToken cancellationToken) 26 | { 27 | _integrationEventPublisher.Publish(new InvitationAcceptedIntegrationEvent(notification)); 28 | 29 | await Task.CompletedTask; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EventReminder.Contracts/Common/PagedList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace EventReminder.Contracts.Common 5 | { 6 | /// 7 | /// Represents the generic paged list. 8 | /// 9 | /// The type of list. 10 | public sealed class PagedList 11 | { 12 | public PagedList(IEnumerable items, int page, int pageSize, int totalCount) 13 | { 14 | Page = page; 15 | PageSize = pageSize; 16 | TotalCount = totalCount; 17 | Items = items.ToList(); 18 | } 19 | 20 | /// 21 | /// Gets the current page. 22 | /// 23 | public int Page { get; } 24 | 25 | /// 26 | /// Gets the page size. The maximum page size is 100. 27 | /// 28 | public int PageSize { get; } 29 | 30 | /// 31 | /// Gets the total number of items. 32 | /// 33 | public int TotalCount { get; } 34 | 35 | /// 36 | /// Gets the flag indicating whether the next page exists. 37 | /// 38 | public bool HasNextPage => Page * PageSize < TotalCount; 39 | 40 | /// 41 | /// Gets the flag indicating whether the previous page exists. 42 | /// 43 | public bool HasPreviousPage => Page > 1; 44 | 45 | /// 46 | /// Gets the items. 47 | /// 48 | public IReadOnlyCollection Items { get; } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /EventReminder.Domain/Core/Primitives/Result/ResultT.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EventReminder.Domain.Core.Primitives.Result 4 | { 5 | /// 6 | /// Represents the result of some operation, with status information and possibly a value and an error. 7 | /// 8 | /// The result value type. 9 | public class Result : Result 10 | { 11 | private readonly TValue _value; 12 | 13 | /// 14 | /// Initializes a new instance of the class with the specified parameters. 15 | /// 16 | /// The result value. 17 | /// The flag indicating if the result is successful. 18 | /// The error. 19 | protected internal Result(TValue value, bool isSuccess, Error error) 20 | : base(isSuccess, error) 21 | => _value = value; 22 | 23 | public static implicit operator Result(TValue value) => Success(value); 24 | 25 | /// 26 | /// Gets the result value if the result is successful, otherwise throws an exception. 27 | /// 28 | /// The result value if the result is successful. 29 | /// when is true. 30 | public TValue Value => IsSuccess 31 | ? _value 32 | : throw new InvalidOperationException("The value of a failure result can not be accessed."); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /EventReminder.Application/GroupEvents/GroupEventNameChanged/PublishIntegrationEventOnGroupEventNameChangedDomainEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using EventReminder.Application.Abstractions.Messaging; 4 | using EventReminder.Domain.Core.Events; 5 | using EventReminder.Domain.Events.DomainEvents; 6 | 7 | namespace EventReminder.Application.GroupEvents.GroupEventNameChanged 8 | { 9 | /// 10 | /// Represents the class. 11 | /// 12 | internal sealed class PublishIntegrationEventOnGroupEventNameChangedDomainEventHandler 13 | : IDomainEventHandler 14 | { 15 | private readonly IIntegrationEventPublisher _integrationEventPublisher; 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The integration event publisher. 21 | public PublishIntegrationEventOnGroupEventNameChangedDomainEventHandler(IIntegrationEventPublisher integrationEventPublisher) => 22 | _integrationEventPublisher = integrationEventPublisher; 23 | 24 | /// 25 | public async Task Handle(GroupEventNameChangedDomainEvent notification, CancellationToken cancellationToken) 26 | { 27 | _integrationEventPublisher.Publish(new GroupEventNameChangedIntegrationEvent(notification)); 28 | 29 | await Task.CompletedTask; 30 | } 31 | } 32 | } 33 | --------------------------------------------------------------------------------