Skip to content

Commit

Permalink
#41 - move admin api controllers to a more appropriate package (api.a…
Browse files Browse the repository at this point in the history
…dmin)
  • Loading branch information
cbellone committed Mar 15, 2015
1 parent 5921c58 commit 576d5f1
Show file tree
Hide file tree
Showing 9 changed files with 852 additions and 2 deletions.
158 changes: 158 additions & 0 deletions src/main/java/alfio/controller/api/admin/CheckInApiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* 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.manager.CheckInManager;
import alfio.model.Event;
import alfio.model.Ticket;
import alfio.model.Ticket.TicketStatus;
import alfio.repository.EventRepository;
import alfio.repository.TicketRepository;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Optional;

import static alfio.util.OptionalWrapper.optionally;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

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

private final TicketRepository ticketRepository;
private final EventRepository eventRepository;
private final CheckInManager checkInManager;

@Data
public static class TicketCode {
private String code;
}

public enum CheckInStatus {
EVENT_NOT_FOUND, TICKET_NOT_FOUND, EMPTY_TICKET_CODE, INVALID_TICKET_CODE, INVALID_TICKET_STATE, ALREADY_CHECK_IN, MUST_PAY, SUCCESS
}

@Data
public static class CheckInResult {
private final CheckInStatus status;
private final String message;
}

@Data
public static class OnSitePaymentConfirmation {
private final boolean status;
private final String message;
}

@Autowired
public CheckInApiController(EventRepository eventRepository, TicketRepository ticketRepository, CheckInManager checkInManager) {
this.eventRepository = eventRepository;
this.ticketRepository = ticketRepository;
this.checkInManager = checkInManager;
}

@RequestMapping(value = "/check-in/{eventId}/ticket/{ticketIdentifier}", method = GET)
public Ticket findTicketWithUUID(@PathVariable("eventId") int eventId, @PathVariable("ticketIdentifier") String ticketIdentifier) {
Optional<Event> event = optionally(() -> eventRepository.findById(eventId));
Optional<Ticket> ticket = optionally(() -> ticketRepository.findByUUID(ticketIdentifier));

if(event.isPresent() && ticket.isPresent()) {
return ticket.get();
} else {
return null;
}
}

@RequestMapping(value = "/check-in/{eventId}/ticket/{ticketIdentifier}", method = POST)
public CheckInResult checkIn(@PathVariable("eventId") int eventId, @PathVariable("ticketIdentifier") String ticketIdentifier, @RequestBody TicketCode ticketCode) {

Optional<Event> event = optionally(() -> eventRepository.findById(eventId));

if (!event.isPresent()) {
return new CheckInResult(CheckInStatus.EVENT_NOT_FOUND, "Event with id " + eventId + " not found");
}

Optional<Ticket> ticket = optionally(() -> ticketRepository.findByUUID(ticketIdentifier));

if (!ticket.isPresent()) {
return new CheckInResult(CheckInStatus.TICKET_NOT_FOUND, "Ticket with uuid " + ticketIdentifier + " not found");
}

if(ticketCode == null || StringUtils.isEmpty(ticketCode.getCode())) {
return new CheckInResult(CheckInStatus.EMPTY_TICKET_CODE, "Missing ticket code");
}

return handleCheckIn(event.get(), ticket.get(), ticketCode.getCode());
}


private CheckInResult handleCheckIn(Event event, Ticket ticket, String ticketCode) {

if (!ticketCode.equals(ticket.ticketCode(event.getPrivateKey()))) {
return new CheckInResult(CheckInStatus.INVALID_TICKET_CODE, "Ticket qr code does not match");
}

final TicketStatus ticketStatus = ticket.getStatus();

if (ticketStatus == TicketStatus.TO_BE_PAID) {
return new CheckInResult(CheckInStatus.MUST_PAY, "Must pay for ticket"); //TODO: must say how much
}

if (ticketStatus == TicketStatus.CHECKED_IN) {
return new CheckInResult(CheckInStatus.ALREADY_CHECK_IN, "Error: already checked in");
}

if (ticket.getStatus() != TicketStatus.ACQUIRED) {
return new CheckInResult(CheckInStatus.INVALID_TICKET_STATE, "Invalid ticket state, expected ACQUIRED state, received " + ticket.getStatus());
}

checkInManager.checkIn(ticket.getUuid());
return new CheckInResult(CheckInStatus.SUCCESS, "success");
}

