diff --git a/coffeecard/CoffeeCard.Library/Services/v2/IPurchaseService.cs b/coffeecard/CoffeeCard.Library/Services/v2/IPurchaseService.cs index bf708f23..acbaead3 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/IPurchaseService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/IPurchaseService.cs @@ -33,6 +33,13 @@ public interface IPurchaseService : IDisposable /// Purchase details Task> GetPurchases(User user); + /// + /// Get all purchases for a user by its Id + /// + /// id of user to fetch purchases from + /// Purchase details for all users purchases + Task> GetPurchases(int userId); + /// /// Handle MobilePay webhook invocation and update purchase accordingly /// diff --git a/coffeecard/CoffeeCard.Library/Services/v2/PurchaseService.cs b/coffeecard/CoffeeCard.Library/Services/v2/PurchaseService.cs index ec80d081..57722787 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/PurchaseService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/PurchaseService.cs @@ -170,6 +170,21 @@ public async Task GetPurchase(int purchaseId, User user) }; } + public async Task> GetPurchases(int userId) + { + var user = await _context.Users + .Where(u => u.Id == userId) + .FirstOrDefaultAsync(); + if (user == null) + { + Log.Error("No user was found by User Id: {Id}", userId); + throw new EntityNotFoundException($"No user was found by User Id: {userId}"); + } + + return await GetPurchases(user); + } + + public async Task> GetPurchases(User user) { return await _context.Purchases diff --git a/coffeecard/CoffeeCard.Tests.Unit/Services/v2/PurchaseServiceTests.cs b/coffeecard/CoffeeCard.Tests.Unit/Services/v2/PurchaseServiceTests.cs index a4fa934d..4071ec94 100644 --- a/coffeecard/CoffeeCard.Tests.Unit/Services/v2/PurchaseServiceTests.cs +++ b/coffeecard/CoffeeCard.Tests.Unit/Services/v2/PurchaseServiceTests.cs @@ -464,6 +464,133 @@ public async Task RefundPurchaseThrowsExceptionWhenAlreadyRefunded(Product produ await Assert.ThrowsAsync(() => purchaseService.RefundPurchase(product.Id)); } + [Theory(DisplayName = "GetPurchases returns all purchases for a user")] + [MemberData(nameof(ProductGenerator))] + public async Task GetPurchasesReturnsAllPurchasesForAUser(Product product) + { + // Arrange + var builder = new DbContextOptionsBuilder() + .UseInMemoryDatabase(nameof(GetPurchasesReturnsAllPurchasesForAUser) + + product.Name); + + var databaseSettings = new DatabaseSettings + { + SchemaName = "test" + }; + var environmentSettings = new EnvironmentSettings() + { + EnvironmentType = EnvironmentType.Test + }; + + await using var context = new CoffeeCardContext(builder.Options, databaseSettings, environmentSettings); + var user = new User + { + Id = 1, + Name = "User1", + Email = "email@email.test", + Password = "password", + Salt = "salt", + DateCreated = new DateTime(year: 2020, month: 11, day: 11), + IsVerified = true, + PrivacyActivated = false, + UserGroup = UserGroup.Board, + UserState = UserState.Active + }; + + var purchase = new Purchase + { + Id = 1, + ProductId = product.Id, + ProductName = product.Name, + Price = product.Price, + NumberOfTickets = product.NumberOfTickets, + ExternalTransactionId = Guid.NewGuid().ToString(), + PurchasedBy = user, + OrderId = "test", + Status = PurchaseStatus.Refunded + }; + context.Add(user); + context.Add(product); + context.Add(purchase); + await context.SaveChangesAsync(); + + var mobilePayService = new Mock(); + var mailService = new Mock(); + var productService = new ProductService(context); + var ticketService = new TicketService(context, new Mock().Object); + var purchaseService = new PurchaseService(context, mobilePayService.Object, ticketService, + mailService.Object, productService); + + // act + var result = await purchaseService.GetPurchases(user); + + // Assert + Assert.Single(result); + } + + [Theory(DisplayName = "GetPurchases given a faulty id throws exception")] + [MemberData(nameof(ProductGenerator))] + public async Task GetPurchasesGivenAFaultyIdThrowsNotFoundException(Product product) + { + // Arrange + var builder = new DbContextOptionsBuilder() + .UseInMemoryDatabase(nameof(GetPurchasesGivenAFaultyIdThrowsNotFoundException) + + product.Name); + + var databaseSettings = new DatabaseSettings + { + SchemaName = "test" + }; + var environmentSettings = new EnvironmentSettings() + { + EnvironmentType = EnvironmentType.Test + }; + + await using var context = new CoffeeCardContext(builder.Options, databaseSettings, environmentSettings); + var user = new User + { + Id = 1, + Name = "User1", + Email = "email@email.test", + Password = "password", + Salt = "salt", + DateCreated = new DateTime(year: 2020, month: 11, day: 11), + IsVerified = true, + PrivacyActivated = false, + UserGroup = UserGroup.Board, + UserState = UserState.Active + }; + + var purchase = new Purchase + { + Id = 1, + ProductId = product.Id, + ProductName = product.Name, + Price = product.Price, + NumberOfTickets = product.NumberOfTickets, + ExternalTransactionId = Guid.NewGuid().ToString(), + PurchasedBy = user, + OrderId = "test", + Status = PurchaseStatus.Refunded + }; + context.Add(user); + context.Add(product); + context.Add(purchase); + await context.SaveChangesAsync(); + + var mobilePayService = new Mock(); + var mailService = new Mock(); + var productService = new ProductService(context); + var ticketService = new TicketService(context, new Mock().Object); + var purchaseService = new PurchaseService(context, mobilePayService.Object, ticketService, + mailService.Object, productService); + + // Act & Assert + await Assert.ThrowsAsync(() => purchaseService.GetPurchases(10)); + } + + + public static IEnumerable ProductGenerator() { var pug = new List { new ProductUserGroup { ProductId = 1 } }; diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/PurchasesController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/PurchasesController.cs index a6569da8..9c928549 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/PurchasesController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/PurchasesController.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using CoffeeCard.Common.Errors; using CoffeeCard.Library.Services.v2; using CoffeeCard.Library.Utils; using CoffeeCard.MobilePay.Generated.Api.PaymentsApi; @@ -52,6 +53,28 @@ public async Task>> GetAllPurch return Ok(purchases); } + /// + /// Get all purchases for a user + /// + /// User Id + /// List of purchases + /// Purchases + /// Invalid credentials + /// User not allowed to view purchases for given user + /// User with userId not found + [HttpGet("user/{userId}")] + [AuthorizeRoles(UserGroup.Board)] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] + [ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)] + public async Task>> GetAllPurchasesForUser([FromRoute] int userId) + { + var purchases = await _purchaseService.GetPurchases(userId); + + return Ok(purchases); + } + /// /// Get purchase ///