-
Notifications
You must be signed in to change notification settings - Fork 0
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
Full changes log merge from beginning of repo #1
base: pull_request_branch
Are you sure you want to change the base?
Changes from all commits
b4714fa
f8f862c
07ac26b
1a809c5
e2260cb
dc9b90e
946dc28
051a8c8
b7cd42f
bdcf05a
a45ece7
a2f33b0
444d9a9
e071b25
fbbbbe3
b34036a
9606f52
4c8a000
123330b
6b3c3ac
602d154
f2e6ded
9582832
4d33a8c
4d05730
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#!/bin/bash | ||
|
||
BASE_URL="http://localhost:8080" | ||
|
||
#values used in the requests | ||
DATES=("2023-11-04T04:15:00" "2023-11-10T11:00:00") | ||
SCREENING_IDS=(1 3) | ||
POST_DATA_FILES=("./post_jsons/json_ticket1.json" "./post_jsons/json_ticket2.json") | ||
|
||
HEADERS="-H 'Content-Type:application/json'" | ||
|
||
for ((i=0; i<${#DATES[@]}; i++)); do | ||
# Make a GET request to the "/screening/find-screenings/{date}" endpoint | ||
DATE="${DATES[i]}" | ||
DATE_ENDPOINT="/screening/find-screenings/$DATE" | ||
DATE_URL="$BASE_URL$DATE_ENDPOINT" | ||
curl -X GET $HEADERS "$DATE_URL" | ||
if [ $? -eq 0 ]; then | ||
echo "GET request to $DATE_URL was successful." | ||
else | ||
echo "GET request to $DATE_URL failed." | ||
exit 1 | ||
fi | ||
|
||
# Make a GET request to the "/seat/get-seats/{screeningId}" endpoint | ||
SCREENING_ID="${SCREENING_IDS[i]}" | ||
SEAT_ENDPOINT="/seat/get-seats/$SCREENING_ID" | ||
SEAT_URL="$BASE_URL$SEAT_ENDPOINT" | ||
curl -X GET $HEADERS "$SEAT_URL" | ||
if [ $? -eq 0 ]; then | ||
echo "GET request to $SEAT_URL was successful." | ||
else | ||
echo "GET request to $SEAT_URL failed." | ||
exit 1 | ||
fi | ||
|
||
# Make a POST request to the "/tickets/book-tickets/" endpoint with JSON data from the specified file | ||
POST_DATA_FILE="${POST_DATA_FILES[i]}" | ||
POST_ENDPOINT="/tickets/book-tickets/" | ||
POST_URL="$BASE_URL$POST_ENDPOINT" | ||
curl -X POST $HEADERS -d "@$POST_DATA_FILE" "$POST_URL" | ||
if [ $? -eq 0 ]; then | ||
echo "POST request to $POST_URL was successful." | ||
else | ||
echo "POST request to $POST_URL failed." | ||
exit 1 | ||
fi | ||
done | ||
|
||
echo "All requests completed." |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#!/bin/bash | ||
|
||
CONTAINER_NAME="mysql-container-nussknacker" | ||
|
||
DB_USER="cinema-app" | ||
DB_PASSWORD="EPqKjMPYhwJIeWKVRVQ7" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hasło do bazy danych zapisane jako plain text wygląda groźnie 😨 Czy hasło musi być w repozytorium? Jak mógłbyś to zapisać by nie było zapisane w git'cie ani widoczne dla kogoś kto tego hasła nie ma? 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To prawda, że trochę się zastanawiałem, czy to tak wrzucać, ale to nie jest taki "prawdziwy" secret. Normalnie jest kilka rozwiązań, najbardziej popularne jakie znam, to (przy używaniu apki w dockerze, a ja tego nie zrobiłem), wrzucić secret jako env variable dla kontenera i ustawić w springu jako enviornment variable, np przy użyciu Property. Spring udostępnia też kilka bibliotek takich, jak jasypt czy spring vault pomocnych przy używaniu secretów. |
||
DB_NAME="cinema" | ||
|
||
SQL_SCRIPTS_DIR="../sql_scripts" | ||
|
||
# Check if the Docker container is already running | ||
if [ ! "$(docker ps -q -f name=$CONTAINER_NAME)" ]; then | ||
# Run the MySQL Docker container if it's not already running | ||
docker run --ulimit nofile=6400:6400 -d --name "$CONTAINER_NAME" -e MYSQL_ROOT_PASSWORD="$DB_PASSWORD" -e MYSQL_DATABASE="$DB_NAME" -e MYSQL_USER="$DB_USER" -e MYSQL_PASSWORD="$DB_PASSWORD" -p 3306:3306 mysql:8.0 | ||
|
||
# Wait for the MySQL container to be up and running (adjust the timeout as needed) | ||
timeout=60 | ||
while ! docker exec "$CONTAINER_NAME" mysql -u"$DB_USER" -p"$DB_PASSWORD" -e "SELECT 1;" &> /dev/null; do | ||
timeout=$((timeout - 1)) | ||
if [ "$timeout" -le 0 ]; then | ||
echo "Timeout waiting for MySQL container to start." | ||
exit 1 | ||
fi | ||
sleep 5 | ||
done | ||
fi | ||
|
||
for SCRIPT in "$SQL_SCRIPTS_DIR"/*.sql; do | ||
SCRIPT_NAME=$(basename "$SCRIPT") | ||
Comment on lines
+28
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Znasz może jakieś narzędzia/biblioteki, które pomagają zarządzać wersjonowaniem bazy danych? Np. gdyby zadeklarowane przez ciebie There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Znam flyway i Liquibase, korzystałem trochę z Liquibase z tych dwóch. Nie wiem, czy jest tam jakaś opcja, żeby bezpośrednio zmienić plik, chyba nie. Można np zrobić changeset liquibase, dodać ten skrypcik jako referencję i wtedy już zmieniać rzeczy za pomocą liquibase. Bazowy skrypt pozostaje jako podstawa. Aktualnie gwarancję, że pliki będą wykonane w prawidłowej kolejności daje to, że akurat ułożyły się alfabetycznie. Narzędziami do wersjonowania można sobie to zapewnić niezależnie od tego. |
||
|
||
docker exec -i "$CONTAINER_NAME" mysql -u"$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" < "$SCRIPT" | ||
|
||
if [ $? -eq 0 ]; then | ||
echo "Script $SCRIPT_NAME executed successfully." | ||
else | ||
echo "Error executing script $SCRIPT_NAME." | ||
exit 1 | ||
fi | ||
done | ||
|
||
echo "All SQL scripts executed successfully." |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[{ | ||
"name":"John", | ||
"surname":"Doe", | ||
"screeningId":1, | ||
"screeningDate":"2023-11-04T04:15:00", | ||
"seatId":1, | ||
"type":"ADULT" | ||
}, | ||
{ | ||
"name":"Andrzej", | ||
"surname":"Brzęczyszczykiewicz", | ||
"screeningId":1, | ||
"screeningDate":"2023-11-04T04:15:00", | ||
"seatId":2, | ||
"type":"CHILD" | ||
} | ||
] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[{ | ||
"name":"John", | ||
"surname":"Doe", | ||
"screeningId":3, | ||
"screeningDate":"2023-11-10T11:00:00", | ||
"seatId":11, | ||
"type":"ADULT" | ||
}] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
To run the app: | ||
|
||
Please install docker, java 21, maven. | ||
|
||
Go to /bash_scripts. | ||
First run the db_setup.bash script - it should start a docker container with mysql 8.0 running. The container is | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fajnie, że napisałeś skrypt :) a dlaczego nie zdecydowałeś się na użycie There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mvn spring-boot::run to jest moje preferowane podejście przy takich szybkich odpaleniach, nie trzeba nic konfigurować na classpath, zwłaszcza jeśli chodzi o zewnętrzne pliki. Budowanie jara i docker-compose czasem wymagają jakiegoś grzebania, a nie bardzo miałem na to czas. Myślę, że w tym przypadku są to równie eleganckie rozwiązania, maven odpala się zwykle szybciej niż docker i bez problemu. |
||
scheduled for 3306 port (default docker). Then the script will attempt to populate the db with initial tables and values. | ||
If everything is ok, "All SQL scripts executed successfully." Should be printed. | ||
|
||
If the docker container runs properly and the db is filled, run (consider a detached terminal) | ||
mvn spring-boot::run | ||
in the repo directory, it should start the app on localhost:8080. | ||
|
||
For a live use case, please run app_preview.bash - it will run two sequences of how the app could be used. If everything | ||
is ok, the respective curl requests and responses should appear, ending with "All requests completed." | ||
|
||
Feel free to fiddle around with the app, hopefully you break something :) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,25 @@ | |
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-web</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.fasterxml.jackson.datatype</groupId> | ||
<artifactId>jackson-datatype-jsr310</artifactId> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-actuator</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.springfox</groupId> | ||
<artifactId>springfox-boot-starter</artifactId> | ||
<version>3.0.0</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-validation</artifactId> | ||
<version>3.1.5</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.mysql</groupId> | ||
<artifactId>mysql-connector-j</artifactId> | ||
|
@@ -41,7 +59,21 @@ | |
<artifactId>spring-boot-starter-test</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
<dependency> | ||
<groupId>org.springframework.data</groupId> | ||
<artifactId>spring-data-jpa</artifactId> | ||
<version>3.1.4</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.h2database</groupId> | ||
<artifactId>h2</artifactId> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fajnie, że używasz in-memory db - h2 👍 |
||
</dependency> | ||
<dependency> | ||
<groupId>junit</groupId> | ||
<artifactId>junit</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
About the app: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dlaczego wybrałeś README jako There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lubię .txt :) Różnica jest taka, że w markdownie można dodać lepszy formatting i np hyperlinki. Trochę ładniej możnaby zrobić. |
||
|
||
I have treated this task more as a prototype app than production-grade code. Following this philosophy, I wanted to | ||
have a cleanly designed app that is decoupled and open for extension, but only provides minimal, required, functionality. | ||
|
||
This approach is also applicable to testing - I have written some sanity tests for the main logic, places where stuff can go wrong | ||
and some integration tests for the database. I've tried to come up with test cases that could break the app, | ||
but please don't expect 100% coverage and a bulletproof app, as that would take a lot of time and I felt it wouldn't | ||
really make sense. | ||
|
||
I implemented all the functionalities required in the pdf file (TouK_recruitment_task_ticket_booking_app_basic). | ||
I assumed that reserving tickets is different from buying tickets, so the app provides 3 states for a seat - | ||
AVAILABLE, RESERVED, BOUGHT. Reservations are valid for 24 hours. Every day at 2AM, a task runner deletes outdated | ||
reservations from the database and frees the reserved seats. | ||
|
||
As for the database - I have used a relational db (MySQL, but this is no personal preference). The entities are pretty | ||
heavily constrained and I don't think there's much redundancy, but some logic is not enforced by the db - for instance, | ||
there is a RoomRowEntity which has a numberOfSeats field, but the number of actual seats referencing that RoomRow isn't | ||
bound by that value. In a production app this should of course be changed. I've put some indexes on columns that are | ||
frequently looked up. | ||
|
||
|
||
Usage: | ||
The app exposes 3 rest endpoints - | ||
|
||
/screening/find-screenings/{date} - get | ||
/seat/get-seats/{screeningId} - get | ||
/tickets/book-tickets/ - post | ||
|
||
The user calls the screenings endpoint with a date and time and receives a sorted list of screenings in the time period | ||
(date - 30mins, date + 4h). | ||
|
||
The user then calls the /get-seats/ endpoint with a particular screeningId and receives a list of seats for this particular | ||
screening - as there cannot be a single place left over in a row between two already reserved places, the user receives | ||
full list - available and unavailable seats. | ||
|
||
The user then calls the post /book-tickets/ endpoint with a json list of tickets. Tickets are of the format: | ||
{ | ||
"name":"John", | ||
"surname":"Doe", | ||
"screeningId":1, | ||
"screeningDate":"2023-11-02 04:15:00", | ||
"seatId":1, | ||
"type":"ADULT" | ||
} | ||
|
||
I have wondered a bit about how to realize that part - I've thought about implementing some simple session mechanism or mock | ||
that would enable the user to book tickets in a timeframe, but, as the pdf requirements stated, operations were to be | ||
exposed as REST, so that would be in conflict. It would also take a significant amount of work to provide some security | ||
user-based session mechanisms and test them. In the end I've chosen a completely stateless approach where the whole | ||
request is processed in one server call. | ||
|
||
As the business use case states that the user chooses a particular screening, and I think it makes sense to only book for | ||
one screening, all tickets have to be for the same screening. | ||
|
||
Tickets can have different holders - it makes sense in a real world scenario (someone can pick up his ticket without | ||
the person making the reservation) and was cleaner to implement :). | ||
|
||
The holder's name and surname should obey the specification provided in the .pdf file. If the surname is two-part, | ||
dashed, both parts must obey the minimum 3 letter length and other rules. | ||
|
||
If the tickets are valid, the system logs them into the db, stamps a reservation expiration date and returns a .json | ||
Reservation object of the form List<Ticket>, ticketCostSum, expirationDate. | ||
|
||
I've also provided a | ||
screenings/find-screenings/{movie}/{date} - get endpoint | ||
to find screenings of a particular movie, seemed natural, but I didn't test that very much. | ||
|
||
Please read how_to_run.txt for running manual. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Celowo się pozbyłeś tych linijek?