Skip to content

Commit

Permalink
feat: consume ticket logs
Browse files Browse the repository at this point in the history
  • Loading branch information
oproprioleonardo committed Nov 10, 2024
1 parent 0033a3b commit e056de3
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import br.com.ifsp.tickets.domain.user.User;

import java.time.LocalDate;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

Expand All @@ -32,10 +31,12 @@ public CreateEventUseCase(ICompanyGateway companyGateway, IEventGateway eventGat
public CreateEventOutput execute(CreateEventInput anIn) {
final User user = anIn.user();
final CompanyID companyID = CompanyID.with(anIn.companyId());
if (!user.canManageEvents() && !user.canManageAnyEvent()) throw new IllegalResourceAccessException("User does not have permission to create events");
if (!user.canManageEvents() && !user.canManageAnyEvent())
throw new IllegalResourceAccessException("User does not have permission to create events");
if (!user.hasCompany() && !user.canManageAnyEvent()) throw new NoCompanyException();
final CompanyID userCompanyID = user.getCompanyID();
if (!user.canManageAnyEvent() && !userCompanyID.equals(companyID)) throw new IllegalResourceAccessException("User does not have permission to create events for this company");
if (!user.canManageAnyEvent() && !userCompanyID.equals(companyID))
throw new IllegalResourceAccessException("User does not have permission to create events for this company");
final Company company = this.companyGateway.findById(companyID).orElseThrow(() -> NotFoundException.with(Company.class, companyID));
final String name = anIn.name();
final String description = anIn.description();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@
import br.com.ifsp.tickets.app.ticket.retrieve.list.IListTicketsByUserUseCase;
import br.com.ifsp.tickets.app.ticket.retrieve.list.ListTicketsByUserUseCase;
import br.com.ifsp.tickets.domain.event.IEventGateway;
import br.com.ifsp.tickets.domain.shared.IDomainEventPublisher;
import br.com.ifsp.tickets.domain.ticket.ITicketGateway;

public class TicketServiceFactory {
private static TicketService ticketService;

public static TicketService create(
IEventGateway eventGateway,
ITicketGateway ticketGateway
ITicketGateway ticketGateway,
IDomainEventPublisher eventPublisher
) {
if (ticketService == null) {
final ICheckTicketUseCase checkTicketUseCase = new CheckTicketUseCase(ticketGateway, eventGateway);
final ICheckTicketUseCase checkTicketUseCase = new CheckTicketUseCase(ticketGateway, eventGateway, eventPublisher);
final IGetTicketByIDUseCase getTicketByIDUseCase = new GetTicketByIDUseCase(ticketGateway, eventGateway);
final IListTicketsByUserUseCase listTicketsByUserUseCase = new ListTicketsByUserUseCase(ticketGateway);
ticketService = new TicketService(checkTicketUseCase, getTicketByIDUseCase, listTicketsByUserUseCase);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import br.com.ifsp.tickets.domain.event.Event;
import br.com.ifsp.tickets.domain.event.EventID;
import br.com.ifsp.tickets.domain.event.IEventGateway;
import br.com.ifsp.tickets.domain.shared.exceptions.IllegalResourceAccessException;
import br.com.ifsp.tickets.domain.shared.exceptions.NotFoundException;
import br.com.ifsp.tickets.domain.shared.IDomainEventPublisher;
import br.com.ifsp.tickets.domain.shared.event.ConsumeTicketError;
import br.com.ifsp.tickets.domain.shared.exceptions.*;
import br.com.ifsp.tickets.domain.ticket.ITicketGateway;
import br.com.ifsp.tickets.domain.ticket.Ticket;
import br.com.ifsp.tickets.domain.ticket.TicketID;
Expand All @@ -13,10 +14,12 @@
public class CheckTicketUseCase implements ICheckTicketUseCase {
private final ITicketGateway ticketGateway;
private final IEventGateway eventGateway;
private final IDomainEventPublisher eventPublisher;

public CheckTicketUseCase(ITicketGateway ticketGateway, IEventGateway eventGateway) {
public CheckTicketUseCase(ITicketGateway ticketGateway, IEventGateway eventGateway, IDomainEventPublisher eventPublisher) {
this.ticketGateway = ticketGateway;
this.eventGateway = eventGateway;
this.eventPublisher = eventPublisher;
}

@Override
Expand All @@ -30,7 +33,16 @@ public void execute(CheckTicketInput anIn) {
if ((!user.canManageTickets() || !user.getCompanyID().equals(event.getCompanyID())) && !user.canManageAnyTicket())
throw new IllegalResourceAccessException("You don't have permission to check this ticket");

ticket.consume(event);
DomainException exception = null;
try {
ticket.consume(event);
} catch (TicketConsumeException | TicketExpiredException e) {
ticket.registerEvent(new ConsumeTicketError(ticket, e.getMessage()));
exception = e;
}
ticket.publishDomainEvents(eventPublisher);
if (exception != null) throw exception;

this.ticketGateway.update(ticket);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package br.com.ifsp.tickets.domain.shared;

public enum DomainEventType {

INFO,
WARNING,
ERROR,
DEBUG,
FATAL

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ public interface IDomainEvent extends Serializable {

String message();

String reason();

String source();

DomainEventType type();

String id();


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package br.com.ifsp.tickets.domain.shared.event;

import br.com.ifsp.tickets.domain.shared.DomainEventType;
import br.com.ifsp.tickets.domain.shared.IDomainEvent;
import br.com.ifsp.tickets.domain.ticket.Ticket;

import java.time.Instant;

public class ConsumeTicketError implements IDomainEvent {

private final String id;
private final String reason;

public ConsumeTicketError(Ticket ticket, String reason) {
this.id = ticket.getId().getValue().toString();
this.reason = reason;
}

@Override
public Instant occurredOn() {
return Instant.now();
}

@Override
public String subject() {
return "ConsumeTicket";
}

@Override
public String message() {
return "Ticket could not be consumed";
}

@Override
public String source() {
return Ticket.class.getName();
}

@Override
public DomainEventType type() {
return DomainEventType.ERROR;
}

@Override
public String id() {
return this.id;
}

@Override
public String reason() {
return this.reason;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package br.com.ifsp.tickets.domain.shared.event;

import br.com.ifsp.tickets.domain.shared.DomainEventType;
import br.com.ifsp.tickets.domain.shared.IDomainEvent;
import br.com.ifsp.tickets.domain.ticket.Ticket;

import java.time.Instant;

public class ConsumeTicketSuccess implements IDomainEvent {

private final String id;

public ConsumeTicketSuccess(Ticket ticket) {
this.id = ticket.getId().getValue().toString();
}

@Override
public Instant occurredOn() {
return Instant.now();
}

@Override
public String subject() {
return "ConsumeTicket";
}

@Override
public String message() {
return "Ticket has been consumed";
}

@Override
public String reason() {
return "";
}

@Override
public String source() {
return Ticket.class.getName();
}

@Override
public DomainEventType type() {
return DomainEventType.INFO;
}

@Override
public String id() {
return this.id;
}
}
33 changes: 11 additions & 22 deletions domain/src/main/java/br/com/ifsp/tickets/domain/ticket/Ticket.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import br.com.ifsp.tickets.domain.event.sale.TicketSale;
import br.com.ifsp.tickets.domain.event.sale.TicketSaleID;
import br.com.ifsp.tickets.domain.shared.Entity;
import br.com.ifsp.tickets.domain.shared.event.ConsumeTicketSuccess;
import br.com.ifsp.tickets.domain.shared.exceptions.ChangeTicketStatusException;
import br.com.ifsp.tickets.domain.shared.exceptions.TicketConsumeException;
import br.com.ifsp.tickets.domain.shared.exceptions.TicketExpiredException;
Expand All @@ -15,7 +16,6 @@

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Optional;

@Getter
Expand All @@ -30,7 +30,7 @@ public class Ticket extends Entity<TicketID> {
private final LocalDate expiredIn;
private final LocalDateTime createdAt;
private TicketStatus status;
private TicketCode code;
private final TicketCode code;
private LocalDateTime lastTimeConsumed;

public Ticket(TicketID ticketID, String document, EventID eventID, TicketSaleID ticketSaleID, String description, TicketStatus status, TicketCode code, LocalDate validIn, LocalDate expiredIn, LocalDateTime createdAt, LocalDateTime lastTimeConsumed, UserID userID) {
Expand All @@ -53,35 +53,24 @@ public static Ticket with(TicketID ticketID, String document, EventID eventID, T
}

public static Ticket newTicket(UserID userID, String document, Event event, TicketSale ticketSale, String description, LocalDate validIn, LocalDate expiredIn) {
return new Ticket(TicketID.unique(), document, event.getId(), ticketSale.getId(), description, TicketStatus.AVAILABLE, TicketCode.generate(), validIn, expiredIn, LocalDateTime.now(ZoneId.of("GMT-3")), null, userID);
return new Ticket(TicketID.unique(), document, event.getId(), ticketSale.getId(), description, TicketStatus.AVAILABLE, TicketCode.generate(), validIn, expiredIn, LocalDateTime.now(), null, userID);
}

public static Ticket newTicketWithId(TicketID ticketID, UserID userID, String document, Event event, TicketSale ticketSale, String description, LocalDate validIn, LocalDate expiredIn) {
return new Ticket(ticketID, document, event.getId(), ticketSale.getId(), description, TicketStatus.AVAILABLE, TicketCode.generate(), validIn, expiredIn, LocalDateTime.now(ZoneId.of("GMT-3")), null, userID);
return new Ticket(ticketID, document, event.getId(), ticketSale.getId(), description, TicketStatus.AVAILABLE, TicketCode.generate(), validIn, expiredIn, LocalDateTime.now(), null, userID);
}

public Optional<UserID> getUserID() {
return Optional.ofNullable(this.userID);
}

public void generateNewCode() {
this.code = TicketCode.generate();
}

public void updateStatus(TicketStatus status) {
this.status = status;
}

public void expire() {
final ZoneId zoneId = ZoneId.of("GMT-3");
final LocalDateTime now = LocalDateTime.now(zoneId);
private boolean expire() {
final LocalDateTime now = LocalDateTime.now();

if (this.status.isExpired())
throw new ChangeTicketStatusException("Ticket is already expired");
if (now.toLocalDate().isAfter(this.expiredIn)) {
this.status = TicketStatus.EXPIRED;
} else throw new ChangeTicketStatusException("Ticket is out of date to be expired, but you can cancel it.");

return true;
} else return false;
}

public void cancel() {
Expand All @@ -99,11 +88,10 @@ public void consume(Event event) {
throw new TicketConsumeException("Ticket is canceled");
if (this.status.isConsumed())
throw new TicketConsumeException("Ticket is already consumed");
if (this.status.isExpired())
if (this.status.isExpired() || this.expire())
throw new TicketExpiredException(this.getId());

final ZoneId zoneId = ZoneId.of("GMT-3");
final LocalDateTime now = LocalDateTime.now(zoneId);
final LocalDateTime now = LocalDateTime.now();

if (now.toLocalDate().isBefore(this.validIn))
throw new TicketConsumeException("Ticket is not valid yet");
Expand All @@ -113,6 +101,7 @@ public void consume(Event event) {

this.status = TicketStatus.CONSUMED;
this.lastTimeConsumed = now;
this.registerEvent(new ConsumeTicketSuccess(this));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import br.com.ifsp.tickets.app.ticket.TicketService;
import br.com.ifsp.tickets.app.ticket.TicketServiceFactory;
import br.com.ifsp.tickets.domain.event.IEventGateway;
import br.com.ifsp.tickets.domain.event.sale.ITicketSaleGateway;
import br.com.ifsp.tickets.domain.shared.IDomainEventPublisher;
import br.com.ifsp.tickets.domain.ticket.ITicketGateway;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -13,11 +13,13 @@
@Configuration
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class TicketConfig {

private final IEventGateway eventGateway;
private final ITicketGateway ticketGateway;
private final IDomainEventPublisher eventPublisher;

@Bean
public TicketService ticketService() {
return TicketServiceFactory.create(eventGateway, ticketGateway);
return TicketServiceFactory.create(eventGateway, ticketGateway, eventPublisher);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package br.com.ifsp.tickets.infra.shared.event;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class DomainEventListener implements ApplicationListener<InfraAppEvent> {

private final ObjectMapper objectMapper;

@Override
public void onApplicationEvent(@NotNull InfraAppEvent event) {
final String eventJson;
try {
eventJson = this.objectMapper.writeValueAsString(event);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}

final String message = "domain event received: " + eventJson;
switch (event.type()) {
case INFO -> log.info(message);
case WARNING -> log.warn(message);
case ERROR, FATAL -> log.error(message);
case DEBUG -> log.debug(message);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package br.com.ifsp.tickets.infra.shared.event;

import br.com.ifsp.tickets.domain.shared.IDomainEvent;
import br.com.ifsp.tickets.domain.shared.IDomainEventPublisher;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class DomainEventPublisher implements IDomainEventPublisher {

private final ApplicationEventPublisher publisher;

@Override
public void publishEvent(IDomainEvent event) {
this.publisher.publishEvent(new InfraAppEvent(this, event));
}
}
Loading

0 comments on commit e056de3

Please sign in to comment.