Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API to retrieve check-in log #1188

Merged
merged 1 commit into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import alfio.manager.user.UserManager;
import alfio.model.*;
import alfio.model.ExtensionSupport.ExtensionMetadataValue;
import alfio.model.api.v1.admin.CheckInLogEntry;
import alfio.model.api.v1.admin.EventCreationRequest;
import alfio.model.api.v1.admin.LinkedSubscriptions;
import alfio.model.group.Group;
Expand Down Expand Up @@ -76,6 +77,7 @@ public class EventApiV1Controller {
private final ExtensionRepository extensionRepository;
private final ConfigurationManager configurationManager;
private final AdminJobManager adminJobManager;
private final CheckInManager checkInManager;

@PostMapping("/create")
@Transactional
Expand Down Expand Up @@ -199,6 +201,17 @@ public ResponseEntity<Boolean> generateTicketsForSubscribers(@PathVariable("slug
}));
}

@GetMapping("/{slug}/check-in-log")
public ResponseEntity<List<CheckInLogEntry>> checkInLog(@PathVariable("slug") String slug,
Principal user) {
try {
return ResponseEntity.ok(checkInManager.retrieveLogEntries(slug, user.getName()));
} catch (Exception ex) {
log.error("Error while loading check-in log entries", ex);
return ResponseEntity.internalServerError().build();
}
}

private LinkedSubscriptions retrieveLinkedSubscriptionsForEvent(String slug, int id, int organizationId) {
var subscriptionIds = eventManager.getLinkedSubscriptionIds(id, organizationId);
return new LinkedSubscriptions(slug, subscriptionIds);
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/alfio/manager/CheckInManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import alfio.manager.system.ConfigurationManager;
import alfio.model.*;
import alfio.model.Ticket.TicketStatus;
import alfio.model.api.v1.admin.CheckInLogEntry;
import alfio.model.audit.ScanAudit;
import alfio.model.checkin.AttendeeSearchResults;
import alfio.model.decorator.TicketPriceContainer;
Expand Down Expand Up @@ -533,6 +534,13 @@ public CheckInStatistics getStatistics(String eventName, String username) {
.orElse(null);
}

public List<CheckInLogEntry> retrieveLogEntries(String eventName, String username) {
return eventRepository.findOptionalEventAndOrganizationIdByShortName(eventName)
.filter(EventManager.checkOwnership(username, organizationRepository))
.map(event -> scanAuditRepository.loadEntries(event.getId()))
.orElse(List.of());
}

private boolean areStatsEnabled(EventAndOrganizationId event) {
return configurationManager.getFor(CHECK_IN_STATS, event.getConfigurationLevel()).getValueAsBooleanOrDefault();
}
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/alfio/model/api/v1/admin/CheckInLogEntry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* 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.model.api.v1.admin;

import alfio.model.audit.ScanAudit;
import alfio.model.modification.AttendeeData;
import alfio.model.support.JSONData;
import alfio.util.Json;
import ch.digitalfondue.npjt.ConstructorAnnotationRowMapper.Column;
import com.fasterxml.jackson.core.type.TypeReference;

import java.util.List;

public class CheckInLogEntry {
private final String ticketId;
private final AttendeeData attendeeData;
private final List<ScanAudit> audit;

public CheckInLogEntry(@Column("t_uuid") String ticketId,
@Column("attendee_data") @JSONData AttendeeData attendeeData,
@Column("scans") String scansAsString) {
this.ticketId = ticketId;
this.attendeeData = attendeeData;
this.audit = Json.fromJson(scansAsString, new TypeReference<>() {});
}

public String getTicketId() {
return ticketId;
}

public AttendeeData getAttendeeData() {
return attendeeData;
}

public List<ScanAudit> getAudit() {
return audit;
}
}
26 changes: 16 additions & 10 deletions src/main/java/alfio/model/audit/ScanAudit.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@

import alfio.manager.support.CheckInStatus;
import ch.digitalfondue.npjt.ConstructorAnnotationRowMapper.Column;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;

import java.time.ZonedDateTime;
import java.time.LocalDateTime;

@Getter
public class ScanAudit {
Expand All @@ -31,23 +34,26 @@ public enum Operation {
}

private final String ticketUuid;
private final int eventId;
private final ZonedDateTime scanTimestamp;
private final LocalDateTime scanTimestamp;
private final String username;
private final CheckInStatus checkInStatus;
private final Operation operation;

public ScanAudit(@Column("ticket_uuid") String ticketUuid,
@Column("event_id_fk") int eventId,
@Column("scan_ts") ZonedDateTime scanTimestamp,
@Column("username") String username,
@Column("check_in_status") CheckInStatus checkInStatus,
@Column("operation") Operation operation) {
@JsonCreator
public ScanAudit(@JsonProperty("ticketUuid") @Column("ticket_uuid") String ticketUuid,
@JsonProperty("scanTimestamp") @Column("scan_ts") LocalDateTime scanTimestamp,
@JsonProperty("username") @Column("username") String username,
@JsonProperty("checkInStatus") @Column("check_in_status") CheckInStatus checkInStatus,
@JsonProperty("operation") @Column("operation") Operation operation) {
this.ticketUuid = ticketUuid;
this.eventId = eventId;
this.scanTimestamp = scanTimestamp;
this.username = username;
this.checkInStatus = checkInStatus;
this.operation = operation;
}

@JsonIgnore
public String getTicketUuid() {
return ticketUuid;
}
}
9 changes: 9 additions & 0 deletions src/main/java/alfio/repository/audit/ScanAuditRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package alfio.repository.audit;

import alfio.manager.support.CheckInStatus;
import alfio.model.api.v1.admin.CheckInLogEntry;
import alfio.model.audit.ScanAudit;
import ch.digitalfondue.npjt.Bind;
import ch.digitalfondue.npjt.Query;
Expand All @@ -38,4 +39,12 @@ Integer insert(@Bind("ticketUuid") String ticketUuid,
@Query("select * from scan_audit where event_id_fk = :eventId")
List<ScanAudit> findAllForEvent(@Bind("eventId") int eventId);

@Query("select t.uuid t_uuid, jsonb_build_object('firstName', t.first_name, 'lastName', t.last_name, 'email', t.email_address, 'metadata', coalesce(t.metadata::jsonb#>'{metadataMap, general, attributes}', '{}')) attendee_data, jsonb_agg(jsonb_build_object('ticketUuid', sa.ticket_uuid, 'scanTimestamp', sa.scan_ts, 'username', sa.username, 'checkInStatus', sa.check_in_status, 'operation', sa.operation)) scans from ticket t\n" +
" join event e on t.event_id = e.id" +
" join scan_audit sa on e.id = sa.event_id_fk and t.uuid = sa.ticket_uuid" +
" where e.id = :eventId" +
" and t.status = 'CHECKED_IN'" +
" group by 1,2")
List<CheckInLogEntry> loadEntries(@Bind("eventId") int eventId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -993,15 +993,15 @@ protected void testBasicFlow(Supplier<ReservationFlowContext> contextSupplier) t
assertEquals(CheckInStatus.OK_READY_TO_BE_CHECKED_IN, ticketAndCheckInResult.getResult().getStatus());
CheckInApiController.TicketCode tc = new CheckInApiController.TicketCode();
tc.setCode(ticketCode);
assertEquals(CheckInStatus.SUCCESS, checkInApiController.checkIn(context.event.getId(), ticketIdentifier, tc, new TestingAuthenticationToken("ciccio", "ciccio")).getResult().getStatus());
assertEquals(CheckInStatus.SUCCESS, checkInApiController.checkIn(context.event.getId(), ticketIdentifier, tc, new TestingAuthenticationToken(context.userId + "_api", "")).getResult().getStatus());
List<ScanAudit> audits = scanAuditRepository.findAllForEvent(context.event.getId());
assertFalse(audits.isEmpty());
assertTrue(audits.stream().anyMatch(sa -> sa.getTicketUuid().equals(ticketIdentifier)));

extLogs = extensionLogRepository.getPage(null, null, null, 100, 0);
assertEventLogged(extLogs, TICKET_CHECKED_IN, 2);


validateCheckInData(context);

TicketAndCheckInResult ticketAndCheckInResultOk = checkInApiController.findTicketWithUUID(context.event.getId(), ticketIdentifier, ticketCode);
assertEquals(CheckInStatus.ALREADY_CHECK_IN, ticketAndCheckInResultOk.getResult().getStatus());
Expand Down Expand Up @@ -1191,6 +1191,10 @@ protected void testBasicFlow(Supplier<ReservationFlowContext> contextSupplier) t

}

protected void validateCheckInData(ReservationFlowContext context) {

}

protected void performAndValidatePayment(ReservationFlowContext context,
String reservationId,
int promoCodeId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import java.util.List;

import static alfio.test.util.IntegrationTestUtil.*;
import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
@ContextConfiguration(classes = {DataSourceConfiguration.class, TestConfiguration.class, ControllerConfiguration.class})
Expand All @@ -71,6 +72,7 @@ class ReservationFlowIntegrationTest extends BaseReservationFlowTest {

private final OrganizationRepository organizationRepository;
private final UserManager userManager;
private final CheckInManager checkInManager;

@Autowired
public ReservationFlowIntegrationTest(OrganizationRepository organizationRepository,
Expand Down Expand Up @@ -109,7 +111,8 @@ public ReservationFlowIntegrationTest(OrganizationRepository organizationReposit
UserRepository userRepository,
OrganizationDeleter organizationDeleter,
PromoCodeDiscountRepository promoCodeDiscountRepository,
PromoCodeRequestManager promoCodeRequestManager) {
PromoCodeRequestManager promoCodeRequestManager,
CheckInManager checkInManager) {
super(configurationRepository,
eventManager,
eventRepository,
Expand Down Expand Up @@ -147,6 +150,7 @@ public ReservationFlowIntegrationTest(OrganizationRepository organizationReposit
promoCodeRequestManager);
this.organizationRepository = organizationRepository;
this.userManager = userManager;
this.checkInManager = checkInManager;
}

private ReservationFlowContext createContext() {
Expand Down Expand Up @@ -174,4 +178,16 @@ protected void performAdditionalTests(ReservationFlowContext context) {
var event = context.event;
BaseIntegrationTest.testTransferEventToAnotherOrg(event.getId(), event.getOrganizationId(), context.userId, jdbcTemplate);
}

@Override
protected void validateCheckInData(ReservationFlowContext context) {
var entries = checkInManager.retrieveLogEntries(context.event.getShortName(), context.userId);
assertEquals(1, entries.size());
var entry = entries.get(0);
assertNotNull(entry.getTicketId());
assertNotNull(entry.getAttendeeData());
assertNotNull(entry.getAttendeeData().getMetadata());
assertNotNull(entry.getAudit());
entry.getAudit().forEach(audit -> assertEquals(context.userId + "_api", audit.getUsername()));
}
}