Example #1
0
        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());
        }
Example #4
0
        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());
        }
Example #5
0
        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()));
        }
Example #6
0
        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)));
        }
Example #7
0
        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));
        }
Example #8
0
        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()));
        }
Example #10
0
        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)));
        }
Example #11
0
        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()));
        }
Example #12
0
        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()));
        }
Example #13
0
        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()));
        }
Example #14
0
        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());
        }
Example #15
0
        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)));
        }