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

Java tutorial

Introduction

Here is the source code for alfio.controller.api.admin.CheckInApiController.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.manager.CheckInManager;
import alfio.manager.EventManager;
import alfio.manager.support.TicketAndCheckInResult;
import alfio.manager.system.ConfigurationManager;
import alfio.model.Event;
import alfio.model.FullTicketInfo;
import alfio.model.system.Configuration;
import alfio.model.system.ConfigurationKeys;
import alfio.util.Json;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.security.Principal;
import java.util.*;

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

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

    private static final String ALFIO_TIMESTAMP_HEADER = "Alfio-TIME";
    private final CheckInManager checkInManager;
    private final EventManager eventManager;
    private final ConfigurationManager configurationManager;

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

    @RequestMapping(value = "/check-in/{eventId}/ticket/{ticketIdentifier}", method = GET)
    public TicketAndCheckInResult findTicketWithUUID(@PathVariable("eventId") int eventId,
            @PathVariable("ticketIdentifier") String ticketIdentifier, @RequestParam("qrCode") String qrCode) {
        return checkInManager.evaluateTicketStatus(eventId, ticketIdentifier, Optional.ofNullable(qrCode));
    }

    @RequestMapping(value = "/check-in/event/{eventName}/ticket/{ticketIdentifier}", method = GET)
    public TicketAndCheckInResult findTicketWithUUID(@PathVariable("eventName") String eventName,
            @PathVariable("ticketIdentifier") String ticketIdentifier, @RequestParam("qrCode") String qrCode) {
        return checkInManager.evaluateTicketStatus(eventName, ticketIdentifier, Optional.ofNullable(qrCode));
    }

    @RequestMapping(value = "/check-in/{eventId}/ticket/{ticketIdentifier}", method = POST)
    public TicketAndCheckInResult checkIn(@PathVariable("eventId") int eventId,
            @PathVariable("ticketIdentifier") String ticketIdentifier, @RequestBody TicketCode ticketCode,
            Principal principal) {
        return checkInManager.checkIn(eventId, ticketIdentifier,
                Optional.ofNullable(ticketCode).map(TicketCode::getCode), principal.getName());
    }

    @RequestMapping(value = "/check-in/event/{eventName}/ticket/{ticketIdentifier}", method = POST)
    public TicketAndCheckInResult checkIn(@PathVariable("eventName") String eventName,
            @PathVariable("ticketIdentifier") String ticketIdentifier, @RequestBody TicketCode ticketCode,
            @RequestParam(value = "offlineUser", required = false) String offlineUser, Principal principal) {
        String user = StringUtils.defaultIfBlank(offlineUser, principal.getName());
        return checkInManager.checkIn(eventName, ticketIdentifier,
                Optional.ofNullable(ticketCode).map(TicketCode::getCode), user);
    }

    @RequestMapping(value = "/check-in/{eventId}/ticket/{ticketIdentifier}/manual-check-in", method = POST)
    public boolean manualCheckIn(@PathVariable("eventId") int eventId,
            @PathVariable("ticketIdentifier") String ticketIdentifier, Principal principal) {
        log.warn("for event id : {} and ticket : {}, a manual check in has been done", eventId, ticketIdentifier);
        return checkInManager.manualCheckIn(eventId, ticketIdentifier, principal.getName());
    }

    @RequestMapping(value = "/check-in/{eventId}/ticket/{ticketIdentifier}/revert-check-in", method = POST)
    public boolean revertCheckIn(@PathVariable("eventId") int eventId,
            @PathVariable("ticketIdentifier") String ticketIdentifier, Principal principal) {
        log.warn("for event id : {} and ticket : {}, a revert of the check in has been done", eventId,
                ticketIdentifier);
        return checkInManager.revertCheckIn(eventId, ticketIdentifier, principal.getName());
    }

    @RequestMapping(value = "/check-in/event/{eventName}/ticket/{ticketIdentifier}/confirm-on-site-payment", method = POST)
    public TicketAndCheckInResult confirmOnSitePayment(@PathVariable("eventName") String eventName,
            @PathVariable("ticketIdentifier") String ticketIdentifier, @RequestBody TicketCode ticketCode,
            @RequestParam(value = "offlineUser", required = false) String offlineUser, Principal principal) {
        String user = StringUtils.defaultIfBlank(offlineUser, principal.getName());
        return checkInManager.confirmOnSitePayment(eventName, ticketIdentifier,
                Optional.ofNullable(ticketCode).map(TicketCode::getCode), user);
    }

    @RequestMapping(value = "/check-in/{eventId}/ticket/{ticketIdentifier}/confirm-on-site-payment", method = POST)
    public OnSitePaymentConfirmation confirmOnSitePayment(@PathVariable("eventId") int eventId,
            @PathVariable("ticketIdentifier") String ticketIdentifier) {
        return checkInManager.confirmOnSitePayment(ticketIdentifier)
                .map(s -> new OnSitePaymentConfirmation(true, "ok"))
                .orElseGet(() -> new OnSitePaymentConfirmation(false,
                        "Ticket with uuid " + ticketIdentifier + " not found"));
    }

    @RequestMapping(value = "/check-in/{eventId}/ticket-identifiers", method = GET)
    public List<Integer> findAllIdentifiersForAdminCheckIn(@PathVariable("eventId") int eventId,
            @RequestParam(value = "changedSince", required = false) Long changedSince, HttpServletResponse response,
            Principal principal) {
        response.setHeader(ALFIO_TIMESTAMP_HEADER, Long.toString(new Date().getTime()));
        return checkInManager.getAttendeesIdentifiers(eventId,
                changedSince == null ? new Date(0) : new Date(changedSince), principal.getName());
    }

    @RequestMapping(value = "/check-in/{eventId}/tickets", method = POST)
    public List<FullTicketInfo> findAllTicketsForAdminCheckIn(@PathVariable("eventId") int eventId,
            @RequestBody List<Integer> ids, Principal principal) {
        validateIdList(ids);
        return checkInManager.getAttendeesInformation(eventId, ids, principal.getName());
    }

    @RequestMapping(value = "/check-in/{eventName}/label-layout", method = GET)
    public ResponseEntity<LabelLayout> getLabelLayoutForEvent(@PathVariable("eventName") String eventName,
            Principal principal) {
        return optionally(() -> eventManager.getSingleEvent(eventName, principal.getName()))
                .filter(checkInManager.isOfflineCheckInAndLabelPrintingEnabled()).map(this::parseLabelLayout)
                .orElseGet(() -> new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED));
    }

    @RequestMapping(value = "/check-in/{eventName}/offline-identifiers", method = GET)
    public List<Integer> getOfflineIdentifiers(@PathVariable("eventName") String eventName,
            @RequestParam(value = "changedSince", required = false) Long changedSince, HttpServletResponse resp,
            Principal principal) {
        Date since = changedSince == null ? new Date(0) : DateUtils.addSeconds(new Date(changedSince), -1);
        Optional<List<Integer>> ids = optionally(() -> eventManager.getSingleEvent(eventName, principal.getName()))
                .filter(checkInManager.isOfflineCheckInEnabled())
                .map(event -> checkInManager.getAttendeesIdentifiers(event, since, principal.getName()));

        resp.setHeader(ALFIO_TIMESTAMP_HEADER, ids.isPresent() ? Long.toString(new Date().getTime()) : "0");
        return ids.orElse(Collections.emptyList());
    }

    @RequestMapping(value = "/check-in/{eventName}/offline", method = POST)
    public Map<String, String> getOfflineEncryptedInfo(@PathVariable("eventName") String eventName,
            @RequestParam(value = "additionalField", required = false) List<String> additionalFields,
            @RequestBody List<Integer> ids, Principal principal) {

        validateIdList(ids);
        return optionally(() -> eventManager.getSingleEvent(eventName, principal.getName())).map(event -> {
            Set<String> addFields = loadLabelLayout(event).map(layout -> {
                Set<String> union = new HashSet<>(layout.content.thirdRow);
                union.addAll(layout.qrCode.additionalInfo);
                if (additionalFields != null && !additionalFields.isEmpty()) {
                    union.addAll(additionalFields);
                }
                return union;
            }).orElseGet(() -> {
                if (additionalFields != null && !additionalFields.isEmpty()) {
                    return new HashSet<>(additionalFields);
                }
                return Collections.singleton("company");
            });
            return checkInManager.getEncryptedAttendeesInformation(event, addFields, ids);
        }).orElse(Collections.emptyMap());
    }

    private static void validateIdList(@RequestBody List<Integer> ids) {
        Validate.isTrue(ids != null && !ids.isEmpty());
        Validate.isTrue(ids.size() <= 200, "Cannot ask more than 200 ids");
    }

    private ResponseEntity<LabelLayout> parseLabelLayout(Event event) {
        return loadLabelLayout(event).map(ResponseEntity::ok)
                .orElseGet(() -> new ResponseEntity<>(HttpStatus.NO_CONTENT));
    }

    private Optional<LabelLayout> loadLabelLayout(Event event) {
        return configurationManager
                .getStringConfigValue(Configuration.from(event.getOrganizationId(), event.getId(),
                        ConfigurationKeys.LABEL_LAYOUT))
                .flatMap(str -> optionally(() -> Json.fromJson(str, LabelLayout.class)));
    }

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

    @Getter
    private static class LabelLayout {

        private final QRCode qrCode;
        private final Content content;
        private final General general;

        @JsonCreator
        private LabelLayout(@JsonProperty("qrCode") QRCode qrCode, @JsonProperty("content") Content content,
                @JsonProperty("general") General general) {
            this.qrCode = qrCode;
            this.content = content;
            this.general = general;
        }

        @Getter
        private static class QRCode {
            private final List<String> additionalInfo;
            private final String infoSeparator;

            @JsonCreator
            private QRCode(@JsonProperty("additionalInfo") List<String> additionalInfo,
                    @JsonProperty("infoSeparator") String infoSeparator) {
                this.additionalInfo = additionalInfo;
                this.infoSeparator = infoSeparator;
            }
        }

        @Getter
        private static class Content {

            private final List<String> thirdRow;

            @JsonCreator
            private Content(@JsonProperty("thirdRow") List<String> thirdRow) {
                this.thirdRow = thirdRow;
            }
        }

        @Getter
        private static class General {
            private final boolean printPartialID;

            @JsonCreator
            private General(@JsonProperty("printPartialID") boolean printPartialID) {
                this.printPartialID = printPartialID;
            }
        }
    }
}