Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/user purchases #288

Merged
merged 3 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions coffeecard/CoffeeCard.Library/Services/v2/IPurchaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ public interface IPurchaseService : IDisposable
/// <returns>Purchase details</returns>
Task<IEnumerable<SimplePurchaseResponse>> GetPurchases(User user);

/// <summary>
/// Get all purchases for a user by its Id
/// </summary>
/// <param name="userId">id of user to fetch purchases from</param>
/// <returns>Purchase details for all users purchases</returns>
Task<IEnumerable<SimplePurchaseResponse>> GetPurchases(int userId);

/// <summary>
/// Handle MobilePay webhook invocation and update purchase accordingly
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions coffeecard/CoffeeCard.Library/Services/v2/PurchaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,21 @@ public async Task<SinglePurchaseResponse> GetPurchase(int purchaseId, User user)
};
}

public async Task<IEnumerable<SimplePurchaseResponse>> 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);
fredpetersen marked this conversation as resolved.
Show resolved Hide resolved
throw new EntityNotFoundException($"No user was found by User Id: {userId}");
}

return await GetPurchases(user);
}


public async Task<IEnumerable<SimplePurchaseResponse>> GetPurchases(User user)
{
return await _context.Purchases
Expand Down
127 changes: 127 additions & 0 deletions coffeecard/CoffeeCard.Tests.Unit/Services/v2/PurchaseServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,133 @@ public async Task RefundPurchaseThrowsExceptionWhenAlreadyRefunded(Product produ
await Assert.ThrowsAsync<IllegalUserOperationException>(() => 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<CoffeeCardContext>()
.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<IMobilePayPaymentsService>();
var mailService = new Mock<Library.Services.IEmailService>();
var productService = new ProductService(context);
var ticketService = new TicketService(context, new Mock<IStatisticService>().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<CoffeeCardContext>()
.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<IMobilePayPaymentsService>();
var mailService = new Mock<Library.Services.IEmailService>();
var productService = new ProductService(context);
var ticketService = new TicketService(context, new Mock<IStatisticService>().Object);
var purchaseService = new PurchaseService(context, mobilePayService.Object, ticketService,
mailService.Object, productService);

// Act & Assert
await Assert.ThrowsAsync<EntityNotFoundException>(() => purchaseService.GetPurchases(10));
}



public static IEnumerable<object[]> ProductGenerator()
{
var pug = new List<ProductUserGroup> { new ProductUserGroup { ProductId = 1 } };
Expand Down
23 changes: 23 additions & 0 deletions coffeecard/CoffeeCard.WebApi/Controllers/v2/PurchasesController.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -52,6 +53,28 @@ public async Task<ActionResult<IEnumerable<SimplePurchaseResponse>>> GetAllPurch
return Ok(purchases);
}

/// <summary>
/// Get all purchases for a user
/// </summary>
/// <param name="userId">User Id</param>
/// <returns>List of purchases</returns>
/// <response code="200">Purchases</response>
/// <response code="401">Invalid credentials</response>
/// <response code="403">User not allowed to view purchases for given user</response>
/// <response code="404">User with userId not found</response>
[HttpGet("user/{userId}")]
[AuthorizeRoles(UserGroup.Board)]
[ProducesResponseType(typeof(List<SimplePurchaseResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)]
public async Task<ActionResult<IEnumerable<SimplePurchaseResponse>>> GetAllPurchasesForUser([FromRoute] int userId)
{
var purchases = await _purchaseService.GetPurchases(userId);

return Ok(purchases);
}

/// <summary>
/// Get purchase
/// </summary>
Expand Down
Loading