Merge branch '2-add-reviews' into 'master'
Resolve "Add Reviews" Closes #2 and #3 See merge request !4
This commit is contained in:
commit
ee4696d737
|
@ -4,6 +4,8 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.JoinTable;
|
import javax.persistence.JoinTable;
|
||||||
|
@ -17,6 +19,7 @@ import javax.persistence.Table;
|
||||||
public class Category {
|
public class Category {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private int id;
|
private int id;
|
||||||
private int ordinal;
|
private int ordinal;
|
||||||
@OneToMany(mappedBy = "category")
|
@OneToMany(mappedBy = "category")
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
package de.mstock.monolith.domain;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import de.mstock.monolith.web.CategoryDTO;
|
||||||
|
import de.mstock.monolith.web.ProductDTO;
|
||||||
|
import de.mstock.monolith.web.ReviewDTO;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class DataTransferObjectFactory {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Data Transfer Object (DTO).
|
||||||
|
*
|
||||||
|
* @param category database entity
|
||||||
|
* @param locale the requested locale
|
||||||
|
* @return DTO
|
||||||
|
*/
|
||||||
|
public CategoryDTO createCategoryDTO(Category category, Locale locale) {
|
||||||
|
CategoryI18n i18n = category.getI18n().get(locale.getLanguage());
|
||||||
|
CategoryDTO categoryDTO = new CategoryDTO();
|
||||||
|
categoryDTO.setName(i18n.getName());
|
||||||
|
categoryDTO.setPrettyUrlFragment(i18n.getPrettyUrlFragment());
|
||||||
|
return categoryDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Data Transfer Object (DTO).
|
||||||
|
*
|
||||||
|
* @param product database entity
|
||||||
|
* @param locale the requested locale
|
||||||
|
* @return DTO
|
||||||
|
*/
|
||||||
|
public ProductDTO createProductDTO(Product product, Locale locale) {
|
||||||
|
return createProductDTO(product, locale, NumberFormat.getCurrencyInstance(locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProductDTO createProductDTO(Product product, Locale locale, NumberFormat numberFormat) {
|
||||||
|
ProductDTO productDTO = createProductWithoutReviewsDTO(product, locale, numberFormat);
|
||||||
|
ProductI18n i18n = product.getI18n().get(locale.getLanguage());
|
||||||
|
productDTO.setReviews(createReviewDTOs(i18n.getReviews()));
|
||||||
|
return productDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates Data Transfer Objects (DTOs).
|
||||||
|
*
|
||||||
|
* @param products database entities
|
||||||
|
* @param locale the requested locale
|
||||||
|
* @return DTOs
|
||||||
|
*/
|
||||||
|
public List<ProductDTO> createProductDTOs(List<Product> products, Locale locale) {
|
||||||
|
List<ProductDTO> productDTOs = new ArrayList<>(products.size());
|
||||||
|
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
|
||||||
|
for (Product product : products) {
|
||||||
|
productDTOs.add(createProductDTO(product, locale, numberFormat));
|
||||||
|
}
|
||||||
|
return productDTOs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates Data Transfer Objects (DTOs) without loading their reviews.
|
||||||
|
*
|
||||||
|
* @param products database entities
|
||||||
|
* @param locale the requested locale
|
||||||
|
* @return DTOs
|
||||||
|
*/
|
||||||
|
public List<ProductDTO> createProductWithoutReviewsDTOs(List<Product> products, Locale locale) {
|
||||||
|
List<ProductDTO> productDTOs = new ArrayList<>(products.size());
|
||||||
|
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
|
||||||
|
for (Product product : products) {
|
||||||
|
productDTOs.add(createProductWithoutReviewsDTO(product, locale, numberFormat));
|
||||||
|
}
|
||||||
|
return productDTOs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProductDTO createProductWithoutReviewsDTO(Product product, Locale locale,
|
||||||
|
NumberFormat numberFormat) {
|
||||||
|
ProductI18n i18n = product.getI18n().get(locale.getLanguage());
|
||||||
|
String price = numberFormat.format(i18n.getPrice());
|
||||||
|
ProductDTO productDTO = new ProductDTO();
|
||||||
|
productDTO.setItemNumber(product.getItemNumber());
|
||||||
|
productDTO.setUnit(product.getUnit());
|
||||||
|
productDTO.setName(i18n.getName());
|
||||||
|
productDTO.setPrettyUrlFragment(i18n.getPrettyUrlFragment());
|
||||||
|
productDTO.setPrice(price);
|
||||||
|
productDTO.setDescription(i18n.getDescription());
|
||||||
|
return productDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Data Transfer Object (DTO).
|
||||||
|
*
|
||||||
|
* @param review database entity
|
||||||
|
* @return DTO
|
||||||
|
*/
|
||||||
|
public ReviewDTO createReviewDTO(Review review) {
|
||||||
|
ReviewDTO dto = new ReviewDTO();
|
||||||
|
dto.setLanguage(review.getLocaleLanguage());
|
||||||
|
dto.setRatingStars(review.getRatingStars());
|
||||||
|
dto.setFirstName(review.getFirstName());
|
||||||
|
dto.setLastName(review.getLastName());
|
||||||
|
dto.setText(review.getText());
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ReviewDTO> createReviewDTOs(List<Review> reviews) {
|
||||||
|
List<ReviewDTO> ratingDTOs = new ArrayList<>(reviews.size());
|
||||||
|
for (Review review : reviews) {
|
||||||
|
ratingDTOs.add(createReviewDTO(review));
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(ratingDTOs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ import java.util.Map;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.EnumType;
|
import javax.persistence.EnumType;
|
||||||
import javax.persistence.Enumerated;
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.MapKey;
|
import javax.persistence.MapKey;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
@ -15,6 +17,7 @@ import javax.persistence.Table;
|
||||||
public class Product {
|
public class Product {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private int id;
|
private int id;
|
||||||
private String itemNumber;
|
private String itemNumber;
|
||||||
@Enumerated(EnumType.ORDINAL)
|
@Enumerated(EnumType.ORDINAL)
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package de.mstock.monolith.domain;
|
package de.mstock.monolith.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.persistence.EmbeddedId;
|
import javax.persistence.EmbeddedId;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.JoinColumns;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.MapsId;
|
import javax.persistence.MapsId;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -21,6 +25,9 @@ public class ProductI18n {
|
||||||
private String prettyUrlFragment;
|
private String prettyUrlFragment;
|
||||||
private BigDecimal price;
|
private BigDecimal price;
|
||||||
private String description;
|
private String description;
|
||||||
|
@OneToMany
|
||||||
|
@JoinColumns({@JoinColumn(name = "localeLanguage"), @JoinColumn(name = "productId")})
|
||||||
|
private List<Review> reviews;
|
||||||
|
|
||||||
public ProductI18nPk getProductI18nPk() {
|
public ProductI18nPk getProductI18nPk() {
|
||||||
return productI18nPk;
|
return productI18nPk;
|
||||||
|
@ -61,4 +68,12 @@ public class ProductI18n {
|
||||||
public void setDescription(String description) {
|
public void setDescription(String description) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Review> getReviews() {
|
||||||
|
return reviews;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReviews(List<Review> reviews) {
|
||||||
|
this.reviews = reviews;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import org.springframework.data.repository.Repository;
|
||||||
|
|
||||||
public interface ProductRepository extends Repository<Product, Integer> {
|
public interface ProductRepository extends Repository<Product, Integer> {
|
||||||
|
|
||||||
@Query("select p from Product p join fetch p.i18n i "
|
@Query("select p from Product p join fetch p.i18n i left join fetch i.reviews r "
|
||||||
+ "where key(i) = ?1 and lower(i.name) = ?2")
|
+ "where key(i) = ?1 and lower(i.name) = ?2")
|
||||||
Product findByI18nName(String language, String name);
|
Product findByI18nName(String language, String name);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
package de.mstock.monolith.domain;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "reviews")
|
||||||
|
public class Review {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private int id;
|
||||||
|
private int productId;
|
||||||
|
private String localeLanguage;
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
private int ratingStars;
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProductId() {
|
||||||
|
return productId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProductId(int productId) {
|
||||||
|
this.productId = productId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocaleLanguage() {
|
||||||
|
return localeLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocaleLanguage(String localeLanguage) {
|
||||||
|
this.localeLanguage = localeLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRatingStars() {
|
||||||
|
return ratingStars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRatingStars(int ratingStars) {
|
||||||
|
this.ratingStars = ratingStars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package de.mstock.monolith.domain;
|
||||||
|
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
public interface ReviewRepository extends CrudRepository<Review, Integer> {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package de.mstock.monolith.service;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import de.mstock.monolith.domain.DataTransferObjectFactory;
|
||||||
|
import de.mstock.monolith.domain.Product;
|
||||||
|
import de.mstock.monolith.domain.ProductRepository;
|
||||||
|
import de.mstock.monolith.domain.Review;
|
||||||
|
import de.mstock.monolith.domain.ReviewRepository;
|
||||||
|
import de.mstock.monolith.web.ReviewDTO;
|
||||||
|
import de.mstock.monolith.web.form.ReviewForm;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ReviewService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ReviewRepository reviewRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProductRepository productRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DataTransferObjectFactory dtoFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a review from a posted form.
|
||||||
|
*
|
||||||
|
* @param reviewForm Post data
|
||||||
|
* @param locale Language context
|
||||||
|
* @param prettyUrlFragment Used to get the product context
|
||||||
|
* @return DTO
|
||||||
|
*/
|
||||||
|
public ReviewDTO saveReview(ReviewForm reviewForm, Locale locale, String prettyUrlFragment) {
|
||||||
|
Product product = productRepository.findByI18nName(locale.getLanguage(), prettyUrlFragment);
|
||||||
|
Review review = new Review();
|
||||||
|
review.setProductId(product.getId());
|
||||||
|
review.setLocaleLanguage(locale.getLanguage());
|
||||||
|
review.setFirstName(reviewForm.getFirstName());
|
||||||
|
review.setLastName(reviewForm.getLastName());
|
||||||
|
review.setRatingStars(reviewForm.getRatingStars());
|
||||||
|
review.setText(reviewForm.getText());
|
||||||
|
return dtoFactory.createReviewDTO(reviewRepository.save(review));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package de.mstock.monolith.service;
|
package de.mstock.monolith.service;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -10,10 +9,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import de.mstock.monolith.domain.Category;
|
import de.mstock.monolith.domain.Category;
|
||||||
import de.mstock.monolith.domain.CategoryI18n;
|
|
||||||
import de.mstock.monolith.domain.CategoryRepository;
|
import de.mstock.monolith.domain.CategoryRepository;
|
||||||
|
import de.mstock.monolith.domain.DataTransferObjectFactory;
|
||||||
import de.mstock.monolith.domain.Product;
|
import de.mstock.monolith.domain.Product;
|
||||||
import de.mstock.monolith.domain.ProductI18n;
|
|
||||||
import de.mstock.monolith.domain.ProductRepository;
|
import de.mstock.monolith.domain.ProductRepository;
|
||||||
import de.mstock.monolith.web.CategoryDTO;
|
import de.mstock.monolith.web.CategoryDTO;
|
||||||
import de.mstock.monolith.web.ProductDTO;
|
import de.mstock.monolith.web.ProductDTO;
|
||||||
|
@ -27,6 +25,9 @@ public class ShopService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ProductRepository productRepository;
|
private ProductRepository productRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DataTransferObjectFactory dtoFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all categories of the current language.
|
* Gets all categories of the current language.
|
||||||
*
|
*
|
||||||
|
@ -36,8 +37,7 @@ public class ShopService {
|
||||||
String language = locale.getLanguage();
|
String language = locale.getLanguage();
|
||||||
List<CategoryDTO> categories = new ArrayList<>();
|
List<CategoryDTO> categories = new ArrayList<>();
|
||||||
for (Category category : categoryRepository.findAllOrdered(language)) {
|
for (Category category : categoryRepository.findAllOrdered(language)) {
|
||||||
CategoryI18n i18n = category.getI18n().get(language);
|
categories.add(dtoFactory.createCategoryDTO(category, locale));
|
||||||
categories.add(new CategoryDTO(i18n.getName(), i18n.getPrettyUrlFragment()));
|
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(categories);
|
return Collections.unmodifiableList(categories);
|
||||||
}
|
}
|
||||||
|
@ -53,33 +53,22 @@ public class ShopService {
|
||||||
if (category == null) {
|
if (category == null) {
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
List<ProductDTO> products = new ArrayList<>();
|
List<ProductDTO> products =
|
||||||
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale);
|
dtoFactory.createProductWithoutReviewsDTOs(category.getProducts(), locale);
|
||||||
for (Product product : category.getProducts()) {
|
|
||||||
ProductI18n i18n = product.getI18n().get(language);
|
|
||||||
String price = currencyFormat.format(i18n.getPrice());
|
|
||||||
products.add(new ProductDTO(product.getItemNumber(), product.getUnit(), i18n.getName(),
|
|
||||||
i18n.getPrettyUrlFragment(), price, i18n.getDescription()));
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(products);
|
return Collections.unmodifiableList(products);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a products in the current language.
|
* Gets a product in the current language.
|
||||||
*
|
*
|
||||||
* @return A simplified Data Transfer Object.
|
* @return A simplified Data Transfer Object.
|
||||||
*/
|
*/
|
||||||
public ProductDTO getProduct(Locale locale, String prettyUrlFragment) {
|
public ProductDTO getProduct(Locale locale, String prettyUrlFragment) {
|
||||||
String language = locale.getLanguage();
|
Product product = productRepository.findByI18nName(locale.getLanguage(), prettyUrlFragment);
|
||||||
Product product = productRepository.findByI18nName(language, prettyUrlFragment);
|
|
||||||
if (product == null) {
|
if (product == null) {
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
ProductI18n i18n = product.getI18n().get(language);
|
return dtoFactory.createProductDTO(product, locale);
|
||||||
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale);
|
|
||||||
String price = currencyFormat.format(i18n.getPrice());
|
|
||||||
return new ProductDTO(product.getItemNumber(), product.getUnit(), i18n.getName(),
|
|
||||||
i18n.getPrettyUrlFragment(), price, i18n.getDescription());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,23 @@ package de.mstock.monolith.web;
|
||||||
|
|
||||||
public class CategoryDTO {
|
public class CategoryDTO {
|
||||||
|
|
||||||
private final String name;
|
private String name;
|
||||||
private final String prettyUrlFragment;
|
private String prettyUrlFragment;
|
||||||
|
|
||||||
public CategoryDTO(String name, String prettyUrlFragment) {
|
|
||||||
this.name = name;
|
|
||||||
this.prettyUrlFragment = prettyUrlFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
public String getPrettyUrlFragment() {
|
public String getPrettyUrlFragment() {
|
||||||
return prettyUrlFragment;
|
return prettyUrlFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPrettyUrlFragment(String prettyUrlFragment) {
|
||||||
|
this.prettyUrlFragment = prettyUrlFragment;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,19 @@ package de.mstock.monolith.web;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|
||||||
|
import de.mstock.monolith.service.ReviewService;
|
||||||
import de.mstock.monolith.service.ShopService;
|
import de.mstock.monolith.service.ShopService;
|
||||||
|
import de.mstock.monolith.web.form.ReviewForm;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
public class ProductController {
|
public class ProductController {
|
||||||
|
@ -19,6 +24,9 @@ public class ProductController {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ShopService shopService;
|
private ShopService shopService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ReviewService reviewService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Product page
|
* Product page
|
||||||
*
|
*
|
||||||
|
@ -28,7 +36,32 @@ public class ProductController {
|
||||||
* @return The template's name.
|
* @return The template's name.
|
||||||
*/
|
*/
|
||||||
@RequestMapping(value = "/products/{prettyUrlFragment:[\\w-]+}", method = RequestMethod.GET)
|
@RequestMapping(value = "/products/{prettyUrlFragment:[\\w-]+}", method = RequestMethod.GET)
|
||||||
public String homepage(@PathVariable String prettyUrlFragment, Model model, Locale locale) {
|
public String product(@PathVariable String prettyUrlFragment, Model model, Locale locale) {
|
||||||
|
model.addAttribute("categories", shopService.getCategories(locale));
|
||||||
|
model.addAttribute("product", shopService.getProduct(locale, prettyUrlFragment));
|
||||||
|
return TEMPLATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post a review
|
||||||
|
*
|
||||||
|
* @param reviewForm Form data
|
||||||
|
* @param bindingResult Form binding result after validation
|
||||||
|
* @param prettyUrlFragment Product context
|
||||||
|
* @param model Template model
|
||||||
|
* @param locale Language context
|
||||||
|
* @return The template's name.
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/products/{prettyUrlFragment:[\\w-]+}", method = RequestMethod.POST)
|
||||||
|
public String post(@Valid ReviewForm reviewForm, BindingResult bindingResult,
|
||||||
|
@PathVariable String prettyUrlFragment, Model model, Locale locale) {
|
||||||
|
if (bindingResult.hasErrors()) {
|
||||||
|
model.addAttribute("success", false);
|
||||||
|
} else {
|
||||||
|
model.addAttribute("success", true);
|
||||||
|
model.addAttribute("reviewPost",
|
||||||
|
reviewService.saveReview(reviewForm, locale, prettyUrlFragment));
|
||||||
|
}
|
||||||
model.addAttribute("categories", shopService.getCategories(locale));
|
model.addAttribute("categories", shopService.getCategories(locale));
|
||||||
model.addAttribute("product", shopService.getProduct(locale, prettyUrlFragment));
|
model.addAttribute("product", shopService.getProduct(locale, prettyUrlFragment));
|
||||||
return TEMPLATE;
|
return TEMPLATE;
|
||||||
|
|
|
@ -1,57 +1,73 @@
|
||||||
package de.mstock.monolith.web;
|
package de.mstock.monolith.web;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import de.mstock.monolith.domain.ProductWeightUnit;
|
import de.mstock.monolith.domain.ProductWeightUnit;
|
||||||
|
|
||||||
public class ProductDTO {
|
public class ProductDTO {
|
||||||
|
|
||||||
private final String itemNumber;
|
private String itemNumber;
|
||||||
private final ProductWeightUnit unit;
|
private ProductWeightUnit unit;
|
||||||
private final String name;
|
private String name;
|
||||||
private final String prettyUrlFragment;
|
private String prettyUrlFragment;
|
||||||
private final String price;
|
private String price;
|
||||||
private final String description;
|
private String description;
|
||||||
|
private List<ReviewDTO> reviews;
|
||||||
/**
|
|
||||||
* A simplified representation of a product.
|
|
||||||
*
|
|
||||||
* @param itemNumber A product's unique item number.
|
|
||||||
* @param unit The unit of a product that relates to its price.
|
|
||||||
* @param name The localized name.
|
|
||||||
* @param price The product's price per unit.
|
|
||||||
* @param description The description of the product.
|
|
||||||
*/
|
|
||||||
public ProductDTO(String itemNumber, ProductWeightUnit unit, String name,
|
|
||||||
String prettyUrlFragment, String price, String description) {
|
|
||||||
this.itemNumber = itemNumber;
|
|
||||||
this.unit = unit;
|
|
||||||
this.name = name;
|
|
||||||
this.prettyUrlFragment = prettyUrlFragment;
|
|
||||||
this.price = price;
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getItemNumber() {
|
public String getItemNumber() {
|
||||||
return itemNumber;
|
return itemNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setItemNumber(String itemNumber) {
|
||||||
|
this.itemNumber = itemNumber;
|
||||||
|
}
|
||||||
|
|
||||||
public ProductWeightUnit getUnit() {
|
public ProductWeightUnit getUnit() {
|
||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUnit(ProductWeightUnit unit) {
|
||||||
|
this.unit = unit;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPrice() {
|
public void setName(String name) {
|
||||||
return price;
|
this.name = name;
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPrettyUrlFragment() {
|
public String getPrettyUrlFragment() {
|
||||||
return prettyUrlFragment;
|
return prettyUrlFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPrettyUrlFragment(String prettyUrlFragment) {
|
||||||
|
this.prettyUrlFragment = prettyUrlFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrice(String price) {
|
||||||
|
this.price = price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ReviewDTO> getReviews() {
|
||||||
|
return reviews;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReviews(List<ReviewDTO> reviews) {
|
||||||
|
this.reviews = reviews;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package de.mstock.monolith.web;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
public class ReviewDTO {
|
||||||
|
|
||||||
|
private Locale locale;
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
private int ratingStars;
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
public String getLanguage() {
|
||||||
|
return locale.getLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLanguage(String language) {
|
||||||
|
this.locale = new Locale(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Presentation layer can use this method to access the full name of the reviewer.
|
||||||
|
*
|
||||||
|
* @return concatenated name
|
||||||
|
*/
|
||||||
|
public String getDisplayName() {
|
||||||
|
if (StringUtils.isNoneBlank(firstName, lastName)) {
|
||||||
|
return firstName + " " + lastName.charAt(0) + ".";
|
||||||
|
}
|
||||||
|
return StringUtils.defaultString(firstName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRatingStars() {
|
||||||
|
return ratingStars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRatingStars(int ratingStars) {
|
||||||
|
this.ratingStars = ratingStars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package de.mstock.monolith.web.form;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Max;
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
public class ReviewForm {
|
||||||
|
|
||||||
|
@Size(max = 80)
|
||||||
|
private String firstName;
|
||||||
|
@Size(max = 80)
|
||||||
|
private String lastName;
|
||||||
|
@Min(1)
|
||||||
|
@Max(5)
|
||||||
|
@NotNull
|
||||||
|
private int ratingStars;
|
||||||
|
@Size(max = 1000)
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRatingStars() {
|
||||||
|
return ratingStars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRatingStars(int ratingStars) {
|
||||||
|
this.ratingStars = ratingStars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
alter table categories_i18n
|
||||||
|
add constraint categories_i18n_category_id_fkey foreign key (category_id)
|
||||||
|
references categories
|
||||||
|
on delete cascade;
|
||||||
|
|
||||||
|
alter table products_i18n
|
||||||
|
add constraint products_i18n_product_id_fkey foreign key (product_id)
|
||||||
|
references products
|
||||||
|
on delete cascade;
|
|
@ -0,0 +1,18 @@
|
||||||
|
create table reviews (
|
||||||
|
id serial primary key,
|
||||||
|
product_id integer,
|
||||||
|
locale_language varchar(2) not null,
|
||||||
|
first_name varchar(80),
|
||||||
|
last_name varchar(80),
|
||||||
|
rating_stars integer not null check (rating_stars between 1 and 5),
|
||||||
|
text varchar(1000),
|
||||||
|
foreign key (product_id, locale_language) references products_i18n(product_id, locale_language)
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into reviews (id, product_id, locale_language, first_name, last_name, rating_stars, text) values
|
||||||
|
(1, 1, 'en', 'John', 'Doe', 5, 'Absolutely perfect!'),
|
||||||
|
(2, 1, 'de', 'Max', 'Mustermann', 3, null),
|
||||||
|
(3, 1, 'de', 'Erika', 'Mustermann', 4, 'Ich liebe dieses Produkt! Leider ist ab und zu auch eine matschige Kiwi dabei.'),
|
||||||
|
(4, 4, 'de', 'Otto', 'Normalverbraucher', 4, 'Genau das was ich gesucht habe!'),
|
||||||
|
(5, 5, 'de', null, null, 1, 'Schmeckt nicht!'),
|
||||||
|
(6, 5, 'en', 'John', null, 2, 'What''s the country of origin of these tomatoes?');
|
|
@ -0,0 +1,3 @@
|
||||||
|
alter sequence categories_id_seq restart with 100;
|
||||||
|
alter sequence products_id_seq restart with 100;
|
||||||
|
alter sequence reviews_id_seq restart with 100;
|
|
@ -15,3 +15,6 @@ features.headline2 = Das ist gut.
|
||||||
features.subheadline2 = Sieh' selbst.
|
features.subheadline2 = Sieh' selbst.
|
||||||
features.headline3 = Und zum Schluss, das hier.
|
features.headline3 = Und zum Schluss, das hier.
|
||||||
features.subheadline3 = Bingo.
|
features.subheadline3 = Bingo.
|
||||||
|
reviews.title = Rezensionen
|
||||||
|
validation.review.success = Danke für deine Rezension!
|
||||||
|
validation.error = Es ist ein Fehler aufgetreten!
|
||||||
|
|
|
@ -15,3 +15,6 @@ features.headline2 = Oh yeah, it's that good.
|
||||||
features.subheadline2 = See for yourself.
|
features.subheadline2 = See for yourself.
|
||||||
features.headline3 = And lastly, this one.
|
features.headline3 = And lastly, this one.
|
||||||
features.subheadline3 = Checkmate.
|
features.subheadline3 = Checkmate.
|
||||||
|
reviews.title = Reviews
|
||||||
|
validation.review.success = Thanks for your review!
|
||||||
|
validation.error = An error occured!
|
||||||
|
|
|
@ -11,15 +11,66 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row reviews" th:fragment="reviews">
|
<div class="row reviews" th:fragment="reviews">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<h2 th:inline="text">Reviews</h2>
|
<h2 th:text="#{reviews.title}">Reviews</h2>
|
||||||
<div class="review">
|
<div th:if="${success != null and success}" class="alert alert-success" role="alert">
|
||||||
|
<p th:text="#{validation.review.success}">Thanks for your review!</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="rating"></span> Great product!
|
<span class="rating" th:switch="${reviewPost.ratingStars}">
|
||||||
|
<th:block th:case="1">★</th:block>
|
||||||
|
<th:block th:case="2">★★</th:block>
|
||||||
|
<th:block th:case="3">★★★</th:block>
|
||||||
|
<th:block th:case="4">★★★★</th:block>
|
||||||
|
<th:block th:case="5">★★★★★</th:block>
|
||||||
|
</span> <span th:text="${reviewPost.displayName}">John D.</span>
|
||||||
</p>
|
</p>
|
||||||
<p>John D.</p>
|
<p th:text="${reviewPost.text}">Lorem ipsum</p>
|
||||||
<p>Lorem ipsum dolor sit amet, consetetur sadipscing
|
</div>
|
||||||
elitr, sed diam nonumy eirmod tempor invidunt ut labore et
|
<div th:if="${success != null and !success}" th:text="#{validation.error}" class="alert alert-danger" role="alert">An error occured!</div>
|
||||||
dolore magna aliquyam.</p>
|
<form th:if="${success == null}" class="form-horizontal" action="#" th:action="@{/products/__${product.prettyUrlFragment}__.html}" th:object="${reviewPost}" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="firstName" class="col-sm-2 control-label">First Name</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="firstName" name="firstName" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="lastName" class="col-sm-2 control-label">Last Name</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" id="lastName" name="lastName" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-2 control-label">Rating</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="radio" id="ratingStars1" name="ratingStars" value="1" checked="checked" /> <label for="ratingStars1">★</label>
|
||||||
|
<input type="radio" id="ratingStars2" name="ratingStars" value="2" /> <label for="ratingStars2">★★</label>
|
||||||
|
<input type="radio" id="ratingStars3" name="ratingStars" value="3" /> <label for="ratingStars3">★★★</label>
|
||||||
|
<input type="radio" id="ratingStars4" name="ratingStars" value="4" /> <label for="ratingStars4">★★★★</label>
|
||||||
|
<input type="radio" id="ratingStars5" name="ratingStars" value="5" /> <label for="ratingStars5">★★★★★</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="text" class="col-sm-2 control-label">Text</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<textarea class="form-control" id="text" name="text"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-default">Submit</button>
|
||||||
|
</form>
|
||||||
|
<hr />
|
||||||
|
<div class="review" th:each="review : ${product.reviews}">
|
||||||
|
<p>
|
||||||
|
<span class="rating" th:switch="${review.ratingStars}">
|
||||||
|
<th:block th:case="1">★</th:block>
|
||||||
|
<th:block th:case="2">★★</th:block>
|
||||||
|
<th:block th:case="3">★★★</th:block>
|
||||||
|
<th:block th:case="4">★★★★</th:block>
|
||||||
|
<th:block th:case="5">★★★★★</th:block>
|
||||||
|
</span> <span th:text="${review.displayName}">John D.</span>
|
||||||
|
</p>
|
||||||
|
<p th:text="${review.text}">Lorem ipsum dolor sit amet,
|
||||||
|
consetetur sadipscing elitr, sed diam nonumy eirmod tempor
|
||||||
|
invidunt ut labore et dolore magna aliquyam.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
package de.mstock.monolith.domain;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.anyString;
|
||||||
|
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import de.mstock.monolith.web.ProductDTO;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class DataTransferObjectFactoryTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private DataTransferObjectFactory dtoFactory;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCreateProductDTOWithoutReviews() {
|
||||||
|
Product product = mock(Product.class, RETURNS_DEEP_STUBS);
|
||||||
|
ProductI18n productI18n = mock(ProductI18n.class);
|
||||||
|
when(product.getI18n().get(any())).thenReturn(productI18n);
|
||||||
|
when(productI18n.getPrice()).thenReturn(BigDecimal.valueOf(1.23));
|
||||||
|
verify(productI18n, never()).getReviews();
|
||||||
|
List<ProductDTO> productDTOs =
|
||||||
|
dtoFactory.createProductWithoutReviewsDTOs(Arrays.asList(product), new Locale("de"));
|
||||||
|
assertThat("The review list is not present", productDTOs.get(0).getReviews(), is(nullValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCreateProductDTOWithReviews() {
|
||||||
|
Product product = mock(Product.class, RETURNS_DEEP_STUBS);
|
||||||
|
Review review = mock(Review.class);
|
||||||
|
when(review.getLocaleLanguage()).thenReturn("de");
|
||||||
|
when(product.getI18n().get(any()).getPrice()).thenReturn(BigDecimal.valueOf(1.23));
|
||||||
|
when(product.getI18n().get(any()).getReviews()).thenReturn(Arrays.asList(review));
|
||||||
|
List<ProductDTO> productDTOs =
|
||||||
|
dtoFactory.createProductDTOs(Arrays.asList(product), new Locale("de"));
|
||||||
|
assertThat("The review list is filled", productDTOs.get(0).getReviews(), is(not(empty())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCreateProductDTOWithEmptyReviews() {
|
||||||
|
Product product = mock(Product.class, RETURNS_DEEP_STUBS);
|
||||||
|
when(product.getI18n().get(any()).getPrice()).thenReturn(BigDecimal.valueOf(1.23));
|
||||||
|
when(product.getI18n().get(any()).getReviews()).thenReturn(Collections.emptyList());
|
||||||
|
List<ProductDTO> productDTOs =
|
||||||
|
dtoFactory.createProductDTOs(Arrays.asList(product), new Locale("de"));
|
||||||
|
assertThat("The review list is present, but empty", productDTOs.get(0).getReviews(),
|
||||||
|
is(empty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldFormatPrice() {
|
||||||
|
Locale locale = new Locale("en", "US");
|
||||||
|
Product product = mock(Product.class, RETURNS_DEEP_STUBS);
|
||||||
|
when(product.getI18n().get(anyString()).getPrice()).thenReturn(BigDecimal.valueOf(1.47));
|
||||||
|
ProductDTO productDTO = dtoFactory.createProductDTO(product, locale);
|
||||||
|
assertThat("Product has a formatted price", productDTO.getPrice(), is(equalTo("$1.47")));
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,13 +3,12 @@ package de.mstock.monolith.service;
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.mockito.Matchers.anyString;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -22,10 +21,9 @@ import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
import de.mstock.monolith.domain.Category;
|
import de.mstock.monolith.domain.Category;
|
||||||
import de.mstock.monolith.domain.CategoryRepository;
|
import de.mstock.monolith.domain.CategoryRepository;
|
||||||
import de.mstock.monolith.domain.Product;
|
import de.mstock.monolith.domain.DataTransferObjectFactory;
|
||||||
import de.mstock.monolith.domain.ProductRepository;
|
import de.mstock.monolith.domain.ProductRepository;
|
||||||
import de.mstock.monolith.web.CategoryDTO;
|
import de.mstock.monolith.web.CategoryDTO;
|
||||||
import de.mstock.monolith.web.ProductDTO;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class ShopServiceTest {
|
public class ShopServiceTest {
|
||||||
|
@ -36,6 +34,9 @@ public class ShopServiceTest {
|
||||||
@Mock
|
@Mock
|
||||||
private ProductRepository productRepository;
|
private ProductRepository productRepository;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private DataTransferObjectFactory dataTransferObjectFactory;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private ShopService shopService;
|
private ShopService shopService;
|
||||||
|
|
||||||
|
@ -45,20 +46,11 @@ public class ShopServiceTest {
|
||||||
Category category = mock(Category.class, RETURNS_DEEP_STUBS);
|
Category category = mock(Category.class, RETURNS_DEEP_STUBS);
|
||||||
List<Category> categoryEntities = Arrays.asList(category, category, category);
|
List<Category> categoryEntities = Arrays.asList(category, category, category);
|
||||||
when(categoryRepository.findAllOrdered(eq(locale.getLanguage()))).thenReturn(categoryEntities);
|
when(categoryRepository.findAllOrdered(eq(locale.getLanguage()))).thenReturn(categoryEntities);
|
||||||
|
when(dataTransferObjectFactory.createCategoryDTO(any(), any()))
|
||||||
|
.thenReturn(mock(CategoryDTO.class));
|
||||||
List<CategoryDTO> categories = shopService.getCategories(locale);
|
List<CategoryDTO> categories = shopService.getCategories(locale);
|
||||||
assertThat("Same amount of categories", categories.size(),
|
assertThat("Same amount of categories", categories.size(),
|
||||||
is(equalTo(categoryEntities.size())));
|
is(equalTo(categoryEntities.size())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldFormatPrice() {
|
|
||||||
Locale locale = new Locale("en", "US");
|
|
||||||
Product product = mock(Product.class, RETURNS_DEEP_STUBS);
|
|
||||||
when(product.getI18n().get(anyString()).getPrice()).thenReturn(BigDecimal.valueOf(1.47));
|
|
||||||
when(productRepository.findByI18nName(eq(locale.getLanguage()), anyString()))
|
|
||||||
.thenReturn(product);
|
|
||||||
ProductDTO productDTO = shopService.getProduct(locale, "foo");
|
|
||||||
assertThat("Product has a formatted price", productDTO.getPrice(), is(equalTo("$1.47")));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue