public async Task <IActionResult> EditProduct([FromRoute(Name = "product_id")] Int32 productId, [FromBody] EditProductRequest request) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // locate product var foundProduct = await _bagsContext.Products .WithUnsafeIncludes() .Where(product => product.Id == productId) .SingleOrDefaultAsync(); if (foundProduct == null) { return(HttpResult.NotFound($"{productId}")); } // update the product foundProduct.Name = request.Name; if (request.Images != null) { foundProduct.ImagesJson = JsonConvert.SerializeObject(request.Images); } await _bagsContext.SaveChangesAsync(); return(HttpResult.Ok(foundProduct.ToUnsafeExpandedWireFormat(_amazon))); }
public async Task <IActionResult> CreateCategory([FromBody] CreateCategoryRequest request) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // check for existing category var foundTagCategory = await _bagsContext.TagCategories .Where(category => category.Name == request.Name) .SingleOrDefaultAsync(); if (foundTagCategory != null) { return(HttpResult.Ok(foundTagCategory.ToBaseWireFormat())); } // create a new tag var newTagCategory = new Models.TagCategory { Name = request.Name }; _bagsContext.TagCategories.Add(newTagCategory); await _bagsContext.SaveChangesAsync(); return(HttpResult.Ok(newTagCategory.ToBaseWireFormat())); }
public async Task <IActionResult> DeleteCategory([FromRoute(Name = "category_id")] Guid categoryId) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // locate category var foundCategory = await _bagsContext.TagCategories .Where(category => category.Id == categoryId) .SingleOrDefaultAsync(); if (foundCategory == null) { return(HttpResult.NoContent()); } // validate it doesn't have any tags if (foundCategory.Tags.Any()) { return(HttpResult.Conflict("Category must have no associated tags before it can be deleted.")); } // delete _bagsContext.TagCategories.Remove(foundCategory); await _bagsContext.SaveChangesAsync(); return(HttpResult.NoContent()); }
public async Task <IActionResult> DeleteTag([FromRoute(Name = "tag_id")] Int32 tagId) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // locate tag var foundTag = await _bagsContext.Tags .Include(tag => tag.Products) .Where(tag => tag.Id == tagId) .SingleOrDefaultAsync(); if (foundTag == null) { return(HttpResult.NoContent()); } // validate it doesn't have any products if (foundTag.Products.Any()) { return(HttpResult.Conflict("Tag must have no associated products before it can be deleted.")); } // delete _bagsContext.Tags.Remove(foundTag); await _bagsContext.SaveChangesAsync(); return(HttpResult.NoContent()); }
public async Task <IActionResult> EditTag([FromRoute(Name = "tag_id")] Int32 tagId, [FromBody] EditTagRequest request) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // locate tag var foundTag = await _bagsContext.Tags .WithSafeIncludes() .Where(tag => tag.Id == tagId) .SingleOrDefaultAsync(); if (foundTag == null) { return(HttpResult.NotFound()); } // verify there are changes if (foundTag.TagCategoryId == request.CategoryId && foundTag.Name == request.Name) { return(HttpResult.Ok(foundTag.ToSafeExpandedWireFormat())); } // locate the new category var newCategory = await _bagsContext.TagCategories .Where(category => category.Id == request.CategoryId) .SingleOrDefaultAsync(); if (newCategory == null) { return(HttpResult.BadRequest($"Category {request.CategoryId} does not exist.")); } // verify no conflict on unique constraint var duplicateTag = await _bagsContext.Tags .WithSafeIncludes() .Where(tag => tag.TagCategoryId == request.CategoryId && tag.Name == request.Name) .SingleOrDefaultAsync(); if (duplicateTag != null) { return(HttpResult.Conflict(duplicateTag.ToSafeExpandedWireFormat())); } // change the category/name foundTag.TagCategory = newCategory; foundTag.Name = request.Name; await _bagsContext.SaveChangesAsync(); return(HttpResult.Ok(foundTag.ToSafeExpandedWireFormat())); }
public async Task <IActionResult> AddTag([FromRoute(Name = "product_id")] Int32 productId, [FromBody] AddTagRequest request) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // validate product var foundProduct = await _bagsContext.Products .WithUnsafeIncludes() .Where(product => product.Id == productId) .SingleOrDefaultAsync(); if (foundProduct == null) { return(HttpResult.NotFound($"{productId}")); } // validate tags var foundTags = await _bagsContext.Tags .WithSafeIncludes() .Where(tag => request.TagIds.Contains(tag.Id)) .ToListAsync(); foreach (var expectedTagId in request.TagIds) { if (!foundTags.Any(foundTag => foundTag.Id == expectedTagId)) { return(HttpResult.NotFound($"{expectedTagId}")); } } // link tag and product var preexistingTags = foundProduct.Tags.Select(productTag => productTag.Tag).ToList(); var productTags = foundTags .Where(foundTag => !preexistingTags.Contains(foundTag)) .Select(foundTag => new Models.ProductTag { Product = foundProduct, Tag = foundTag }) .ToList(); _bagsContext.ProductTags.AddRange(productTags); await _bagsContext.SaveChangesAsync(); return(HttpResult.Ok(foundProduct.ToUnsafeExpandedWireFormat(_amazon))); }
public async Task <IActionResult> GetProductsByTags([FromQuery(Name = "tag_id")] IEnumerable <Int32> tagIds, [FromQuery(Name = "starting_product_id")] UInt16 startingId = 0, [FromQuery(Name = "products_per_page")] UInt16 itemsPerPage = 10, [FromQuery(Name = "min_price")] UInt32 minPrice = 0, [FromQuery(Name = "max_price")] UInt32 maxPrice = Int32.MaxValue) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } var tagIdsList = tagIds.ToList(); // FIXME: typecast necessary until https://github.com/aspnet/EntityFramework/issues/5663 is fixed Int32 startingId32 = startingId; Int32 minPriceSigned = (Int32)Math.Min(minPrice, Int32.MaxValue); Int32 maxPriceSigned = (Int32)Math.Min(maxPrice, Int32.MaxValue); UInt32 count = (UInt32)Math.Max(0, tagIdsList.Count); var query = $@" SELECT DISTINCT TOP({itemsPerPage}) products.Id as Id, products.Name as Name, products.Price as Price, products.ImagesJson as ImagesJson, products.Asin as Asin FROM Products products LEFT OUTER JOIN AmazonProducts amazonProducts ON products.Id = amazonProducts.ProductId"; if (count > 0) { query += @" INNER JOIN ( SELECT ProductId FROM ProductTags WHERE TagId IN (" + String.Join(", ", tagIds.Select((id, i) => $"@p{i}")) + $@") GROUP BY ProductId HAVING COUNT(*) = {count} ) AS tags ON tags.ProductId = products.Id" ; } query += $@" WHERE amazonProducts.Price BETWEEN {minPriceSigned} AND {maxPriceSigned} AND products.Id >= {startingId} AND amazonProducts.Available = 1"; // locate matching products var matchingProducts = _bagsContext.Products .WithUnsafeIncludes() .FromSql(query, tagIds.Select(id => id as Object).ToArray()) // FIXME: This should be `ToListAsync` or `AsAsyncEnumerable` https://github.com/aspnet/EntityFramework/issues/5640 .ToList() .Select(product => product.ToUnsafeExpandedWireFormat(_amazon)) .ToList(); return(HttpResult.Ok(matchingProducts)); }
public async Task <IActionResult> RemoveTag([FromRoute(Name = "product_id")] Int32 productId, [FromRoute(Name = "tag_id")] Int32 tagId) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // validate product var foundProduct = await _bagsContext.Products .Where(product => product.Id == productId) .SingleOrDefaultAsync(); if (foundProduct == null) { return(HttpResult.NotFound($"{productId}")); } // validate tag var foundTag = await _bagsContext.Tags .Where(tag => tag.Id == tagId) .SingleOrDefaultAsync(); if (foundTag == null) { return(HttpResult.NotFound($"{tagId}")); } // locate product tag var foundProductTag = await _bagsContext.ProductTags .Where(productTag => productTag.ProductId == foundProduct.Id && productTag.TagId == foundTag.Id) .SingleOrDefaultAsync(); if (foundProductTag == null) { return(HttpResult.NoContent()); } // delete _bagsContext.ProductTags.Remove(foundProductTag); await _bagsContext.SaveChangesAsync(); return(HttpResult.NoContent()); }
public async Task <IActionResult> GetCategory([FromRoute(Name = "category_id")] Guid categoryId) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // get category var foundTagCategory = await _bagsContext.TagCategories .WithUnsafeIncludes() .Where(category => category.Id == categoryId) .SingleOrDefaultAsync(); if (foundTagCategory == null) { return(HttpResult.NotFound()); } return(HttpResult.Ok(foundTagCategory.ToUnsafeExpandedWireFormat())); }
public async Task <IActionResult> GetProduct([FromRoute(Name = "product_id")] Int32 productId) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // locate product var foundProduct = await _bagsContext.Products .WithUnsafeIncludes() .Where(product => product.Id == productId) .SingleOrDefaultAsync(); if (foundProduct == null) { return(HttpResult.NotFound()); } return(HttpResult.Ok(foundProduct.ToUnsafeExpandedWireFormat(_amazon))); }
public async Task <IActionResult> GetTag([FromRoute(Name = "tag_id")] Int32 tagId) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // locate tag var foundTag = await _bagsContext.Tags .WithUnsafeIncludes() .Where(tag => tag.Id == tagId) .SingleOrDefaultAsync(); if (foundTag == null) { return(HttpResult.NotFound()); } return(HttpResult.Ok(foundTag.ToUnsafeExpandedWireFormat())); }
public async Task <IActionResult> EditCategory([FromRoute(Name = "category_id")] Guid categoryId, [FromBody] EditCategoryRequest request) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // locate category var foundCategory = await _bagsContext.TagCategories .Where(category => category.Id == categoryId) .SingleOrDefaultAsync(); if (foundCategory == null) { return(HttpResult.NotFound()); } // verify there are actual changes if (foundCategory.Name == request.Name) { return(HttpResult.Ok(foundCategory.ToBaseWireFormat())); } // verify no conflicts with unique constraint var duplicateCategory = await _bagsContext.TagCategories .Where(category => category.Name == request.Name) .SingleOrDefaultAsync(); if (duplicateCategory != null) { return(HttpResult.Conflict(duplicateCategory.ToBaseWireFormat())); } // change the name foundCategory.Name = request.Name; await _bagsContext.SaveChangesAsync(); return(HttpResult.Ok(foundCategory.ToBaseWireFormat())); }
public async Task <IActionResult> CreateTag([FromBody] CreateTagRequest request) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // verify category exists var category = await _bagsContext .TagCategories .Where(x => x.Id == request.CategoryId) .SingleOrDefaultAsync(); if (category == null) { return(HttpResult.BadRequest($"Category {request.CategoryId} does not exist.")); } // check for existing tag var existingTag = await _bagsContext.Tags .WithSafeIncludes() .Where(tag => tag.Name == request.Name && tag.TagCategoryId == request.CategoryId) .SingleOrDefaultAsync(); if (existingTag != null) { return(HttpResult.Ok(existingTag.ToSafeExpandedWireFormat())); } // create a new tag var newTag = new Models.Tag { Name = request.Name, TagCategory = category }; _bagsContext.Tags.Add(newTag); await _bagsContext.SaveChangesAsync(); return(HttpResult.Ok(newTag.ToSafeExpandedWireFormat())); }
public async Task <IActionResult> DeleteProduct([FromRoute(Name = "product_id")] Int32 productId) { // validate input if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // locate product var foundProduct = await _bagsContext.Products .Where(product => product.Id == productId) .SingleOrDefaultAsync(); if (foundProduct == null) { return(HttpResult.NoContent()); } // delete _bagsContext.Products.Remove(foundProduct); await _bagsContext.SaveChangesAsync(); return(HttpResult.NoContent()); }
public async Task <IActionResult> CreateProduct([FromBody] CreateProductFromAsinRequest request) { if (!ModelState.IsValid) { return(HttpResult.BadRequest(ModelState)); } // see if the product already exists var foundProduct = await _bagsContext.Products .WithUnsafeIncludes() .Where(product => product.AmazonProduct.Asin == request.Asin) .FirstOrDefaultAsync(); if (foundProduct != null) { return(HttpResult.Ok(foundProduct.ToUnsafeExpandedWireFormat(_amazon))); } var result = await _amazon.GetProductDetailsXml(request.Asin); var xElement = XElement.Parse(result); XNamespace ns = "http://webservices.amazon.com/AWSECommerceService/2011-08-01"; var item = xElement .Elements(ns + "Items") .Single() .Elements(ns + "Item") .Single(); var images = item .Elements(ns + "ImageSets") .Single() .Elements(ns + "ImageSet") .DistinctBy(imageSet => imageSet.Elements(ns + "LargeImage").Single().Elements(ns + "URL").Single().Value) .Select(imageSet => new Product.Image { Priority = (imageSet.Attribute("Category")?.Value == "primary") ? 10U : 100U, Small = _amazon.ConvertImageLinkToHttps(imageSet.Elements(ns + "SmallImage").Single().Elements(ns + "URL").Single().Value), Medium = _amazon.ConvertImageLinkToHttps(imageSet.Elements(ns + "MediumImage").Single().Elements(ns + "URL").Single().Value), Large = _amazon.ConvertImageLinkToHttps(imageSet.Elements(ns + "LargeImage").Single().Elements(ns + "URL").Single().Value) }) .OrderBy(image => image.Priority); var lowestNewPrice = Double.Parse(item .Elements(ns + "OfferSummary") .Single() .Elements(ns + "LowestNewPrice") .Single() .Elements(ns + "Amount") .Single() .Value) / 100; var title = item .Elements(ns + "ItemAttributes") .Single() .Elements(ns + "Title") .Single() .Value; // create product var newProduct = new Models.Product { Name = title, Price = Convert.ToInt64(lowestNewPrice), Asin = request.Asin, AmazonProduct = new AmazonProduct { Asin = request.Asin, Available = true, LastChecked = DateTimeOffset.UtcNow, Price = Convert.ToInt32(lowestNewPrice) }, ImagesJson = JsonConvert.SerializeObject(images) }; _bagsContext.Products.Add(newProduct); await _bagsContext.SaveChangesAsync(); return(HttpResult.Ok(newProduct.ToUnsafeExpandedWireFormat(_amazon))); }