Skip to content

Commit

Permalink
Merge branch 'develop' into pr/jra/prod-release
Browse files Browse the repository at this point in the history
  • Loading branch information
marfavi authored Nov 2, 2023
2 parents d3db543 + daed7ca commit 4a15139
Show file tree
Hide file tree
Showing 7 changed files with 328 additions and 10 deletions.
4 changes: 4 additions & 0 deletions coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using CoffeeCard.Models.DataTransferObjects.v2.Product;
using CoffeeCard.Models.Entities;

namespace CoffeeCard.Library.Services.v2
Expand All @@ -10,5 +11,8 @@ public interface IProductService : IDisposable
Task<IEnumerable<Product>> GetPublicProductsAsync();
Task<IEnumerable<Product>> GetProductsForUserAsync(User user);
Task<Product> GetProductAsync(int productId);
Task<ChangedProductResponse> AddProduct(AddProductRequest product);

Task<ChangedProductResponse> UpdateProduct(UpdateProductRequest product);
}
}
78 changes: 78 additions & 0 deletions coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using CoffeeCard.Common.Errors;
using CoffeeCard.Library.Persistence;
using CoffeeCard.Models.DataTransferObjects.v2.Product;
using CoffeeCard.Models.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -53,6 +55,82 @@ public async Task<Product> GetProductAsync(int productId)
return product;
}

private async Task<bool> CheckProductUniquenessAsync(string name, int price)
{
var product = await _context.Products
.FirstOrDefaultAsync(p => (p.Name == name && p.Price == price));

return product == null;
}

public async Task<ChangedProductResponse> AddProduct(AddProductRequest newProduct)
{
var unique = await CheckProductUniquenessAsync(newProduct.Name, newProduct.Price);
if (!unique)
{
throw new ConflictException($"Product already exists with name {newProduct.Name} and price of {newProduct.Price}");
}

var product = new Product()
{
Price = newProduct.Price,
Description = newProduct.Description,
Name = newProduct.Name,
NumberOfTickets = newProduct.NumberOfTickets,
ExperienceWorth = 0,
Visible = newProduct.Visible
};

_context.Products.Add(product);
await _context.SaveChangesAsync();

var productUserGroups = newProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup
{
ProductId = product.Id,
UserGroup = userGroup
}).ToList();

_context.ProductUserGroups.AddRange(productUserGroups);


await _context.SaveChangesAsync();


var result = new ChangedProductResponse
{
Price = product.Price,
Description = product.Description,
Name = product.Name,
NumberOfTickets = product.NumberOfTickets,
Visible = product.Visible
};

return result;
}

public async Task<ChangedProductResponse> UpdateProduct(UpdateProductRequest changedProduct)
{
var product = await GetProductAsync(changedProduct.Id);
product.Price = changedProduct.Price;
product.Description = changedProduct.Description;
product.NumberOfTickets = changedProduct.NumberOfTickets;
product.Name = changedProduct.Name;
product.Visible = changedProduct.Visible;

await _context.SaveChangesAsync();

var result = new ChangedProductResponse
{
Price = product.Price,
Description = product.Description,
Name = product.Name,
NumberOfTickets = product.NumberOfTickets,
Visible = product.Visible
};

return result;
}

public void Dispose()
{
_context?.Dispose();
Expand Down
2 changes: 1 addition & 1 deletion coffeecard/CoffeeCard.Library/Utils/ProductExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static bool IsPerk(this Product product)
throw new ArgumentNullException($"{nameof(Product.ProductUserGroup)} must not be null");
}

return product.ProductUserGroup.Any(pug => pug.UserGroup != UserGroup.Customer);
return product.ProductUserGroup.All(pug => pug.UserGroup != UserGroup.Customer);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using CoffeeCard.Models.Entities;

