/*
* Copyright 2011, MyCellar
*
* This file is part of MyCellar.
*
* MyCellar is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* MyCellar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MyCellar. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.mycellar.interfaces.web.services.stock;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import jpasearch.repository.query.ResultParameters;
import jpasearch.repository.query.SearchParameters;
import jpasearch.repository.query.builder.ResultBuilder;
import jpasearch.repository.query.builder.SearchBuilder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import fr.mycellar.domain.shared.NamedEntity_;
import fr.mycellar.domain.shared.exception.BusinessException;
import fr.mycellar.domain.stock.Arrival;
import fr.mycellar.domain.stock.Bottle_;
import fr.mycellar.domain.stock.Cellar;
import fr.mycellar.domain.stock.CellarShare;
import fr.mycellar.domain.stock.CellarShare_;
import fr.mycellar.domain.stock.Cellar_;
import fr.mycellar.domain.stock.Drink;
import fr.mycellar.domain.stock.DrinkBottle;
import fr.mycellar.domain.stock.Movement;
import fr.mycellar.domain.stock.Movement_;
import fr.mycellar.domain.stock.Stock;
import fr.mycellar.domain.stock.Stock_;
import fr.mycellar.domain.user.User;
import fr.mycellar.domain.wine.Appellation_;
import fr.mycellar.domain.wine.Wine;
import fr.mycellar.domain.wine.Wine_;
import fr.mycellar.interfaces.facades.stock.StockServiceFacade;
import fr.mycellar.interfaces.web.security.CurrentUserService;
import fr.mycellar.interfaces.web.services.FilterCouple;
import fr.mycellar.interfaces.web.services.ListWithCount;
import fr.mycellar.interfaces.web.services.OrderCouple;
import fr.mycellar.interfaces.web.services.SearchParametersUtil;
/**
* @author speralta
*/
@Named
@Singleton
@Path("/stock")
public class StockWebService {
private StockServiceFacade stockServiceFacade;
private CurrentUserService currentUserService;
private SearchParametersUtil searchParametersUtil;
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("cellars")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public ListWithCount<Cellar> getCellarsForCurrentUser( //
@QueryParam("first") int first, //
@QueryParam("count") @DefaultValue("10") int count, //
@QueryParam("filters") List<FilterCouple> filters, //
@QueryParam("sort") List<OrderCouple> orders, //
@QueryParam("like") String term) {
User user = currentUserService.getCurrentUser();
SearchParameters<Cellar> searchParameters = searchParametersUtil.getSearchBuilder(first, count, filters, orders, Cellar.class) //
.disjunction() //
.on(Cellar_.owner).equalsTo(user) //
.on(Cellar_.shares).to(CellarShare_.email).equalsTo(user.getEmail()) //
.and().build();
List<Cellar> cellars;
if (count == 0) {
cellars = new ArrayList<>();
} else {
cellars = stockServiceFacade.getCellarsLike(term, searchParameters);
}
return new ListWithCount<>(stockServiceFacade.countCellarsLike(term, searchParameters), cellars);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("cellars/{id}")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public Cellar getCellarById(@PathParam("id") int cellarId) {
User user = currentUserService.getCurrentUser();
if (!stockServiceFacade.hasReadRight(cellarId, user.getEmail())) {
throw new AccessDeniedException("Cannot access to this cellar.");
}
return stockServiceFacade.getCellarById(cellarId);
}
@DELETE
@Path("cellars/{id}")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public void deleteCellarById(@PathParam("id") int cellarId) throws BusinessException {
User user = currentUserService.getCurrentUser();
if (!stockServiceFacade.isOwner(cellarId, user.getEmail())) {
throw new AccessDeniedException("Cannot delete this cellar.");
}
stockServiceFacade.deleteCellar(stockServiceFacade.getCellarById(cellarId));
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("cellars/{id}")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public Cellar saveCellar(@PathParam("id") int id, Cellar cellar) throws BusinessException {
User user = currentUserService.getCurrentUser();
if (!stockServiceFacade.isOwner(id, user.getEmail())) {
throw new AccessDeniedException("Cannot modify this cellar.");
}
if ((id == cellar.getId()) && (stockServiceFacade.getCellarById(id) != null)) {
return stockServiceFacade.saveCellar(cellar);
}
throw new RuntimeException();
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("cellars")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public Cellar saveCellar(Cellar cellar) throws BusinessException {
User user = currentUserService.getCurrentUser();
if (cellar.getId() == null) {
cellar.setOwner(user);
return stockServiceFacade.saveCellar(cellar);
}
throw new RuntimeException();
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("validateCellar")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public void validateCellar(Cellar cellar) throws BusinessException {
if (cellar.getOwner() != null) {
User user = currentUserService.getCurrentUser();
if (!user.equals(cellar.getOwner())) {
throw new AccessDeniedException("Cannot modify this cellar.");
}
}
stockServiceFacade.validateCellar(cellar);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("movements")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public ListWithCount<Movement> getMovements(@QueryParam("cellarId") Integer cellarId, @QueryParam("first") int first, @QueryParam("count") int count,
@QueryParam("filters") List<FilterCouple> filters, @QueryParam("sort") List<OrderCouple> orders) {
if ((cellarId != null) && !stockServiceFacade.hasReadRight(cellarId, currentUserService.getCurrentUserEmail())) {
throw new AccessDeniedException("No read access to this cellar.");
}
SearchBuilder<Movement> searchBuilder = searchParametersUtil.getSearchBuilder(first, count, filters, orders, Movement.class);
if (cellarId != null) {
searchBuilder.distinct().on(Movement_.cellar).to(Cellar_.id).equalsTo(cellarId);
} else {
User user = currentUserService.getCurrentUser();
searchBuilder.distinct().disjunction() //
.on(Movement_.cellar).to(Cellar_.owner).equalsTo(user) //
.on(Movement_.cellar).to(Cellar_.shares).to(CellarShare_.email).equalsTo(user.getEmail());
}
SearchParameters<Movement> searchParameters = searchBuilder.build();
List<Movement> movements;
if (count == 0) {
movements = new ArrayList<>();
} else {
movements = stockServiceFacade.getMovements(searchParameters);
}
return new ListWithCount<>(stockServiceFacade.countMovements(searchParameters), movements);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("wines")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public ListWithCount<Wine> getWinesFromStocksForCellarLike( //
@QueryParam("cellarId") Integer cellarId, //
@QueryParam("input") String input, //
@QueryParam("first") int first, //
@QueryParam("count") int count, //
@QueryParam("filters") List<FilterCouple> filters, //
@QueryParam("sort") List<OrderCouple> orders) {
if ((cellarId != null) && !stockServiceFacade.hasReadRight(cellarId, currentUserService.getCurrentUserEmail())) {
throw new AccessDeniedException("No read access to this cellar.");
}
SearchBuilder<Stock> searchBuilder = searchParametersUtil.getSearchBuilder(first, count, filters, orders, Stock.class);
if (cellarId != null) {
searchBuilder.distinct().on(Stock_.cellar).to(Cellar_.id).equalsTo(cellarId);
} else {
User user = currentUserService.getCurrentUser();
searchBuilder.distinct().disjunction() //
.on(Stock_.cellar).to(Cellar_.owner).equalsTo(user) //
.on(Stock_.cellar).to(Cellar_.shares).to(CellarShare_.email).equalsTo(user.getEmail());
}
if (StringUtils.isNotBlank(input)) {
searchBuilder.fullText(Stock_.bottle).to(Bottle_.wine).to(Wine_.appellation).to(Appellation_.region).to(NamedEntity_.name) //
.andOn(Stock_.bottle).to(Bottle_.wine).to(Wine_.appellation).to(NamedEntity_.name) //
.andOn(Stock_.bottle).to(Bottle_.wine).to(Wine_.producer).to(NamedEntity_.name) //
.andOn(Stock_.bottle).to(Bottle_.wine).to(NamedEntity_.name) //
.andOn(Stock_.bottle).to(Bottle_.wine).to(Wine_.vintage) //
.andOn(Stock_.bottle).to(Bottle_.format).to(NamedEntity_.name) //
.andMode().search(input);
}
SearchParameters<Stock> searchParameters = searchBuilder.build();
ResultParameters<Stock, Wine> resultParameters = new ResultBuilder<>(Stock_.bottle).to(Bottle_.wine).build();
List<Wine> wines;
if (count == 0) {
wines = new ArrayList<>();
} else {
wines = stockServiceFacade.getFromStocks(searchParameters, resultParameters);
}
return new ListWithCount<>(stockServiceFacade.countFromStocks(searchParameters, resultParameters), wines);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("stocks")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public ListWithCount<Stock> getStocksForWine(@QueryParam("wineId") Integer wineId, @QueryParam("first") int first, @QueryParam("count") int count,
@QueryParam("filters") List<FilterCouple> filters, @QueryParam("sort") List<OrderCouple> orders) {
User user = currentUserService.getCurrentUser();
SearchParameters<Stock> searchParameters = searchParametersUtil.getSearchBuilder(first, count, filters, orders, Stock.class) //
.on(Stock_.bottle).to(Bottle_.wine).to(Wine_.id).equalsTo(wineId) //
.disjunction() //
.on(Stock_.cellar).to(Cellar_.owner).equalsTo(user) //
.on(Stock_.cellar).to(Cellar_.shares).to(CellarShare_.email).equalsTo(user.getEmail()) //
.and().build();
List<Stock> stocks;
if (count == 0) {
stocks = new ArrayList<>();
} else {
stocks = stockServiceFacade.getStocks(searchParameters);
}
return new ListWithCount<>(stockServiceFacade.countStocks(searchParameters), stocks);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("cellarShares")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public ListWithCount<CellarShare> getCellarSharesForCurrentUser( //
@QueryParam("first") int first, //
@QueryParam("count") @DefaultValue("10") int count, //
@QueryParam("filters") List<FilterCouple> filters, //
@QueryParam("sort") List<OrderCouple> orders, //
@QueryParam("like") String term) {
User user = currentUserService.getCurrentUser();
SearchParameters<CellarShare> searchParameters = searchParametersUtil.getSearchBuilder(first, count, filters, orders, CellarShare.class) //
.distinct() //
.disjunction() //
.on(CellarShare_.cellar).to(Cellar_.owner).equalsTo(user) //
.on(CellarShare_.cellar).to(Cellar_.shares).to(CellarShare_.email).equalsTo(user.getEmail()) //
.and().build();
List<CellarShare> cellarShares;
if (count == 0) {
cellarShares = new ArrayList<>();
} else {
cellarShares = stockServiceFacade.getCellarShares(searchParameters);
}
return new ListWithCount<>(stockServiceFacade.countCellarShares(searchParameters), cellarShares);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("cellarShares/{id}")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public CellarShare getCellarShareById(@PathParam("id") int cellarShareId) {
User user = currentUserService.getCurrentUser();
CellarShare cellarShare = stockServiceFacade.getCellarShareById(cellarShareId);
if ((cellarShare != null) && !user.equals(cellarShare.getCellar().getOwner())) {
throw new AccessDeniedException("No access to this cellar share.");
}
return cellarShare;
}
@DELETE
@Path("cellarShares/{id}")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public void deleteCellarShareById(@PathParam("id") int cellarShareId) throws BusinessException {
CellarShare cellarShare = getCellarShareById(cellarShareId);
stockServiceFacade.deleteCellarShare(cellarShare);
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("cellarShares/{id}")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public CellarShare saveCellarShare(@PathParam("id") int id, CellarShare cellarShare) throws BusinessException {
CellarShare inBase = getCellarShareById(id);
if ((id == cellarShare.getId()) && (inBase != null)) {
return stockServiceFacade.saveCellarShare(cellarShare);
}
throw new RuntimeException();
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("cellarShares")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public CellarShare saveCellarShare(CellarShare cellarShare) throws BusinessException {
User user = currentUserService.getCurrentUser();
if (!user.equals(cellarShare.getCellar().getOwner())) {
throw new AccessDeniedException("Cannot modify a cellar share which is not owned by user.");
}
if (cellarShare.getId() == null) {
return stockServiceFacade.saveCellarShare(cellarShare);
}
throw new RuntimeException();
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("validateCellarShare")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public void validateCellarShare(CellarShare cellarShare) throws BusinessException {
User user = currentUserService.getCurrentUser();
if (!user.equals(cellarShare.getCellar().getOwner())) {
throw new AccessDeniedException("Cannot modify a cellar share which is not owned by user.");
}
stockServiceFacade.validateCellarShare(cellarShare);
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("arrival")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public void arrival(Arrival arrival) throws BusinessException {
if (!stockServiceFacade.hasModifyRight(arrival.getCellar().getId(), currentUserService.getCurrentUserEmail())) {
throw new AccessDeniedException("Current user isn't the owner of the cellar.");
}
stockServiceFacade.arrival(arrival);
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("drink")
@PreAuthorize("hasRole('ROLE_CELLAR')")
public void drink(Drink drink) throws BusinessException {
for (DrinkBottle drinkBottle : drink.getDrinkBottles()) {
if (!stockServiceFacade.hasModifyRight(drinkBottle.getCellar().getId(), currentUserService.getCurrentUserEmail())) {
throw new AccessDeniedException("Current user isn't the owner of the cellar.");
}
}
stockServiceFacade.drink(drink);
}
// BEANS
@Inject
public void setStockServiceFacade(StockServiceFacade stockServiceFacade) {
this.stockServiceFacade = stockServiceFacade;
}
@Inject
public void setCurrentUserService(CurrentUserService currentUserService) {
this.currentUserService = currentUserService;
}
@Inject
public void setSearchParametersUtil(SearchParametersUtil searchParametersUtil) {
this.searchParametersUtil = searchParametersUtil;
}
}