Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5ce2926
Seat and Room domain models added
Jun 29, 2019
9de1ac6
Movie and screening domain models added
Jun 29, 2019
e8a260b
Reservation and ticket domain models added
Jun 30, 2019
5902980
application.properties for h2 database set
Jun 30, 2019
6883415
Domain model schema sql script added
Jun 30, 2019
582402e
Sql script with initial data added
Jun 30, 2019
cadc4a1
Screening repository added
Jun 30, 2019
6b61c31
ScreeningService added
Jun 30, 2019
0720dd9
MapStruct dependency to build.gradle added
Jun 30, 2019
83f7ab2
ShortScreeningDTO added
Jun 30, 2019
7536fd7
ScreeningMapper added
Jun 30, 2019
595bae1
ScreeningController added
Jun 30, 2019
7d57e4d
RoomSeatRepository and method for available seats added
Jun 30, 2019
d575ebf
GET screening details and screening DTO added
Jun 30, 2019
d8d8083
DTOs for post reservation and mappers added
Jul 1, 2019
c5eaef3
Reservation controller and service added
Jul 1, 2019
0c5b331
isOnEdge attribute to seat domain model added
Jul 2, 2019
49c36a8
Find all seats in row and find occupied seats in row repository metho…
Jul 2, 2019
af7e011
ReservationValidationService added with parameterized test
mateuszkp96 Jul 3, 2019
d89cb63
TicketType service added to calculate payment for tickets
mateuszkp96 Jul 3, 2019
1811959
Validation in save method of ReservationService added
mateuszkp96 Jul 3, 2019
4339486
Cascade persist in reservation for Tickets added
mateuszkp96 Jul 3, 2019
a584589
Exceptions in services changed to ArgumentNotValidException with Http…
mateuszkp96 Jul 3, 2019
ab5b7f0
build jar added
mateuszkp96 Jul 3, 2019
1596c81
execution permission for gradlew added
mateuszkp96 Jul 4, 2019
29fbdf3
expected exception changed in test of seatvalidationService
mateuszkp96 Jul 4, 2019
d42503b
Build and run shell script added
mateuszkp96 Jul 4, 2019
9a2f5a8
Run use cases shell script added
mateuszkp96 Jul 4, 2019
b921619
run use cases shell script without jq json formating added
mateuszkp96 Jul 4, 2019
cb5f3c7
Create README.md
mateuszkp96 Jul 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# TicketBookingApp
Seat reservation system for a multiplex.

### Technologies used
* Java
* Spring Boot
* H2 Database

### Assumptions
* There cannot be a single place left over in a row between two already reserved places. The seat that is on the edge can always be reserved.
* Seats can be booked at latest 15 minutes before the screening begins.
* Reservation expiration time is set to 10 minutes before the screening begins.

### Build
Application is written in Java JDK version 11. The application uses Gradle wrapper to build and then run app using *java -jar*.

To build and run app type command:
*bash build_and_run_script.sh PORT_NUMBER*
where *PORT_NUMBER* is number of port of localhost on which app runs. When *PORT_NUMBER* is not defined, application starts on port 8080.

If is problem to run script, check if file *gradlew* has a execution permision.

### Run use case
There are two scripts which run use case.

First is *run_use_cases.sh* which uses **jq** for pretty JSON formatting.

Second is *run_use_cases_without_jq.sh* which print JSON in normal way (as String).

To run use case type command:
*bash SCRIPT_NAME PORT_NUMBER*
where *PORT_NUMBER* is number of port of localhost on which app runs. When *PORT_NUMBER* is not defined, application make calls on port 8080.

11 changes: 10 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
apply plugin: 'io.spring.dependency-management'

group = 'com.mkopec'
version = '0.0.1-SNAPSHOT'
version = '0.0.1'
sourceCompatibility = '11'

