diff --git a/book-my-show/backend/java/bms-monolith/pom.xml b/book-my-show/backend/java/bms-monolith/pom.xml index 2583367..76f9214 100644 --- a/book-my-show/backend/java/bms-monolith/pom.xml +++ b/book-my-show/backend/java/bms-monolith/pom.xml @@ -50,6 +50,11 @@ mysql-connector-j runtime + + org.springframework.boot + spring-boot-starter-validation + + org.projectlombok lombok diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/config/JpaConfig.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/config/JpaConfig.java new file mode 100644 index 0000000..ca00d90 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/config/JpaConfig.java @@ -0,0 +1,16 @@ +package org.lbcc.bms.bms_monolith.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.data.domain.AuditorAware; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +import java.util.Optional; + +@EnableJpaAuditing +public class JpaConfig { + + @Bean + public AuditorAware auditorProvider() { + return () -> Optional.of("system"); // TODO: Replace with code to fetch username when spring-security is implemented. + } +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/constants/BMSConstants.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/constants/BMSConstants.java new file mode 100644 index 0000000..c479131 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/constants/BMSConstants.java @@ -0,0 +1,15 @@ +package org.lbcc.bms.bms_monolith.common.constants; + +public final class BMSConstants { + + private BMSConstants() { + } + + public static final String STATUS_200 = "200"; + public static final String MESSAGE_200 = "Request processed successfully"; + public static final String EVENT_SUCCESS_MESSAGE = "Events fetched successfully"; + public static final String UNEXPECTED_ERROR_MESSAGE = "An unexpected error occurred"; + public static final String UNEXPECTED_ERROR_CODE = "UNEXPECTED_ERROR"; + public static final String EVENT_SERVICE_ERROR = "EVENT_SERVICE_ERROR"; + public static final String INVALID_PAGINATION_PARAMETER = "INVALID_PAGINATION_PARAMETER"; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/BaseAuditingEntity.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/BaseAuditingEntity.java index f02e651..8c7f0f6 100644 --- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/BaseAuditingEntity.java +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/BaseAuditingEntity.java @@ -1,12 +1,21 @@ package org.lbcc.bms.bms_monolith.common.entity; -import jakarta.persistence.Id; import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.SuperBuilder; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; import java.time.Instant; import java.util.UUID; @@ -17,14 +26,27 @@ @NoArgsConstructor @Setter @SuperBuilder +@EntityListeners(AuditingEntityListener.class) public abstract class BaseAuditingEntity { @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column(columnDefinition = "CHAR(36)") private UUID id; + @CreatedDate + @Column(updatable = false) private Instant createdDate; + + @LastModifiedDate + @Column(insertable = false) private Instant lastModifiedDate; + @CreatedBy + @Column(updatable = false) private String createdBy; + + @LastModifiedBy + @Column(insertable = false) private String lastModifiedBy; } diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/response/ApiErrorResponse.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/response/ApiErrorResponse.java index eb5cfa5..14d2369 100644 --- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/response/ApiErrorResponse.java +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/response/ApiErrorResponse.java @@ -14,7 +14,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @NoArgsConstructor public class ApiErrorResponse { - private boolean success; + @Builder.Default + private boolean success = false; private String message; private String code; @Builder.Default diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/controller/EventController.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/controller/EventController.java new file mode 100644 index 0000000..8c9b5f1 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/controller/EventController.java @@ -0,0 +1,41 @@ +package org.lbcc.bms.bms_monolith.controller; + +import org.lbcc.bms.bms_monolith.common.constants.BMSConstants; +import org.lbcc.bms.bms_monolith.common.response.ApiListResponse; +import org.lbcc.bms.bms_monolith.entity.Event; +import org.lbcc.bms.bms_monolith.service.IEventService; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.data.web.SortDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping(path="/events") +public class EventController { + + private final IEventService iEventService; + + public EventController(IEventService iEventService) { + this.iEventService = iEventService; + } + + @GetMapping + public ResponseEntity> getAllEvents( + @PageableDefault(page = 0, size = 10) + @SortDefault(sort = "startDate", direction = Sort.Direction.DESC) Pageable pageable) { + + Page eventsPage = iEventService.getAllEvents(pageable); + + ApiListResponse response = ApiListResponse.builder() + .success(true) + .message(BMSConstants.EVENT_SUCCESS_MESSAGE) + .setPage(eventsPage) + .build(); + + return ResponseEntity.ok(response); + } + +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Event.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Event.java new file mode 100644 index 0000000..5e5f6ab --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Event.java @@ -0,0 +1,56 @@ +package org.lbcc.bms.bms_monolith.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.CascadeType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.FetchType; +import jakarta.persistence.Column; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.lbcc.bms.bms_monolith.common.entity.BaseAuditingEntity; + +import java.time.Instant; +import java.util.List; + +@Entity +@Getter @Setter +@AllArgsConstructor @NoArgsConstructor +public class Event extends BaseAuditingEntity { + + @Size(min=10, max=50, message = "Title must be between 10 and 50 characters.") + @Column(nullable = false, length = 50) + private String title; + + @Column(length = 1000) + private String description; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "vendor_id") + private Vendor vendor; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "venue_id") + private Venue venue; + + @OneToMany(mappedBy = "event", cascade = CascadeType.ALL) + private List show; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "event_type_id") + private EventType eventType; + + @Column(nullable = false) + private Instant startDate; + + @Column(length = 255) + private String thumbnailUrl; + + @Column(nullable = false) + private Instant endDate; + +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/EventShow.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/EventShow.java new file mode 100644 index 0000000..23ad904 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/EventShow.java @@ -0,0 +1,50 @@ +package org.lbcc.bms.bms_monolith.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.Enumerated; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.EnumType; +import jakarta.persistence.CascadeType; +import jakarta.persistence.FetchType; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; + +import lombok.experimental.SuperBuilder; +import org.lbcc.bms.bms_monolith.common.entity.BaseAuditingEntity; +import org.lbcc.bms.bms_monolith.entity.enums.Genre; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +@Entity +@SuperBuilder +public class EventShow extends BaseAuditingEntity { + + @ElementCollection(targetClass = Genre.class) + @CollectionTable(name = "show_genre", joinColumns = @JoinColumn(name = "show_id")) + @Column(name = "genre") + @Enumerated(EnumType.STRING) + private List genres; + + @NotNull + private Instant startDate; + + @NotNull + private Instant endDate; + + @ManyToOne + @JoinColumn(name = "event_id", nullable = false) + private Event event; + + @OneToMany(mappedBy = "show", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private List seatInShows; + + @OneToMany(mappedBy = "show", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private List seatTypeInShows; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/EventType.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/EventType.java new file mode 100644 index 0000000..181321b --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/EventType.java @@ -0,0 +1,18 @@ +package org.lbcc.bms.bms_monolith.entity; + +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.lbcc.bms.bms_monolith.common.entity.BaseAuditingEntity; + +@Entity +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class EventType extends BaseAuditingEntity { + + private String label; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Seat.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Seat.java new file mode 100644 index 0000000..e80426e --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Seat.java @@ -0,0 +1,34 @@ +package org.lbcc.bms.bms_monolith.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Enumerated; +import jakarta.persistence.EnumType; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.lbcc.bms.bms_monolith.common.entity.BaseAuditingEntity; +import org.lbcc.bms.bms_monolith.entity.enums.OperationalStatus; + +@Entity +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Seat extends BaseAuditingEntity { + + @ManyToOne + @JoinColumn(name = "seat_type_id") + private SeatType seatType; + + @ManyToOne + @JoinColumn(name = "venue_id") + private Venue venue; + + @Enumerated(EnumType.STRING) + private OperationalStatus operationalStatus; + + private String label; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/SeatInShow.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/SeatInShow.java new file mode 100644 index 0000000..80aef33 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/SeatInShow.java @@ -0,0 +1,32 @@ +package org.lbcc.bms.bms_monolith.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Enumerated; +import jakarta.persistence.EnumType; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.lbcc.bms.bms_monolith.common.entity.BaseAuditingEntity; +import org.lbcc.bms.bms_monolith.entity.enums.BookingStatus; + +@Entity +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class SeatInShow extends BaseAuditingEntity { + + @ManyToOne + @JoinColumn(name = "seat_type_in_show_id") + private SeatTypeInShow seatTypeInShow; + + @ManyToOne + @JoinColumn(name = "show_id") + private EventShow show; + + @Enumerated(EnumType.STRING) + private BookingStatus bookingStatus; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/SeatType.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/SeatType.java new file mode 100644 index 0000000..3975c2b --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/SeatType.java @@ -0,0 +1,25 @@ +package org.lbcc.bms.bms_monolith.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.lbcc.bms.bms_monolith.common.entity.BaseAuditingEntity; + +import java.math.BigDecimal; + +@Entity +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class SeatType extends BaseAuditingEntity { + + private String label; + + @Column(precision = 10, scale = 2) + private BigDecimal price; + +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/SeatTypeInShow.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/SeatTypeInShow.java new file mode 100644 index 0000000..c56bf6a --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/SeatTypeInShow.java @@ -0,0 +1,26 @@ +package org.lbcc.bms.bms_monolith.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Column; +import lombok.experimental.SuperBuilder; +import org.lbcc.bms.bms_monolith.common.entity.BaseAuditingEntity; + +import java.math.BigDecimal; + +@Entity +@SuperBuilder +public class SeatTypeInShow extends BaseAuditingEntity { + + @ManyToOne + @JoinColumn(name = "seat_type_id") + private SeatType seatType; + + @ManyToOne + @JoinColumn(name = "show_id") + private EventShow show; + + @Column(precision = 10, scale = 2) + private BigDecimal price; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Vendor.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Vendor.java new file mode 100644 index 0000000..0b7ec1f --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Vendor.java @@ -0,0 +1,38 @@ +package org.lbcc.bms.bms_monolith.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.Enumerated; +import jakarta.persistence.EnumType; +import jakarta.persistence.Column; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.lbcc.bms.bms_monolith.common.entity.BaseAuditingEntity; +import org.lbcc.bms.bms_monolith.entity.enums.VendorStatus; + +@Entity +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Vendor extends BaseAuditingEntity { + + private String name; + private String contactNumber; + private String email; + private String address; + private String website; + + @Enumerated(EnumType.STRING) + private VendorStatus status; + + @Column(nullable = true, length = 100) + @Size(min = 10, max = 100, message = "Registration date must be between 10 and 100 characters.") + private String registrationDate; + + @Column(nullable = false, length = 255) + @Size(min = 25, max = 255, message = "Logo URL must be between 25 and 255 characters.") + private String logoUrl; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Venue.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Venue.java new file mode 100644 index 0000000..2c49496 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/Venue.java @@ -0,0 +1,44 @@ +package org.lbcc.bms.bms_monolith.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.Enumerated; +import jakarta.persistence.EnumType; +import jakarta.persistence.CascadeType; +import jakarta.persistence.FetchType; +import jakarta.persistence.OneToMany; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.lbcc.bms.bms_monolith.common.entity.BaseAuditingEntity; +import org.lbcc.bms.bms_monolith.entity.enums.OperationalStatus; +import org.lbcc.bms.bms_monolith.entity.enums.VenueType; + +import java.math.BigDecimal; +import java.util.List; + +@Entity +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Venue extends BaseAuditingEntity { + + private String address; + private BigDecimal latitude; + private BigDecimal longitude; + private Integer totalSeatingCapacity; + + @Enumerated(EnumType.STRING) + private VenueType venueType; + + @Enumerated(EnumType.STRING) + private OperationalStatus operationalStatus; + + @OneToMany(mappedBy = "venue", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @NotNull(message = "Seats list cannot be null.") + @Size(min = 1, max = 100, message = "Seats list must contain between 1 and 100 seats.") + private List seats; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/BookingStatus.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/BookingStatus.java new file mode 100644 index 0000000..c8e3c16 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/BookingStatus.java @@ -0,0 +1,7 @@ +package org.lbcc.bms.bms_monolith.entity.enums; + +public enum BookingStatus { + BOOKED, + AVAILABLE, + NOT_AVAILABLE; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/Genre.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/Genre.java new file mode 100644 index 0000000..61853ef --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/Genre.java @@ -0,0 +1,9 @@ +package org.lbcc.bms.bms_monolith.entity.enums; + +public enum Genre { + COMEDY, + DRAMA, + ACTION, + THRILLER, + HORROR; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/OperationalStatus.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/OperationalStatus.java new file mode 100644 index 0000000..e98f001 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/OperationalStatus.java @@ -0,0 +1,6 @@ +package org.lbcc.bms.bms_monolith.entity.enums; + +public enum OperationalStatus { + OPERATIONAL, + NON_OPERATIONAL; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/VendorStatus.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/VendorStatus.java new file mode 100644 index 0000000..1543c19 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/VendorStatus.java @@ -0,0 +1,7 @@ +package org.lbcc.bms.bms_monolith.entity.enums; + +public enum VendorStatus { + ACTIVE, + INACTIVE, + SUSPENDED; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/VenueType.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/VenueType.java new file mode 100644 index 0000000..bcc5056 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/entity/enums/VenueType.java @@ -0,0 +1,6 @@ +package org.lbcc.bms.bms_monolith.entity.enums; + +public enum VenueType { + AUDITORIUM, + STADIUM; +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/exception/EventServiceException.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/exception/EventServiceException.java new file mode 100644 index 0000000..1c1b7d5 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/exception/EventServiceException.java @@ -0,0 +1,8 @@ +package org.lbcc.bms.bms_monolith.exception; + +public class EventServiceException extends RuntimeException { + + public EventServiceException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/exception/GlobalExceptionHandler.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..bc90f06 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/exception/GlobalExceptionHandler.java @@ -0,0 +1,44 @@ +package org.lbcc.bms.bms_monolith.exception; + +import org.lbcc.bms.bms_monolith.common.constants.BMSConstants; +import org.lbcc.bms.bms_monolith.common.response.ApiErrorResponse; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +import java.time.Instant; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(EventServiceException.class) + public ResponseEntity handleEventServiceException(EventServiceException ex) { + return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, ex.getMessage(), BMSConstants.EVENT_SERVICE_ERROR); + } + + @ExceptionHandler(InvalidPaginationParameterException.class) + public ResponseEntity handleInvalidPaginationParameter(InvalidPaginationParameterException ex) { + ApiErrorResponse errorResponse = ApiErrorResponse.builder() + .success(false) + .message(ex.getMessage()) + .code(BMSConstants.INVALID_PAGINATION_PARAMETER) + .timestamp(Instant.now()) + .build(); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleGlobalException(Exception ex) { + return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, BMSConstants.UNEXPECTED_ERROR_MESSAGE, BMSConstants.UNEXPECTED_ERROR_CODE); + } + + private ResponseEntity buildErrorResponse(HttpStatus status, String message, String code) { + ApiErrorResponse errorResponse = ApiErrorResponse.builder() + .message(message) + .code(code) + .build(); + return ResponseEntity.status(status).body(errorResponse); + } +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/exception/InvalidPaginationParameterException.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/exception/InvalidPaginationParameterException.java new file mode 100644 index 0000000..4a3d3cc --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/exception/InvalidPaginationParameterException.java @@ -0,0 +1,8 @@ +package org.lbcc.bms.bms_monolith.exception; + +public class InvalidPaginationParameterException extends RuntimeException { + + public InvalidPaginationParameterException(String message) { + super(message); + } +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/repository/IEventRepository.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/repository/IEventRepository.java new file mode 100644 index 0000000..efa9979 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/repository/IEventRepository.java @@ -0,0 +1,17 @@ +package org.lbcc.bms.bms_monolith.repository; + +import org.lbcc.bms.bms_monolith.entity.Event; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface IEventRepository extends JpaRepository { + + @Query("SELECT e FROM Event e JOIN FETCH e.vendor JOIN FETCH e.venue JOIN FETCH e.eventType ORDER BY e.startDate DESC") + Page findAllWithDetails(Pageable pageable); +} \ No newline at end of file diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/service/IEventService.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/service/IEventService.java new file mode 100644 index 0000000..f3c62c7 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/service/IEventService.java @@ -0,0 +1,10 @@ +package org.lbcc.bms.bms_monolith.service; + +import org.lbcc.bms.bms_monolith.entity.Event; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface IEventService { + + Page getAllEvents(Pageable pageable); +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/service/impl/EventServiceImpl.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/service/impl/EventServiceImpl.java new file mode 100644 index 0000000..ce8e843 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/service/impl/EventServiceImpl.java @@ -0,0 +1,36 @@ +package org.lbcc.bms.bms_monolith.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.lbcc.bms.bms_monolith.entity.Event; +import org.lbcc.bms.bms_monolith.exception.EventServiceException; +import org.lbcc.bms.bms_monolith.repository.IEventRepository; +import org.lbcc.bms.bms_monolith.service.IEventService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class EventServiceImpl implements IEventService { + + private final IEventRepository IEventRepository; + + @Autowired + public EventServiceImpl(IEventRepository IEventRepository) { + this.IEventRepository = IEventRepository; + } + + @Override + public Page getAllEvents(Pageable pageable) { + try { + Page eventsPage = IEventRepository.findAllWithDetails(pageable); + log.info("Fetched {} events with pageable {}", eventsPage.getTotalElements(), pageable); + + return eventsPage; + } catch (Exception e) { + log.error("Error fetching events: {}", e.getMessage()); + throw new EventServiceException("Failed to fetch events", e); + } + } +} diff --git a/book-my-show/backend/java/bms-monolith/src/main/resources/application.properties b/book-my-show/backend/java/bms-monolith/src/main/resources/application.properties deleted file mode 100644 index c41048a..0000000 --- a/book-my-show/backend/java/bms-monolith/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=BMS Monolith diff --git a/book-my-show/backend/java/bms-monolith/src/main/resources/application.yaml b/book-my-show/backend/java/bms-monolith/src/main/resources/application.yaml new file mode 100644 index 0000000..5ac2e86 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/main/resources/application.yaml @@ -0,0 +1,19 @@ +spring: + application: + name: BMS Monolith + datasource: + url: jdbc:mysql://localhost:3306/bms + username: ${DB_USERNAME:root} + password: ${DB_PASSWORD:root} + driver-class-name: com.mysql.cj.jdbc.Driver + jpa: + show-sql: true + hibernate: + ddl-auto: update + sql: + init: + mode: always + +server: + servlet: + context-path: /api/v1 \ No newline at end of file diff --git a/book-my-show/backend/java/bms-monolith/src/test/java/org/lbcc/bms/bms_monolith/controller/EventControllerTest.java b/book-my-show/backend/java/bms-monolith/src/test/java/org/lbcc/bms/bms_monolith/controller/EventControllerTest.java new file mode 100644 index 0000000..e528b5e --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/test/java/org/lbcc/bms/bms_monolith/controller/EventControllerTest.java @@ -0,0 +1,107 @@ +package org.lbcc.bms.bms_monolith.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.lbcc.bms.bms_monolith.common.response.ApiListResponse; +import org.lbcc.bms.bms_monolith.entity.Event; +import org.lbcc.bms.bms_monolith.exception.EventServiceException; +import org.lbcc.bms.bms_monolith.service.IEventService; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.http.HttpStatus; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.UUID; +import static org.mockito.Mockito.when; + +public class EventControllerTest { + + @Mock + private IEventService eventService; + + @InjectMocks + private EventController eventController; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testGetAllEventsSuccess() { + Event event = new Event(); + event.setId(UUID.randomUUID()); + event.setTitle("Sample Event"); + + Page eventPage = new PageImpl<>(List.of(event), PageRequest.of(0, 10), 1); + + when(eventService.getAllEvents(any(Pageable.class))).thenReturn(eventPage); + + ResponseEntity> response = eventController.getAllEvents(PageRequest.of(0, 10)); + + ApiListResponse expectedResponse = ApiListResponse.builder() + .success(true) + .message("Events fetched successfully") + .data(eventPage.getContent()) + .totalItems((int) eventPage.getTotalElements()) + .totalPages(eventPage.getTotalPages()) + .currentPage(eventPage.getNumber()) + .pageSize(eventPage.getSize()) + .hasNextPage(eventPage.hasNext()) + .hasPreviousPage(eventPage.hasPrevious()) + .build(); + + // Assertions to verify response + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(expectedResponse.getMessage(), response.getBody().getMessage()); + assertEquals(expectedResponse.isSuccess(), response.getBody().isSuccess()); + assertEquals(1, response.getBody().getTotalItems()); + } + + @Test + void testGetAllEventsInvalidPageParameters() { + Page emptyPage = Page.empty(PageRequest.of(0, 10)); + + when(eventService.getAllEvents(PageRequest.of(0, 10))).thenReturn(emptyPage); + + ResponseEntity> response = eventController.getAllEvents(PageRequest.of(0, 10)); + + ApiListResponse expectedResponse = ApiListResponse.builder() + .success(false) + .message("Invalid page or size parameters") + .data(emptyPage.getContent()) + .totalItems((int) emptyPage.getTotalElements()) + .totalPages(emptyPage.getTotalPages()) + .currentPage(0) + .pageSize(emptyPage.getSize()) + .hasNextPage(emptyPage.hasNext()) + .hasPreviousPage(emptyPage.hasPrevious()) + .build(); + + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals("Events fetched successfully", response.getBody().getMessage()); + assertEquals(true, response.getBody().isSuccess()); + } + + @Test + void testGetAllEventsExceptionHandling() { + when(eventService.getAllEvents(any(Pageable.class))) + .thenThrow(new EventServiceException("Failed to fetch events", new RuntimeException())); + + EventServiceException exception = assertThrows(EventServiceException.class, () -> { + eventController.getAllEvents(PageRequest.of(0, 10)); + }); + + assertEquals("Failed to fetch events", exception.getMessage()); + } + +} diff --git a/book-my-show/backend/java/bms-monolith/src/test/java/org/lbcc/bms/bms_monolith/service/EventServiceImplTest.java b/book-my-show/backend/java/bms-monolith/src/test/java/org/lbcc/bms/bms_monolith/service/EventServiceImplTest.java new file mode 100644 index 0000000..606ae21 --- /dev/null +++ b/book-my-show/backend/java/bms-monolith/src/test/java/org/lbcc/bms/bms_monolith/service/EventServiceImplTest.java @@ -0,0 +1,68 @@ +package org.lbcc.bms.bms_monolith.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.lbcc.bms.bms_monolith.entity.Event; +import org.lbcc.bms.bms_monolith.exception.EventServiceException; +import org.lbcc.bms.bms_monolith.repository.IEventRepository; +import org.lbcc.bms.bms_monolith.service.impl.EventServiceImpl; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +public class EventServiceImplTest { + + @Mock + private IEventRepository IEventRepository; + + @InjectMocks + private EventServiceImpl eventService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testGetAllEventsSuccess() { + Event event = new Event(); + event.setTitle("Sample Event"); + event.setDescription("Sample Description"); + + Pageable pageable = PageRequest.of(0, 10); + Page eventPage = new PageImpl<>(Collections.singletonList(event), pageable, 1); + + when(IEventRepository.findAllWithDetails(pageable)).thenReturn(eventPage); + + Page response = eventService.getAllEvents(pageable); + + assertNotNull(response); + assertEquals(1, response.getTotalElements()); + assertEquals("Sample Event", response.getContent().get(0).getTitle()); + assertEquals("Sample Description", response.getContent().get(0).getDescription()); + } + + @Test + void testGetAllEventsExceptionHandling() { + when(IEventRepository.findAllWithDetails(any(Pageable.class))).thenThrow(new RuntimeException("Database error")); + + EventServiceException exception = assertThrows(EventServiceException.class, () -> { + eventService.getAllEvents(PageRequest.of(0, 10)); + }); + + assertEquals("Failed to fetch events", exception.getMessage()); + } +}