alfio.controller.api.admin.AdditionalServiceApiController.java Source code

Java tutorial

Introduction

Here is the source code for alfio.controller.api.admin.AdditionalServiceApiController.java

Source

/**
 * This file is part of alf.io.
 *
 * alf.io 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.
 *
 * alf.io 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 alf.io.  If not, see <http://www.gnu.org/licenses/>.
 */
package alfio.controller.api.admin;

import alfio.model.AdditionalService;
import alfio.model.Event;
import alfio.model.PriceContainer;
import alfio.model.modification.EventModification;
import alfio.model.result.ValidationResult;
import alfio.repository.AdditionalServiceRepository;
import alfio.repository.AdditionalServiceTextRepository;
import alfio.repository.EventRepository;
import alfio.util.MonetaryUtil;
import alfio.util.Validator;
import ch.digitalfondue.npjt.AffectedRowCountAndKey;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.Validate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.security.Principal;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static alfio.util.OptionalWrapper.optionally;

@RestController
@RequestMapping("/admin/api")
@Log4j2
public class AdditionalServiceApiController {

    private final EventRepository eventRepository;
    private final AdditionalServiceRepository additionalServiceRepository;
    private final AdditionalServiceTextRepository additionalServiceTextRepository;

    @Autowired
    public AdditionalServiceApiController(EventRepository eventRepository,
            AdditionalServiceRepository additionalServiceRepository,
            AdditionalServiceTextRepository additionalServiceTextRepository) {
        this.eventRepository = eventRepository;
        this.additionalServiceRepository = additionalServiceRepository;
        this.additionalServiceTextRepository = additionalServiceTextRepository;
    }

