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

create endpoints to add, update, and deactivate a product #215

Merged
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()
TTA777 marked this conversation as resolved.
Show resolved Hide resolved
{
Price = newProduct.Price,
Description = newProduct.Description,
Name = newProduct.Name,
NumberOfTickets = newProduct.NumberOfTickets,
ExperienceWorth = 0,
TTA777 marked this conversation as resolved.
Show resolved Hide resolved
Visible = newProduct.Visible
};

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

var productUserGroups = newProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup
{
ProductId = product.Id,
TTA777 marked this conversation as resolved.
Show resolved Hide resolved
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
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
TTA777 marked this conversation as resolved.
Show resolved Hide resolved
{
/// <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; }
TTA777 marked this conversation as resolved.
Show resolved Hide resolved

/// <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; }
TTA777 marked this conversation as resolved.
Show resolved Hide resolved

/// <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
TTA777 marked this conversation as resolved.
Show resolved Hide resolved
{
/// <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
Loading