├── README.md ├── Tarteeb.Api ├── Properties │ ├── serviceDependencies.json │ ├── serviceDependencies.local.json │ ├── launchSettings.json │ └── ServiceDependencies │ │ └── local │ │ └── appInsights1.arm.json ├── Brokers │ ├── Storages │ │ ├── IStorageBroker.cs │ │ ├── StorageBroker.Users.Configurations.cs │ │ ├── IStorageBroker.Teams.cs │ │ ├── IStorageBroker.Users.cs │ │ ├── IStorageBroker.Tickets.cs │ │ ├── StorageBroker.Teams.cs │ │ ├── StorageBroker.Tickets.cs │ │ ├── StorageBroker.Users.cs │ │ └── StorageBroker.cs │ ├── DateTimes │ │ ├── IDateTimeBroker.cs │ │ └── DateTimeBroker.cs │ ├── Tokens │ │ ├── ITokenBroker.cs │ │ └── TokenBroker.cs │ └── Loggings │ │ ├── ILoggingBroker.cs │ │ └── LoggingBroker.cs ├── Models │ ├── Foundations │ │ ├── Tickets │ │ │ ├── Priority.cs │ │ │ ├── TicketStatus.cs │ │ │ ├── Exceptions │ │ │ │ ├── NullTicketException.cs │ │ │ │ ├── InvalidTicketException.cs │ │ │ │ ├── NotFoundTicketException.cs │ │ │ │ ├── LockedTicketException.cs │ │ │ │ ├── AlreadyExistsTicketException.cs │ │ │ │ ├── TicketDependencyException.cs │ │ │ │ ├── TicketServiceException.cs │ │ │ │ ├── TicketValidationException.cs │ │ │ │ ├── InvalidTicketReferenceException.cs │ │ │ │ ├── FailedTicketStorageException.cs │ │ │ │ ├── FailedTicketServiceException.cs │ │ │ │ └── TicketDependencyValidationException.cs │ │ │ └── Ticket.cs │ │ ├── Teams │ │ │ ├── Exceptions │ │ │ │ ├── NullTeamException.cs │ │ │ │ ├── InvalidTeamException.cs │ │ │ │ ├── NotFoundTeamException.cs │ │ │ │ ├── LockedTeamException.cs │ │ │ │ ├── AlreadyExistsTeamException.cs │ │ │ │ ├── TeamDependencyException.cs │ │ │ │ ├── TeamServiceException.cs │ │ │ │ ├── TeamValidationException.cs │ │ │ │ ├── FailedTeamStorageException.cs │ │ │ │ ├── FailedTeamServiceException.cs │ │ │ │ └── TeamDependencyValidationException.cs │ │ │ └── Team.cs │ │ ├── Tokens │ │ │ └── TokenConfiguration.cs │ │ └── Users │ │ │ ├── Exceptions │ │ │ ├── NullUserException.cs │ │ │ ├── InvalidUserException.cs │ │ │ ├── LockedUserException.cs │ │ │ ├── UserDependencyException.cs │ │ │ ├── AlreadyExistsUserException.cs │ │ │ ├── UserServiceException.cs │ │ │ ├── UserValidationException.cs │ │ │ ├── InvalidUserReferenceException.cs │ │ │ ├── FailedUserStorageException.cs │ │ │ ├── FailedUserServiceException.cs │ │ │ ├── UserDependencyValidationException.cs │ │ │ └── NotFoundUserException.cs │ │ │ └── User.cs │ ├── Processings │ │ └── Users │ │ │ ├── InvalidUserProcessingException.cs │ │ │ ├── UserProcessingValidationException.cs │ │ │ └── UserProcessingDependencyException.cs │ └── Orchestrations │ │ └── UserTokens │ │ ├── UserToken.cs │ │ └── Exceptions │ │ ├── InvalidUserCreadentialOrchestrationException.cs │ │ ├── UserTokenOrchestrationServiceException.cs │ │ ├── UserTokenOrchestrationDependencyException.cs │ │ ├── FailedUserTokenOrchestrationException.cs │ │ └── UserTokenOrchestrationValidationException.cs ├── Services │ ├── Foundations │ │ ├── Securities │ │ │ ├── ISecurityService.cs │ │ │ ├── SecurityService.cs │ │ │ ├── SecurityService.Validations.cs │ │ │ └── SecurityService.Exceptions.cs │ │ ├── Teams │ │ │ ├── ITeamService.cs │ │ │ └── TeamService.cs │ │ ├── Users │ │ │ ├── IUserService.cs │ │ │ └── UserService.cs │ │ └── Tickets │ │ │ ├── ITicketService.cs │ │ │ └── TicketService.cs │ ├── Processings │ │ └── Users │ │ │ ├── IUserProcessingService.cs │ │ │ ├── UserProcessingService.cs │ │ │ ├── UserProcessingService.Validations.cs │ │ │ └── UserProcessingService.Exceptions.cs │ └── Orchestrations │ │ ├── IUserSecurityOrchestrationService.cs │ │ ├── UserSecurityOrchestrationService.Validations.cs │ │ ├── UserSecurityOrchestrationService.cs │ │ └── UserSecurityOrchestrationService.Exceptions.cs ├── Controllers │ ├── HomeController.cs │ └── AccountsController.cs ├── appsettings.json ├── Migrations │ ├── 20221110181513_ChangePriority.cs │ ├── 20230102085421_AddPasswordOnUser.cs │ ├── 20221110175226_RenameTicketToTickets.cs │ ├── 20221113120827_AddTeam.cs │ ├── 20230210045018_UniqueEmail.cs │ ├── 20221107175023_AddTasks.cs │ ├── 20221112084050_AddUsers.cs │ ├── 20221107175023_AddTasks.Designer.cs │ ├── 20221110181513_ChangePriority.Designer.cs │ ├── 20221110171741_RenameTaskToTicket.Designer.cs │ ├── 20221110175226_RenameTicketToTickets.Designer.cs │ ├── 20221110171741_RenameTaskToTicket.cs │ └── 20221113120827_AddTeam.Designer.cs ├── Program.cs └── Tarteeb.Api.csproj ├── Tarteeb.Api.Infrastructure.Build ├── Tarteeb.Api.Infrastructure.Build.csproj └── Program.cs ├── .github └── workflows │ ├── build.yml │ └── master_tarteeb.yml ├── LICENSE ├── Tarteeb.Api.Tests.Unit ├── Services │ ├── Foundations │ │ ├── Securities │ │ │ ├── SecurityServiceTests.Logic.Create.cs │ │ │ ├── SecurityServiceTests.cs │ │ │ └── SecurityServiceTests.Validations.Create.cs │ │ ├── Users │ │ │ ├── UserServiceTests.Logic.RetrieveAll.cs │ │ │ ├── UserServiceTests.Logic.RetrieveById.cs │ │ │ ├── UserServiceTests.Logic.Add.cs │ │ │ ├── UserServiceTests.Logic.RemoveById.cs │ │ │ ├── UserServiceTests.Logic.Modify.cs │ │ │ ├── UserServiceTests.Exceptions.RetrieveAll.cs │ │ │ ├── UserServiceTests.Validations.RetrieveById.cs │ │ │ ├── UserServiceTests.Exceptions.RetrieveById.cs │ │ │ └── UserServiceTests.Validations.RemoveById.cs │ │ ├── Teams │ │ │ ├── TeamServiceTests.Logic.RetrieveAll.cs │ │ │ ├── TeamServiceTests.Logic.RetrieveById.cs │ │ │ ├── TeamServiceTests.Logic.Add.cs │ │ │ ├── TeamServiceTests.Logic.RemoveById.cs │ │ │ ├── TeamServiceTests.Logic.Modify.cs │ │ │ ├── TeamServiceTests.Exceptions.RetrieveAll.cs │ │ │ ├── TeamServiceTests.Validations.RetrieveById.cs │ │ │ ├── TeamServiceTests.Validations.RemoveById.cs │ │ │ └── TeamServiceTests.Exceptions.RetrieveById.cs │ │ └── Tickets │ │ │ ├── TicketServiceTests.Logic.RetrieveAll.cs │ │ │ ├── TicketServiceTests.Logic.RetrieveById.cs │ │ │ ├── TicketServiceTests.Logic.Add.cs │ │ │ ├── TicketServiceTests.Logic.RemoveById.cs │ │ │ ├── TicketServiceTests.Logic.Modify.cs │ │ │ ├── TicketServiceTests.Exceptions.RetrieveAll.cs │ │ │ └── TicketServiceTests.Validations.RetrieveById.cs │ ├── Processings │ │ └── Users │ │ │ ├── UserProcessingsServiceTests.Logic.cs │ │ │ ├── UserProcessingsServiceTests.Exceptions.Retrieve.cs │ │ │ ├── UserProcessingsServiceTests.cs │ │ │ └── UserProcessingsServiceTests.Validation.Retrieve.cs │ └── Orchestrations │ │ ├── UserSecurityOrchestrationServiceTests.Logic.Create.cs │ │ └── UserSecurityOrchestrationServiceTests.cs └── Tarteeb.Api.Tests.Unit.csproj └── Tarteeb.sln /README.md: -------------------------------------------------------------------------------- 1 | # Tarteeb 2 | A Standard-compliant task management software 3 | 4 | -------------------------------------------------------------------------------- /Tarteeb.Api/Properties/serviceDependencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "secrets1": { 4 | "type": "secrets" 5 | }, 6 | "appInsights1": { 7 | "type": "appInsights", 8 | "connectionId": "APPLICATIONINSIGHTS_CONNECTION_STRING", 9 | "dynamicId": null 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Storages/IStorageBroker.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | namespace Tarteeb.Api.Brokers.Storages 7 | { 8 | public partial interface IStorageBroker 9 | { } 10 | } 11 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Priority.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | namespace Tarteeb.Api.Models.Foundations.Tickets 7 | { 8 | public enum Priority 9 | { 10 | LOW, 11 | MEDIUM, 12 | HIGH 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Processings/Users/InvalidUserProcessingException.cs: -------------------------------------------------------------------------------- 1 | using Xeptions; 2 | 3 | namespace Tarteeb.Api.Models.Processings.Users 4 | { 5 | public class InvalidUserProcessingException : Xeption 6 | { 7 | public InvalidUserProcessingException() 8 | : base(message: "Invalid email and password, Please correct the errors and try again.") 9 | { } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/DateTimes/IDateTimeBroker.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | 8 | namespace Tarteeb.Api.Brokers.DateTimes 9 | { 10 | public interface IDateTimeBroker 11 | { 12 | DateTimeOffset GetCurrentDateTime(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tarteeb.Api.Infrastructure.Build/Tarteeb.Api.Infrastructure.Build.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Tokens/ITokenBroker.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Tarteeb.Api.Models.Foundations.Users; 7 | 8 | namespace Tarteeb.Api.Brokers.Tokens 9 | { 10 | public interface ITokenBroker 11 | { 12 | string GenerateJWT(User user); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/TicketStatus.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | namespace Tarteeb.Api.Models.Foundations.Tickets 7 | { 8 | public enum TicketStatus 9 | { 10 | UNKNOWN, 11 | TODO, 12 | INPROGRESS, 13 | DONE 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Processings/Users/UserProcessingValidationException.cs: -------------------------------------------------------------------------------- 1 | using Xeptions; 2 | 3 | namespace Tarteeb.Api.Models.Processings.Users 4 | { 5 | public class UserProcessingValidationException : Xeption 6 | { 7 | public UserProcessingValidationException(Xeption innerException) 8 | : base(message: "PostImpression validation error occurred, please try again.", innerException) 9 | { } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Foundations/Securities/ISecurityService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Tarteeb.Api.Models.Foundations.Users; 7 | 8 | namespace Tarteeb.Api.Services.Foundations.Securities; 9 | 10 | public interface ISecurityService 11 | { 12 | string CreateToken(User user); 13 | } 14 | -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Loggings/ILoggingBroker.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | 8 | namespace Tarteeb.Api.Brokers.Loggings 9 | { 10 | public interface ILoggingBroker 11 | { 12 | void LogError(Exception exception); 13 | void LogCritical(Exception exception); 14 | } 15 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Orchestrations/UserTokens/UserToken.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | 8 | namespace Tarteeb.Api.Models.Orchestrations.UserTokens 9 | { 10 | public class UserToken 11 | { 12 | public Guid UserId { get; set; } 13 | public string Token { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/DateTimes/DateTimeBroker.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | 8 | namespace Tarteeb.Api.Brokers.DateTimes 9 | { 10 | public class DateTimeBroker : IDateTimeBroker 11 | { 12 | public DateTimeOffset GetCurrentDateTime() => 13 | DateTimeOffset.UtcNow; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/NullTeamException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 9 | { 10 | public class NullTeamException : Xeption 11 | { 12 | public NullTeamException() : base(message: "Team is null.") 13 | { } 14 | } 15 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Processings/Users/IUserProcessingService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Tarteeb.Api.Models.Foundations.Users; 7 | 8 | namespace Tarteeb.Api.Services.Processings.Users 9 | { 10 | public interface IUserProcessingService 11 | { 12 | User RetrieveUserByCredentails(string email, string password); 13 | } 14 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tokens/TokenConfiguration.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | namespace Tarteeb.Api.Models.Foundations.Tokens 7 | { 8 | public class TokenConfiguration 9 | { 10 | public string Key { get; set; } 11 | public string Issuer { get; set; } 12 | public string Audience { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/NullTicketException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 9 | { 10 | public class NullTicketException : Xeption 11 | { 12 | public NullTicketException() : base(message: "Ticket is null.") 13 | { } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/NullUserException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 9 | { 10 | public class NullUserException : Xeption 11 | { 12 | public NullUserException() 13 | : base(message: "User is null.") 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Orchestrations/IUserSecurityOrchestrationService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Tarteeb.Api.Models.Orchestrations.UserTokens; 7 | 8 | namespace Tarteeb.Api.Services.Orchestrations 9 | { 10 | public interface IUserSecurityOrchestrationService 11 | { 12 | UserToken CreateUserToken(string email, string password); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/InvalidTeamException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 9 | { 10 | public class InvalidTeamException : Xeption 11 | { 12 | public InvalidTeamException() 13 | : base(message: "Team is invalid.") 14 | { } 15 | } 16 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/InvalidUserException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 9 | { 10 | public class InvalidUserException : Xeption 11 | { 12 | public InvalidUserException() 13 | : base(message: "User is invalid.") 14 | { } 15 | } 16 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Properties/serviceDependencies.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "secrets1": { 4 | "type": "secrets.user" 5 | }, 6 | "appInsights1": { 7 | "secretStore": "LocalSecretsFile", 8 | "resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/microsoft.insights/components/tarteeb", 9 | "type": "appInsights.azure", 10 | "connectionId": "APPLICATIONINSIGHTS_CONNECTION_STRING", 11 | "dynamicId": null 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/InvalidTicketException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 9 | { 10 | public class InvalidTicketException : Xeption 11 | { 12 | public InvalidTicketException() 13 | : base(message: "Ticket is invalid.") 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Team.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Teams 9 | { 10 | public class Team 11 | { 12 | public Guid Id { get; set; } 13 | public string TeamName { get; set; } 14 | public DateTimeOffset CreatedDate { get; set; } 15 | public DateTimeOffset UpdatedDate { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/NotFoundTeamException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 10 | { 11 | public class NotFoundTeamException : Xeption 12 | { 13 | public NotFoundTeamException(Guid teamId) 14 | : base(message: $"Couldn't find team with id: {teamId}.") 15 | { } 16 | } 17 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Microsoft.AspNetCore.Mvc; 7 | using RESTFulSense.Controllers; 8 | 9 | namespace Tarteeb.Api.Controllers 10 | { 11 | [ApiController] 12 | [Route("api/[controller]")] 13 | public class HomeController : RESTFulController 14 | { 15 | [HttpGet] 16 | public ActionResult GetHomeMessage() => Ok("Tarteeb is running..."); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/LockedTeamException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 10 | { 11 | public class LockedTeamException : Xeption 12 | { 13 | public LockedTeamException(Exception innerException) 14 | : base(message: "Team is locked, please try again.", innerException) 15 | { } 16 | } 17 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/AlreadyExistsTeamException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 10 | { 11 | public class AlreadyExistsTeamException : Xeption 12 | { 13 | public AlreadyExistsTeamException(Exception innerException) 14 | : base(message: "Team already exists.", innerException) 15 | { } 16 | } 17 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/TeamDependencyException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 9 | { 10 | public class TeamDependencyException : Xeption 11 | { 12 | public TeamDependencyException(Xeption innerException) 13 | : base(message: "Team dependency error occurred, contact support.", innerException) 14 | { } 15 | } 16 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/NotFoundTicketException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 10 | { 11 | public class NotFoundTicketException : Xeption 12 | { 13 | public NotFoundTicketException(Guid ticketId) 14 | : base(message: $"Couldn't find ticket with id: {ticketId}.") 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/LockedUserException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 10 | { 11 | public class LockedUserException : Xeption 12 | { 13 | public LockedUserException(Exception innerException) 14 | : base(message: "User is locked, please try again.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/UserDependencyException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 9 | { 10 | public class UserDependencyException : Xeption 11 | { 12 | public UserDependencyException(Xeption innerException) 13 | : base(message: "User dependency error occurred, contact support.", innerException) 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/TeamServiceException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 10 | { 11 | public class TeamServiceException : Xeption 12 | { 13 | public TeamServiceException(Exception innerException) 14 | : base(message: "Team service error occurred, contact support.", innerException) 15 | { } 16 | } 17 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/TeamValidationException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 9 | { 10 | public class TeamValidationException : Xeption 11 | { 12 | public TeamValidationException(Xeption innerException) 13 | : base(message: "Team validation error occured, fix the errors and try again.", innerException) 14 | { } 15 | } 16 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/LockedTicketException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 10 | { 11 | public class LockedTicketException : Xeption 12 | { 13 | public LockedTicketException(Exception innerException) 14 | : base(message: "Ticket is locked, please try again.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/AlreadyExistsUserException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 10 | { 11 | public partial class AlreadyExistsUserException : Xeption 12 | { 13 | public AlreadyExistsUserException(Exception innerException) 14 | : base(message: "User already exists.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/UserServiceException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 10 | { 11 | public class UserServiceException : Xeption 12 | { 13 | public UserServiceException(Exception innerException) 14 | : base(message: "User service error occured, contact support.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/UserValidationException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 9 | { 10 | public class UserValidationException : Xeption 11 | { 12 | public UserValidationException(Xeption innerExeption) 13 | : base(message: "User validation error occured, fix the errors and try again.", innerExeption) 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Processings/Users/UserProcessingDependencyException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Processings.Users 9 | { 10 | public class UserProcessingDependencyException : Xeption 11 | { 12 | public UserProcessingDependencyException(Xeption innerException) 13 | : base(message: "User dependency error occurred, contact support.", innerException) 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/AlreadyExistsTicketException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 10 | { 11 | public class AlreadyExistsTicketException : Xeption 12 | { 13 | public AlreadyExistsTicketException(Exception innerException) 14 | : base(message: "Ticket already exists.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/TicketDependencyException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 9 | { 10 | public class TicketDependencyException : Xeption 11 | { 12 | public TicketDependencyException(Xeption innerException) 13 | : base(message: "Ticket dependency error occurred, contact support.", innerException) 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/TicketServiceException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 10 | { 11 | public class TicketServiceException : Xeption 12 | { 13 | public TicketServiceException(Exception innerException) 14 | : base(message: "Ticket service error occurred, contact support.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/TicketValidationException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 9 | { 10 | public class TicketValidationException : Xeption 11 | { 12 | public TicketValidationException(Xeption innerException) 13 | : base(message: "Ticket validation error occured, fix the errors and try again.", innerException) 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/FailedTeamStorageException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 10 | { 11 | public class FailedTeamStorageException : Xeption 12 | { 13 | public FailedTeamStorageException(Exception innerException) 14 | : base(message: "Failed team storage error occurred, contact support.", innerException) 15 | { } 16 | } 17 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/InvalidUserReferenceException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 10 | { 11 | public class InvalidUserReferenceException : Xeption 12 | { 13 | public InvalidUserReferenceException(Exception innerException) 14 | : base(message: "Invalid user reference error occured.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Storages/StorageBroker.Users.Configurations.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 7 | using Tarteeb.Api.Models.Foundations.Users; 8 | 9 | namespace Tarteeb.Api.Brokers.Storages 10 | { 11 | public partial class StorageBroker 12 | { 13 | public void ConfigureUserEmail(EntityTypeBuilder builder) 14 | { 15 | builder.HasIndex(user => user.Email).IsUnique(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/FailedUserStorageException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 10 | { 11 | public class FailedUserStorageException : Xeption 12 | { 13 | public FailedUserStorageException(Exception innerException) 14 | : base(message: "Failed user storage error occurred, contact support.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/InvalidTicketReferenceException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 10 | { 11 | public class InvalidTicketReferenceException : Xeption 12 | { 13 | public InvalidTicketReferenceException(Exception innerException) 14 | : base(message: "Invalid ticket reference error occurred.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/FailedUserServiceException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 10 | { 11 | public class FailedUserServiceException : Xeption 12 | { 13 | public FailedUserServiceException(Exception innerException) 14 | : base(message: "Failed user service error occured, please contact support", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Orchestrations/UserTokens/Exceptions/InvalidUserCreadentialOrchestrationException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Orchestrations.UserTokens.Exceptions 9 | { 10 | public class InvalidUserCredentialOrchestrationException : Xeption 11 | { 12 | public InvalidUserCredentialOrchestrationException() 13 | : base(message: "Credential missing. Fix the error and try again.") 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/FailedTicketStorageException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 10 | { 11 | public class FailedTicketStorageException : Xeption 12 | { 13 | public FailedTicketStorageException(Exception innerException) 14 | : base(message: "Failed ticket storage error occurred, contact support.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "Jwt": { 10 | "Key": "dh%8sadjGfjh&657HVD%NfrUNHG5689", 11 | "Issuer": "https://localhost:7253", 12 | "Audience": "https://localhost:7253" 13 | }, 14 | "AllowedHosts": "*", 15 | "ApplicationInsights": { 16 | "ConnectionString": "InstrumentationKey=8eb5f5d8-ad7c-4665-b7d6-df8736747959;IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/" 17 | } 18 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/FailedTeamServiceException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 10 | { 11 | public class FailedTeamServiceException : Xeption 12 | { 13 | public FailedTeamServiceException(Exception innerException) 14 | : base(message: "Failed team service error occurred, please contact support.", 15 | innerException) 16 | { } 17 | } 18 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/FailedTicketServiceException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 10 | { 11 | public class FailedTicketServiceException : Xeption 12 | { 13 | public FailedTicketServiceException(Exception innerException) 14 | : base(message: "Failed ticket service error occurred, please contact support", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/UserDependencyValidationException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 9 | { 10 | public class UserDependencyValidationException : Xeption 11 | { 12 | public UserDependencyValidationException(Xeption innerException) 13 | : base(message: "User dependency validation error occurred, fix the error and try again ", innerException) 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Tarteeb Build Process 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | build: 11 | runs-on: windows-2022 12 | steps: 13 | - name: Check out 14 | uses: actions/checkout@v2 15 | - name: Setup .Net 16 | uses: actions/setup-dotnet@v1 17 | with: 18 | dotnet-version: 7.0.100 19 | include-prerelease: true 20 | - name: Restore Packages 21 | run: dotnet restore 22 | - name: Build Projects 23 | run: dotnet build --no-restore 24 | - name: Run Tests 25 | run: dotnet test --no-build --verbosity normal 26 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221110181513_ChangePriority.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | 8 | #nullable disable 9 | 10 | namespace Tarteeb.Api.Migrations 11 | { 12 | public partial class ChangePriority : Migration 13 | { 14 | protected override void Up(MigrationBuilder migrationBuilder) 15 | { 16 | 17 | } 18 | 19 | protected override void Down(MigrationBuilder migrationBuilder) 20 | { 21 | 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Exceptions/TicketDependencyValidationException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Tickets.Exceptions 9 | { 10 | public class TicketDependencyValidationException : Xeption 11 | { 12 | public TicketDependencyValidationException(Xeption innerException) 13 | : base(message: "Ticket dependency validation error occurred, fix the errors and try again.", innerException) 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Orchestrations/UserTokens/Exceptions/UserTokenOrchestrationServiceException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Orchestrations.UserTokens.Exceptions 9 | { 10 | public class UserTokenOrchestrationServiceException : Xeption 11 | { 12 | public UserTokenOrchestrationServiceException(Xeption innerException) 13 | : base(message: "User token service error occurred, contact support.", innerException) 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Teams/Exceptions/TeamDependencyValidationException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Teams.Exceptions 9 | { 10 | public class TeamDependencyValidationException : Xeption 11 | { 12 | public TeamDependencyValidationException(Xeption innerException) 13 | : base(message: "Team dependency validation error occurred, fix the errors and try again.", 14 | innerException) 15 | { } 16 | } 17 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Orchestrations/UserTokens/Exceptions/UserTokenOrchestrationDependencyException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Orchestrations.UserTokens.Exceptions 9 | { 10 | public class UserTokenOrchestrationDependencyException : Xeption 11 | { 12 | public UserTokenOrchestrationDependencyException(Xeption innerException) 13 | : base(message: "User token dependency error occurred, contact support.", innerException) 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Orchestrations/UserTokens/Exceptions/FailedUserTokenOrchestrationException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Orchestrations.UserTokens.Exceptions 10 | { 11 | public class FailedUserTokenOrchestrationException: Xeption 12 | { 13 | public FailedUserTokenOrchestrationException(Exception innerException) 14 | : base(message: "Failed user token service error occurred, contact support.", innerException) 15 | { } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Orchestrations/UserTokens/Exceptions/UserTokenOrchestrationValidationException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Xeptions; 7 | 8 | namespace Tarteeb.Api.Models.Orchestrations.UserTokens.Exceptions 9 | { 10 | public class UserTokenOrchestrationValidationException : Xeption 11 | { 12 | public UserTokenOrchestrationValidationException(Xeption innerException) 13 | : base(message: "User token validation error occurred, fix the errors and try again.", innerException) 14 | { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/Exceptions/NotFoundUserException.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Xeptions; 8 | 9 | namespace Tarteeb.Api.Models.Foundations.Users.Exceptions 10 | { 11 | public class NotFoundUserException : Xeption 12 | { 13 | public NotFoundUserException(Guid userId) 14 | : base(message: $"Could not find user with id:{userId}.") 15 | { } 16 | 17 | public NotFoundUserException() 18 | : base(message: "Could not find user with given credentials") 19 | { } 20 | } 21 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Storages/IStorageBroker.Teams.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Tarteeb.Api.Models.Foundations.Teams; 10 | 11 | namespace Tarteeb.Api.Brokers.Storages 12 | { 13 | public partial interface IStorageBroker 14 | { 15 | ValueTask InsertTeamAsync(Team team); 16 | IQueryable SelectAllTeams(); 17 | ValueTask SelectTeamByIdAsync(Guid id); 18 | ValueTask UpdateTeamAsync(Team team); 19 | ValueTask DeleteTeamAsync(Team team); 20 | } 21 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Storages/IStorageBroker.Users.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Tarteeb.Api.Models.Foundations.Users; 10 | 11 | namespace Tarteeb.Api.Brokers.Storages 12 | { 13 | public partial interface IStorageBroker 14 | { 15 | ValueTask InsertUserAsync(User user); 16 | IQueryable SelectAllUsers(); 17 | ValueTask SelectUserByIdAsync(Guid id); 18 | ValueTask UpdateUserAsync(User user); 19 | ValueTask DeleteUserAsync(User user); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tarteeb.Api/Program.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Hosting; 8 | 9 | namespace Tarteeb.Api 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) => 14 | CreateHostBuilder(args).Build().Run(); 15 | 16 | public static IHostBuilder CreateHostBuilder(string[] args) 17 | { 18 | return Host.CreateDefaultBuilder(args) 19 | .ConfigureWebHostDefaults(webBuilder => 20 | webBuilder.UseStartup()); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Foundations/Teams/ITeamService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Tarteeb.Api.Models.Foundations.Teams; 10 | 11 | namespace Tarteeb.Api.Services.Foundations.Teams 12 | { 13 | public interface ITeamService 14 | { 15 | ValueTask AddTeamAsync(Team team); 16 | IQueryable RetrieveAllTeams(); 17 | ValueTask RetrieveTeamByIdAsync(Guid teamId); 18 | ValueTask ModifyTeamAsync(Team team); 19 | ValueTask RemoveTeamByIdAsync(Guid teamId); 20 | } 21 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Foundations/Users/IUserService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Tarteeb.Api.Models.Foundations.Users; 10 | 11 | namespace Tarteeb.Api.Services.Foundations.Users 12 | { 13 | public interface IUserService 14 | { 15 | ValueTask AddUserAsync(User user); 16 | IQueryable RetrieveAllUsers(); 17 | ValueTask RetrieveUserByIdAsync(Guid userId); 18 | ValueTask ModifyUserAsync(User user); 19 | ValueTask RemoveUserByIdAsync(Guid userId); 20 | } 21 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Storages/IStorageBroker.Tickets.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Tarteeb.Api.Models.Foundations.Tickets; 10 | 11 | namespace Tarteeb.Api.Brokers.Storages 12 | { 13 | public partial interface IStorageBroker 14 | { 15 | ValueTask InsertTicketAsync(Ticket ticket); 16 | IQueryable SelectAllTickets(); 17 | ValueTask SelectTicketByIdAsync(Guid id); 18 | ValueTask UpdateTicketAsync(Ticket ticket); 19 | ValueTask DeleteTicketAsync(Ticket ticket); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Foundations/Tickets/ITicketService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Tarteeb.Api.Models.Foundations.Tickets; 10 | 11 | namespace Tarteeb.Api.Services.Foundations.Tickets 12 | { 13 | public interface ITicketService 14 | { 15 | ValueTask AddTicketAsync(Ticket ticket); 16 | IQueryable RetrieveAllTickets(); 17 | ValueTask RetrieveTicketByIdAsync(Guid ticketId); 18 | ValueTask ModifyTicketAsync(Ticket ticket); 19 | ValueTask RemoveTicketByIdAsync(Guid ticketId); 20 | } 21 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Loggings/LoggingBroker.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace Tarteeb.Api.Brokers.Loggings 10 | { 11 | public class LoggingBroker : ILoggingBroker 12 | { 13 | private ILogger logger; 14 | 15 | public LoggingBroker(ILogger logger) => 16 | this.logger = logger; 17 | 18 | public void LogError(Exception exception) => 19 | this.logger.LogError(exception.Message, exception); 20 | 21 | public void LogCritical(Exception exception) => 22 | this.logger.LogCritical(exception, exception.Message); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Users/User.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Users 9 | { 10 | public class User 11 | { 12 | public Guid Id { get; set; } 13 | public string FirstName { get; set; } 14 | public string LastName { get; set; } 15 | public string PhoneNumber { get; set; } 16 | public string Email { get; set; } 17 | public DateTimeOffset BirthDate { get; set; } 18 | public DateTimeOffset CreatedDate { get; set; } 19 | public DateTimeOffset UpdatedDate { get; set; } 20 | public Guid? ManagerId { get; set; } 21 | public string Password { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20230102085421_AddPasswordOnUser.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace Tarteeb.Api.Migrations 6 | { 7 | /// 8 | public partial class AddPasswordOnUser : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AddColumn( 14 | name: "Password", 15 | table: "Users", 16 | type: "nvarchar(max)", 17 | nullable: true); 18 | } 19 | 20 | /// 21 | protected override void Down(MigrationBuilder migrationBuilder) 22 | { 23 | migrationBuilder.DropColumn( 24 | name: "Password", 25 | table: "Users"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tarteeb.Api/Models/Foundations/Tickets/Ticket.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | 8 | namespace Tarteeb.Api.Models.Foundations.Tickets 9 | { 10 | public class Ticket 11 | { 12 | public Guid Id { get; set; } 13 | public string Title { get; set; } 14 | public string Description { get; set; } 15 | public Priority Priority { get; set; } 16 | public DateTimeOffset Deadline { get; set; } 17 | public Guid? AssigneeId { get; set; } 18 | public TicketStatus Status { get; set; } 19 | public DateTimeOffset CreatedDate { get; set; } 20 | public DateTimeOffset UpdatedDate { get; set; } 21 | public Guid CreatedUserId { get; set; } 22 | public Guid UpdatedUserId { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:23173", 8 | "sslPort": 44333 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Tarteeb.Api": { 21 | "commandName": "Project", 22 | "dotnetRunMessages": "true", 23 | "launchBrowser": true, 24 | "launchUrl": "swagger", 25 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Foundations/Securities/SecurityService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Tarteeb.Api.Brokers.Loggings; 7 | using Tarteeb.Api.Brokers.Tokens; 8 | using Tarteeb.Api.Models.Foundations.Users; 9 | 10 | namespace Tarteeb.Api.Services.Foundations.Securities; 11 | 12 | public partial class SecurityService : ISecurityService 13 | { 14 | private readonly ITokenBroker tokenBroker; 15 | private readonly ILoggingBroker loggingBroker; 16 | 17 | public SecurityService( 18 | ITokenBroker tokenBroker, 19 | ILoggingBroker loggingBroker) 20 | { 21 | this.tokenBroker = tokenBroker; 22 | this.loggingBroker = loggingBroker; 23 | } 24 | 25 | public string CreateToken(User user) => 26 | TryCatch(() => 27 | { 28 | ValidateUser(user); 29 | 30 | return tokenBroker.GenerateJWT(user); 31 | }); 32 | 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Elbek 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 | -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Storages/StorageBroker.Teams.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Microsoft.EntityFrameworkCore; 10 | using Tarteeb.Api.Models.Foundations.Teams; 11 | 12 | namespace Tarteeb.Api.Brokers.Storages 13 | { 14 | public partial class StorageBroker 15 | { 16 | public DbSet Teams { get; set; } 17 | 18 | public async ValueTask InsertTeamAsync(Team team) => 19 | await InsertAsync(team); 20 | 21 | public IQueryable SelectAllTeams() => 22 | SelectAll(); 23 | 24 | public async ValueTask SelectTeamByIdAsync(Guid id) => 25 | await SelectAsync(id); 26 | 27 | public async ValueTask UpdateTeamAsync(Team team) => 28 | await UpdateAsync(team); 29 | 30 | public async ValueTask DeleteTeamAsync(Team team) => 31 | await DeleteAsync(team); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Storages/StorageBroker.Tickets.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Microsoft.EntityFrameworkCore; 10 | using Tarteeb.Api.Models.Foundations.Tickets; 11 | 12 | namespace Tarteeb.Api.Brokers.Storages 13 | { 14 | public partial class StorageBroker 15 | { 16 | public DbSet Tickets { get; set; } 17 | 18 | public async ValueTask InsertTicketAsync(Ticket ticket) => 19 | await InsertAsync(ticket); 20 | 21 | public IQueryable SelectAllTickets() => 22 | SelectAll(); 23 | 24 | public async ValueTask SelectTicketByIdAsync(Guid id) => 25 | await SelectAsync(id); 26 | 27 | public async ValueTask UpdateTicketAsync(Ticket ticket) => 28 | await UpdateAsync(ticket); 29 | 30 | public async ValueTask DeleteTicketAsync(Ticket ticket) => 31 | await DeleteAsync(ticket); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Storages/StorageBroker.Users.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Tarteeb.Api.Models.Foundations.Users; 10 | using Microsoft.EntityFrameworkCore; 11 | 12 | namespace Tarteeb.Api.Brokers.Storages 13 | { 14 | public partial class StorageBroker 15 | { 16 | public DbSet Users { get; set; } 17 | 18 | public async ValueTask InsertUserAsync(User user) => 19 | await InsertAsync(user); 20 | 21 | public IQueryable SelectAllUsers() => 22 | SelectAll(); 23 | 24 | public async ValueTask SelectUserByIdAsync(Guid id) => 25 | await SelectAsync(id); 26 | 27 | public async ValueTask UpdateUserAsync(User user) => 28 | await UpdateAsync(user); 29 | 30 | public async ValueTask DeleteUserAsync(User user) => 31 | await DeleteAsync(user); 32 | 33 | protected override void OnModelCreating(ModelBuilder modelBuilder) 34 | { 35 | ConfigureUserEmail(modelBuilder.Entity()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221110175226_RenameTicketToTickets.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace Tarteeb.Api.Migrations 6 | { 7 | public partial class RenameTicketToTickets : Migration 8 | { 9 | protected override void Up(MigrationBuilder migrationBuilder) 10 | { 11 | migrationBuilder.DropPrimaryKey( 12 | name: "PK_Ticket", 13 | table: "Ticket"); 14 | 15 | migrationBuilder.RenameTable( 16 | name: "Ticket", 17 | newName: "Tickets"); 18 | 19 | migrationBuilder.AddPrimaryKey( 20 | name: "PK_Tickets", 21 | table: "Tickets", 22 | column: "Id"); 23 | } 24 | 25 | protected override void Down(MigrationBuilder migrationBuilder) 26 | { 27 | migrationBuilder.DropPrimaryKey( 28 | name: "PK_Tickets", 29 | table: "Tickets"); 30 | 31 | migrationBuilder.RenameTable( 32 | name: "Tickets", 33 | newName: "Ticket"); 34 | 35 | migrationBuilder.AddPrimaryKey( 36 | name: "PK_Ticket", 37 | table: "Ticket", 38 | column: "Id"); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Processings/Users/UserProcessingService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System.Linq; 7 | using Tarteeb.Api.Brokers.Loggings; 8 | using Tarteeb.Api.Models.Foundations.Users; 9 | using Tarteeb.Api.Services.Foundations.Users; 10 | 11 | namespace Tarteeb.Api.Services.Processings.Users 12 | { 13 | public partial class UserProcessingService : IUserProcessingService 14 | { 15 | private readonly IUserService userService; 16 | private readonly ILoggingBroker loggingBroker; 17 | 18 | public UserProcessingService(IUserService userService, ILoggingBroker loggingBroker) 19 | { 20 | this.userService = userService; 21 | this.loggingBroker = loggingBroker; 22 | } 23 | 24 | public User RetrieveUserByCredentails(string email, string password) => 25 | TryCatch(() => 26 | { 27 | ValidateEmailAndPassword(email, password); 28 | IQueryable allUser = this.userService.RetrieveAllUsers(); 29 | 30 | return allUser.FirstOrDefault(retrievedUser => retrievedUser.Email.Equals(email) 31 | && retrievedUser.Password.Equals(password)); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Securities/SecurityServiceTests.Logic.Create.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using FluentAssertions; 7 | using Moq; 8 | using Tarteeb.Api.Models.Foundations.Users; 9 | using Xunit; 10 | 11 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations; 12 | 13 | public partial class SecurityServiceTests 14 | { 15 | [Fact] 16 | public void ShouldCreateToken() 17 | { 18 | //given 19 | User randomUser = CreateRandomUser(); 20 | User inputUser = randomUser; 21 | string randomString = CreateRandomString(); 22 | string createdToken = randomString; 23 | string expectedToken = createdToken; 24 | 25 | this.tokenBrokerMock.Setup(broker => 26 | broker.GenerateJWT(inputUser)).Returns(expectedToken); 27 | 28 | //when 29 | string actualToken = securityService.CreateToken(inputUser); 30 | 31 | //then 32 | actualToken.Should().BeEquivalentTo(expectedToken); 33 | 34 | this.tokenBrokerMock.Verify(broker => 35 | broker.GenerateJWT(inputUser), Times.Once); 36 | 37 | this.tokenBrokerMock.VerifyNoOtherCalls(); 38 | this.loggingBrokerMock.VerifyNoOtherCalls(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Tarteeb.Api.Tests.Unit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | disable 6 | disable 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | runtime; build; native; contentfiles; analyzers; buildtransitive 25 | all 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221113120827_AddTeam.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Microsoft.EntityFrameworkCore.Migrations; 8 | 9 | #nullable disable 10 | 11 | namespace Tarteeb.Api.Migrations 12 | { 13 | public partial class AddTeam : Migration 14 | { 15 | protected override void Up(MigrationBuilder migrationBuilder) 16 | { 17 | migrationBuilder.CreateTable( 18 | name: "Teams", 19 | columns: table => new 20 | { 21 | Id = table.Column(type: "uniqueidentifier", nullable: false), 22 | TeamName = table.Column(type: "nvarchar(max)", nullable: true), 23 | CreatedDate = table.Column(type: "datetimeoffset", nullable: false), 24 | UpdatedDate = table.Column(type: "datetimeoffset", nullable: false) 25 | }, 26 | constraints: table => 27 | { 28 | table.PrimaryKey("PK_Teams", x => x.Id); 29 | }); 30 | } 31 | 32 | protected override void Down(MigrationBuilder migrationBuilder) 33 | { 34 | migrationBuilder.DropTable( 35 | name: "Teams"); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Users/UserServiceTests.Logic.RetrieveAll.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System.Linq; 7 | using FluentAssertions; 8 | using Moq; 9 | using Tarteeb.Api.Models.Foundations.Users; 10 | using Xunit; 11 | 12 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Users 13 | { 14 | public partial class UserServiceTests 15 | { 16 | [Fact] 17 | public void ShouldRetrieveAllUsers() 18 | { 19 | // given 20 | IQueryable randomUsers = CreateRandomUsers(); 21 | IQueryable storageUsers = randomUsers; 22 | IQueryable expectedUser = storageUsers; 23 | 24 | this.storageBrokerMock.Setup(broker => 25 | broker.SelectAllUsers()).Returns(storageUsers); 26 | 27 | // when 28 | IQueryable actualUser = this.userService.RetrieveAllUsers(); 29 | 30 | // then 31 | actualUser.Should().BeEquivalentTo(expectedUser); 32 | 33 | this.storageBrokerMock.Verify(broker => 34 | broker.SelectAllUsers(), Times.Once()); 35 | 36 | this.storageBrokerMock.VerifyNoOtherCalls(); 37 | this.loggingBrokerMock.VerifyNoOtherCalls(); 38 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Teams/TeamServiceTests.Logic.RetrieveAll.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System.Linq; 7 | using FluentAssertions; 8 | using Moq; 9 | using Tarteeb.Api.Models.Foundations.Teams; 10 | using Xunit; 11 | 12 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Teams 13 | { 14 | public partial class TeamServiceTests 15 | { 16 | [Fact] 17 | public void ShouldRetrieveAllTeams() 18 | { 19 | // given 20 | IQueryable randomTeams = CreateRandomTeams(); 21 | IQueryable storageTeams = randomTeams; 22 | IQueryable expectedTeams = storageTeams; 23 | 24 | this.storageBrokerMock.Setup(broker => 25 | broker.SelectAllTeams()).Returns(storageTeams); 26 | 27 | // when 28 | IQueryable actualTeams = this.teamService.RetrieveAllTeams(); 29 | 30 | // then 31 | actualTeams.Should().BeEquivalentTo(expectedTeams); 32 | 33 | this.storageBrokerMock.Verify(broker => 34 | broker.SelectAllTeams(), Times.Once()); 35 | 36 | this.storageBrokerMock.VerifyNoOtherCalls(); 37 | this.loggingBrokerMock.VerifyNoOtherCalls(); 38 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Tickets/TicketServiceTests.Logic.RetrieveAll.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System.Linq; 7 | using FluentAssertions; 8 | using Moq; 9 | using Tarteeb.Api.Models.Foundations.Tickets; 10 | using Xunit; 11 | 12 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Tickets 13 | { 14 | public partial class TicketServiceTests 15 | { 16 | [Fact] 17 | public void ShouldRetrieveAllTickets() 18 | { 19 | // given 20 | IQueryable randomTickets = CreateRandomTickets(); 21 | IQueryable storageTickets = randomTickets; 22 | IQueryable expectedTickets = storageTickets; 23 | 24 | this.storageBrokerMock.Setup(broker => 25 | broker.SelectAllTickets()).Returns(storageTickets); 26 | 27 | // when 28 | IQueryable actualTicket = 29 | this.ticketService.RetrieveAllTickets(); 30 | 31 | // then 32 | actualTicket.Should().BeEquivalentTo(expectedTickets); 33 | 34 | this.storageBrokerMock.Verify(broker => 35 | broker.SelectAllTickets(), Times.Once); 36 | 37 | this.storageBrokerMock.VerifyNoOtherCalls(); 38 | this.loggingBrokerMock.VerifyNoOtherCalls(); 39 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20230210045018_UniqueEmail.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace Tarteeb.Api.Migrations 6 | { 7 | /// 8 | public partial class UniqueEmail : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AlterColumn( 14 | name: "Email", 15 | table: "Users", 16 | type: "nvarchar(450)", 17 | nullable: true, 18 | oldClrType: typeof(string), 19 | oldType: "nvarchar(max)", 20 | oldNullable: true); 21 | 22 | migrationBuilder.CreateIndex( 23 | name: "IX_Users_Email", 24 | table: "Users", 25 | column: "Email", 26 | unique: true, 27 | filter: "[Email] IS NOT NULL"); 28 | } 29 | 30 | /// 31 | protected override void Down(MigrationBuilder migrationBuilder) 32 | { 33 | migrationBuilder.DropIndex( 34 | name: "IX_Users_Email", 35 | table: "Users"); 36 | 37 | migrationBuilder.AlterColumn( 38 | name: "Email", 39 | table: "Users", 40 | type: "nvarchar(max)", 41 | nullable: true, 42 | oldClrType: typeof(string), 43 | oldType: "nvarchar(450)", 44 | oldNullable: true); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Processings/Users/UserProcessingsServiceTests.Logic.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using FluentAssertions; 9 | using Moq; 10 | using Tarteeb.Api.Models.Foundations.Users; 11 | using Xunit; 12 | 13 | namespace Tarteeb.Api.Tests.Unit.Services.Processings.Users 14 | { 15 | public partial class UserProcessingsServiceTests 16 | { 17 | [Fact] 18 | public void ShoulRetrieveUserByCredentails() 19 | { 20 | // given 21 | string inputEmail = GetrandomString(); 22 | string inputPassword = GetrandomString(); 23 | var expectedUser = new User { Email = inputEmail, Password = inputPassword }; 24 | var users = new List { expectedUser }; 25 | 26 | this.userServiceMock.Setup(service => 27 | service.RetrieveAllUsers()) 28 | .Returns(users.AsQueryable()); 29 | 30 | // when 31 | User actualUser = userProcessingsService. 32 | RetrieveUserByCredentails(inputEmail, inputPassword); 33 | 34 | // then 35 | actualUser.Should().BeEquivalentTo(expectedUser); 36 | 37 | this.userServiceMock.Verify(service => 38 | service.RetrieveAllUsers(), Times.Once); 39 | 40 | this.userServiceMock.VerifyNoOtherCalls(); 41 | this.loggingBrokerMock.VerifyNoOtherCalls(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Users/UserServiceTests.Logic.RetrieveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Users; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Users 15 | { 16 | public partial class UserServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldRetrieveUserByIdAsync() 20 | { 21 | // given 22 | Guid randomUserId = Guid.NewGuid(); 23 | Guid inputUserId = randomUserId; 24 | User randomUser = CreateRandomUser(); 25 | User storedUser = randomUser; 26 | User exectedUser = storedUser.DeepClone(); 27 | 28 | this.storageBrokerMock.Setup(broker => 29 | broker.SelectUserByIdAsync(randomUserId)).ReturnsAsync(storedUser); 30 | 31 | // when 32 | User actualUser = await this.userService.RetrieveUserByIdAsync(inputUserId); 33 | 34 | // then 35 | actualUser.Should().BeEquivalentTo(exectedUser); 36 | 37 | this.storageBrokerMock.Verify(broker => 38 | broker.SelectUserByIdAsync(inputUserId), Times.Once); 39 | 40 | this.storageBrokerMock.VerifyNoOtherCalls(); 41 | this.loggingBrokerMock.VerifyNoOtherCalls(); 42 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Processings/Users/UserProcessingService.Validations.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Tarteeb.Api.Models.Foundations.Users; 7 | using Tarteeb.Api.Models.Processings.Users; 8 | 9 | namespace Tarteeb.Api.Services.Processings.Users 10 | { 11 | public partial class UserProcessingService 12 | { 13 | private static void ValidateEmailAndPassword(string email, string password) 14 | { 15 | Validate( 16 | (Rule: IsInvalid(email), Parameter: nameof(User.Email)), 17 | (Rule: IsInvalid(password), Parameter: nameof(User.Password))); 18 | } 19 | 20 | private static dynamic IsInvalid(string text) => new 21 | { 22 | Condition = string.IsNullOrWhiteSpace(text), 23 | Message = "Text is required" 24 | }; 25 | 26 | private static void Validate(params (dynamic Rule, string Parameter)[] validations) 27 | { 28 | var invalidUserProcessingException = 29 | new InvalidUserProcessingException(); 30 | 31 | foreach ((dynamic rule, string parameter) in validations) 32 | { 33 | if (rule.Condition) 34 | { 35 | invalidUserProcessingException.UpsertDataList( 36 | key: parameter, 37 | value: rule.Message); 38 | } 39 | } 40 | 41 | invalidUserProcessingException.ThrowIfContainsErrors(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Teams/TeamServiceTests.Logic.RetrieveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Teams; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Teams 15 | { 16 | public partial class TeamServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldRetrieveTeamByIdAsync() 20 | { 21 | // given 22 | Guid randomTeamId = Guid.NewGuid(); 23 | Guid inputTeamId = randomTeamId; 24 | Team randomTeam = CreateRandomTeam(); 25 | Team storageTeam = randomTeam; 26 | Team expectedTeam = storageTeam.DeepClone(); 27 | 28 | this.storageBrokerMock.Setup(broker => 29 | broker.SelectTeamByIdAsync(inputTeamId)).ReturnsAsync(storageTeam); 30 | 31 | // when 32 | Team actualTeam = 33 | await this.teamService.RetrieveTeamByIdAsync(inputTeamId); 34 | 35 | // then 36 | actualTeam.Should().BeEquivalentTo(expectedTeam); 37 | 38 | this.storageBrokerMock.Verify(broker => 39 | broker.SelectTeamByIdAsync(inputTeamId), Times.Once); 40 | 41 | this.storageBrokerMock.VerifyNoOtherCalls(); 42 | this.loggingBrokerMock.VerifyNoOtherCalls(); 43 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Tickets/TicketServiceTests.Logic.RetrieveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Tickets; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Tickets 15 | { 16 | public partial class TicketServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldRetriveTicketByIdAsync() 20 | { 21 | // given 22 | Guid randomTicketId = Guid.NewGuid(); 23 | Guid inputTicketId = randomTicketId; 24 | Ticket randomTicket = CreateRandomTicket(); 25 | Ticket storedTicket = randomTicket; 26 | Ticket expectedTicket = storedTicket.DeepClone(); 27 | 28 | this.storageBrokerMock.Setup(broker => 29 | broker.SelectTicketByIdAsync(randomTicketId)).ReturnsAsync(storedTicket); 30 | 31 | // when 32 | Ticket actualTicket = 33 | await this.ticketService.RetrieveTicketByIdAsync(inputTicketId); 34 | 35 | // then 36 | actualTicket.Should().BeEquivalentTo(expectedTicket); 37 | 38 | this.storageBrokerMock.Verify(broker => 39 | broker.SelectTicketByIdAsync(inputTicketId), Times.Once); 40 | 41 | this.storageBrokerMock.VerifyNoOtherCalls(); 42 | this.loggingBrokerMock.VerifyNoOtherCalls(); 43 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /.github/workflows/master_tarteeb.yml: -------------------------------------------------------------------------------- 1 | # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy 2 | # More GitHub Actions for Azure: https://github.com/Azure/actions 3 | 4 | name: Build and deploy ASP.Net Core app to Azure Web App - tarteeb 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | 12 | jobs: 13 | build: 14 | runs-on: windows-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - name: Set up .NET Core 20 | uses: actions/setup-dotnet@v1 21 | with: 22 | dotnet-version: '7.x' 23 | include-prerelease: true 24 | 25 | - name: Build with dotnet 26 | run: dotnet build --configuration Release 27 | 28 | - name: dotnet publish 29 | run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp 30 | 31 | - name: Upload artifact for deployment job 32 | uses: actions/upload-artifact@v2 33 | with: 34 | name: .net-app 35 | path: ${{env.DOTNET_ROOT}}/myapp 36 | 37 | deploy: 38 | runs-on: windows-latest 39 | needs: build 40 | environment: 41 | name: 'Production' 42 | url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} 43 | 44 | steps: 45 | - name: Download artifact from build job 46 | uses: actions/download-artifact@v2 47 | with: 48 | name: .net-app 49 | 50 | - name: Deploy to Azure Web App 51 | id: deploy-to-webapp 52 | uses: azure/webapps-deploy@v2 53 | with: 54 | app-name: 'tarteeb' 55 | slot-name: 'Production' 56 | publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_53666BE2EF35438DA33DB8DDB1BC8DBC }} 57 | package: . 58 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221107175023_AddTasks.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Microsoft.EntityFrameworkCore.Migrations; 8 | 9 | #nullable disable 10 | 11 | namespace Tarteeb.Api.Migrations 12 | { 13 | public partial class AddTasks : Migration 14 | { 15 | protected override void Up(MigrationBuilder migrationBuilder) 16 | { 17 | migrationBuilder.CreateTable( 18 | name: "Tasks", 19 | columns: table => new 20 | { 21 | Id = table.Column(type: "uniqueidentifier", nullable: false), 22 | Title = table.Column(type: "nvarchar(max)", nullable: true), 23 | Description = table.Column(type: "nvarchar(max)", nullable: true), 24 | AssigneeId = table.Column(type: "uniqueidentifier", nullable: true), 25 | Status = table.Column(type: "int", nullable: false), 26 | CreatedDate = table.Column(type: "datetimeoffset", nullable: false), 27 | UpdatedDate = table.Column(type: "datetimeoffset", nullable: false), 28 | CreatedUserId = table.Column(type: "uniqueidentifier", nullable: false), 29 | UpdatedUserId = table.Column(type: "uniqueidentifier", nullable: false) 30 | }, 31 | constraints: table => 32 | { 33 | table.PrimaryKey("PK_Tasks", x => x.Id); 34 | }); 35 | } 36 | 37 | protected override void Down(MigrationBuilder migrationBuilder) 38 | { 39 | migrationBuilder.DropTable( 40 | name: "Tasks"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221112084050_AddUsers.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Microsoft.EntityFrameworkCore.Migrations; 8 | 9 | #nullable disable 10 | 11 | namespace Tarteeb.Api.Migrations 12 | { 13 | public partial class AddUsers : Migration 14 | { 15 | protected override void Up(MigrationBuilder migrationBuilder) 16 | { 17 | migrationBuilder.CreateTable( 18 | name: "Users", 19 | columns: table => new 20 | { 21 | Id = table.Column(type: "uniqueidentifier", nullable: false), 22 | FirstName = table.Column(type: "nvarchar(max)", nullable: true), 23 | LastName = table.Column(type: "nvarchar(max)", nullable: true), 24 | PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), 25 | Email = table.Column(type: "nvarchar(max)", nullable: true), 26 | BirthDate = table.Column(type: "datetimeoffset", nullable: false), 27 | CreatedDate = table.Column(type: "datetimeoffset", nullable: false), 28 | UpdatedDate = table.Column(type: "datetimeoffset", nullable: false), 29 | ManagerId = table.Column(type: "uniqueidentifier", nullable: true) 30 | }, 31 | constraints: table => 32 | { 33 | table.PrimaryKey("PK_Users", x => x.Id); 34 | }); 35 | } 36 | 37 | protected override void Down(MigrationBuilder migrationBuilder) 38 | { 39 | migrationBuilder.DropTable( 40 | name: "Users"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Users/UserServiceTests.Logic.Add.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Users; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Users 15 | { 16 | public partial class UserServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldAddUserAsync() 20 | { 21 | // given 22 | DateTimeOffset randomDateTime = GetRandomDateTimeOffset(); 23 | User randomUser = CreateRandomUser(randomDateTime); 24 | User inputUser = randomUser; 25 | User persistedUser = inputUser; 26 | User expectedUser = persistedUser.DeepClone(); 27 | 28 | this.dateTimeBrokerMock.Setup(broker => 29 | broker.GetCurrentDateTime()).Returns(randomDateTime); 30 | 31 | this.storageBrokerMock.Setup(broker => 32 | broker.InsertUserAsync(inputUser)) 33 | .ReturnsAsync(persistedUser); 34 | 35 | // when 36 | User actualUser = await this.userService.AddUserAsync(inputUser); 37 | 38 | // then 39 | actualUser.Should().BeEquivalentTo(expectedUser); 40 | 41 | this.dateTimeBrokerMock.Verify(broker => 42 | broker.GetCurrentDateTime(), Times.Once); 43 | 44 | this.storageBrokerMock.Verify(broker => 45 | broker.InsertUserAsync(inputUser), Times.Once); 46 | 47 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 48 | this.storageBrokerMock.VerifyNoOtherCalls(); 49 | this.loggingBrokerMock.VerifyNoOtherCalls(); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Teams/TeamServiceTests.Logic.Add.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //=============================== 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Teams; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Teams 15 | { 16 | public partial class TeamServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldAddTeamAsync() 20 | { 21 | // given 22 | DateTimeOffset randomDateTime = GetRandomDateTime(); 23 | Team randomTeam = CreateRandomTeam(randomDateTime); 24 | Team inputTeam = randomTeam; 25 | Team persistedTeam = inputTeam; 26 | Team expectedTeam = persistedTeam.DeepClone(); 27 | 28 | this.dateTimeBrokerMock.Setup(broker => 29 | broker.GetCurrentDateTime()).Returns(randomDateTime); 30 | 31 | this.storageBrokerMock.Setup(broker => 32 | broker.InsertTeamAsync(inputTeam)) 33 | .ReturnsAsync(persistedTeam); 34 | 35 | // when 36 | Team actuaTeam = await this.teamService 37 | .AddTeamAsync(inputTeam); 38 | 39 | // then 40 | actuaTeam.Should().BeEquivalentTo(expectedTeam); 41 | 42 | this.dateTimeBrokerMock.Verify(broker => 43 | broker.GetCurrentDateTime(), Times.Once); 44 | 45 | this.storageBrokerMock.Verify(broker => 46 | broker.InsertTeamAsync(inputTeam), Times.Once); 47 | 48 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 49 | this.storageBrokerMock.VerifyNoOtherCalls(); 50 | this.loggingBrokerMock.VerifyNoOtherCalls(); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Tokens/TokenBroker.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.IdentityModel.Tokens.Jwt; 8 | using System.Security.Claims; 9 | using System.Text; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.IdentityModel.Tokens; 12 | using Tarteeb.Api.Models.Foundations.Tokens; 13 | using Tarteeb.Api.Models.Foundations.Users; 14 | 15 | namespace Tarteeb.Api.Brokers.Tokens 16 | { 17 | public class TokenBroker : ITokenBroker 18 | { 19 | private readonly TokenConfiguration tokenConfiguration; 20 | 21 | public TokenBroker(IConfiguration configuration) 22 | { 23 | this.tokenConfiguration = new TokenConfiguration(); 24 | configuration.Bind("Jwt", this.tokenConfiguration); 25 | } 26 | 27 | public string GenerateJWT(User user) 28 | { 29 | 30 | byte[] convertedKeyToBytes = 31 | Encoding.UTF8.GetBytes(this.tokenConfiguration.Key); 32 | 33 | var securityKey = 34 | new SymmetricSecurityKey(convertedKeyToBytes); 35 | 36 | var cridentials = 37 | new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); 38 | 39 | var claims = new[] 40 | { 41 | new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), 42 | new Claim(ClaimTypes.Email, user.Email) 43 | }; 44 | 45 | var token = new JwtSecurityToken( 46 | this.tokenConfiguration.Issuer, 47 | this.tokenConfiguration.Audience, 48 | claims, 49 | expires: DateTime.Now.AddDays(1), 50 | signingCredentials: cridentials); 51 | 52 | return new JwtSecurityTokenHandler().WriteToken(token); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Tickets/TicketServiceTests.Logic.Add.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Tickets; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Tickets 15 | { 16 | public partial class TicketServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldAddTicketAsync() 20 | { 21 | // given 22 | DateTimeOffset randomDateTime = GetRandomDateTime(); 23 | Ticket randomTicket = CreateRandomTicket(randomDateTime); 24 | Ticket inputTicket = randomTicket; 25 | Ticket persistedTicket = inputTicket; 26 | Ticket expectedTicket = persistedTicket.DeepClone(); 27 | 28 | this.dateTimeBrokerMock.Setup(broker => 29 | broker.GetCurrentDateTime()).Returns(randomDateTime); 30 | 31 | this.storageBrokerMock.Setup(broker => 32 | broker.InsertTicketAsync(inputTicket)) 33 | .ReturnsAsync(persistedTicket); 34 | 35 | // when 36 | Ticket actualTicket = await this.ticketService.AddTicketAsync(inputTicket); 37 | 38 | // then 39 | actualTicket.Should().BeEquivalentTo(expectedTicket); 40 | 41 | this.dateTimeBrokerMock.Verify(broker => 42 | broker.GetCurrentDateTime(), Times.Once); 43 | 44 | this.storageBrokerMock.Verify(broker => 45 | broker.InsertTicketAsync(inputTicket), Times.Once); 46 | 47 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 48 | this.storageBrokerMock.VerifyNoOtherCalls(); 49 | this.loggingBrokerMock.VerifyNoOtherCalls(); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Orchestrations/UserSecurityOrchestrationService.Validations.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Tarteeb.Api.Models.Foundations.Users; 7 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 8 | using Tarteeb.Api.Models.Orchestrations.UserTokens.Exceptions; 9 | 10 | namespace Tarteeb.Api.Services.Orchestrations 11 | { 12 | public partial class UserSecurityOrchestrationService 13 | { 14 | private static void ValidateEmailAndPassword(string email, string password) 15 | { 16 | Validate( 17 | (Rule: IsInvalid(email), Parameter: nameof(User.Email)), 18 | (Rule: IsInvalid(password), Parameter: nameof(User.Password))); 19 | } 20 | 21 | private void ValidateUserExists(User user) 22 | { 23 | if (user is null) 24 | { 25 | throw new NotFoundUserException(); 26 | } 27 | } 28 | 29 | private static dynamic IsInvalid(string text) => new 30 | { 31 | Condition = string.IsNullOrWhiteSpace(text), 32 | Message = "Value is required" 33 | }; 34 | 35 | private static void Validate(params (dynamic Rule, string Parameter)[] validations) 36 | { 37 | var invalidUserCreadentialOrchestrationException = 38 | new InvalidUserCredentialOrchestrationException(); 39 | 40 | foreach ((dynamic rule, string parameter) in validations) 41 | { 42 | if (rule.Condition) 43 | { 44 | invalidUserCreadentialOrchestrationException.UpsertDataList( 45 | key: parameter, 46 | value: rule.Message); 47 | } 48 | } 49 | 50 | invalidUserCreadentialOrchestrationException.ThrowIfContainsErrors(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Foundations/Securities/SecurityService.Validations.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Tarteeb.Api.Models.Foundations.Users; 8 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 9 | 10 | namespace Tarteeb.Api.Services.Foundations.Securities 11 | { 12 | public partial class SecurityService 13 | { 14 | private void ValidateUser(User user) 15 | { 16 | ValidateUserNotNull(user); 17 | 18 | Validate( 19 | (Rule: IsInvalid(user.Id), Parameter: nameof(User.Id)), 20 | (Rule: IsInvalid(user.Email), Parameter: nameof(User.Email))); 21 | } 22 | 23 | private static dynamic IsInvalid(string text) => new 24 | { 25 | Condition = string.IsNullOrWhiteSpace(text), 26 | Message = "Text is required" 27 | }; 28 | 29 | private static dynamic IsInvalid(Guid id) => new 30 | { 31 | Condition = id == default, 32 | Message = "Id is required" 33 | }; 34 | 35 | private static void ValidateUserNotNull(User user) 36 | { 37 | if (user is null) 38 | { 39 | throw new NullUserException(); 40 | } 41 | } 42 | 43 | private static void Validate(params (dynamic Rule, string Parameter)[] validations) 44 | { 45 | var invalidUserException = new InvalidUserException(); 46 | 47 | foreach ((dynamic rule, string parameter) in validations) 48 | { 49 | if (rule.Condition) 50 | { 51 | invalidUserException.UpsertDataList( 52 | key: parameter, 53 | value: rule.Message); 54 | } 55 | } 56 | invalidUserException.ThrowIfContainsErrors(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Securities/SecurityServiceTests.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq.Expressions; 8 | using Moq; 9 | using Tarteeb.Api.Brokers.Loggings; 10 | using Tarteeb.Api.Brokers.Tokens; 11 | using Tarteeb.Api.Models.Foundations.Users; 12 | using Tarteeb.Api.Services.Foundations; 13 | using Tarteeb.Api.Services.Foundations.Securities; 14 | using Tynamix.ObjectFiller; 15 | using Xeptions; 16 | 17 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations; 18 | 19 | public partial class SecurityServiceTests 20 | { 21 | private readonly Mock tokenBrokerMock; 22 | private readonly Mock loggingBrokerMock; 23 | private readonly ISecurityService securityService; 24 | 25 | public SecurityServiceTests() 26 | { 27 | this.tokenBrokerMock = new Mock(); 28 | this.loggingBrokerMock = new Mock(); 29 | 30 | this.securityService = new SecurityService( 31 | tokenBrokerMock.Object, 32 | loggingBrokerMock.Object); 33 | } 34 | 35 | private Expression> SameExceptionAs(Xeption expectedException) => 36 | actualException => actualException.SameExceptionAs(expectedException); 37 | 38 | private string CreateRandomString() => 39 | new MnemonicString().GetValue(); 40 | 41 | private static User CreateRandomUser() => 42 | CreateUserFiller(dates: GetRandomDateTimeOffset()).Create(); 43 | 44 | private static DateTimeOffset GetRandomDateTimeOffset() => 45 | new DateTimeRange(earliestDate: DateTime.UnixEpoch).GetValue(); 46 | 47 | private static Filler CreateUserFiller(DateTimeOffset dates) 48 | { 49 | var filler = new Filler(); 50 | 51 | filler.Setup() 52 | .OnType().Use(dates); 53 | 54 | return filler; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tarteeb.Api/Tarteeb.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | /subscriptions/892dd3c3-3966-40a3-8503-76152027a3e7/resourceGroups/tarteeb/providers/microsoft.insights/components/tarteeb 6 | 2fffb16c-3e15-47b2-a365-20241733061f 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | 23 | 24 | all 25 | runtime; build; native; contentfiles; analyzers; buildtransitive 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Teams/TeamServiceTests.Logic.RemoveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Teams; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Teams 15 | { 16 | public partial class TeamServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldRemoveTeamByIdAsync() 20 | { 21 | // given 22 | Guid randomId = Guid.NewGuid(); 23 | Guid inputTeamId = randomId; 24 | Team randomTeam = CreateRandomTeam(); 25 | Team storageTeam = randomTeam; 26 | Team expectedInputTeam = storageTeam; 27 | Team deletedTeam = expectedInputTeam; 28 | Team expectedTeam = deletedTeam.DeepClone(); 29 | 30 | this.storageBrokerMock.Setup(broker => 31 | broker.SelectTeamByIdAsync(inputTeamId)) 32 | .ReturnsAsync(storageTeam); 33 | 34 | this.storageBrokerMock.Setup(broker => 35 | broker.DeleteTeamAsync(expectedInputTeam)) 36 | .ReturnsAsync(deletedTeam); 37 | 38 | // when 39 | Team actualTeam = await this.teamService 40 | .RemoveTeamByIdAsync(inputTeamId); 41 | 42 | // then 43 | actualTeam.Should().BeEquivalentTo(expectedTeam); 44 | 45 | this.storageBrokerMock.Verify(broker => 46 | broker.SelectTeamByIdAsync(inputTeamId), Times.Once); 47 | 48 | this.storageBrokerMock.Verify(broker => 49 | broker.DeleteTeamAsync(expectedInputTeam), Times.Once); 50 | 51 | this.storageBrokerMock.VerifyNoOtherCalls(); 52 | this.loggingBrokerMock.VerifyNoOtherCalls(); 53 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Users/UserServiceTests.Logic.RemoveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Users; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Users 15 | { 16 | public partial class UserServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldRemoveUserByIdAsync() 20 | { 21 | // given 22 | Guid randomId = Guid.NewGuid(); 23 | Guid inputUserId = randomId; 24 | User randomUser = CreateRandomUser(); 25 | User storageUser = randomUser; 26 | User expectedInputUser = storageUser; 27 | User deletedUser = expectedInputUser; 28 | User expectedUser = deletedUser.DeepClone(); 29 | 30 | this.storageBrokerMock.Setup(broker => 31 | broker.SelectUserByIdAsync(inputUserId)) 32 | .ReturnsAsync(storageUser); 33 | 34 | this.storageBrokerMock.Setup(broker => 35 | broker.DeleteUserAsync(expectedInputUser)) 36 | .ReturnsAsync(deletedUser); 37 | 38 | // when 39 | User actualUser = await this.userService 40 | .RemoveUserByIdAsync(inputUserId); 41 | 42 | // then 43 | actualUser.Should().BeEquivalentTo(expectedUser); 44 | 45 | this.storageBrokerMock.Verify(broker => 46 | broker.SelectUserByIdAsync(inputUserId), Times.Once); 47 | 48 | this.storageBrokerMock.Verify(broker => 49 | broker.DeleteUserAsync(expectedInputUser), Times.Once); 50 | 51 | this.storageBrokerMock.VerifyNoOtherCalls(); 52 | this.loggingBrokerMock.VerifyNoOtherCalls(); 53 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Foundations/Securities/SecurityService.Exceptions.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 8 | using Xeptions; 9 | 10 | namespace Tarteeb.Api.Services.Foundations.Securities 11 | { 12 | public partial class SecurityService 13 | { 14 | private delegate string ReturningTokenFunction(); 15 | 16 | private string TryCatch(ReturningTokenFunction returningTokenFunction) 17 | { 18 | try 19 | { 20 | return returningTokenFunction(); 21 | } 22 | catch (NullUserException nullUserException) 23 | { 24 | throw CreateAndLogValidationException(nullUserException); 25 | } 26 | catch (InvalidUserException invalidUserException) 27 | { 28 | throw CreateAndLogValidationException(invalidUserException); 29 | } 30 | catch (Exception serviceException) 31 | { 32 | var failedUserServiceException = 33 | new FailedUserServiceException(serviceException); 34 | 35 | throw CreateAndLogServiceException(failedUserServiceException); 36 | } 37 | 38 | } 39 | 40 | private UserValidationException CreateAndLogValidationException(Xeption exception) 41 | { 42 | var userValidationException = 43 | new UserValidationException(exception); 44 | 45 | this.loggingBroker.LogError(userValidationException); 46 | 47 | return userValidationException; 48 | } 49 | 50 | private UserServiceException CreateAndLogServiceException(Xeption exception) 51 | { 52 | var userServiceException = 53 | new UserServiceException(exception); 54 | 55 | this.loggingBroker.LogError(userServiceException); 56 | 57 | return userServiceException; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Orchestrations/UserSecurityOrchestrationService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System.Linq; 7 | using Tarteeb.Api.Brokers.Loggings; 8 | using Tarteeb.Api.Models.Foundations.Users; 9 | using Tarteeb.Api.Models.Orchestrations.UserTokens; 10 | using Tarteeb.Api.Services.Foundations.Securities; 11 | using Tarteeb.Api.Services.Foundations.Users; 12 | 13 | namespace Tarteeb.Api.Services.Orchestrations 14 | { 15 | public partial class UserSecurityOrchestrationService : IUserSecurityOrchestrationService 16 | { 17 | private readonly IUserService userService; 18 | private readonly ISecurityService securityService; 19 | private readonly ILoggingBroker loggingBroker; 20 | 21 | public UserSecurityOrchestrationService( 22 | IUserService userService, 23 | ISecurityService securityService, 24 | ILoggingBroker loggingBroker) 25 | { 26 | this.userService = userService; 27 | this.securityService = securityService; 28 | this.loggingBroker = loggingBroker; 29 | } 30 | 31 | public UserToken CreateUserToken(string email, string password) => 32 | TryCatch(() => 33 | { 34 | ValidateEmailAndPassword(email, password); 35 | User maybeUser = RetrieveUserByEmailAndPassword(email, password); 36 | ValidateUserExists(maybeUser); 37 | string token = this.securityService.CreateToken(maybeUser); 38 | 39 | return new UserToken 40 | { 41 | UserId = maybeUser.Id, 42 | Token = token 43 | }; 44 | }); 45 | 46 | private User RetrieveUserByEmailAndPassword(string email, string password) 47 | { 48 | IQueryable allUser = this.userService.RetrieveAllUsers(); 49 | 50 | return allUser.FirstOrDefault(retrievedUser => retrievedUser.Email.Equals(email) 51 | && retrievedUser.Password.Equals(password)); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Tickets/TicketServiceTests.Logic.RemoveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Tickets; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Tickets 15 | { 16 | public partial class TicketServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldRemoveTicketByIdAsync() 20 | { 21 | // given 22 | Guid randomId = Guid.NewGuid(); 23 | Guid inputTicketId = randomId; 24 | Ticket randomTicket = CreateRandomTicket(); 25 | Ticket storageTicket = randomTicket; 26 | Ticket expectedInputTicket = storageTicket; 27 | Ticket deletedTicket = expectedInputTicket; 28 | Ticket expectedTicket = deletedTicket.DeepClone(); 29 | 30 | this.storageBrokerMock.Setup(broker => 31 | broker.SelectTicketByIdAsync(inputTicketId)) 32 | .ReturnsAsync(storageTicket); 33 | 34 | this.storageBrokerMock.Setup(broker => 35 | broker.DeleteTicketAsync(expectedInputTicket)) 36 | .ReturnsAsync(deletedTicket); 37 | 38 | // when 39 | Ticket actualTicket = await this.ticketService 40 | .RemoveTicketByIdAsync(inputTicketId); 41 | 42 | // then 43 | actualTicket.Should().BeEquivalentTo(expectedTicket); 44 | 45 | this.storageBrokerMock.Verify(broker => 46 | broker.SelectTicketByIdAsync(inputTicketId), Times.Once); 47 | 48 | this.storageBrokerMock.Verify(broker => 49 | broker.DeleteTicketAsync(expectedInputTicket), Times.Once); 50 | 51 | this.storageBrokerMock.VerifyNoOtherCalls(); 52 | this.loggingBrokerMock.VerifyNoOtherCalls(); 53 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Processings/Users/UserProcessingsServiceTests.Exceptions.Retrieve.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using FluentAssertions; 8 | using Moq; 9 | using Tarteeb.Api.Models.Processings.Users; 10 | using Xeptions; 11 | using Xunit; 12 | 13 | namespace Tarteeb.Api.Tests.Unit.Services.Processings.Users 14 | { 15 | public partial class UserProcessingsServiceTests 16 | { 17 | [Theory] 18 | [MemberData(nameof(UserDependencyExceptions))] 19 | public void ShoudThrowDependencyExceptionOnRetrieveIfDependencyErrorOccursAndLogItAsync( 20 | Xeption dependencyException) 21 | { 22 | // given 23 | string someString = GetrandomString(); 24 | 25 | var expectedUserProcessingDependencyException = 26 | new UserProcessingDependencyException(dependencyException); 27 | 28 | this.userServiceMock.Setup(service => service.RetrieveAllUsers()) 29 | .Throws(dependencyException); 30 | 31 | // when 32 | Action retrieveUserByAction = () => 33 | this.userProcessingsService.RetrieveUserByCredentails(email: someString, password: someString); 34 | 35 | UserProcessingDependencyException actualUserProcessingDependencyException = 36 | Assert.Throws(retrieveUserByAction); 37 | 38 | // then 39 | actualUserProcessingDependencyException.Should().BeEquivalentTo( 40 | expectedUserProcessingDependencyException); 41 | 42 | this.userServiceMock.Verify(service => service.RetrieveAllUsers(), Times.Once); 43 | 44 | this.loggingBrokerMock.Verify(broker => 45 | broker.LogError(It.Is(SameExceptionAs( 46 | expectedUserProcessingDependencyException))), 47 | Times.Once); 48 | 49 | this.userServiceMock.VerifyNoOtherCalls(); 50 | this.loggingBrokerMock.VerifyNoOtherCalls(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Processings/Users/UserProcessingService.Exceptions.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Tarteeb.Api.Models.Foundations.Users; 7 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 8 | using Tarteeb.Api.Models.Processings.Users; 9 | using Xeptions; 10 | 11 | namespace Tarteeb.Api.Services.Processings.Users 12 | { 13 | public partial class UserProcessingService 14 | { 15 | private delegate User ReturningUserFunction(); 16 | 17 | private User TryCatch(ReturningUserFunction returningUserFunction) 18 | { 19 | try 20 | { 21 | return returningUserFunction(); 22 | } 23 | catch (InvalidUserProcessingException invalidUserProcessingException) 24 | { 25 | throw CreateAndLogValidationException(invalidUserProcessingException); 26 | } 27 | catch (UserDependencyException userDependencyException) 28 | { 29 | throw CreateAndLogDependencyException(userDependencyException); 30 | } 31 | catch (UserServiceException userServiceException) 32 | { 33 | throw CreateAndLogDependencyException(userServiceException); 34 | } 35 | } 36 | 37 | private UserProcessingValidationException CreateAndLogValidationException(Xeption exception) 38 | { 39 | var userProcessingValidationException = 40 | new UserProcessingValidationException(exception); 41 | 42 | this.loggingBroker.LogError(userProcessingValidationException); 43 | 44 | return userProcessingValidationException; 45 | } 46 | 47 | private UserProcessingDependencyException CreateAndLogDependencyException(Xeption exception) 48 | { 49 | var userProcessingDependencyException = new UserProcessingDependencyException(exception); 50 | this.loggingBroker.LogError(userProcessingDependencyException); 51 | 52 | return userProcessingDependencyException; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tarteeb.Api.Infrastructure.Build/Program.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //=============================== 5 | 6 | using ADotNet.Clients; 7 | using ADotNet.Models.Pipelines.GithubPipelines.DotNets; 8 | using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; 9 | using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV1s; 10 | 11 | var githubPipeline = new GithubPipeline 12 | { 13 | Name = "Tarteeb Build Process", 14 | 15 | OnEvents = new Events 16 | { 17 | Push = new PushEvent 18 | { 19 | Branches = new string[] { "master" } 20 | }, 21 | 22 | PullRequest = new PullRequestEvent 23 | { 24 | Branches = new string[] { "master" } 25 | } 26 | }, 27 | 28 | Jobs = new Jobs 29 | { 30 | Build = new BuildJob 31 | { 32 | RunsOn = BuildMachines.Windows2022, 33 | 34 | Steps = new List 35 | { 36 | new CheckoutTaskV2 37 | { 38 | Name = "Check out" 39 | }, 40 | 41 | new SetupDotNetTaskV1 42 | { 43 | Name = "Setup .Net", 44 | 45 | TargetDotNetVersion = new TargetDotNetVersion 46 | { 47 | DotNetVersion = "7.0.100", 48 | IncludePrerelease = true 49 | } 50 | }, 51 | 52 | new RestoreTask 53 | { 54 | Name = "Restore Packages" 55 | }, 56 | 57 | new DotNetBuildTask 58 | { 59 | Name = "Build Projects" 60 | }, 61 | 62 | new TestTask 63 | { 64 | Name = "Run Tests" 65 | } 66 | } 67 | } 68 | } 69 | }; 70 | 71 | var adotnetClient = new ADotNetClient(); 72 | 73 | adotnetClient.SerializeAndWriteToFile( 74 | adoPipeline: githubPipeline, 75 | path: "../../../../.github/workflows/build.yml"); 76 | -------------------------------------------------------------------------------- /Tarteeb.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32825.248 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tarteeb.Api", "Tarteeb.Api\Tarteeb.Api.csproj", "{B9288356-F87C-4D2F-835C-43C8E0D1F379}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tarteeb.Api.Infrastructure.Build", "Tarteeb.Api.Infrastructure.Build\Tarteeb.Api.Infrastructure.Build.csproj", "{DF3F7A6B-D6B0-4BC5-8567-AD7009146848}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tarteeb.Api.Tests.Unit", "Tarteeb.Api.Tests.Unit\Tarteeb.Api.Tests.Unit.csproj", "{3498A3E3-475F-4442-9B9F-60C572DC9B67}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {B9288356-F87C-4D2F-835C-43C8E0D1F379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {B9288356-F87C-4D2F-835C-43C8E0D1F379}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {B9288356-F87C-4D2F-835C-43C8E0D1F379}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {B9288356-F87C-4D2F-835C-43C8E0D1F379}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {DF3F7A6B-D6B0-4BC5-8567-AD7009146848}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {DF3F7A6B-D6B0-4BC5-8567-AD7009146848}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {DF3F7A6B-D6B0-4BC5-8567-AD7009146848}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {DF3F7A6B-D6B0-4BC5-8567-AD7009146848}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {3498A3E3-475F-4442-9B9F-60C572DC9B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {3498A3E3-475F-4442-9B9F-60C572DC9B67}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {3498A3E3-475F-4442-9B9F-60C572DC9B67}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {3498A3E3-475F-4442-9B9F-60C572DC9B67}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {72C341B3-EBED-4C92-A7F7-549182919620} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Orchestrations/UserSecurityOrchestrationServiceTests.Logic.Create.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System.Linq; 7 | using FluentAssertions; 8 | using Moq; 9 | using Tarteeb.Api.Models.Foundations.Users; 10 | using Tarteeb.Api.Models.Orchestrations.UserTokens; 11 | using Xunit; 12 | 13 | namespace Tarteeb.Api.Tests.Unit.Services.Orchestrations 14 | { 15 | public partial class UserSecurityOrchestrationServiceTests 16 | { 17 | [Fact] 18 | public void ShoudCreateUserToken() 19 | { 20 | // given 21 | string randomString = GetRandomString(); 22 | string token = randomString; 23 | User randomUser = CreateRandomUser(); 24 | User existingUser = randomUser; 25 | 26 | IQueryable randomUsers = 27 | CreateRandomUsersIncluding(existingUser); 28 | 29 | IQueryable retrievedUsers = randomUsers; 30 | 31 | UserToken expectedUserToken = new UserToken 32 | { 33 | UserId = existingUser.Id, 34 | Token = token 35 | }; 36 | 37 | this.userServiceMock.Setup(service => 38 | service.RetrieveAllUsers()).Returns(retrievedUsers); 39 | 40 | this.securityServiceMock.Setup(service => 41 | service.CreateToken(existingUser)).Returns(token); 42 | 43 | // when 44 | UserToken actualUserToken = this.userSecurityOrchestrationService 45 | .CreateUserToken(existingUser.Email, existingUser.Password); 46 | 47 | // then 48 | actualUserToken.Should().BeEquivalentTo(expectedUserToken); 49 | 50 | this.userServiceMock.Verify(service => service.RetrieveAllUsers(), 51 | Times.Once); 52 | 53 | this.securityServiceMock.Verify(service => service.CreateToken( 54 | existingUser), Times.Once); 55 | 56 | this.userServiceMock.VerifyNoOtherCalls(); 57 | this.securityServiceMock.VerifyNoOtherCalls(); 58 | this.loggingBrokerMock.VerifyNoOtherCalls(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Processings/Users/UserProcessingsServiceTests.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq.Expressions; 8 | using Moq; 9 | using Tarteeb.Api.Brokers.Loggings; 10 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 11 | using Tarteeb.Api.Services.Foundations.Users; 12 | using Tarteeb.Api.Services.Processings.Users; 13 | using Tynamix.ObjectFiller; 14 | using Xeptions; 15 | using Xunit; 16 | 17 | namespace Tarteeb.Api.Tests.Unit.Services.Processings.Users 18 | { 19 | public partial class UserProcessingsServiceTests 20 | { 21 | private readonly Mock userServiceMock; 22 | private readonly Mock loggingBrokerMock; 23 | private readonly IUserProcessingService userProcessingsService; 24 | 25 | public UserProcessingsServiceTests() 26 | { 27 | this.userServiceMock = new Mock(); 28 | this.loggingBrokerMock = new Mock(); 29 | 30 | this.userProcessingsService = new UserProcessingService( 31 | userService: this.userServiceMock.Object, 32 | loggingBroker: this.loggingBrokerMock.Object); 33 | } 34 | 35 | public static TheoryData UserDependencyExceptions() 36 | { 37 | var someInnerException = new Xeption(); 38 | 39 | return new TheoryData 40 | { 41 | new UserDependencyException(someInnerException), 42 | new UserServiceException(someInnerException) 43 | }; 44 | } 45 | 46 | private static string GetrandomString() => 47 | new MnemonicString(wordCount: GetRandomNumber()).GetValue(); 48 | 49 | private static Expression> SameExceptionAs(Xeption expectedException) => 50 | actualException => actualException.SameExceptionAs(expectedException); 51 | 52 | private static int GetRandomNumber() => 53 | new IntRange(min: 2, max: 10).GetValue(); 54 | 55 | private static DateTimeOffset GetRandomDate() => 56 | new DateTimeRange(earliestDate: DateTime.UnixEpoch).GetValue(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221107175023_AddTasks.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using Tarteeb.Api.Brokers.Storages; 9 | 10 | #nullable disable 11 | 12 | namespace Tarteeb.Api.Migrations 13 | { 14 | [DbContext(typeof(StorageBroker))] 15 | [Migration("20221107175023_AddTasks")] 16 | partial class AddTasks 17 | { 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder 22 | .HasAnnotation("ProductVersion", "6.0.10") 23 | .HasAnnotation("Relational:MaxIdentifierLength", 128); 24 | 25 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); 26 | 27 | modelBuilder.Entity("Tarteeb.Api.Models.Tasks.Task", b => 28 | { 29 | b.Property("Id") 30 | .ValueGeneratedOnAdd() 31 | .HasColumnType("uniqueidentifier"); 32 | 33 | b.Property("AssigneeId") 34 | .HasColumnType("uniqueidentifier"); 35 | 36 | b.Property("CreatedDate") 37 | .HasColumnType("datetimeoffset"); 38 | 39 | b.Property("CreatedUserId") 40 | .HasColumnType("uniqueidentifier"); 41 | 42 | b.Property("Description") 43 | .HasColumnType("nvarchar(max)"); 44 | 45 | b.Property("Status") 46 | .HasColumnType("int"); 47 | 48 | b.Property("Title") 49 | .HasColumnType("nvarchar(max)"); 50 | 51 | b.Property("UpdatedDate") 52 | .HasColumnType("datetimeoffset"); 53 | 54 | b.Property("UpdatedUserId") 55 | .HasColumnType("uniqueidentifier"); 56 | 57 | b.HasKey("Id"); 58 | 59 | b.ToTable("Tasks"); 60 | }); 61 | #pragma warning restore 612, 618 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Teams/TeamServiceTests.Logic.Modify.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Teams; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Teams 15 | { 16 | public partial class TeamServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldModifyTeamAsync() 20 | { 21 | // given 22 | DateTimeOffset randomDate = GetRandomDateTime(); 23 | Team randomTeam = CreateRandomModifyTeam(randomDate); 24 | Team inputTeam = randomTeam; 25 | Team storageTeam = inputTeam.DeepClone(); 26 | storageTeam.UpdatedDate = randomTeam.CreatedDate; 27 | Team updatedTeam = inputTeam; 28 | Team exceptedTeam = updatedTeam.DeepClone(); 29 | Guid TeamId = inputTeam.Id; 30 | 31 | this.dateTimeBrokerMock.Setup(broker => 32 | broker.GetCurrentDateTime()).Returns(randomDate); 33 | 34 | this.storageBrokerMock.Setup(broker => 35 | broker.SelectTeamByIdAsync(TeamId)) 36 | .ReturnsAsync(storageTeam); 37 | 38 | this.storageBrokerMock.Setup(broker => 39 | broker.UpdateTeamAsync(inputTeam)) 40 | .ReturnsAsync(updatedTeam); 41 | 42 | // when 43 | Team actualTeam = 44 | await this.teamService.ModifyTeamAsync(inputTeam); 45 | 46 | // then 47 | actualTeam.Should().BeEquivalentTo(exceptedTeam); 48 | 49 | this.dateTimeBrokerMock.Verify(broker => 50 | broker.GetCurrentDateTime(), Times.Once); 51 | 52 | this.storageBrokerMock.Verify(broker => 53 | broker.SelectTeamByIdAsync(TeamId), Times.Once); 54 | 55 | this.storageBrokerMock.Verify(broker => 56 | broker.UpdateTeamAsync(inputTeam), Times.Once); 57 | 58 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 59 | this.storageBrokerMock.VerifyNoOtherCalls(); 60 | this.loggingBrokerMock.VerifyNoOtherCalls(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Users/UserServiceTests.Logic.Modify.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Users; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Users 15 | { 16 | public partial class UserServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldModifyUserAsync() 20 | { 21 | // given 22 | DateTimeOffset randomDate = GetRandomDateTimeOffset(); 23 | User randomUser = CreateRandomModifyUser(randomDate); 24 | User inputUser = randomUser; 25 | User storageUser = inputUser.DeepClone(); 26 | storageUser.UpdatedDate = randomUser.CreatedDate; 27 | User updatedUser = inputUser; 28 | User exceptedUser = updatedUser.DeepClone(); 29 | Guid userId = inputUser.Id; 30 | 31 | this.dateTimeBrokerMock.Setup(broker => 32 | broker.GetCurrentDateTime()).Returns(randomDate); 33 | 34 | this.storageBrokerMock.Setup(broker => 35 | broker.SelectUserByIdAsync(userId)) 36 | .ReturnsAsync(storageUser); 37 | 38 | this.storageBrokerMock.Setup(broker => 39 | broker.UpdateUserAsync(inputUser)) 40 | .ReturnsAsync(updatedUser); 41 | 42 | // when 43 | User actualUser = 44 | await this.userService.ModifyUserAsync(inputUser); 45 | 46 | // then 47 | actualUser.Should().BeEquivalentTo(exceptedUser); 48 | 49 | this.dateTimeBrokerMock.Verify(broker => 50 | broker.GetCurrentDateTime(), Times.Once); 51 | 52 | this.storageBrokerMock.Verify(broker => 53 | broker.SelectUserByIdAsync(userId), Times.Once); 54 | 55 | this.storageBrokerMock.Verify(broker => 56 | broker.UpdateUserAsync(inputUser), Times.Once); 57 | 58 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 59 | this.storageBrokerMock.VerifyNoOtherCalls(); 60 | this.loggingBrokerMock.VerifyNoOtherCalls(); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Processings/Users/UserProcessingsServiceTests.Validation.Retrieve.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using FluentAssertions; 8 | using Moq; 9 | using Tarteeb.Api.Models.Foundations.Users; 10 | using Tarteeb.Api.Models.Processings.Users; 11 | using Xunit; 12 | 13 | namespace Tarteeb.Api.Tests.Unit.Services.Processings.Users 14 | { 15 | public partial class UserProcessingsServiceTests 16 | { 17 | [Fact] 18 | public void ShouldThrowValidationExceptionOnUpsertIfEmailAndPasswordAreInvalidAndLogItAsync() 19 | { 20 | //given 21 | string invalidEmail = string.Empty; 22 | string invalidPassword = string.Empty; 23 | var invalidUserProcessingException = new InvalidUserProcessingException(); 24 | 25 | invalidUserProcessingException.AddData( 26 | key: nameof(User.Email), 27 | values: "Text is required"); 28 | 29 | invalidUserProcessingException.AddData( 30 | key: nameof(User.Password), 31 | values: "Text is required"); 32 | 33 | var expectedUserProcessingValidationException = 34 | new UserProcessingValidationException(invalidUserProcessingException); 35 | 36 | //when 37 | Action retrieveUserByAction = () => 38 | this.userProcessingsService.RetrieveUserByCredentails(invalidEmail, invalidPassword); 39 | 40 | UserProcessingValidationException actualUserProcessingValidationException = 41 | Assert.Throws(retrieveUserByAction); 42 | 43 | //then 44 | actualUserProcessingValidationException.Should().BeEquivalentTo( 45 | expectedUserProcessingValidationException); 46 | 47 | this.loggingBrokerMock.Verify(broker => 48 | broker.LogError(It.Is(SameExceptionAs( 49 | expectedUserProcessingValidationException))), Times.Once); 50 | 51 | this.userServiceMock.Verify(service => 52 | service.RetrieveAllUsers(), Times.Never); 53 | 54 | this.loggingBrokerMock.VerifyNoOtherCalls(); 55 | this.userServiceMock.VerifyNoOtherCalls(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Tickets/TicketServiceTests.Logic.Modify.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //=============================== 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Force.DeepCloner; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Tickets; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Tickets 15 | { 16 | public partial class TicketServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldModifyTicketAsync() 20 | { 21 | // given 22 | DateTimeOffset randomDate = GetRandomDateTime(); 23 | Ticket randomTicket = CreateRandomModifyTicket(randomDate); 24 | Ticket inputTicket = randomTicket; 25 | Ticket storageTicket = inputTicket.DeepClone(); 26 | storageTicket.UpdatedDate = randomTicket.CreatedDate; 27 | Ticket updatedTicket = inputTicket; 28 | Ticket expectedTicket = updatedTicket.DeepClone(); 29 | Guid ticketId = inputTicket.Id; 30 | 31 | this.dateTimeBrokerMock.Setup(broker => 32 | broker.GetCurrentDateTime()).Returns(randomDate); 33 | 34 | this.storageBrokerMock.Setup(broker => 35 | broker.SelectTicketByIdAsync(ticketId)).ReturnsAsync(storageTicket); 36 | 37 | this.storageBrokerMock.Setup(broker => 38 | broker.UpdateTicketAsync(inputTicket)).ReturnsAsync(updatedTicket); 39 | 40 | // when 41 | Ticket actualTicket = 42 | await this.ticketService.ModifyTicketAsync(inputTicket); 43 | 44 | // then 45 | actualTicket.Should().BeEquivalentTo(expectedTicket); 46 | 47 | this.dateTimeBrokerMock.Verify(broker => 48 | broker.GetCurrentDateTime(), Times.Once); 49 | 50 | this.storageBrokerMock.Verify(broker => 51 | broker.SelectTicketByIdAsync(ticketId), Times.Once); 52 | 53 | this.storageBrokerMock.Verify(broker => 54 | broker.UpdateTicketAsync(inputTicket), Times.Once); 55 | 56 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 57 | this.storageBrokerMock.VerifyNoOtherCalls(); 58 | this.loggingBrokerMock.VerifyNoOtherCalls(); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Controllers/AccountsController.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using Microsoft.AspNetCore.Mvc; 7 | using RESTFulSense.Controllers; 8 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 9 | using Tarteeb.Api.Models.Orchestrations.UserTokens; 10 | using Tarteeb.Api.Models.Orchestrations.UserTokens.Exceptions; 11 | using Tarteeb.Api.Services.Orchestrations; 12 | 13 | namespace Tarteeb.Api.Controllers 14 | { 15 | [ApiController] 16 | [Route("api/[controller]/[action]")] 17 | public class AccountsController : RESTFulController 18 | { 19 | private readonly IUserSecurityOrchestrationService userSecurityOrchestrationService; 20 | 21 | public AccountsController(IUserSecurityOrchestrationService userSecurityOrchestrationService) => 22 | this.userSecurityOrchestrationService = userSecurityOrchestrationService; 23 | 24 | [HttpGet] 25 | public ActionResult Login(string email, string password) 26 | { 27 | try 28 | { 29 | return this.userSecurityOrchestrationService.CreateUserToken(email, password); 30 | } 31 | catch (UserTokenOrchestrationValidationException userTokenOrchestrationValidationException) 32 | when (userTokenOrchestrationValidationException.InnerException is InvalidUserException) 33 | 34 | { 35 | return BadRequest(userTokenOrchestrationValidationException.InnerException); 36 | } 37 | catch (UserTokenOrchestrationValidationException userTokenOrchestrationValidationException) 38 | when (userTokenOrchestrationValidationException.InnerException is NotFoundUserException) 39 | { 40 | return NotFound(userTokenOrchestrationValidationException.InnerException); 41 | } 42 | catch (UserTokenOrchestrationDependencyException userTokenOrchestrationDependencyException) 43 | { 44 | return InternalServerError(userTokenOrchestrationDependencyException.InnerException); 45 | } 46 | catch (UserTokenOrchestrationServiceException userTokenOrchestrationServiceException) 47 | { 48 | return InternalServerError(userTokenOrchestrationServiceException.InnerException); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Tarteeb.Api/Brokers/Storages/StorageBroker.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using EFxceptions; 9 | using Microsoft.EntityFrameworkCore; 10 | using Microsoft.Extensions.Configuration; 11 | 12 | namespace Tarteeb.Api.Brokers.Storages 13 | { 14 | public partial class StorageBroker : EFxceptionsContext, IStorageBroker 15 | { 16 | private readonly IConfiguration configuration; 17 | 18 | public StorageBroker(IConfiguration configuration) 19 | { 20 | this.configuration = configuration; 21 | this.Database.Migrate(); 22 | } 23 | 24 | private async ValueTask InsertAsync(T @object) 25 | { 26 | var broker = new StorageBroker(this.configuration); 27 | 28 | broker.Entry(@object).State = EntityState.Added; 29 | await broker.SaveChangesAsync(); 30 | 31 | return @object; 32 | } 33 | 34 | private IQueryable SelectAll() where T : class 35 | { 36 | var broker = new StorageBroker(this.configuration); 37 | 38 | return broker.Set(); 39 | } 40 | 41 | private async ValueTask SelectAsync(params object[] objectIds) where T : class 42 | { 43 | var broker = new StorageBroker(this.configuration); 44 | 45 | return await broker.FindAsync(objectIds); 46 | } 47 | 48 | private async ValueTask UpdateAsync(T @object) 49 | { 50 | var broker = new StorageBroker(this.configuration); 51 | broker.Entry(@object).State = EntityState.Modified; 52 | await broker.SaveChangesAsync(); 53 | 54 | return @object; 55 | } 56 | 57 | private async ValueTask DeleteAsync(T @object) 58 | { 59 | var broker = new StorageBroker(this.configuration); 60 | broker.Entry(@object).State = EntityState.Deleted; 61 | await broker.SaveChangesAsync(); 62 | 63 | return @object; 64 | } 65 | 66 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 67 | { 68 | string connectionString = 69 | this.configuration.GetConnectionString(name: "DefaultConnection"); 70 | 71 | optionsBuilder.UseSqlServer(connectionString); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Tarteeb.Api/Properties/ServiceDependencies/local/appInsights1.arm.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "resourceGroupName": { 6 | "type": "string", 7 | "defaultValue": "tarteeb", 8 | "metadata": { 9 | "_parameterType": "resourceGroup", 10 | "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." 11 | } 12 | }, 13 | "resourceGroupLocation": { 14 | "type": "string", 15 | "defaultValue": "eastus", 16 | "metadata": { 17 | "_parameterType": "location", 18 | "description": "Location of the resource group. Resource groups could have different location than resources." 19 | } 20 | }, 21 | "resourceLocation": { 22 | "type": "string", 23 | "defaultValue": "[parameters('resourceGroupLocation')]", 24 | "metadata": { 25 | "_parameterType": "location", 26 | "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." 27 | } 28 | } 29 | }, 30 | "resources": [ 31 | { 32 | "type": "Microsoft.Resources/resourceGroups", 33 | "name": "[parameters('resourceGroupName')]", 34 | "location": "[parameters('resourceGroupLocation')]", 35 | "apiVersion": "2019-10-01" 36 | }, 37 | { 38 | "type": "Microsoft.Resources/deployments", 39 | "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat('tarteeb', subscription().subscriptionId)))]", 40 | "resourceGroup": "[parameters('resourceGroupName')]", 41 | "apiVersion": "2019-10-01", 42 | "dependsOn": [ 43 | "[parameters('resourceGroupName')]" 44 | ], 45 | "properties": { 46 | "mode": "Incremental", 47 | "template": { 48 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 49 | "contentVersion": "1.0.0.0", 50 | "resources": [ 51 | { 52 | "name": "tarteeb", 53 | "type": "microsoft.insights/components", 54 | "location": "[parameters('resourceLocation')]", 55 | "kind": "web", 56 | "properties": {}, 57 | "apiVersion": "2015-05-01" 58 | } 59 | ] 60 | } 61 | } 62 | } 63 | ], 64 | "metadata": { 65 | "_dependencyType": "appInsights.azure" 66 | } 67 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221110181513_ChangePriority.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using Tarteeb.Api.Brokers.Storages; 9 | 10 | #nullable disable 11 | 12 | namespace Tarteeb.Api.Migrations 13 | { 14 | [DbContext(typeof(StorageBroker))] 15 | [Migration("20221110181513_ChangePriority")] 16 | partial class ChangePriority 17 | { 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder 22 | .HasAnnotation("ProductVersion", "6.0.10") 23 | .HasAnnotation("Relational:MaxIdentifierLength", 128); 24 | 25 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); 26 | 27 | modelBuilder.Entity("Tarteeb.Api.Models.Tickets.Ticket", b => 28 | { 29 | b.Property("Id") 30 | .ValueGeneratedOnAdd() 31 | .HasColumnType("uniqueidentifier"); 32 | 33 | b.Property("AssigneeId") 34 | .HasColumnType("uniqueidentifier"); 35 | 36 | b.Property("CreatedDate") 37 | .HasColumnType("datetimeoffset"); 38 | 39 | b.Property("CreatedUserId") 40 | .HasColumnType("uniqueidentifier"); 41 | 42 | b.Property("Deadline") 43 | .HasColumnType("datetimeoffset"); 44 | 45 | b.Property("Description") 46 | .HasColumnType("nvarchar(max)"); 47 | 48 | b.Property("Priority") 49 | .HasColumnType("int"); 50 | 51 | b.Property("Status") 52 | .HasColumnType("int"); 53 | 54 | b.Property("Title") 55 | .HasColumnType("nvarchar(max)"); 56 | 57 | b.Property("UpdatedDate") 58 | .HasColumnType("datetimeoffset"); 59 | 60 | b.Property("UpdatedUserId") 61 | .HasColumnType("uniqueidentifier"); 62 | 63 | b.HasKey("Id"); 64 | 65 | b.ToTable("Tickets"); 66 | }); 67 | #pragma warning restore 612, 618 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221110171741_RenameTaskToTicket.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using Tarteeb.Api.Brokers.Storages; 9 | 10 | #nullable disable 11 | 12 | namespace Tarteeb.Api.Migrations 13 | { 14 | [DbContext(typeof(StorageBroker))] 15 | [Migration("20221110171741_RenameTaskToTicket")] 16 | partial class RenameTaskToTicket 17 | { 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder 22 | .HasAnnotation("ProductVersion", "6.0.10") 23 | .HasAnnotation("Relational:MaxIdentifierLength", 128); 24 | 25 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); 26 | 27 | modelBuilder.Entity("Tarteeb.Api.Models.Tickets.Ticket", b => 28 | { 29 | b.Property("Id") 30 | .ValueGeneratedOnAdd() 31 | .HasColumnType("uniqueidentifier"); 32 | 33 | b.Property("AssigneeId") 34 | .HasColumnType("uniqueidentifier"); 35 | 36 | b.Property("CreatedDate") 37 | .HasColumnType("datetimeoffset"); 38 | 39 | b.Property("CreatedUserId") 40 | .HasColumnType("uniqueidentifier"); 41 | 42 | b.Property("Deadline") 43 | .HasColumnType("datetimeoffset"); 44 | 45 | b.Property("Description") 46 | .HasColumnType("nvarchar(max)"); 47 | 48 | b.Property("Priority") 49 | .HasColumnType("int"); 50 | 51 | b.Property("Status") 52 | .HasColumnType("int"); 53 | 54 | b.Property("Title") 55 | .HasColumnType("nvarchar(max)"); 56 | 57 | b.Property("UpdatedDate") 58 | .HasColumnType("datetimeoffset"); 59 | 60 | b.Property("UpdatedUserId") 61 | .HasColumnType("uniqueidentifier"); 62 | 63 | b.HasKey("Id"); 64 | 65 | b.ToTable("Ticket"); 66 | }); 67 | #pragma warning restore 612, 618 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221110175226_RenameTicketToTickets.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using Tarteeb.Api.Brokers.Storages; 9 | 10 | #nullable disable 11 | 12 | namespace Tarteeb.Api.Migrations 13 | { 14 | [DbContext(typeof(StorageBroker))] 15 | [Migration("20221110175226_RenameTicketToTickets")] 16 | partial class RenameTicketToTickets 17 | { 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder 22 | .HasAnnotation("ProductVersion", "6.0.10") 23 | .HasAnnotation("Relational:MaxIdentifierLength", 128); 24 | 25 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); 26 | 27 | modelBuilder.Entity("Tarteeb.Api.Models.Tickets.Ticket", b => 28 | { 29 | b.Property("Id") 30 | .ValueGeneratedOnAdd() 31 | .HasColumnType("uniqueidentifier"); 32 | 33 | b.Property("AssigneeId") 34 | .HasColumnType("uniqueidentifier"); 35 | 36 | b.Property("CreatedDate") 37 | .HasColumnType("datetimeoffset"); 38 | 39 | b.Property("CreatedUserId") 40 | .HasColumnType("uniqueidentifier"); 41 | 42 | b.Property("Deadline") 43 | .HasColumnType("datetimeoffset"); 44 | 45 | b.Property("Description") 46 | .HasColumnType("nvarchar(max)"); 47 | 48 | b.Property("Priority") 49 | .HasColumnType("int"); 50 | 51 | b.Property("Status") 52 | .HasColumnType("int"); 53 | 54 | b.Property("Title") 55 | .HasColumnType("nvarchar(max)"); 56 | 57 | b.Property("UpdatedDate") 58 | .HasColumnType("datetimeoffset"); 59 | 60 | b.Property("UpdatedUserId") 61 | .HasColumnType("uniqueidentifier"); 62 | 63 | b.HasKey("Id"); 64 | 65 | b.ToTable("Tickets"); 66 | }); 67 | #pragma warning restore 612, 618 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Foundations/Users/UserService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Tarteeb.Api.Brokers.DateTimes; 10 | using Tarteeb.Api.Brokers.Loggings; 11 | using Tarteeb.Api.Brokers.Storages; 12 | using Tarteeb.Api.Models.Foundations.Users; 13 | 14 | namespace Tarteeb.Api.Services.Foundations.Users 15 | { 16 | public partial class UserService : IUserService 17 | { 18 | private readonly IStorageBroker storageBroker; 19 | private readonly IDateTimeBroker dateTimeBroker; 20 | private readonly ILoggingBroker loggingBroker; 21 | 22 | public UserService( 23 | IStorageBroker storageBroker, 24 | IDateTimeBroker dateTimeBroker, 25 | ILoggingBroker loggingBroker) 26 | { 27 | this.storageBroker = storageBroker; 28 | this.dateTimeBroker = dateTimeBroker; 29 | this.loggingBroker = loggingBroker; 30 | } 31 | 32 | public ValueTask AddUserAsync(User user) => 33 | TryCatch(async () => 34 | { 35 | ValidateUserOnAdd(user); 36 | 37 | return await this.storageBroker.InsertUserAsync(user); 38 | }); 39 | 40 | public IQueryable RetrieveAllUsers() => 41 | TryCatch(() => this.storageBroker.SelectAllUsers()); 42 | 43 | public ValueTask RetrieveUserByIdAsync(Guid userId) => 44 | TryCatch(async () => 45 | { 46 | ValidateUserId(userId); 47 | 48 | User maybeUser = 49 | await this.storageBroker.SelectUserByIdAsync(userId); 50 | 51 | ValidateStorageUser(maybeUser, userId); 52 | 53 | return maybeUser; 54 | }); 55 | 56 | public ValueTask ModifyUserAsync(User user) => 57 | TryCatch(async () => 58 | { 59 | ValidateUserOnModify(user); 60 | var maybeUser = await this.storageBroker.SelectUserByIdAsync(user.Id); 61 | ValidateAginstStorageUserOnModify(inputUser: user, storageUser: maybeUser); 62 | 63 | return await this.storageBroker.UpdateUserAsync(user); 64 | }); 65 | 66 | public ValueTask RemoveUserByIdAsync(Guid userId) => 67 | TryCatch(async () => 68 | { 69 | ValidateUserId(userId); 70 | 71 | User maybeUser = 72 | await this.storageBroker.SelectUserByIdAsync(userId); 73 | 74 | ValidateStorageUser(maybeUser, userId); 75 | 76 | return await this.storageBroker.DeleteUserAsync(maybeUser); 77 | }); 78 | } 79 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Foundations/Teams/TeamService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Tarteeb.Api.Brokers.DateTimes; 10 | using Tarteeb.Api.Brokers.Loggings; 11 | using Tarteeb.Api.Brokers.Storages; 12 | using Tarteeb.Api.Models.Foundations.Teams; 13 | 14 | namespace Tarteeb.Api.Services.Foundations.Teams 15 | { 16 | public partial class TeamService : ITeamService 17 | { 18 | private readonly IStorageBroker storageBroker; 19 | private readonly IDateTimeBroker dateTimeBroker; 20 | private readonly ILoggingBroker loggingBroker; 21 | 22 | public TeamService( 23 | IStorageBroker storageBroker, 24 | IDateTimeBroker dateTimeBroker, 25 | ILoggingBroker loggingBroker) 26 | 27 | { 28 | this.storageBroker = storageBroker; 29 | this.dateTimeBroker = dateTimeBroker; 30 | this.loggingBroker = loggingBroker; 31 | } 32 | 33 | public ValueTask AddTeamAsync(Team team) => 34 | TryCatch(async () => 35 | { 36 | ValidateTeam(team); 37 | 38 | return await this.storageBroker.InsertTeamAsync(team); 39 | }); 40 | 41 | public IQueryable RetrieveAllTeams() => 42 | TryCatch(() => this.storageBroker.SelectAllTeams()); 43 | 44 | public ValueTask RetrieveTeamByIdAsync(Guid teamId) => 45 | TryCatch(async () => 46 | { 47 | ValidateTeamId(teamId); 48 | 49 | Team maybeTeam = 50 | await storageBroker.SelectTeamByIdAsync(teamId); 51 | 52 | ValidateStorageTeamExists(maybeTeam, teamId); 53 | 54 | return maybeTeam; 55 | }); 56 | 57 | public ValueTask ModifyTeamAsync(Team team) => 58 | TryCatch(async () => 59 | { 60 | ValidateTeamOnModify(team); 61 | 62 | var maybeTeam = 63 | await this.storageBroker.SelectTeamByIdAsync(team.Id); 64 | 65 | ValidateAgainstStorageTeamOnModify(inputTeam: team, storageTeam: maybeTeam); 66 | 67 | return await this.storageBroker.UpdateTeamAsync(team); 68 | }); 69 | 70 | public ValueTask RemoveTeamByIdAsync(Guid teamId) => 71 | TryCatch(async () => 72 | { 73 | ValidateTeamId(teamId); 74 | 75 | Team maybeTeam = 76 | await this.storageBroker.SelectTeamByIdAsync(teamId); 77 | 78 | ValidateStorageTeamExists(maybeTeam, teamId); 79 | 80 | return await this.storageBroker.DeleteTeamAsync(maybeTeam); 81 | }); 82 | } 83 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Foundations/Tickets/TicketService.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Tarteeb.Api.Brokers.DateTimes; 10 | using Tarteeb.Api.Brokers.Loggings; 11 | using Tarteeb.Api.Brokers.Storages; 12 | using Tarteeb.Api.Models.Foundations.Tickets; 13 | 14 | namespace Tarteeb.Api.Services.Foundations.Tickets 15 | { 16 | public partial class TicketService : ITicketService 17 | { 18 | private readonly IStorageBroker storageBroker; 19 | private readonly IDateTimeBroker dateTimeBroker; 20 | private readonly ILoggingBroker loggingBroker; 21 | 22 | public TicketService( 23 | IStorageBroker storageBroker, 24 | IDateTimeBroker dateTimeBroker, 25 | ILoggingBroker loggingBroker) 26 | { 27 | this.storageBroker = storageBroker; 28 | this.dateTimeBroker = dateTimeBroker; 29 | this.loggingBroker = loggingBroker; 30 | } 31 | 32 | public ValueTask AddTicketAsync(Ticket ticket) => 33 | TryCatch(async () => 34 | { 35 | ValidateTicketOnAdd(ticket); 36 | 37 | return await this.storageBroker.InsertTicketAsync(ticket); 38 | }); 39 | 40 | public IQueryable RetrieveAllTickets() => 41 | TryCatch(() => this.storageBroker.SelectAllTickets()); 42 | 43 | public ValueTask RetrieveTicketByIdAsync(Guid ticketId) => 44 | TryCatch(async () => 45 | { 46 | ValidateTicketId(ticketId); 47 | 48 | Ticket maybeTicket = 49 | await this.storageBroker.SelectTicketByIdAsync(ticketId); 50 | 51 | ValidateStorageTicket(maybeTicket, ticketId); 52 | 53 | return maybeTicket; 54 | }); 55 | 56 | public ValueTask ModifyTicketAsync(Ticket ticket) => 57 | TryCatch(async () => 58 | { 59 | ValidateTicketOnModify(ticket); 60 | var maybeTicket = await this.storageBroker.SelectTicketByIdAsync(ticket.Id); 61 | 62 | ValidateStorageTicket(maybeTicket, ticket.Id); 63 | ValidateAginstStorageTicketOnModify(inputTicket: ticket, storageTicket: maybeTicket); 64 | 65 | return await this.storageBroker.UpdateTicketAsync(ticket); 66 | }); 67 | 68 | public ValueTask RemoveTicketByIdAsync(Guid ticketId) => 69 | TryCatch(async () => 70 | { 71 | ValidateTicketId(ticketId); 72 | 73 | Ticket maybeTicket = await this.storageBroker 74 | .SelectTicketByIdAsync(ticketId); 75 | 76 | ValidateStorageTicket(maybeTicket, ticketId); 77 | 78 | return await this.storageBroker.DeleteTicketAsync(maybeTicket); 79 | }); 80 | } 81 | } -------------------------------------------------------------------------------- /Tarteeb.Api/Services/Orchestrations/UserSecurityOrchestrationService.Exceptions.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 8 | using Tarteeb.Api.Models.Orchestrations.UserTokens; 9 | using Tarteeb.Api.Models.Orchestrations.UserTokens.Exceptions; 10 | using Xeptions; 11 | 12 | namespace Tarteeb.Api.Services.Orchestrations 13 | { 14 | public partial class UserSecurityOrchestrationService 15 | { 16 | private delegate UserToken ReturningUserTokenFunction(); 17 | 18 | private UserToken TryCatch(ReturningUserTokenFunction returningUserTokenFunction) 19 | { 20 | try 21 | { 22 | return returningUserTokenFunction(); 23 | } 24 | catch (InvalidUserCredentialOrchestrationException invalidUserCreadentialOrchestrationException) 25 | { 26 | throw CreateAndLogValidationException(invalidUserCreadentialOrchestrationException); 27 | } 28 | catch (NotFoundUserException notFoundUserException) 29 | { 30 | throw CreateAndLogValidationException(notFoundUserException); 31 | } 32 | catch (UserDependencyException userDependencyException) 33 | { 34 | throw CreateAndLogDependencyException(userDependencyException); 35 | } 36 | catch (UserServiceException userServiceException) 37 | { 38 | throw CreateAndLogDependencyException(userServiceException); 39 | } 40 | catch(Exception exception) 41 | { 42 | var failedUserTokenOrchestrationException = 43 | new FailedUserTokenOrchestrationException(exception); 44 | 45 | throw CreateAndLogServiceException(failedUserTokenOrchestrationException); 46 | } 47 | } 48 | 49 | private UserTokenOrchestrationValidationException CreateAndLogValidationException(Xeption exception) 50 | { 51 | var userTokenOrchestrationValidationException = new UserTokenOrchestrationValidationException(exception); 52 | this.loggingBroker.LogError(userTokenOrchestrationValidationException); 53 | 54 | return userTokenOrchestrationValidationException; 55 | } 56 | 57 | private UserTokenOrchestrationDependencyException CreateAndLogDependencyException(Xeption exception) 58 | { 59 | var userTokenOrchestrationDependencyException = new UserTokenOrchestrationDependencyException(exception); 60 | this.loggingBroker.LogError(userTokenOrchestrationDependencyException); 61 | 62 | return userTokenOrchestrationDependencyException; 63 | } 64 | 65 | private UserTokenOrchestrationServiceException CreateAndLogServiceException(Xeption exception) 66 | { 67 | var userTokenOrchestrationServiceException = 68 | new UserTokenOrchestrationServiceException(exception); 69 | 70 | this.loggingBroker.LogError(userTokenOrchestrationServiceException); 71 | 72 | return userTokenOrchestrationServiceException; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221110171741_RenameTaskToTicket.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using Microsoft.EntityFrameworkCore.Migrations; 8 | 9 | #nullable disable 10 | 11 | namespace Tarteeb.Api.Migrations 12 | { 13 | public partial class RenameTaskToTicket : Migration 14 | { 15 | protected override void Up(MigrationBuilder migrationBuilder) 16 | { 17 | migrationBuilder.DropTable( 18 | name: "Tasks"); 19 | 20 | migrationBuilder.CreateTable( 21 | name: "Ticket", 22 | columns: table => new 23 | { 24 | Id = table.Column(type: "uniqueidentifier", nullable: false), 25 | Title = table.Column(type: "nvarchar(max)", nullable: true), 26 | Description = table.Column(type: "nvarchar(max)", nullable: true), 27 | Priority = table.Column(type: "int", nullable: false), 28 | Deadline = table.Column(type: "datetimeoffset", nullable: false), 29 | AssigneeId = table.Column(type: "uniqueidentifier", nullable: true), 30 | Status = table.Column(type: "int", nullable: false), 31 | CreatedDate = table.Column(type: "datetimeoffset", nullable: false), 32 | UpdatedDate = table.Column(type: "datetimeoffset", nullable: false), 33 | CreatedUserId = table.Column(type: "uniqueidentifier", nullable: false), 34 | UpdatedUserId = table.Column(type: "uniqueidentifier", nullable: false) 35 | }, 36 | constraints: table => 37 | { 38 | table.PrimaryKey("PK_Ticket", x => x.Id); 39 | }); 40 | } 41 | 42 | protected override void Down(MigrationBuilder migrationBuilder) 43 | { 44 | migrationBuilder.DropTable( 45 | name: "Ticket"); 46 | 47 | migrationBuilder.CreateTable( 48 | name: "Tasks", 49 | columns: table => new 50 | { 51 | Id = table.Column(type: "uniqueidentifier", nullable: false), 52 | AssigneeId = table.Column(type: "uniqueidentifier", nullable: true), 53 | CreatedDate = table.Column(type: "datetimeoffset", nullable: false), 54 | CreatedUserId = table.Column(type: "uniqueidentifier", nullable: false), 55 | Description = table.Column(type: "nvarchar(max)", nullable: true), 56 | Status = table.Column(type: "int", nullable: false), 57 | Title = table.Column(type: "nvarchar(max)", nullable: true), 58 | UpdatedDate = table.Column(type: "datetimeoffset", nullable: false), 59 | UpdatedUserId = table.Column(type: "uniqueidentifier", nullable: false) 60 | }, 61 | constraints: table => 62 | { 63 | table.PrimaryKey("PK_Tasks", x => x.Id); 64 | }); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Tarteeb.Api/Migrations/20221113120827_AddTeam.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using Tarteeb.Api.Brokers.Storages; 9 | 10 | #nullable disable 11 | 12 | namespace Tarteeb.Api.Migrations 13 | { 14 | [DbContext(typeof(StorageBroker))] 15 | [Migration("20221113120827_AddTeam")] 16 | partial class AddTeam 17 | { 18 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 19 | { 20 | #pragma warning disable 612, 618 21 | modelBuilder 22 | .HasAnnotation("ProductVersion", "6.0.10") 23 | .HasAnnotation("Relational:MaxIdentifierLength", 128); 24 | 25 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); 26 | 27 | modelBuilder.Entity("Tarteeb.Api.Models.Teams.Team", b => 28 | { 29 | b.Property("Id") 30 | .ValueGeneratedOnAdd() 31 | .HasColumnType("uniqueidentifier"); 32 | 33 | b.Property("CreatedDate") 34 | .HasColumnType("datetimeoffset"); 35 | 36 | b.Property("TeamName") 37 | .HasColumnType("nvarchar(max)"); 38 | 39 | b.Property("UpdatedDate") 40 | .HasColumnType("datetimeoffset"); 41 | 42 | b.HasKey("Id"); 43 | 44 | b.ToTable("Teams"); 45 | }); 46 | 47 | modelBuilder.Entity("Tarteeb.Api.Models.Tickets.Ticket", b => 48 | { 49 | b.Property("Id") 50 | .ValueGeneratedOnAdd() 51 | .HasColumnType("uniqueidentifier"); 52 | 53 | b.Property("AssigneeId") 54 | .HasColumnType("uniqueidentifier"); 55 | 56 | b.Property("CreatedDate") 57 | .HasColumnType("datetimeoffset"); 58 | 59 | b.Property("CreatedUserId") 60 | .HasColumnType("uniqueidentifier"); 61 | 62 | b.Property("Deadline") 63 | .HasColumnType("datetimeoffset"); 64 | 65 | b.Property("Description") 66 | .HasColumnType("nvarchar(max)"); 67 | 68 | b.Property("Priority") 69 | .HasColumnType("int"); 70 | 71 | b.Property("Status") 72 | .HasColumnType("int"); 73 | 74 | b.Property("Title") 75 | .HasColumnType("nvarchar(max)"); 76 | 77 | b.Property("UpdatedDate") 78 | .HasColumnType("datetimeoffset"); 79 | 80 | b.Property("UpdatedUserId") 81 | .HasColumnType("uniqueidentifier"); 82 | 83 | b.HasKey("Id"); 84 | 85 | b.ToTable("Tickets"); 86 | }); 87 | #pragma warning restore 612, 618 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Securities/SecurityServiceTests.Validations.Create.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using FluentAssertions; 7 | using Moq; 8 | using Tarteeb.Api.Models.Foundations.Users; 9 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 10 | using Xunit; 11 | 12 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations 13 | { 14 | 15 | public partial class SecurityServiceTests 16 | { 17 | [Fact] 18 | public void ShouldThrowValidationExceptionOnCreateJWTIfInputIsNullAndLogItAsync() 19 | { 20 | //given 21 | User noUser = null; 22 | var nullUserException = new NullUserException(); 23 | 24 | var expectedUserValidationException = 25 | new UserValidationException(nullUserException); 26 | 27 | //when 28 | UserValidationException actualUserValidationException = 29 | Assert.Throws(() => this.securityService.CreateToken(noUser)); 30 | 31 | //then 32 | actualUserValidationException.Should().BeEquivalentTo( 33 | expectedUserValidationException); 34 | 35 | this.loggingBrokerMock.Verify(broker => 36 | broker.LogError(It.Is(SameExceptionAs( 37 | expectedUserValidationException))), Times.Once); 38 | 39 | this.tokenBrokerMock.Verify(broker => 40 | broker.GenerateJWT(It.IsAny()), Times.Never); 41 | 42 | this.loggingBrokerMock.VerifyNoOtherCalls(); 43 | this.tokenBrokerMock.VerifyNoOtherCalls(); 44 | } 45 | 46 | [Theory] 47 | [InlineData(null)] 48 | [InlineData("")] 49 | [InlineData(" ")] 50 | public void ShouldThrowValidationExceptionOnCreateJWTIfUserIsInvalidAndLogItAsync( 51 | string invalidString) 52 | { 53 | //given 54 | var invalidUser = new User 55 | { 56 | Email = invalidString 57 | }; 58 | 59 | var invalidUserException = new InvalidUserException(); 60 | 61 | invalidUserException.AddData( 62 | key: nameof(User.Id), 63 | values: "Id is required"); 64 | 65 | invalidUserException.AddData( 66 | key: nameof(User.Email), 67 | values: "Text is required"); 68 | 69 | var expectedUserValidationException = new UserValidationException( 70 | invalidUserException); 71 | 72 | //when 73 | UserValidationException actualUserValidationException = 74 | Assert.Throws(() => this.securityService.CreateToken(invalidUser)); 75 | 76 | //then 77 | actualUserValidationException.Should().BeEquivalentTo( 78 | expectedUserValidationException); 79 | 80 | this.loggingBrokerMock.Verify(broker => 81 | broker.LogError(It.Is(SameExceptionAs( 82 | expectedUserValidationException))), Times.Once); 83 | 84 | this.tokenBrokerMock.Verify(broker => 85 | broker.GenerateJWT(It.IsAny()), Times.Never); 86 | 87 | this.loggingBrokerMock.VerifyNoOtherCalls(); 88 | this.tokenBrokerMock.VerifyNoOtherCalls(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Users/UserServiceTests.Exceptions.RetrieveAll.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using FluentAssertions; 8 | using Microsoft.Data.SqlClient; 9 | using Moq; 10 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 11 | using Xunit; 12 | 13 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Users 14 | { 15 | public partial class UserServiceTests 16 | { 17 | [Fact] 18 | public void ShouldThrowCriticalDependencyExceptionOnRetrieveAllIfSqlErrorOccursAndLogIt() 19 | { 20 | // given 21 | SqlException sqlException = CreateSqlException(); 22 | 23 | var failedUserStorageException = 24 | new FailedUserStorageException(sqlException); 25 | 26 | var expectedUserDependencyException = 27 | new UserDependencyException(failedUserStorageException); 28 | 29 | this.storageBrokerMock.Setup(broker => 30 | broker.SelectAllUsers()) 31 | .Throws(sqlException); 32 | 33 | // when 34 | Action retrieveAllUsersAction = () => 35 | this.userService.RetrieveAllUsers(); 36 | 37 | UserDependencyException actualUserDependencyException = 38 | Assert.Throws(retrieveAllUsersAction); 39 | 40 | // then 41 | actualUserDependencyException.Should().BeEquivalentTo(expectedUserDependencyException); 42 | 43 | this.storageBrokerMock.Verify(broker => 44 | broker.SelectAllUsers(), Times.Once); 45 | 46 | this.loggingBrokerMock.Verify(broker => 47 | broker.LogCritical(It.Is(SameExceptionAs( 48 | expectedUserDependencyException))), Times.Once); 49 | 50 | this.storageBrokerMock.VerifyNoOtherCalls(); 51 | this.loggingBrokerMock.VerifyNoOtherCalls(); 52 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 53 | } 54 | 55 | [Fact] 56 | public void ShouldThrowServiceExceptionOnRetrieveAllWhenAllServiceErrorOccursAndLogIt() 57 | { 58 | // given 59 | string exceptionMessage = GetRandomString(); 60 | var serviceException = new Exception(exceptionMessage); 61 | 62 | var failedUserServiceException = 63 | new FailedUserServiceException(serviceException); 64 | 65 | var expecteduserServiceException = 66 | new UserServiceException(failedUserServiceException); 67 | 68 | this.storageBrokerMock.Setup(broker => 69 | broker.SelectAllUsers()).Throws(serviceException); 70 | 71 | // when 72 | Action retrieveAllUserAction = () => 73 | this.userService.RetrieveAllUsers(); 74 | 75 | // then 76 | Assert.Throws(retrieveAllUserAction); 77 | 78 | this.storageBrokerMock.Verify(broker => 79 | broker.SelectAllUsers(), Times.Once); 80 | 81 | this.loggingBrokerMock.Verify(broker => 82 | broker.LogError(It.Is(SameExceptionAs( 83 | expecteduserServiceException))), Times.Once); 84 | 85 | this.storageBrokerMock.VerifyNoOtherCalls(); 86 | this.loggingBrokerMock.VerifyNoOtherCalls(); 87 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Orchestrations/UserSecurityOrchestrationServiceTests.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Linq.Expressions; 10 | using Moq; 11 | using RESTFulSense.Models; 12 | using Tarteeb.Api.Brokers.Loggings; 13 | using Tarteeb.Api.Models.Foundations.Users; 14 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 15 | using Tarteeb.Api.Services.Foundations.Securities; 16 | using Tarteeb.Api.Services.Foundations.Users; 17 | using Tarteeb.Api.Services.Orchestrations; 18 | using Tynamix.ObjectFiller; 19 | using Xeptions; 20 | using Xunit; 21 | 22 | namespace Tarteeb.Api.Tests.Unit.Services.Orchestrations 23 | { 24 | public partial class UserSecurityOrchestrationServiceTests 25 | { 26 | private readonly Mock userServiceMock; 27 | private readonly Mock securityServiceMock; 28 | private readonly Mock loggingBrokerMock; 29 | private readonly IUserSecurityOrchestrationService userSecurityOrchestrationService; 30 | 31 | public UserSecurityOrchestrationServiceTests() 32 | { 33 | userServiceMock = new Mock(); 34 | securityServiceMock = new Mock(); 35 | loggingBrokerMock = new Mock(); 36 | 37 | this.userSecurityOrchestrationService = new UserSecurityOrchestrationService( 38 | userService: userServiceMock.Object, 39 | securityService: securityServiceMock.Object, 40 | loggingBroker: loggingBrokerMock.Object); 41 | } 42 | 43 | public static TheoryData UserDependencyExceptions() 44 | { 45 | var someInnerException = new Xeption(); 46 | 47 | return new TheoryData 48 | { 49 | new UserDependencyException(someInnerException), 50 | new UserServiceException(someInnerException) 51 | }; 52 | } 53 | 54 | private static string GetRandomString() => 55 | new MnemonicString().GetValue(); 56 | 57 | private static DateTimeOffset GetRandomDate() => 58 | new DateTimeRange(earliestDate: DateTime.UnixEpoch).GetValue(); 59 | 60 | private static int GetRandomNumber() => 61 | new IntRange(min: 2, max: 9).GetValue(); 62 | 63 | private static Expression> SameExceptionAs(Xeption expectedException) => 64 | actualException => actualException.SameExceptionAs(expectedException); 65 | 66 | private IQueryable CreateRandomUsers() => 67 | CreateUserFiller().Create(count: GetRandomNumber()).AsQueryable(); 68 | 69 | private IQueryable CreateRandomUsersIncluding(User user) 70 | { 71 | List users = CreateUserFiller() 72 | .Create(count: GetRandomNumber()).ToList(); 73 | 74 | users.Add(user); 75 | 76 | return users.AsQueryable(); 77 | } 78 | 79 | private User CreateRandomUser() => 80 | CreateUserFiller().Create(); 81 | 82 | 83 | private static Filler CreateUserFiller() 84 | { 85 | DateTimeOffset dates = GetRandomDate(); 86 | var filler = new Filler(); 87 | 88 | filler.Setup() 89 | .OnType().Use(dates); 90 | 91 | return filler; 92 | } 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Teams/TeamServiceTests.Exceptions.RetrieveAll.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using FluentAssertions; 8 | using Microsoft.Data.SqlClient; 9 | using Moq; 10 | using Tarteeb.Api.Models.Foundations.Teams.Exceptions; 11 | using Xunit; 12 | 13 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Teams 14 | { 15 | public partial class TeamServiceTests 16 | { 17 | [Fact] 18 | public void ShouldThrowCriticalDependencyExceptionOnRetrieveAllIfSqlErrorOccursAndLogIt() 19 | { 20 | // given 21 | SqlException sqlException = CreateSqlException(); 22 | 23 | var failedTeamStorageException = 24 | new FailedTeamStorageException(sqlException); 25 | 26 | var expectedTeamDependencyException = 27 | new TeamDependencyException(failedTeamStorageException); 28 | 29 | this.storageBrokerMock.Setup(broker => 30 | broker.SelectAllTeams()).Throws(sqlException); 31 | 32 | // when 33 | Action retrieveAllTeamsAction = () => 34 | this.teamService.RetrieveAllTeams(); 35 | 36 | TeamDependencyException actualTeamDependencyException = 37 | Assert.Throws(retrieveAllTeamsAction); 38 | 39 | // then 40 | actualTeamDependencyException.Should().BeEquivalentTo(expectedTeamDependencyException); 41 | 42 | this.storageBrokerMock.Verify(broker => 43 | broker.SelectAllTeams(), Times.Once); 44 | 45 | this.loggingBrokerMock.Verify(broker => 46 | broker.LogCritical(It.Is(SameExceptionAs( 47 | expectedTeamDependencyException))), Times.Once); 48 | 49 | this.storageBrokerMock.VerifyNoOtherCalls(); 50 | this.loggingBrokerMock.VerifyNoOtherCalls(); 51 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 52 | } 53 | 54 | [Fact] 55 | public void ShouldThrowServiceExceptionOnRetrieveAllWhenAllServiceErrorOccursAndLogIt() 56 | { 57 | // given 58 | string exceptionMessage = GetRandomString(); 59 | var serviceException = new Exception(exceptionMessage); 60 | 61 | var failedTeamServiceException = 62 | new FailedTeamServiceException(serviceException); 63 | 64 | var expectedTeamServiceException = 65 | new TeamServiceException(failedTeamServiceException); 66 | 67 | this.storageBrokerMock.Setup(broker => 68 | broker.SelectAllTeams()).Throws(serviceException); 69 | 70 | // when 71 | Action retrieveAllTeamAction = () => 72 | this.teamService.RetrieveAllTeams(); 73 | 74 | TeamServiceException actualTeamServiceException = 75 | Assert.Throws(retrieveAllTeamAction); 76 | 77 | // then 78 | actualTeamServiceException.Should().BeEquivalentTo(expectedTeamServiceException); 79 | 80 | this.storageBrokerMock.Verify(broker => 81 | broker.SelectAllTeams(), Times.Once); 82 | 83 | this.loggingBrokerMock.Verify(broker => 84 | broker.LogError(It.Is(SameExceptionAs( 85 | expectedTeamServiceException))), Times.Once); 86 | 87 | this.storageBrokerMock.VerifyNoOtherCalls(); 88 | this.loggingBrokerMock.VerifyNoOtherCalls(); 89 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Tickets/TicketServiceTests.Exceptions.RetrieveAll.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using FluentAssertions; 8 | using Microsoft.Data.SqlClient; 9 | using Moq; 10 | using Tarteeb.Api.Models.Foundations.Tickets.Exceptions; 11 | using Xunit; 12 | 13 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Tickets 14 | { 15 | public partial class TicketServiceTests 16 | { 17 | [Fact] 18 | public void ShouldThrowCriticalDependencyExceptionOnRetrieveAllWhenSqlExceptionOccursAndLogIt() 19 | { 20 | // given 21 | SqlException sqlException = CreateSqlException(); 22 | var failedTicketServiceException = new FailedTicketServiceException(sqlException); 23 | 24 | var expectedTicketDependencyException = 25 | new TicketDependencyException(failedTicketServiceException); 26 | 27 | this.storageBrokerMock.Setup(broker => 28 | broker.SelectAllTickets()).Throws(sqlException); 29 | 30 | // when 31 | Action retrieveAllTicketAction = () => 32 | this.ticketService.RetrieveAllTickets(); 33 | 34 | TicketDependencyException actualTicketDependencyException = 35 | Assert.Throws(retrieveAllTicketAction); 36 | 37 | // then 38 | actualTicketDependencyException.Should().BeEquivalentTo(expectedTicketDependencyException); 39 | 40 | this.storageBrokerMock.Verify(broker => 41 | broker.SelectAllTickets(), Times.Once); 42 | 43 | this.loggingBrokerMock.Verify(broker => 44 | broker.LogCritical(It.Is(SameExceptionAs(expectedTicketDependencyException))), 45 | Times.Once); 46 | 47 | this.storageBrokerMock.VerifyNoOtherCalls(); 48 | this.loggingBrokerMock.VerifyNoOtherCalls(); 49 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 50 | } 51 | 52 | [Fact] 53 | public void ShouldThrowServiceExceptionOnRetrieveAllWhenAllServiceErrorOccursAndLogIt() 54 | { 55 | // given 56 | string exceptionMessage = GetRandomMessage(); 57 | var serviceException = new Exception(exceptionMessage); 58 | var failedTicketServiceException = new FailedTicketServiceException(serviceException); 59 | 60 | var expectedTicketServiceException = 61 | new TicketServiceException(failedTicketServiceException); 62 | 63 | this.storageBrokerMock.Setup(broker => 64 | broker.SelectAllTickets()).Throws(serviceException); 65 | 66 | // when 67 | Action retrieveAllTicketAction = () => 68 | this.ticketService.RetrieveAllTickets(); 69 | 70 | TicketServiceException actualTicketServiceException = 71 | Assert.Throws(retrieveAllTicketAction); 72 | 73 | // then 74 | actualTicketServiceException.Should().BeEquivalentTo(expectedTicketServiceException); 75 | 76 | this.storageBrokerMock.Verify(broker => 77 | broker.SelectAllTickets(), Times.Once); 78 | 79 | this.loggingBrokerMock.Verify(broker => 80 | broker.LogError(It.Is(SameExceptionAs( 81 | expectedTicketServiceException))), Times.Once); 82 | 83 | this.storageBrokerMock.VerifyNoOtherCalls(); 84 | this.loggingBrokerMock.VerifyNoOtherCalls(); 85 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Users/UserServiceTests.Validations.RetrieveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Moq; 10 | using Tarteeb.Api.Models.Foundations.Users; 11 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Users 15 | { 16 | public partial class UserServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldThrowVlidationExceptionOnRetrieveByIdIfIdIsInvalidAndLogItAsync() 20 | { 21 | // given 22 | var invlidUserId = Guid.Empty; 23 | var invalidUserException = new InvalidUserException(); 24 | 25 | invalidUserException.AddData( 26 | key: nameof(User.Id), 27 | values: "Id is required"); 28 | 29 | var expectedUserValidationException = new UserValidationException(invalidUserException); 30 | 31 | // when 32 | ValueTask retrieveUserByIdTask = 33 | this.userService.RetrieveUserByIdAsync(invlidUserId); 34 | 35 | UserValidationException actualUserValidationException = 36 | await Assert.ThrowsAsync(retrieveUserByIdTask.AsTask); 37 | 38 | // then 39 | actualUserValidationException.Should().BeEquivalentTo(expectedUserValidationException); 40 | 41 | this.loggingBrokerMock.Verify(broker => 42 | broker.LogError(It.Is(SameExceptionAs( 43 | expectedUserValidationException))), Times.Once); 44 | 45 | this.storageBrokerMock.Verify(broker => 46 | broker.SelectUserByIdAsync(It.IsAny()), Times.Never); 47 | 48 | this.loggingBrokerMock.VerifyNoOtherCalls(); 49 | this.storageBrokerMock.VerifyNoOtherCalls(); 50 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 51 | } 52 | 53 | [Fact] 54 | public async Task ShouldThrowValidationExceptionOnRetrieveByIdIfUserIsNotFoundAndLogItAsync() 55 | { 56 | // given 57 | Guid someUserId = Guid.NewGuid(); 58 | User noUser = null; 59 | var notFoundUserValidationException = new NotFoundUserException(someUserId); 60 | 61 | var expectedValidationException = 62 | new UserValidationException(notFoundUserValidationException); 63 | 64 | this.storageBrokerMock.Setup(broker => 65 | broker.SelectUserByIdAsync(It.IsAny())).ReturnsAsync(noUser); 66 | 67 | // when 68 | ValueTask retrieveByIdUserTask = 69 | this.userService.RetrieveUserByIdAsync(someUserId); 70 | 71 | UserValidationException actualValidationException = 72 | await Assert.ThrowsAsync( 73 | retrieveByIdUserTask.AsTask); 74 | 75 | // then 76 | actualValidationException.Should().BeEquivalentTo(expectedValidationException); 77 | 78 | this.storageBrokerMock.Verify(broker => 79 | broker.SelectUserByIdAsync(It.IsAny()), Times.Once); 80 | 81 | this.loggingBrokerMock.Verify(broker => 82 | broker.LogError(It.Is(SameExceptionAs(expectedValidationException))), Times.Once); 83 | 84 | this.storageBrokerMock.VerifyNoOtherCalls(); 85 | this.loggingBrokerMock.VerifyNoOtherCalls(); 86 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Teams/TeamServiceTests.Validations.RetrieveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Moq; 10 | using Tarteeb.Api.Models.Foundations.Teams; 11 | using Tarteeb.Api.Models.Foundations.Teams.Exceptions; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Teams 15 | { 16 | public partial class TeamServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldThrowValidationExceptionOnRetrieveByIdIfIdIsInvalidAndLogItAsync() 20 | { 21 | // given 22 | var invalidTeamId = Guid.Empty; 23 | var invalidTeamException = new InvalidTeamException(); 24 | 25 | invalidTeamException.AddData( 26 | key: nameof(Team.Id), 27 | values: "Id is required"); 28 | 29 | var expectedTeamValidationException = new 30 | TeamValidationException(invalidTeamException); 31 | 32 | // when 33 | ValueTask retrieveTeamByIdTask = 34 | this.teamService.RetrieveTeamByIdAsync(invalidTeamId); 35 | 36 | TeamValidationException actualTeamValidationException = 37 | await Assert.ThrowsAsync( 38 | retrieveTeamByIdTask.AsTask); 39 | 40 | // then 41 | actualTeamValidationException.Should().BeEquivalentTo(expectedTeamValidationException); 42 | 43 | this.loggingBrokerMock.Verify(broker => 44 | broker.LogError(It.Is(SameExceptionAs( 45 | expectedTeamValidationException))), Times.Once); 46 | 47 | this.storageBrokerMock.Verify(broker => 48 | broker.SelectTeamByIdAsync(It.IsAny()), Times.Never); 49 | 50 | this.loggingBrokerMock.VerifyNoOtherCalls(); 51 | this.storageBrokerMock.VerifyNoOtherCalls(); 52 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 53 | } 54 | 55 | [Fact] 56 | public async Task ShouldThrowNotFoundExceptionOnRetrieveByIdIfTeamIsNotFoundAndLogItAsync() 57 | { 58 | // given 59 | Guid someTeamId = Guid.NewGuid(); 60 | Team noTeam = null; 61 | 62 | var notFoundTeamException = 63 | new NotFoundTeamException(someTeamId); 64 | 65 | var expectedTeamValidationException = 66 | new TeamValidationException(notFoundTeamException); 67 | 68 | this.storageBrokerMock.Setup(broker => 69 | broker.SelectTeamByIdAsync(It.IsAny())) 70 | .ReturnsAsync(noTeam); 71 | 72 | // when 73 | ValueTask retrieveTeamByIdTask = 74 | this.teamService.RetrieveTeamByIdAsync(someTeamId); 75 | 76 | TeamValidationException actualTeamValidationException = 77 | await Assert.ThrowsAsync( 78 | retrieveTeamByIdTask.AsTask); 79 | 80 | // then 81 | actualTeamValidationException.Should().BeEquivalentTo(expectedTeamValidationException); 82 | 83 | this.storageBrokerMock.Verify(broker => 84 | broker.SelectTeamByIdAsync(It.IsAny()), Times.Once()); 85 | 86 | this.loggingBrokerMock.Verify(broker => 87 | broker.LogError(It.Is(SameExceptionAs( 88 | expectedTeamValidationException))), Times.Once); 89 | 90 | this.storageBrokerMock.VerifyNoOtherCalls(); 91 | this.loggingBrokerMock.VerifyNoOtherCalls(); 92 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Tickets/TicketServiceTests.Validations.RetrieveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Moq; 10 | using Tarteeb.Api.Models.Foundations.Tickets; 11 | using Tarteeb.Api.Models.Foundations.Tickets.Exceptions; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Tickets 15 | { 16 | public partial class TicketServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldThrowValidationExceptionOnRetrieveByIdIfIdIsInvalidAndLogItAsync() 20 | { 21 | // given 22 | var invalidTicketId = Guid.Empty; 23 | 24 | var invalidTicketException = 25 | new InvalidTicketException(); 26 | 27 | invalidTicketException.AddData( 28 | key: nameof(Ticket.Id), 29 | values: "Id is required"); 30 | 31 | var expectedTicketValidationException = new 32 | TicketValidationException(invalidTicketException); 33 | 34 | // when 35 | ValueTask retrieveTicketByIdTask = 36 | this.ticketService.RetrieveTicketByIdAsync(invalidTicketId); 37 | 38 | TicketValidationException actualTicketValidationException = 39 | await Assert.ThrowsAsync(retrieveTicketByIdTask.AsTask); 40 | 41 | // then 42 | actualTicketValidationException.Should().BeEquivalentTo(expectedTicketValidationException); 43 | 44 | this.loggingBrokerMock.Verify(broker => 45 | broker.LogError(It.Is(SameExceptionAs( 46 | expectedTicketValidationException))), Times.Once); 47 | 48 | this.storageBrokerMock.Verify(broker => 49 | broker.SelectTicketByIdAsync(It.IsAny()), Times.Never); 50 | 51 | this.loggingBrokerMock.VerifyNoOtherCalls(); 52 | this.storageBrokerMock.VerifyNoOtherCalls(); 53 | } 54 | 55 | [Fact] 56 | public async Task ShouldThrowValidationExceptionOnRetrieveByIdIfTicketNotFoundAndLogItAsync() 57 | { 58 | // given 59 | Guid someTicketId = Guid.NewGuid(); 60 | Ticket noTicket = null; 61 | 62 | var notFoundTicketValidationException = 63 | new NotFoundTicketException(someTicketId); 64 | 65 | var expectedValidationException = 66 | new TicketValidationException(notFoundTicketValidationException); 67 | 68 | this.storageBrokerMock.Setup(broker => 69 | broker.SelectTicketByIdAsync(It.IsAny())).ReturnsAsync(noTicket); 70 | 71 | // when 72 | ValueTask retrieveByIdTicketTask = 73 | this.ticketService.RetrieveTicketByIdAsync(someTicketId); 74 | 75 | TicketValidationException actualValidationException = 76 | await Assert.ThrowsAsync( 77 | retrieveByIdTicketTask.AsTask); 78 | 79 | // then 80 | actualValidationException.Should().BeEquivalentTo(expectedValidationException); 81 | 82 | this.storageBrokerMock.Verify(broker => 83 | broker.SelectTicketByIdAsync(It.IsAny()), Times.Once); 84 | 85 | this.loggingBrokerMock.Verify(broker => 86 | broker.LogError(It.Is(SameExceptionAs( 87 | expectedValidationException))), Times.Once); 88 | 89 | this.storageBrokerMock.VerifyNoOtherCalls(); 90 | this.loggingBrokerMock.VerifyNoOtherCalls(); 91 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Users/UserServiceTests.Exceptions.RetrieveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Microsoft.Data.SqlClient; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Users; 12 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 13 | using Xunit; 14 | 15 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Users 16 | { 17 | public partial class UserServiceTests 18 | { 19 | [Fact] 20 | public async Task ShouldThrowCriticalDependencyExceptionOnRetrieveByIdIfSqlErrorOccursAndLogItAsync() 21 | { 22 | // given 23 | Guid someId = Guid.NewGuid(); 24 | SqlException sqlException = CreateSqlException(); 25 | var failedUserStorageException = new FailedUserStorageException(sqlException); 26 | var expectedUserDependencyException = new UserDependencyException(failedUserStorageException); 27 | 28 | this.storageBrokerMock.Setup(broker => 29 | broker.SelectUserByIdAsync(It.IsAny())).ThrowsAsync(sqlException); 30 | 31 | // when 32 | ValueTask retrieveUserByIdTask = 33 | this.userService.RetrieveUserByIdAsync(someId); 34 | 35 | UserDependencyException actualUserDependencyException = 36 | await Assert.ThrowsAsync(retrieveUserByIdTask.AsTask); 37 | 38 | // then 39 | actualUserDependencyException.Should().BeEquivalentTo(expectedUserDependencyException); 40 | 41 | this.storageBrokerMock.Verify(broker => 42 | broker.SelectUserByIdAsync(It.IsAny()), Times.Once); 43 | 44 | this.loggingBrokerMock.Verify(broker => 45 | broker.LogCritical(It.Is(SameExceptionAs(expectedUserDependencyException))), Times.Once); 46 | 47 | this.storageBrokerMock.VerifyNoOtherCalls(); 48 | this.loggingBrokerMock.VerifyNoOtherCalls(); 49 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 50 | } 51 | 52 | [Fact] 53 | public async Task ShouldThrowServiceExceptionOnRetrieveByIdAsyncIfServiceErrorOccursAndLogItAsync() 54 | { 55 | // given 56 | Guid someId = Guid.NewGuid(); 57 | var serviceException = new Exception(); 58 | 59 | var failedUserServiceException = 60 | new FailedUserServiceException(serviceException); 61 | 62 | var expectedUserServiceExcpetion = 63 | new UserServiceException(failedUserServiceException); 64 | 65 | this.storageBrokerMock.Setup(broker => 66 | broker.SelectUserByIdAsync(It.IsAny())).ThrowsAsync(serviceException); 67 | 68 | // when 69 | ValueTask retrieveUserById = 70 | this.userService.RetrieveUserByIdAsync(someId); 71 | 72 | UserServiceException actualUserServiceException = 73 | await Assert.ThrowsAsync(retrieveUserById.AsTask); 74 | 75 | // then 76 | actualUserServiceException.Should().BeEquivalentTo(expectedUserServiceExcpetion); 77 | 78 | this.storageBrokerMock.Verify(broker => 79 | broker.SelectUserByIdAsync(It.IsAny()), Times.Once); 80 | 81 | this.loggingBrokerMock.Verify(broker => 82 | broker.LogError(It.Is(SameExceptionAs( 83 | expectedUserServiceExcpetion))), Times.Once); 84 | 85 | this.storageBrokerMock.VerifyNoOtherCalls(); 86 | this.loggingBrokerMock.VerifyNoOtherCalls(); 87 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Users/UserServiceTests.Validations.RemoveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Moq; 10 | using Tarteeb.Api.Models.Foundations.Users; 11 | using Tarteeb.Api.Models.Foundations.Users.Exceptions; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Users 15 | { 16 | public partial class UserServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldThrowValidationExceptionOnRemoveIfIdIsInvalidAndLogItAsync() 20 | { 21 | // given 22 | Guid invalidUserId = Guid.Empty; 23 | var invalidUserException = new InvalidUserException(); 24 | 25 | invalidUserException.AddData( 26 | key: nameof(User.Id), 27 | values: "Id is required"); 28 | 29 | var expectedUserValidationException = 30 | new UserValidationException(invalidUserException); 31 | 32 | // when 33 | ValueTask removeUserByIdTask = 34 | this.userService.RemoveUserByIdAsync(invalidUserId); 35 | 36 | UserValidationException actualUserValidationException = 37 | await Assert.ThrowsAsync(removeUserByIdTask.AsTask); 38 | 39 | // then 40 | actualUserValidationException.Should().BeEquivalentTo(expectedUserValidationException); 41 | 42 | this.loggingBrokerMock.Verify(broker => 43 | broker.LogError(It.Is(SameExceptionAs( 44 | expectedUserValidationException))), Times.Once); 45 | 46 | this.storageBrokerMock.Verify(broker => 47 | broker.DeleteUserAsync(It.IsAny()), Times.Never); 48 | 49 | this.loggingBrokerMock.VerifyNoOtherCalls(); 50 | this.storageBrokerMock.VerifyNoOtherCalls(); 51 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 52 | } 53 | 54 | [Fact] 55 | public async Task ShouldThrowNotFoundExceptionOnRemoveIfUserIsNotFoundAndLogItAsync() 56 | { 57 | // given 58 | Guid randomUserId = Guid.NewGuid(); 59 | Guid inputUserId = randomUserId; 60 | User noUser = null; 61 | 62 | var notFoundUserException = 63 | new NotFoundUserException(inputUserId); 64 | 65 | var expectedUserValidationException = 66 | new UserValidationException(notFoundUserException); 67 | 68 | this.storageBrokerMock.Setup(broker => 69 | broker.SelectUserByIdAsync(It.IsAny())) 70 | .ReturnsAsync(noUser); 71 | 72 | // when 73 | ValueTask removeUserByIdTask = 74 | this.userService.RemoveUserByIdAsync(inputUserId); 75 | 76 | UserValidationException actualUserValidationException = 77 | await Assert.ThrowsAsync(removeUserByIdTask.AsTask); 78 | 79 | // then 80 | actualUserValidationException.Should().BeEquivalentTo(expectedUserValidationException); 81 | 82 | this.storageBrokerMock.Verify(broker => 83 | broker.SelectUserByIdAsync(It.IsAny()), Times.Once); 84 | 85 | this.loggingBrokerMock.Verify(broker => 86 | broker.LogError(It.Is(SameExceptionAs( 87 | expectedUserValidationException))), Times.Once); 88 | 89 | this.storageBrokerMock.Verify(broker => 90 | broker.DeleteUserAsync(It.IsAny()), Times.Never); 91 | 92 | this.storageBrokerMock.VerifyNoOtherCalls(); 93 | this.loggingBrokerMock.VerifyNoOtherCalls(); 94 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Teams/TeamServiceTests.Validations.RemoveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Moq; 10 | using Tarteeb.Api.Models.Foundations.Teams; 11 | using Tarteeb.Api.Models.Foundations.Teams.Exceptions; 12 | using Xunit; 13 | 14 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Teams 15 | { 16 | public partial class TeamServiceTests 17 | { 18 | [Fact] 19 | public async Task ShouldThrowValidationExceptionOnRemoveIfIdIsInvalidAndLogItAsync() 20 | { 21 | // given 22 | Guid invalidTeamId = Guid.Empty; 23 | 24 | var invalidTeamException = new InvalidTeamException(); 25 | 26 | invalidTeamException.AddData( 27 | key: nameof(Team.Id), 28 | values: "Id is required"); 29 | 30 | var expectedTeamValidationException = 31 | new TeamValidationException(invalidTeamException); 32 | 33 | // when 34 | ValueTask removeTeamByIdTask = 35 | this.teamService.RemoveTeamByIdAsync(invalidTeamId); 36 | 37 | TeamValidationException actualTeamValidationException = 38 | await Assert.ThrowsAsync( 39 | removeTeamByIdTask.AsTask); 40 | 41 | // then 42 | actualTeamValidationException.Should().BeEquivalentTo(expectedTeamValidationException); 43 | 44 | this.loggingBrokerMock.Verify(broker => 45 | broker.LogError(It.Is(SameExceptionAs( 46 | expectedTeamValidationException))), Times.Once); 47 | 48 | this.storageBrokerMock.Verify(broker => 49 | broker.DeleteTeamAsync(It.IsAny()), Times.Never); 50 | 51 | this.loggingBrokerMock.VerifyNoOtherCalls(); 52 | this.storageBrokerMock.VerifyNoOtherCalls(); 53 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 54 | } 55 | 56 | [Fact] 57 | public async Task ShouldThrowNotFoundExceptionOnRemoveIfTeamIsNotFoundAndLogItAsync() 58 | { 59 | // given 60 | Guid randomTeamId = Guid.NewGuid(); 61 | Guid inputTeamId = randomTeamId; 62 | Team noTeam = null; 63 | 64 | var notFoundTeamException = 65 | new NotFoundTeamException(inputTeamId); 66 | 67 | var expectedTeamValidationException = 68 | new TeamValidationException(notFoundTeamException); 69 | 70 | this.storageBrokerMock.Setup(broker => 71 | broker.SelectTeamByIdAsync(It.IsAny())).ReturnsAsync(noTeam); 72 | 73 | // when 74 | ValueTask removeTeamByIdTask = 75 | this.teamService.RemoveTeamByIdAsync(inputTeamId); 76 | 77 | TeamValidationException actualTeamValidationException = 78 | await Assert.ThrowsAsync( 79 | removeTeamByIdTask.AsTask); 80 | 81 | // then 82 | actualTeamValidationException.Should().BeEquivalentTo(expectedTeamValidationException); 83 | 84 | this.storageBrokerMock.Verify(broker => 85 | broker.SelectTeamByIdAsync(It.IsAny()), Times.Once); 86 | 87 | this.loggingBrokerMock.Verify(broker => 88 | broker.LogError(It.Is(SameExceptionAs( 89 | expectedTeamValidationException))), Times.Once); 90 | 91 | this.storageBrokerMock.Verify(broker => 92 | broker.DeleteTeamAsync(It.IsAny()), Times.Never); 93 | 94 | this.storageBrokerMock.VerifyNoOtherCalls(); 95 | this.loggingBrokerMock.VerifyNoOtherCalls(); 96 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /Tarteeb.Api.Tests.Unit/Services/Foundations/Teams/TeamServiceTests.Exceptions.RetrieveById.cs: -------------------------------------------------------------------------------- 1 | //================================= 2 | // Copyright (c) Coalition of Good-Hearted Engineers 3 | // Free to use to bring order in your workplace 4 | //================================= 5 | 6 | using System; 7 | using System.Threading.Tasks; 8 | using FluentAssertions; 9 | using Microsoft.Data.SqlClient; 10 | using Moq; 11 | using Tarteeb.Api.Models.Foundations.Teams; 12 | using Tarteeb.Api.Models.Foundations.Teams.Exceptions; 13 | using Xunit; 14 | 15 | namespace Tarteeb.Api.Tests.Unit.Services.Foundations.Teams 16 | { 17 | public partial class TeamServiceTests 18 | { 19 | [Fact] 20 | public async Task ShouldThrowCriticalDependencyExceptionOnRetrieveByIdIfSqlErrorOccursAndLogItAsync() 21 | { 22 | // given 23 | Guid someId = Guid.NewGuid(); 24 | SqlException sqlException = CreateSqlException(); 25 | 26 | var failedTeamStorageException = 27 | new FailedTeamStorageException(sqlException); 28 | 29 | var expectedTeamDependencyException = 30 | new TeamDependencyException(failedTeamStorageException); 31 | 32 | this.storageBrokerMock.Setup(broker => 33 | broker.SelectTeamByIdAsync(It.IsAny())) 34 | .ThrowsAsync(sqlException); 35 | 36 | // when 37 | ValueTask retrieveTeamByIdTask = 38 | this.teamService.RetrieveTeamByIdAsync(someId); 39 | 40 | TeamDependencyException actaulTeamDependencyException = 41 | await Assert.ThrowsAsync( 42 | retrieveTeamByIdTask.AsTask); 43 | 44 | // then 45 | actaulTeamDependencyException.Should().BeEquivalentTo( 46 | expectedTeamDependencyException); 47 | 48 | this.storageBrokerMock.Verify(broker => 49 | broker.SelectTeamByIdAsync(It.IsAny()), Times.Once); 50 | 51 | this.loggingBrokerMock.Verify(broker => 52 | broker.LogCritical(It.Is(SameExceptionAs( 53 | expectedTeamDependencyException))), Times.Once); 54 | 55 | this.storageBrokerMock.VerifyNoOtherCalls(); 56 | this.loggingBrokerMock.VerifyNoOtherCalls(); 57 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 58 | } 59 | 60 | [Fact] 61 | public async Task ShouldThrowServiceExceptionOnRetrieveByIdAsyncIfServiceErrorOccursAndLogItAsync() 62 | { 63 | // given 64 | Guid someId = Guid.NewGuid(); 65 | var serviceException = new Exception(); 66 | 67 | var failedTeamServiceException = 68 | new FailedTeamServiceException(serviceException); 69 | 70 | var expectedTeamServiceExcpetion = 71 | new TeamServiceException(failedTeamServiceException); 72 | 73 | this.storageBrokerMock.Setup(broker => 74 | broker.SelectTeamByIdAsync(It.IsAny())).ThrowsAsync(serviceException); 75 | 76 | // when 77 | ValueTask retrieveTeamById = 78 | this.teamService.RetrieveTeamByIdAsync(someId); 79 | 80 | TeamServiceException actualTeamServiceException = 81 | await Assert.ThrowsAsync(retrieveTeamById.AsTask); 82 | 83 | // then 84 | actualTeamServiceException.Should().BeEquivalentTo(expectedTeamServiceExcpetion); 85 | 86 | this.storageBrokerMock.Verify(broker => 87 | broker.SelectTeamByIdAsync(It.IsAny()), Times.Once); 88 | 89 | this.loggingBrokerMock.Verify(broker => 90 | broker.LogError(It.Is(SameExceptionAs( 91 | expectedTeamServiceExcpetion))), Times.Once); 92 | 93 | this.storageBrokerMock.VerifyNoOtherCalls(); 94 | this.loggingBrokerMock.VerifyNoOtherCalls(); 95 | this.dateTimeBrokerMock.VerifyNoOtherCalls(); 96 | } 97 | } 98 | } --------------------------------------------------------------------------------