@RequestMapping(value = "/check-in/{eventId}/ticket/{ticketIdentifier}/confirm-on-site-payment", method = POST)
public OnSitePaymentConfirmation confirmOnSitePayment(@PathVariable("eventId") int eventId, @PathVariable("ticketIdentifier") String ticketIdentifier) {

Optional<Ticket> ticket = optionally(() -> ticketRepository.findByUUID(ticketIdentifier));

if (!ticket.isPresent()) {
return new OnSitePaymentConfirmation(false, "Ticket with uuid " + ticketIdentifier + " not found");
}

Ticket t = ticket.get();

if (t.getStatus() != TicketStatus.TO_BE_PAID) {
return new OnSitePaymentConfirmation(false, "Invalid ticket state, expected TO_BE_PAID state, received " + t.getStatus());
}

checkInManager.acquire(t.getUuid());
return new OnSitePaymentConfirmation(true, "ok");
}

@RequestMapping(value = "/check-in/{eventId}/ticket", method = GET)
public List<Ticket> listAllTickets(@PathVariable("eventId") int eventId) {
return ticketRepository.findAllByEventId(eventId);
}
}
178 changes: 178 additions & 0 deletions src/main/java/alfio/controller/api/admin/EventApiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/**
* 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.manager.EventManager;
import alfio.manager.support.OrderSummary;
import alfio.model.TicketReservation;
import alfio.model.modification.EventModification;
import alfio.model.modification.EventWithStatistics;
import alfio.model.modification.TicketAllocationModification;
import alfio.model.modification.TicketCategoryModification;
import alfio.model.transaction.PaymentProxy;
import alfio.util.ValidationResult;
import alfio.util.Validator;
import com.opencsv.CSVReader;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.security.Principal;
import java.util.*;
import java.util.stream.Collectors;

import static org.springframework.web.bind.annotation.RequestMethod.*;

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

private static final String OK = "OK";
private final EventManager eventManager;

@Autowired
public EventApiController(EventManager eventManager) {
this.eventManager = eventManager;
}

@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String unhandledException(Exception e) {
if(!IllegalArgumentException.class.isInstance(e)) {
log.warn("unhandled exception", e);
}
return e.getMessage();
}


@RequestMapping(value = "/paymentProxies", method = GET)
@ResponseStatus(HttpStatus.OK)
public List<PaymentProxy> getPaymentProxies() {
return Arrays.asList(PaymentProxy.values());
}

@RequestMapping(value = "/events", method = GET)
public List<EventWithStatistics> getAllEvents(Principal principal) {
return eventManager.getAllEventsWithStatistics(principal.getName());
}

@RequestMapping(value = "/events/{name}", method = GET)
public Map<String, Object> getSingleEvent(@PathVariable("name") String eventName, Principal principal) {
Map<String, Object> out = new HashMap<>();
final String username = principal.getName();
final EventWithStatistics event = eventManager.getSingleEventWithStatistics(eventName, username);
out.put("event", event);
out.put("organization", eventManager.loadOrganizer(event.getEvent(), username));
return out;
}

@RequestMapping(value = "/events/check", method = POST)
public ValidationResult validateEvent(@RequestBody EventModification eventModification) {
return ValidationResult.success();
}

@RequestMapping(value = "/events/new", method = POST)
public String insertEvent(@RequestBody EventModification eventModification) {
eventManager.createEvent(eventModification);
return OK;
}

@RequestMapping(value = "/events/{id}/header/update", method = POST)
public ValidationResult updateHeader(@PathVariable("id") int id, @RequestBody EventModification eventModification, Errors errors, Principal principal) {
return Validator.validateEventHeader(eventModification, errors).ifSuccess(() -> eventManager.updateEventHeader(id, eventModification, principal.getName()));
}

@RequestMapping(value = "/events/{id}/prices/update", method = POST)
public ValidationResult updatePrices(@PathVariable("id") int id, @RequestBody EventModification eventModification, Errors errors, Principal principal) {
return Validator.validateEventPrices(eventModification, errors).ifSuccess(() -> eventManager.updateEventPrices(id, eventModification, principal.getName()));
}

@RequestMapping(value = "/events/{eventId}/categories/{categoryId}/update", method = POST)
public ValidationResult updateExistingCategory(@PathVariable("eventId") int eventId, @PathVariable("categoryId") int categoryId, @RequestBody TicketCategoryModification category, Errors errors, Principal principal) {
return Validator.validateCategory(category, errors).ifSuccess(() -> eventManager.updateCategory(categoryId, eventId, category, principal.getName()));
}

@RequestMapping(value = "/events/{eventId}/categories/new", method = POST)
public ValidationResult createCategory(@PathVariable("eventId") int eventId, @RequestBody TicketCategoryModification category, Errors errors, Principal principal) {
return Validator.validateCategory(category, errors).ifSuccess(() -> eventManager.insertCategory(eventId, category, principal.getName()));
}

@RequestMapping(value = "/events/reallocate", method = PUT)
public String reallocateTickets(@RequestBody TicketAllocationModification form) {
eventManager.reallocateTickets(form.getSrcCategoryId(), form.getTargetCategoryId(), form.getEventId());
return OK;
}

@RequestMapping(value = "/events/{eventName}/pending-payments")
public List<SerializablePair<TicketReservation, OrderSummary>> getPendingPayments(@PathVariable("eventName") String eventName, Principal principal) {
return eventManager.getPendingPayments(eventName, principal.getName()).stream().map(SerializablePair::fromPair).collect(Collectors.toList());
}

@RequestMapping(value = "/events/{eventName}/pending-payments/{reservationId}/confirm", method = POST)
public String confirmPayment(@PathVariable("eventName") String eventName, @PathVariable("reservationId") String reservationId, Principal principal) {
eventManager.confirmPayment(eventName, reservationId, principal.getName());
return OK;
}

@RequestMapping(value = "/events/{eventName}/pending-payments/{reservationId}", method = DELETE)
public String deletePendingPayment(@PathVariable("eventName") String eventName, @PathVariable("reservationId") String reservationId, Principal principal) {
eventManager.deletePendingOfflinePayment(eventName, reservationId, principal.getName());
return OK;
}

@RequestMapping(value = "/events/{eventName}/pending-payments/bulk-confirmation", method = POST)
public List<Triple<Boolean, String, String>> bulkConfirmation(@PathVariable("eventName") String eventName,
Principal principal,
@RequestParam("file") MultipartFile file) throws IOException {

try(InputStreamReader isr = new InputStreamReader(file.getInputStream())) {
CSVReader reader = new CSVReader(isr);
String username = principal.getName();
return reader.readAll().stream()
.map(line -> {
String reservationID = null;
try {
Validate.isTrue(line.length >= 2);
reservationID = line[0];
eventManager.confirmPayment(eventName, reservationID, new BigDecimal(line[1]), username);
return Triple.of(Boolean.TRUE, reservationID, "");
} catch (Exception e) {
return Triple.of(Boolean.FALSE, Optional.ofNullable(reservationID).orElse(""), e.getMessage());
}
})
.collect(Collectors.toList());
}
}

@RequestMapping(value = "/events/{eventName}/categories/{categoryId}/tickets/{ticketId}/toggle-locking", method = PUT)
public boolean toggleTicketLocking(@PathVariable("eventName") String eventName,
@PathVariable("categoryId") int categoryId,
@PathVariable("ticketId") int ticketId,
Principal principal) {
return eventManager.toggleTicketLocking(eventName, categoryId, ticketId, principal.getName());
}

}
Loading

0 comments on commit 576d5f1

Please sign in to comment.