    @ExceptionHandler({ IllegalArgumentException.class })
    public ResponseEntity<String> handleBadRequest(Exception e) {
        log.warn("bad input detected", e);
        return new ResponseEntity<>("bad input parameters", HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler({ Exception.class })
    public ResponseEntity<String> handleError(Exception e) {
        log.error("error", e);
        return new ResponseEntity<>("internal server error", HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @RequestMapping(value = "/event/{eventId}/additional-services", method = RequestMethod.GET)
    public List<EventModification.AdditionalService> loadAll(@PathVariable("eventId") int eventId) {
        return eventRepository.findOptionalById(eventId).map(event -> additionalServiceRepository
                .loadAllForEvent(eventId).stream().map(as -> EventModification.AdditionalService.from(as)//.withAdditionalFields() TODO to be implemented
                        .withText(additionalServiceTextRepository.findAllByAdditionalServiceId(as.getId()))
                        .withZoneId(event.getZoneId()).withPriceContainer(buildPriceContainer(event, as)).build())
                .collect(Collectors.toList())).orElse(Collections.emptyList());
    }

    @RequestMapping(value = "/event/{eventId}/additional-services/{additionalServiceId}", method = RequestMethod.PUT)
    @Transactional
    public ResponseEntity<EventModification.AdditionalService> update(@PathVariable("eventId") int eventId,
            @PathVariable("additionalServiceId") int additionalServiceId,
            @RequestBody EventModification.AdditionalService additionalService, BindingResult bindingResult) {
        ValidationResult validationResult = Validator.validateAdditionalService(additionalService, bindingResult);
        Validate.isTrue(validationResult.isSuccess(), "validation failed");
        Validate.isTrue(additionalServiceId == additionalService.getId(), "wrong input");
        return eventRepository.findOptionalById(eventId).map(event -> {
            int result = additionalServiceRepository.update(additionalServiceId, additionalService.isFixPrice(),
                    additionalService.getOrdinal(), additionalService.getAvailableQuantity(),
                    additionalService.getMaxQtyPerOrder(),
                    additionalService.getInception().toZonedDateTime(event.getZoneId()),
                    additionalService.getExpiration().toZonedDateTime(event.getZoneId()),
                    additionalService.getVat(), additionalService.getVatType(),
                    Optional.ofNullable(additionalService.getPrice()).map(MonetaryUtil::unitToCents).orElse(0));
            Validate.isTrue(result <= 1, "too many records updated");
            Stream.concat(additionalService.getTitle().stream(), additionalService.getDescription().stream())
                    .forEach(t -> {
                        if (t.getId() != null) {
                            additionalServiceTextRepository.update(t.getId(), t.getLocale(), t.getType(),
                                    t.getValue());
                        } else {
                            additionalServiceTextRepository.insert(additionalService.getId(), t.getLocale(),
                                    t.getType(), t.getValue());
                        }
                    });
            return ResponseEntity.ok(additionalService);
        }).orElseThrow(IllegalArgumentException::new);
    }

    @RequestMapping(value = "/event/{eventId}/additional-services", method = RequestMethod.POST)
    @Transactional
    public ResponseEntity<EventModification.AdditionalService> insert(@PathVariable("eventId") int eventId,
            @RequestBody EventModification.AdditionalService additionalService, BindingResult bindingResult) {
        ValidationResult validationResult = Validator.validateAdditionalService(additionalService, bindingResult);
        Validate.isTrue(validationResult.isSuccess(), "validation failed");
        return eventRepository.findOptionalById(eventId).map(event -> {
            AffectedRowCountAndKey<Integer> result = additionalServiceRepository.insert(eventId,
                    Optional.ofNullable(additionalService.getPrice()).map(MonetaryUtil::unitToCents).orElse(0),
                    additionalService.isFixPrice(), additionalService.getOrdinal(),
                    additionalService.getAvailableQuantity(), additionalService.getMaxQtyPerOrder(),
                    additionalService.getInception().toZonedDateTime(event.getZoneId()),
                    additionalService.getExpiration().toZonedDateTime(event.getZoneId()),
                    additionalService.getVat(), additionalService.getVatType(), additionalService.getType(),
                    additionalService.getSupplementPolicy());
            Validate.isTrue(result.getAffectedRowCount() == 1, "too many records updated");
            int id = result.getKey();
            Stream.concat(additionalService.getTitle().stream(), additionalService.getDescription().stream())
                    .forEach(t -> additionalServiceTextRepository.insert(id, t.getLocale(), t.getType(),
                            t.getValue()));

            return ResponseEntity.ok(EventModification.AdditionalService
                    .from(additionalServiceRepository.getById(result.getKey(), eventId))
                    .withText(additionalServiceTextRepository.findAllByAdditionalServiceId(result.getKey()))
                    .withZoneId(event.getZoneId()).build());
        }).orElseThrow(IllegalArgumentException::new);
    }

    @RequestMapping(value = "/event/{eventId}/additional-services/{additionalServiceId}", method = RequestMethod.DELETE)
    @Transactional
    public ResponseEntity<String> remove(@PathVariable("eventId") int eventId,
            @PathVariable("additionalServiceId") int additionalServiceId, Principal principal) {
        return eventRepository.findOptionalById(eventId)
                .map(event -> optionally(() -> additionalServiceRepository.getById(additionalServiceId, eventId))
                        .map(as -> {
                            log.debug("{} is deleting additional service #{}", principal.getName(),
                                    additionalServiceId);
                            int deletedTexts = additionalServiceTextRepository
                                    .deleteAdditionalServiceTexts(additionalServiceId);
                            log.debug("deleted {} texts", deletedTexts);
                            //TODO add configuration fields and values
                            additionalServiceRepository.delete(additionalServiceId, eventId);
                            log.debug("additional service #{} successfully deleted", additionalServiceId);
                            return ResponseEntity.ok("OK");
                        }).orElseGet(
                                () -> new ResponseEntity<>("additional service not found", HttpStatus.NOT_FOUND)))
                .orElseGet(() -> new ResponseEntity<>("event not found", HttpStatus.NOT_FOUND));
    }

    @RequestMapping(value = "/additional-services/validate", method = RequestMethod.POST)
    public ValidationResult checkAdditionalService(
            @RequestBody EventModification.AdditionalService additionalService, BindingResult bindingResult) {
        return Validator.validateAdditionalService(additionalService, bindingResult);
    }

    private static PriceContainer buildPriceContainer(final Event event, final AdditionalService as) {
        return new PriceContainer() {
            @Override
            public int getSrcPriceCts() {
                return as.isFixPrice() ? as.getSrcPriceCts() : 0;
            }

            @Override
            public String getCurrencyCode() {
                return event.getCurrency();
            }

            @Override
            public Optional<BigDecimal> getOptionalVatPercentage() {
                return getVatStatus() == VatStatus.NONE ? Optional.empty() : Optional.of(event.getVat());
            }

            @Override
            public VatStatus getVatStatus() {
                return AdditionalService.getVatStatus(as.getVatType(), event.getVatStatus());
            }
        };
    }
}