namespace CoffeeCard.Models.DataTransferObjects.v2.Product
{
/// <summary>
/// Initiate a new product add request.
/// </summary>
/// <example>
/// {
/// "Name": "Latte",
/// "Price": 25,
/// "NumberOfTickets": 10,
/// "Description": "xxx",
/// "Visible": true
/// }
/// </example>
public class AddProductRequest
{
/// <summary>
/// Gets or sets the price of the product.
/// </summary>
/// <value>Product Price</value>
/// <example> 10 </example>
[Required]
[Range(0, int.MaxValue, ErrorMessage = "Price must be a non-negative integer.")]
public int Price { get; set; }

/// <summary>
/// Gets or sets the number of tickets associated with the product.
/// </summary>
/// <value> Number of tickets associated with a product </value>
/// <example> 5 </example>
[Required]
[Range(0, int.MaxValue, ErrorMessage = "Number of Tickets must be a non-negative integer.")]
public int NumberOfTickets { get; set; }

/// <summary>
/// Gets or sets the name of the product.
/// </summary>
/// <value> Product Name </value>
/// <example> Latte </example>
[Required]
[MinLength(1, ErrorMessage = "Name cannot be an empty string.")]
public string Name { get; set; }

/// <summary>
/// Gets or sets the description of the product.
/// </summary>
/// <value> Product Description </value>
/// <example> A homemade latte with soy milk </example>
[Required]
[MinLength(1, ErrorMessage = "Description cannot be an empty string.")]
public string Description { get; set; }

/// <summary>
/// Gets or sets the visibility of the product. Default is true.
/// </summary>
/// <value> Product Visibility </value>
/// <example> true </example>
[DefaultValue(true)]
public bool Visible { get; set; } = true;

[Required]
public IEnumerable<UserGroup> AllowedUserGroups { get; set; }

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace CoffeeCard.Models.DataTransferObjects.v2.Product
{
/// <summary>
/// Represents the product response.
/// </summary>
public class ChangedProductResponse
{
/// <summary>
/// Gets or sets the price of the product.
/// </summary>
/// <example>
/// {
/// "Price": 150,
/// "NumberOfTickets": 10,
/// "Name": "Espresso",
/// "Description": "A coffee made by forcing steam through ground coffee beans.",
/// "Visible": false
/// }
/// </example>
[Required]
[Range(0, int.MaxValue, ErrorMessage = "Price must be a non-negative integer.")]
public int Price { get; set; }

/// <summary>
/// Gets or sets the number of tickets associated with the product.
/// </summary>
/// <value> Number of Tickets of a Product </value>
/// <example> 5 </example>
[Required]
[Range(0, int.MaxValue, ErrorMessage = "Number of tickets must be a non-negative integer.")]
public int NumberOfTickets { get; set; }

/// <summary>
/// Gets or sets the name of the product.
/// </summary>
/// <value> Product Name </value>
/// <example> Espresso </example>
[Required]
[MinLength(1, ErrorMessage = "Name cannot be an empty string.")]
public string Name { get; set; }

/// <summary>
/// Gets or sets the description of the product.
/// </summary>
/// <value> Product Description </value>
/// <example> A homemade espresso from fresh beans </example>
[Required]
[MinLength(1, ErrorMessage = "Description cannot be an empty string.")]
public string Description { get; set; }

/// <summary>
/// Gets or sets the visibility of the product.
/// </summary>
/// <value> Product Visibility </value>
/// <example> true </example>
[Required]
public bool Visible { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace CoffeeCard.Models.DataTransferObjects.v2.Product
{
/// /// <summary>
/// Initiate an update product request.
/// </summary>
/// <example>
/// {
/// "Id": 1,
/// "Price": 150,
/// "NumberOfTickets": 10,
/// "Name": "Espresso",
/// "Description": "A coffee made by forcing steam through ground coffee beans.",
/// "Visible": false
/// }
/// </example>
public class UpdateProductRequest
{
/// <summary>
/// Gets or sets the ID of the product to update.
/// </summary>
/// <value> Product Id </value>
/// <example> 1 </example>
[Required]
public int Id { get; set; }

/// <summary>
/// Gets or sets the updated price of the product.
/// </summary>
/// <value> Product Price </value>
/// <example> 10 </example>
[Required]
[Range(0, int.MaxValue, ErrorMessage = "Price must be a non-negative integer.")]
public int Price { get; set; }

/// <summary>
/// Gets or sets the updated number of tickets associated with the product.
/// </summary>
/// <value> Number of Tickets of a Product </value>
/// <example> 5 </example>
[Required]
[Range(0, int.MaxValue, ErrorMessage = "Number of Tickets must be a non-negative integer.")]
public int NumberOfTickets { get; set; }

/// <summary>
/// Gets or sets the updated name of the product.
/// </summary>
/// <value> Product Name </value>
/// <example> Espresso </example>
[Required]
[MinLength(1, ErrorMessage = "Name cannot be an empty string.")]
public string Name { get; set; }

/// <summary>
/// Gets or sets the updated description of the product.
/// </summary>
/// <value> Product Description </value>
/// <example> A homemade espresso from fresh beans </example>
[Required]
[MinLength(1, ErrorMessage = "Description cannot be an empty string.")]
public string Description { get; set; }

/// <summary>
/// Gets or sets the updated visibility of the product. Default is true.
/// </summary>
/// <value> Product Visibility </value>
/// <example> true </example>
[DefaultValue(true)]
public bool Visible { get; set; } = true;
}
}
Loading

0 comments on commit 4a15139

Please sign in to comment.