configurations {
Expand All @@ -19,11 +19,20 @@ repositories {
mavenCentral()
}

bootJar {
launchScript()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

compile group: 'org.mapstruct', name: 'mapstruct-jdk8', version: '1.2.0.Final'
compileOnly 'org.mapstruct:mapstruct-processor:1.2.0.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.2.0.Final'

}
7 changes: 7 additions & 0 deletions build_and_run_script.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env sh

passed_port=$1
default_port=8080
port=${passed_port:-$default_port}

./gradlew build && java -jar build/libs/ticket-booking-app-0.0.1.jar --server.port=$((port))
Empty file modified gradlew
100644 → 100755
Empty file.
77 changes: 77 additions & 0 deletions run_use_cases.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
generate_post_data()
{
cat <<EOF
{
"screeningID": "$screening_id",
"firstName": "$first_name",
"lastName": "$last_name",
"tickets": [
{
"seatID": "$seat_1",
"ticketTypeID": "$type_2"
},
{
"seatID": "$seat_2",
"ticketTypeID": "$type_3"
},
{
"seatID": "$seat_3",
"ticketTypeID": "$type_1"
},
{
"seatID": "$seat_4",
"ticketTypeID": "$type_1"
}
]
}
EOF
}


passed_port=$1
default_port=8080
port=${passed_port:-$default_port}

host="localhost:$port"

selected_date=`date --date="next day" +%Y-%m-%d`
time_start="14:00:00"
time_end="22:00:00"

list_movies_endpoint="$host/screening?date=$selected_date&timeStart=$time_start&timeEnd=$time_end"

echo "Selected date: $selected_date"
echo "Time interval start: $time_start"
echo "Time interval end: $time_end"
echo "The endpoint: $list_movies_endpoint"

curl -v ${list_movies_endpoint} | jq .

screening_id=2
echo "Selected screening: $screening_id"

curl -v "$host/screening/$screening_id" | jq .


first_name="Ola"
last_name="Kalinowska-Jaworska"
type_1=1
type_2=2
type_3=3

seat_1=1
seat_2=2
seat_3=11
seat_4=12



reservation_endpoint="$host/reservation"
echo "Post reservation request body"
echo $(generate_post_data) | jq .

curl -v \
-H 'Content-Type: application/json' \
-d "$(generate_post_data)" \
${reservation_endpoint} | jq .
78 changes: 78 additions & 0 deletions run_use_cases_without_jq.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
generate_post_data()
{
cat <<EOF
{
"screeningID": "$screening_id",
"firstName": "$first_name",
"lastName": "$last_name",
"tickets": [
{
"seatID": "$seat_1",
"ticketTypeID": "$type_2"
},
{
"seatID": "$seat_2",
"ticketTypeID": "$type_3"
},
{
"seatID": "$seat_3",
"ticketTypeID": "$type_1"
},
{
"seatID": "$seat_4",
"ticketTypeID": "$type_1"
}
]
}
EOF
}


passed_port=$1
default_port=8080
port=${passed_port:-$default_port}

host="localhost:$port"

selected_date=`date --date="next day" +%Y-%m-%d`
time_start="14:00:00"
time_end="22:00:00"

list_movies_endpoint="$host/screening?date=$selected_date&timeStart=$time_start&timeEnd=$time_end"

echo "Selected date: $selected_date"
echo "Time interval start: $time_start"
echo "Time interval end: $time_end"
echo "The endpoint: $list_movies_endpoint"

curl -v ${list_movies_endpoint}

screening_id=2
echo "Selected screening: $screening_id"

curl -v "$host/screening/$screening_id"


first_name="Ola"
last_name="Kalinowska-Jaworska"
type_1=1
type_2=2
type_3=3

seat_1=1
seat_2=2
seat_3=11
seat_4=12



reservation_endpoint="$host/reservation"
echo "Post reservation request body"
echo $(generate_post_data)

curl -v \
-H 'Content-Type: application/json' \
-d "$(generate_post_data)" \
${reservation_endpoint}

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.mkopec.ticketbookingapp.controller;

import com.mkopec.ticketbookingapp.domain.Reservation;
import com.mkopec.ticketbookingapp.dtos.ReservationPostDTO;
import com.mkopec.ticketbookingapp.dtos.ShortReservationDTO;
import com.mkopec.ticketbookingapp.mapper.ReservationMapper;
import com.mkopec.ticketbookingapp.service.ReservationService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/reservation")
public class ReservationController {
private final ReservationService service;
private final ReservationMapper mapper;

@PostMapping
public ShortReservationDTO postReservation(@RequestBody ReservationPostDTO postDTO) {
Reservation reservation = mapper.toReservation(postDTO);
return mapper.toShortReservationDTO(service.saveReservation(reservation));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nie jestem fanem mapperów, ale spoko. Generalnie do service'u mogłoby przyjść DTO i mogłoby być zwrócone DTO i byłoby OK

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.mkopec.ticketbookingapp.controller;

import com.mkopec.ticketbookingapp.dtos.ScreeningDTO;
import com.mkopec.ticketbookingapp.dtos.ShortScreeningDTO;
import com.mkopec.ticketbookingapp.mapper.RoomSeatMapper;
import com.mkopec.ticketbookingapp.mapper.ScreeningMapper;
import com.mkopec.ticketbookingapp.service.ScreeningService;
import lombok.RequiredArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/screening")
public class ScreeningController {
private final ScreeningService service;
private final ScreeningMapper mapper;

private final RoomSeatMapper roomSeatMapper;

@GetMapping
public List<ShortScreeningDTO> getScreeningsInDayAndTimeInterval(
@RequestParam("date") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date,
@RequestParam("timeStart") @DateTimeFormat(pattern = "HH:mm:ss") LocalTime timeStart,
@RequestParam("timeEnd") @DateTimeFormat(pattern = "HH:mm:ss") LocalTime timeEnd) {

return mapper.toShortScreeningDTOs(service.findAllInDayAndTimeInterval(date, timeStart, timeEnd));
}

@GetMapping("/{screeningID}")
public ScreeningDTO getScreeningDetails(@PathVariable Long screeningID) {
ScreeningDTO screeningDTO = mapper.toScreeningDTO(service.findByID(screeningID));
screeningDTO.setAvailableSeats(roomSeatMapper.toRoomSeatDTOs(service.findAvailableSeats(screeningID)));
return screeningDTO;
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/mkopec/ticketbookingapp/domain/Movie.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.mkopec.ticketbookingapp.domain;

import lombok.Data;

import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import static javax.persistence.GenerationType.IDENTITY;

@Entity
@Data
public class Movie {

@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;

private String title;

private String director;

@Basic
private java.time.LocalTime length;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import? i po co ten @Basic ?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nie wiem o który import tutaj chodzi, prawdopodobnie o import static. Słyszałem, że dobrą praktyką jest import statyczny tych stałych (zwłaszcza gdy w jednej adnotacji może ich występować więcej). A adnotacja @basic to chyba pozostałość po wcześniejszych wersjach springa, ogólnie wzorowałem się na: https://www.baeldung.com/hibernate-date-time a tam pola oznaczone są tą adnotacją. W springu 5 działa dobrze już bez tej adnotacji.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chodziło mi o to, że tą klasę LocalTime można zaimportować i nie mieć kropeczek wtedy (java.time można się pozbyć)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Racja, mój błąd

}
39 changes: 39 additions & 0 deletions src/main/java/com/mkopec/ticketbookingapp/domain/Reservation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.mkopec.ticketbookingapp.domain;

import lombok.Data;

import javax.persistence.*;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

importy z gwiazdkami nie są preferowane - dobre IDE i tak ci je schowa, a jak chcesz zobaczyć jaka klasa jest importowana to tak nie zobaczysz :(

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nie wiedziałem o tym, ale to kwestia ustawień IDE. Teraz już będę pamiętał

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

import static javax.persistence.CascadeType.PERSIST;
import static javax.persistence.FetchType.LAZY;
import static javax.persistence.GenerationType.IDENTITY;

@Entity
@Data
public class Reservation {

@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "screening_id")
private Screening screening;

@Column(name = "first_name")
private String firstName;

@Column(name = "last_name")
private String lastName;

@Basic
private LocalDateTime expirationTime;

private BigDecimal payment;

@OneToMany(mappedBy = "reservation", fetch = LAZY, cascade = PERSIST)
private List<Ticket> tickets;
}
20 changes: 20 additions & 0 deletions src/main/java/com/mkopec/ticketbookingapp/domain/Room.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.mkopec.ticketbookingapp.domain;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import static javax.persistence.GenerationType.IDENTITY;

@Entity
@Data
public class Room {

@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;

private Integer number;
}
Loading