Initial commit

This commit is contained in:
user
2026-07-12 13:47:34 +04:00
commit 51e2e789d0
37 changed files with 2005 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
clear
# Get dependencies
# go mod tidy
# Build server
go build .
# Delete previous database
sudo rm ./SQLiteDatabase.db
# Create new database
./cmd --new-schema
# Run server
./cmd --host=127.0.0.1 --port=8080 --limit=10 --offset=0 --write-timeout=10 --read-timeout=10
+11
View File
@@ -0,0 +1,11 @@
clear
# A 1 tables in Room 1
curl -X POST -H 'Content-Type: application/json' -d '{"name":11,"max_people":1,"room":"Room 1"}' localhost:8080/table --silent
# A 2 tables in Room 2
curl -X POST -H 'Content-Type: application/json' -d '{"name":21,"max_people":2,"room":"Room 2"}' localhost:8080/table --silent
curl -X POST -H 'Content-Type: application/json' -d '{"name":22,"max_people":4,"room":"Room 2"}' localhost:8080/table --silent
# A 3 tables in Room 3
curl -X POST -H 'Content-Type: application/json' -d '{"name":31,"max_people":3,"room":"Room 3"}' localhost:8080/table --silent
curl -X POST -H 'Content-Type: application/json' -d '{"name":32,"max_people":5,"room":"Room 3"}' localhost:8080/table --silent
curl -X POST -H 'Content-Type: application/json' -d '{"name":33,"max_people":6,"room":"Room 3"}' localhost:8080/table --silent
+7
View File
@@ -0,0 +1,7 @@
clear
# We move the table with id 6 from room 3 to room 1, rename it to 12
curl -X PUT -H 'Content-Type: application/json' -d '{"id":6,"name":12,"max_people":6,"room":"Room 1"}' localhost:8080/table --silent
# Now there are an equal number of tables in each room
+7
View File
@@ -0,0 +1,7 @@
clear
# Get all record of Tables
# curl -X GET -H 'Content-Type: application/json' localhost:8080/tables
# Get only one record of Table by id 6
curl -X GET -H 'Content-Type: application/json' localhost:8080/table/6
+4
View File
@@ -0,0 +1,4 @@
clear
# They decided to remove one table from the establishment. It was a table with id 6
curl -X DELETE localhost:8080/table/6 --silent
+4
View File
@@ -0,0 +1,4 @@
clear
# We will create the first booking for the very first table with a reservation for 1 hour
curl -X POST -H 'Content-Type: application/json' -d '{"id_tables":1,"reservation":"2000-01-01T12:00:00Z","time_reserve":"01:00","who_booked":"Ivan 1"}' localhost:8080/booking --silent
+4
View File
@@ -0,0 +1,4 @@
clear
# Ivan warned that he did not have time and asked to postpone the reservation of the table an hour later, he also informed that the duration of the reservation should be increased in time
curl -X PUT -H 'Content-Type: application/json' -d '{"id_tables":1,"reservation":"2000-01-01T13:00:00Z","time_reserve":"02:00","who_booked":"Ivan 1"}' localhost:8080/booking --silent
+7
View File
@@ -0,0 +1,7 @@
clear
# Get all record of Booking
# curl -X GET -H 'Content-Type: application/json' localhost:8080/booking
# Get only one record of Booking by id 1
curl -X GET -H 'Content-Type: application/json' localhost:8080/booking/1
+4
View File
@@ -0,0 +1,4 @@
clear
# Delete one record of Booking by id 1
curl -X DELETE localhost:8080/booking/1 --silent
BIN
View File
Binary file not shown.
Executable
BIN
View File
Binary file not shown.
Executable
BIN
View File
Binary file not shown.
Executable
+125
View File
@@ -0,0 +1,125 @@
package main
import (
"fmt"
"github.com/gorilla/handlers"
"go_project/internal/configuration"
"go_project/internal/connection"
"go_project/internal/controllers"
"go_project/internal/routes"
"go_project/internal/schema"
"go_project/pkg/behaivor"
"go_project/pkg/file"
"log"
"net/http"
"strconv"
)
func main() {
configuration.Init()
// Create database
if configuration.GetForceCreateSchema() {
sqlDBFileName := configuration.GetDatabaseLocation()
file.MustCreate(sqlDBFileName)
sqlHandler, err := connection.Open(sqlDBFileName)
behaivor.Anxiety("Database connection error", err)
schema.MustCreate(sqlHandler)
log.Println("Database created")
return
} else if configuration.GetCreateSchema() {
sqlDBFileName := configuration.GetDatabaseLocation()
if file.MustIsNotExist(sqlDBFileName) {
file.MustCreate(sqlDBFileName)
} else {
return
}
sqlHandler, err := connection.Open(sqlDBFileName)
behaivor.Anxiety("Database connection error", err)
schema.MustCreate(sqlHandler)
log.Println("Database created")
return
}
// Create database end
// NOTE: Temp data for testing
MustFillDatabase()
serverRouter := routes.DefaultRouter()
// Adding CORS policy
corsHandler := handlers.CORS(
handlers.AllowedOrigins([]string{"*"}), // Allow all origins
handlers.AllowedMethods([]string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}), // Allow common HTTP methods
handlers.AllowedHeaders([]string{"Content-Type"}), // Allow specific headers
)
serverHost := configuration.GetServerHost()
serverPort := strconv.FormatInt(int64(configuration.GetServerPort()), 10)
serverAddr := serverHost + ":" + serverPort
serverWriteTimeout := configuration.GetWriteTimeout()
serverReadTimeout := configuration.GetReadTimeout()
server := &http.Server{
Handler: corsHandler(serverRouter),
Addr: serverAddr,
WriteTimeout: serverWriteTimeout,
ReadTimeout: serverReadTimeout,
}
log.Println("Preparations for listen and serve are complete. Starting server at " + serverAddr)
log.Fatal(server.ListenAndServe())
}
// NOTE: Temp data for testing
func MustFillDatabase() {
sqlHandler, err := connection.OpenDefault()
behaivor.Anxiety("Database connection error", err)
log.Println("Database filling operations are started")
defer connection.MustClose(sqlHandler)
// Fill database with Tables data
for i := 1; i <= 5; i++ {
createTableParams := controllers.CreateTableParams{
Name: i,
MaxPeople: 2,
Room: "Room 1",
}
log.Printf("Current CreateTableParams: %v\n", createTableParams)
_, err := controllers.CreateTable(sqlHandler, createTableParams)
if err != nil {
continue
}
}
//Fill database with Bookings data
for i := 0; i < 10; i++ {
reservationTime := fmt.Sprintf("2025-01-01T%02d:00:00", 10+i)
createBookingParams := controllers.CreateBookingParams{
IdTables: i%5 + 1,
Reservation: reservationTime,
TimeReserve: "01:00",
WhoBooked: "John Doe",
}
log.Printf("Current CreateBookingParams: %v\n", createBookingParams)
_, err := controllers.CreateBooking(sqlHandler, createBookingParams)
if err != nil {
continue
}
}
log.Println("Database filling operations are completed")
}
Executable
+42
View File
@@ -0,0 +1,42 @@
module go_project
go 1.22.2
require github.com/mattn/go-sqlite3 v1.14.24
require (
github.com/gin-gonic/gin v1.10.0
github.com/gorilla/mux v1.8.1
github.com/jmoiron/sqlx v1.4.0
)
require (
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Executable
+106
View File
@@ -0,0 +1,106 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+110
View File
@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Table Booking</title>
<link rel="stylesheet" href="styles.css">
<script src="script.js" defer></script>
</head>
<body>
<header>
<h1>Table and Booking Management</h1>
</header>
<main>
<!-- Section to display and manage tables -->
<section id="manage-tables">
<h2>Manage Tables</h2>
<table id="tables">
<thead>
<tr>
<th>ID</th>
<th>Name (Number)</th>
<th>Max People</th>
<th>Room</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- Data will be populated here -->
</tbody>
</table>
<h3>Create/Update Table</h3>
<form id="table-form">
<label for="table-id">Table ID (leave blank to create new):</label>
<input type="number" id="table-id" name="id">
<label for="table-name">Name (Number):</label>
<input type="number" id="table-name" name="name" required>
<label for="table-max-people">Max People:</label>
<input type="number" id="table-max-people" name="max_people" required>
<label for="table-room">Room:</label>
<input type="text" id="table-room" name="room" required>
<button type="submit">Save Table</button>
</form>
<h3>Find Table by ID</h3>
<form id="find-table-form">
<label for="find-table-id">Enter Table ID:</label>
<input type="number" id="find-table-id" name="id" required>
<button type="submit">Find Table</button>
</form>
</section>
<!-- Section to display and manage bookings -->
<section id="manage-bookings">
<h2>Manage Bookings</h2>
<table id="bookings">
<thead>
<tr>
<th>ID</th>
<th>Table ID</th>
<th>Reservation DateTime</th>
<th>Time Reserve</th>
<th>Who Booked</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<!-- Data will be populated here -->
</tbody>
</table>
<h3>Create/Update Booking</h3>
<form id="booking-form">
<label for="booking-id">Booking ID (leave blank to create new):</label>
<input type="number" id="booking-id" name="id">
<label for="booking-table-id">Table ID:</label>
<input type="number" id="booking-table-id" name="id_tables" required>
<label for="reservation-datetime">Reservation Date & Time:</label>
<input type="datetime-local" id="reservation-datetime" name="reservation" required>
<label for="time-reserve">Time Reserve:</label>
<input type="text" id="time-reserve" name="time_reserve" placeholder="e.g., 2 hours" required>
<label for="who-booked">Who Booked:</label>
<input type="text" id="who-booked" name="who_booked" required>
<button type="submit">Save Booking</button>
</form>
<!-- Section to find a booking by ID -->
<h3>Find Booking by ID</h3>
<form id="find-booking-form">
<label for="find-booking-id">Enter Booking ID:</label>
<input type="number" id="find-booking-id" name="id" required>
<button type="submit">Find Booking</button>
</form>
</section>
</main>
</body>
</html>
+249
View File
@@ -0,0 +1,249 @@
document.addEventListener("DOMContentLoaded", () => {
const apiUrl = "http://localhost:8080"; // Replace with your actual backend URL
// Fetch and populate table data
function loadTables() {
fetch(`${apiUrl}/tables`)
.then((response) => response.json())
.then((tables) => {
const tableBody = document.querySelector("#tables tbody");
tableBody.innerHTML = ""; // Clear existing rows
tables.forEach((table) => {
const row = document.createElement("tr");
row.innerHTML = `
<td>${table.id}</td>
<td>${table.name}</td>
<td>${table.max_people}</td>
<td>${table.room}</td>
<td>
<button data-id="${table.id}" class="edit">Edit</button>
<button data-id="${table.id}" class="delete">Delete</button>
</td>
`;
tableBody.appendChild(row);
});
})
.catch((error) => console.error("Error loading tables:", error));
}
// Handle form submission to create or update a table
document.querySelector("#table-form").addEventListener("submit", (e) => {
e.preventDefault();
const id = parseInt(document.querySelector("#table-id").value, 10) || undefined;
const name = parseInt(document.querySelector("#table-name").value, 10); // Adjusted for name as integer
const maxPeople = parseInt(document.querySelector("#table-max-people").value, 10);
const room = document.querySelector("#table-room").value;
const method = id ? "PUT" : "POST";
const endpoint = id ? `/table` : `/table`;
fetch(`${apiUrl}${endpoint}`, {
method,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ id, name, max_people: maxPeople, room }),
})
.then((response) => {
if (!response.ok) {
return response.text().then((text) => {
if (text.trim() === "") {
throw new Error("Empty response from server");
}
try {
const errorJson = JSON.parse(text);
throw new Error(errorJson.message || "Failed to save table");
} catch {
throw new Error("Unexpected server response: " + text);
}
});
}
return response.text().then((text) => {
if (text.trim() === "") {
return {}; // Handle empty body for successful responses
}
return JSON.parse(text);
});
})
.then(() => {
document.querySelector("#table-form").reset();
loadTables();
alert(`Table ${id ? 'updated' : 'created'} successfully.`);
})
.catch((error) => {
console.error("Error saving table:", error);
alert(`Failed to save table: ${error.message}`);
});
});
// Handle table row actions (edit/delete)
document.querySelector("#tables").addEventListener("click", (e) => {
const button = e.target;
const id = button.dataset.id;
if (button.classList.contains("edit")) {
fetch(`${apiUrl}/table/${id}`)
.then((response) => response.json())
.then((table) => {
document.querySelector("#table-id").value = table.id;
document.querySelector("#table-name").value = table.name;
document.querySelector("#table-max-people").value = table.max_people;
document.querySelector("#table-room").value = table.room;
})
.catch((error) => console.error("Error fetching table:", error));
} else if (button.classList.contains("delete")) {
fetch(`${apiUrl}/table/${id}`, { method: "DELETE" })
.then(() => loadTables())
.catch((error) => console.error("Error deleting table:", error));
}
});
// Handle find table by ID
document.querySelector("#find-table-form").addEventListener("submit", (e) => {
e.preventDefault();
const id = parseInt(document.querySelector("#find-table-id").value, 10);
fetch(`${apiUrl}/table/${id}`)
.then((response) => {
if (!response.ok) {
throw new Error(`Table with ID ${id} not found`);
}
return response.json();
})
.then((table) => {
alert(`Table ID: ${table.id}\nName (Number): ${table.name}\nMax People: ${table.max_people}\nRoom: ${table.room}`);
})
.catch((error) => {
alert(`Error: ${error.message}`);
console.error("Error finding table:", error);
});
});
// Initial load
loadTables();
// Fetch and populate booking data
function loadBookings() {
fetch(`${apiUrl}/bookings`)
.then((response) => response.json())
.then((bookings) => {
const bookingBody = document.querySelector("#bookings tbody");
bookingBody.innerHTML = ""; // Clear existing rows
bookings.forEach((booking) => {
const row = document.createElement("tr");
row.innerHTML = `
<td>${booking.id}</td>
<td>${booking.id_tables}</td>
<td>${booking.reservation}</td>
<td>${booking.time_reserve}</td>
<td>${booking.who_booked}</td>
<td>
<button data-id="${booking.id}" class="edit">Edit</button>
<button data-id="${booking.id}" class="delete">Delete</button>
</td>
`;
bookingBody.appendChild(row);
});
})
.catch((error) => console.error("Error loading bookings:", error));
}
// Handle form submission to create or update a booking
document.querySelector("#booking-form").addEventListener("submit", (e) => {
e.preventDefault();
const id = parseInt(document.querySelector("#booking-id").value, 10) || undefined;
const tableId = parseInt(document.querySelector("#booking-table-id").value, 10);
const reservationDatetime = String(document.querySelector("#reservation-datetime").value);
const timeReserve = document.querySelector("#time-reserve").value;
const whoBooked = document.querySelector("#who-booked").value;
const method = id ? "PUT" : "POST";
const endpoint = id ? `/booking` : `/booking`;
fetch(`${apiUrl}${endpoint}`, {
method,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ id, id_tables: tableId, reservation: reservationDatetime, time_reserve: timeReserve, who_booked: whoBooked }),
})
.then((response) => {
if (!response.ok) {
return response.text().then((text) => {
if (text.trim() === "") {
throw new Error("Empty response from server");
}
try {
const errorJson = JSON.parse(text);
throw new Error(errorJson.message || "Failed to save table");
} catch {
throw new Error("Unexpected server response: " + text);
}
});
}
return response.text().then((text) => {
if (text.trim() === "") {
return {}; // Handle empty body for successful responses
}
return JSON.parse(text);
});
})
.then(() => {
document.querySelector("#booking-form").reset();
loadBookings();
alert(`Booking ${id ? 'updated' : 'created'} successfully.`);
})
.catch((error) => {
console.error("Error saving booking:", error);
alert(`Failed to save booking: ${error.message}`);
});
});
// Handle booking row actions (edit/delete)
document.querySelector("#bookings").addEventListener("click", (e) => {
const button = e.target;
const id = button.dataset.id;
if (button.classList.contains("edit")) {
fetch(`${apiUrl}/booking/${id}`)
.then((response) => response.json())
.then((booking) => {
document.querySelector("#booking-id").value = booking.id;
document.querySelector("#booking-table-id").value = booking.id_tables;
document.querySelector("#reservation-datetime").value = booking.reservation;
document.querySelector("#time-reserve").value = booking.time_reserve;
document.querySelector("#who-booked").value = booking.who_booked;
})
.catch((error) => console.error("Error fetching booking:", error));
} else if (button.classList.contains("delete")) {
fetch(`${apiUrl}/booking/${id}`, { method: "DELETE" })
.then(() => loadBookings())
.catch((error) => console.error("Error deleting booking:", error));
}
});
// Handle find booking by ID
document.querySelector("#find-booking-form").addEventListener("submit", (e) => {
e.preventDefault();
const id = document.querySelector("#find-booking-id").value;
fetch(`${apiUrl}/booking/${id}`)
.then((response) => {
if (!response.ok) throw new Error("Booking not found");
return response.json();
})
.then((booking) => {
alert(`Booking ID: ${booking.id}\nTable ID: ${booking.id_tables}\nReservation: ${booking.reservation}\nTime Reserve: ${booking.time_reserve}\nWho Booked: ${booking.who_booked}`);
})
.catch((error) => {
alert(`Error: ${error.message}`);
console.error("Error finding booking:", error);
});
});
// Initial load
loadBookings();
});
+40
View File
@@ -0,0 +1,40 @@
body {
font-family: Arial, sans-serif;
margin: 20px;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
table, th, td {
border: 1px solid black;
}
th, td {
padding: 10px;
text-align: left;
}
form {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
}
input, select {
padding: 5px;
margin-bottom: 10px;
width: 100%;
box-sizing: border-box;
}
button {
padding: 10px 15px;
background-color: #007BFF;
color: white;
border: none;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
+86
View File
@@ -0,0 +1,86 @@
package configuration
import (
"flag"
"time"
)
var databaseLocationFlag *string
var forceCreateSchemaFlag *bool
var createSchemaFlag *bool
var serverHostFlag *string
var serverPortFlag *int
var recordsLimitFlag *int
var recordsOffsetFlag *int
// Write timeout in seconds
var writeTimeoutFlag *int
// Read timeout in seconds
var readTimeoutFlag *int
func Init() {
databaseLocationFlag = flag.String("file", "SQLiteDatabase.db", "The file contains the sql lite database")
forceCreateSchemaFlag = flag.Bool("force-new-schema", false, "Force create a new database by erasing the data. Overrides the new flag")
createSchemaFlag = flag.Bool("new-schema", false, "Create a new database")
serverHostFlag = flag.String("host", "localhost", "The host for the server")
serverPortFlag = flag.Int("port", 8080, "The port for the server")
recordsLimitFlag = flag.Int("limit", 10, "The number of records to return")
recordsOffsetFlag = flag.Int("offset", 0, "The number of records to skip")
writeTimeoutFlag = flag.Int("write-timeout", 10, "The write timeout in seconds")
readTimeoutFlag = flag.Int("read-timeout", 10, "The read timeout in seconds")
flag.Parse()
}
func GetForceCreateSchema() bool {
return *forceCreateSchemaFlag
}
func GetCreateSchema() bool {
return *createSchemaFlag
}
func GetWriteTimeout() time.Duration {
result := time.Duration(*writeTimeoutFlag) * time.Second
return result
}
func GetReadTimeout() time.Duration {
result := time.Duration(*readTimeoutFlag) * time.Second
return result
}
func GetServerHost() string {
return *serverHostFlag
}
func GetRecordsOffset() int {
return *recordsOffsetFlag
}
func GetRecordsLimit() int {
return *recordsLimitFlag
}
func GetServerPort() int {
return *serverPortFlag
}
func GetDatabaseLocation() string {
return *databaseLocationFlag
}
+33
View File
@@ -0,0 +1,33 @@
package connection
import (
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
"go_project/internal/configuration"
"go_project/pkg/behaivor"
)
func OpenDefault() (db *sqlx.DB, err error) {
connectionParameters := configuration.GetDatabaseLocation()
return Open(connectionParameters)
}
func Open(connectionParameters string) (db *sqlx.DB, err error) {
db, err = sqlx.Open("sqlite3", connectionParameters)
if err != nil {
return nil, err
}
err = db.Ping()
if err != nil {
return nil, err
}
return db, err
}
// Closing the database with error checking
func MustClose(db *sqlx.DB) {
err := db.Close()
behaivor.Anxiety("Database closing error", err)
}
+143
View File
@@ -0,0 +1,143 @@
package controllers
import (
"database/sql"
"errors"
"github.com/jmoiron/sqlx"
"go_project/internal/models"
"log"
)
type (
ListBookingParams struct {
Limit int `db:"limit" json:"limit"`
Offset int `db:"offset" json:"offset"`
}
CreateBookingParams struct {
IdTables int `db:"id_tables" json:"id_tables"`
Reservation string `db:"reservation" json:"reservation"`
TimeReserve string `db:"time_reserve" json:"time_reserve"`
WhoBooked string `db:"who_booked" json:"who_booked"`
}
UpdateBookingParams struct {
Id int `db:"id" json:"id"`
IdTables int `db:"id_tables" json:"id_tables"`
Reservation string `db:"reservation" json:"reservation"`
TimeReserve string `db:"time_reserve" json:"time_reserve"`
WhoBooked string `db:"who_booked" json:"who_booked"`
}
)
func BookingById(db *sqlx.DB, id int) (*models.Booking, error) {
rows, err := db.Query("SELECT * FROM Bookings WHERE id = ?", id)
if err != nil {
return nil, err
}
defer rows.Close()
if isEmpty := !rows.Next(); isEmpty {
return nil, nil
}
result := new(models.Booking)
err = rows.Scan(&result.Id, &result.IdTables, &result.ReservationDatetime, &result.TimeReserve, &result.WhoBooked)
return result, err
}
func AllBookings(db *sqlx.DB, params ListBookingParams) ([]models.Booking, error) {
query := `SELECT * FROM Bookings ORDER BY id LIMIT $1 OFFSET $2`
rows, err := db.Query(query, params.Limit, params.Offset)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
defer rows.Close()
var result []models.Booking
for rows.Next() {
var currentBooking models.Booking
err = rows.Scan(&currentBooking.Id, &currentBooking.IdTables, &currentBooking.ReservationDatetime, &currentBooking.TimeReserve, &currentBooking.WhoBooked)
if err != nil {
return nil, err
}
result = append(result, currentBooking)
}
return result, err
}
func CreateBooking(db *sqlx.DB, params CreateBookingParams) (*models.Booking, error) {
query := `
INSERT INTO Bookings (id_tables, reservation, time_reserve, who_booked)
VALUES ($1, $2, $3, $4)
RETURNING id, id_tables, reservation, time_reserve, who_booked
`
var result models.Booking
err := db.QueryRow(query,
params.IdTables,
params.Reservation,
params.TimeReserve,
params.WhoBooked,
).Scan(
&result.Id,
&result.IdTables,
&result.ReservationDatetime,
&result.TimeReserve,
&result.WhoBooked,
)
if err != nil {
log.Printf("CreateBooking: Insertion operation of the Bookings object is failed: %v\n", err)
return nil, err
}
log.Println("CreateBooking: Insertion operation of the Bookings object is successful")
return &result, nil
}
func DeleteBookingById(db *sqlx.DB, id int) (sql.Result, error) {
sqlResult, err := db.Exec("DELETE FROM Bookings WHERE id = ?", id)
if err == nil {
log.Println("DeleteBookingById: Deletion operation of the Bookings object is successful")
}
return sqlResult, err
}
func UpdateBooking(db *sqlx.DB, params UpdateBookingParams) (*models.Booking, error) {
query := `
UPDATE Bookings
SET id_tables = $1, reservation = $2, time_reserve = $3
WHERE id = $4
RETURNING id, id_tables, reservation, time_reserve, who_booked
`
result := &models.Booking{}
err := db.QueryRow(query, params.IdTables, params.Reservation, params.TimeReserve, params.Id).
Scan(&result.Id, &result.IdTables, &result.ReservationDatetime, &result.TimeReserve, &result.WhoBooked)
if err != nil {
switch {
case err == sql.ErrNoRows:
log.Printf("UpdateBooking: No rows affected for Booking ID %d", params.Id)
return nil, errors.New("update failed: no rows affected")
default:
log.Printf("UpdateBooking: Error executing query for Booking ID %d: %v", params.Id, err)
return nil, err
}
}
log.Printf("UpdateBooking: Successfully updated Booking ID %d", result.Id)
return result, nil
}
+135
View File
@@ -0,0 +1,135 @@
package controllers
import (
"database/sql"
"errors"
"github.com/jmoiron/sqlx"
"go_project/internal/models"
"log"
)
type (
ListTableParams struct {
Limit int `db:"limit" json:"limit"`
Offset int `db:"offset" json:"offset"`
}
CreateTableParams struct {
Name int `db:"name" json:"name"`
MaxPeople int `db:"max_people" json:"max_people"`
Room string `db:"room" json:"room"`
}
UpdateTableParams struct {
Id int `db:"id" json:"id"`
Name int `db:"name" json:"name"`
MaxPeople int `db:"max_people" json:"max_people"`
Room string `db:"room" json:"room"`
}
)
func TableById(db *sqlx.DB, id int) (*models.Table, error) {
rows, err := db.Query("SELECT * FROM Tables WHERE id = ?", id)
if err != nil {
return nil, err
}
defer rows.Close()
if isEmpty := !rows.Next(); isEmpty {
return nil, nil
}
result := new(models.Table)
err = rows.Scan(&result.Id, &result.Name, &result.MaxPeople, &result.Room)
return result, err
}
func AllTables(db *sqlx.DB, params ListTableParams) ([]models.Table, error) {
query := `SELECT * FROM Tables ORDER BY id LIMIT $1 OFFSET $2`
rows, err := db.Query(query, params.Limit, params.Offset)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
defer rows.Close()
var result []models.Table
for rows.Next() {
var currentTable models.Table
err = rows.Scan(&currentTable.Id, &currentTable.Name, &currentTable.MaxPeople, &currentTable.Room)
if err != nil {
return nil, err
}
result = append(result, currentTable)
}
return result, err
}
func CreateTable(db *sqlx.DB, params CreateTableParams) (*models.Table, error) {
query := `
INSERT INTO Tables (name, max_people, room)
VALUES (?, ?, ?)
RETURNING id, name, max_people, room
`
var table models.Table
err := db.QueryRow(query, params.Name, params.MaxPeople, params.Room).Scan(
&table.Id,
&table.Name,
&table.MaxPeople,
&table.Room,
)
if err != nil {
log.Printf("CreateTable: Insertion operation of the Tables object is failed: %v\n", err)
return nil, err
}
log.Println("CreateTable: Insertion operation of the Tables object is successful")
return &table, nil
}
func DeleteTableById(db *sqlx.DB, id int) (sql.Result, error) {
sqlResult, err := db.Exec("DELETE FROM Tables WHERE id = ?", id)
if err == nil {
log.Println("DeleteTableById: Deletion operation of the Tables object is successful")
}
return sqlResult, err
}
func UpdateTable(db *sqlx.DB, params UpdateTableParams) (*models.Table, error) {
query := `
UPDATE Tables
SET name = $1, max_people = $2, room = $3
WHERE id = $4
RETURNING id, name, max_people, room
`
result := &models.Table{}
err := db.QueryRow(query, params.Name, params.MaxPeople, params.Room, params.Id).
Scan(&result.Id, &result.Name, &result.MaxPeople, &result.Room)
if err != nil {
switch {
case errors.Is(err, sql.ErrNoRows):
log.Printf("No rows affected for Table ID %d", params.Id)
return nil, errors.New("no rows affected: update failed")
default:
log.Printf("Query execution error for Table ID %d: %v", params.Id, err)
return nil, err
}
}
log.Printf("Table ID %d updated successfully", result.Id)
return result, nil
}
+213
View File
@@ -0,0 +1,213 @@
package handlers
import (
"encoding/json"
"github.com/gorilla/mux"
"go_project/internal/configuration"
"go_project/internal/connection"
"go_project/internal/controllers"
"log"
"net/http"
"strconv"
)
func GetBookingById(response http.ResponseWriter, request *http.Request) {
log.Println("GetBookingById Serving:", request.URL.Path, "from", request.Host)
sqlHandler, err := connection.OpenDefault()
if err != nil {
log.Println("Error opening database connection:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
defer connection.MustClose(sqlHandler)
requestVars := mux.Vars(request)
bookingIdStringValue := requestVars["id"]
id, err := strconv.Atoi(bookingIdStringValue)
if err != nil {
log.Println("Current id string value of the booking record to get: \"" + bookingIdStringValue + "\"")
log.Println("Error parse id string value to int:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
booking, err := controllers.BookingById(sqlHandler, id)
if err != nil {
log.Println("Error getting booking by id from database:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
bookingMarshaled, err := json.Marshal(booking)
if err != nil {
log.Println("Error marshaling booking to json string format:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.Header().Set("Content-Type", "application/json")
_, err = response.Write(bookingMarshaled)
if err != nil {
log.Println("Error writing booking to response:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
}
func GetAllBookings(response http.ResponseWriter, request *http.Request) {
log.Println("GetAllBookings Serving:", request.URL.Path, "from", request.Host)
sqlHandler, err := connection.OpenDefault()
if err != nil {
log.Println("Error opening database connection:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
defer connection.MustClose(sqlHandler)
limit, offset := configuration.GetRecordsLimit(), configuration.GetRecordsOffset()
listBookingParams := controllers.ListBookingParams{Limit: limit, Offset: offset}
allBookings, err := controllers.AllBookings(sqlHandler, listBookingParams)
if err != nil {
log.Println("Error getting all bookings from database:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
allBookingsMarshaled, err := json.Marshal(allBookings)
if err != nil {
log.Println("Error marshaling all bookings:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.Header().Set("Content-Type", "application/json")
_, err = response.Write(allBookingsMarshaled)
if err != nil {
log.Println("Error writing all bookings to response:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
}
func CreateBooking(response http.ResponseWriter, request *http.Request) {
log.Println("CreateBooking Serving:", request.URL.Path, "from", request.Host)
sqlHandler, err := connection.OpenDefault()
if err != nil {
log.Println("Error opening database connection:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
defer connection.MustClose(sqlHandler)
var createBookingParams controllers.CreateBookingParams
err = json.NewDecoder(request.Body).Decode(&createBookingParams)
if err != nil {
log.Println("CreateBooking: Values of createBookingParams:", createBookingParams)
log.Println("Error unmarshaling booking from json string format:", err)
response.WriteHeader(http.StatusBadRequest)
return
}
newBooking, err := controllers.CreateBooking(sqlHandler, createBookingParams)
if err != nil {
log.Println("Error creating booking:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
newBookingMarshaled, err := json.Marshal(newBooking)
if err != nil {
log.Println("Error marshaling booking to json string format:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.Header().Set("Content-Type", "application/json")
_, err = response.Write(newBookingMarshaled)
if err != nil {
log.Println("Error writing booking to response:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
}
func UpdateBooking(response http.ResponseWriter, request *http.Request) {
log.Println("UpdateBookingById Serving:", request.URL.Path, "from", request.Host)
sqlHandler, err := connection.OpenDefault()
if err != nil {
log.Println("Error opening database connection:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
defer connection.MustClose(sqlHandler)
var updateBookingParams controllers.UpdateBookingParams
err = json.NewDecoder(request.Body).Decode(&updateBookingParams)
if err != nil {
log.Println("UpdateBooking: Values of updateBookingParams:", updateBookingParams)
log.Println("Error unmarshaling booking from json string format:", err)
response.WriteHeader(http.StatusBadRequest)
return
}
newBooking, err := controllers.UpdateBooking(sqlHandler, updateBookingParams)
if err != nil {
log.Println("Error updating booking by id from database:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
log.Println("SQL result: Updated booking record is:", newBooking)
response.WriteHeader(http.StatusOK)
}
func DeleteBookingById(response http.ResponseWriter, request *http.Request) {
log.Println("DeleteBookingById Serving:", request.URL.Path, "from", request.Host)
sqlHandler, err := connection.OpenDefault()
if err != err {
log.Println("Error opening database connection:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
defer connection.MustClose(sqlHandler)
requestVars := mux.Vars(request)
bookingIdStringValue := requestVars["id"]
id, err := strconv.Atoi(bookingIdStringValue)
if err != nil {
log.Println("Current id string value of the booking record to delete: \"" + bookingIdStringValue + "\"")
log.Println("Error parse id string value to int:", err)
response.WriteHeader(http.StatusBadRequest)
return
}
sqlResult, err := controllers.DeleteBookingById(sqlHandler, id)
if err != nil {
log.Println("Error deleting booking by id from database:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
rowsAffected, err := sqlResult.RowsAffected()
if err != nil {
log.Println("Error getting number of rows affected by an delete:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
log.Println("SQL result: Number of rows affected by an delete:", rowsAffected)
response.WriteHeader(http.StatusOK)
}
+213
View File
@@ -0,0 +1,213 @@
package handlers
import (
"encoding/json"
"github.com/gorilla/mux"
"go_project/internal/configuration"
"go_project/internal/connection"
"go_project/internal/controllers"
"log"
"net/http"
"strconv"
)
func GetAllTables(response http.ResponseWriter, request *http.Request) {
log.Println("GetAllTables Serving:", request.URL.Path, "from", request.Host)
sqlHandler, err := connection.OpenDefault()
if err != nil {
log.Println("Error opening database connection:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
defer connection.MustClose(sqlHandler)
limit, offset := configuration.GetRecordsLimit(), configuration.GetRecordsOffset()
listTableParams := controllers.ListTableParams{Limit: limit, Offset: offset}
allTables, err := controllers.AllTables(sqlHandler, listTableParams)
if err != nil {
log.Println("Error getting all tables from database:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
allTablesMarshaled, err := json.Marshal(allTables)
if err != nil {
log.Println("Error marshaling all tables:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.Header().Set("Content-Type", "application/json")
_, err = response.Write(allTablesMarshaled)
if err != nil {
log.Println("Error writing all tables to response:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
}
func GetTableById(response http.ResponseWriter, request *http.Request) {
log.Println("GetTableById Serving:", request.URL.Path, "from", request.Host)
sqlHandler, err := connection.OpenDefault()
if err != nil {
log.Println("Error opening database connection:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
defer connection.MustClose(sqlHandler)
requestVars := mux.Vars(request)
tableIdStringValue := requestVars["id"]
id, err := strconv.Atoi(tableIdStringValue)
if err != nil {
log.Println("Current id string value of the table record to get: \"" + tableIdStringValue + "\"")
log.Println("Error parse id string value to int:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
table, err := controllers.TableById(sqlHandler, id)
if err != nil {
log.Println("Error getting table by id from database:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
tableMarshaled, err := json.Marshal(table)
if err != nil {
log.Println("Error marshaling table to json string format:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.Header().Set("Content-Type", "application/json")
_, err = response.Write(tableMarshaled)
if err != nil {
log.Println("Error writing table to response:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
}
func CreateTable(response http.ResponseWriter, request *http.Request) {
log.Println("CreateTable Serving:", request.URL.Path, "from", request.Host)
sqlHandler, err := connection.OpenDefault()
if err != nil {
log.Println("Error opening database connection:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
defer connection.MustClose(sqlHandler)
var createTableParams controllers.CreateTableParams
err = json.NewDecoder(request.Body).Decode(&createTableParams)
if err != nil {
log.Println("CreateTable: Values of createTableParams:", createTableParams)
log.Println("Error unmarshaling table from json string format:", err)
response.WriteHeader(http.StatusBadRequest)
return
}
newTable, err := controllers.CreateTable(sqlHandler, createTableParams)
if err != nil {
log.Println("Error creating table:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
newTableMarshaled, err := json.Marshal(newTable)
if err != nil {
log.Println("Error marshaling table to json string format:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.Header().Set("Content-Type", "application/json")
_, err = response.Write(newTableMarshaled)
if err != nil {
log.Println("Error writing table to response:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusOK)
}
func DeleteTableById(response http.ResponseWriter, request *http.Request) {
log.Println("DeleteTableById Serving:", request.URL.Path, "from", request.Host)
sqlHandler, err := connection.OpenDefault()
if err != nil {
log.Println("Error opening database connection:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
defer connection.MustClose(sqlHandler)
requestVars := mux.Vars(request)
tableIdStringValue := requestVars["id"]
id, err := strconv.Atoi(tableIdStringValue)
if err != nil {
log.Println("Current id string value of the table record to delete: \"" + tableIdStringValue + "\"")
log.Println("Error parse id string value to int:", err)
response.WriteHeader(http.StatusBadRequest)
return
}
sqlResult, err := controllers.DeleteTableById(sqlHandler, id)
if err != nil {
log.Println("Error deleting table by id from database:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
rowsAffected, err := sqlResult.RowsAffected()
if err != nil {
log.Println("Error getting number of rows affected by an delete:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
log.Println("SQL result: Number of rows affected by an delete:", rowsAffected)
response.WriteHeader(http.StatusOK)
}
func UpdateTable(response http.ResponseWriter, request *http.Request) {
log.Println("UpdateTableById Serving:", request.URL.Path, "from", request.Host)
sqlHandler, err := connection.OpenDefault()
if err != nil {
log.Println("Error opening database connection:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
defer connection.MustClose(sqlHandler)
var updateTableParams controllers.UpdateTableParams
err = json.NewDecoder(request.Body).Decode(&updateTableParams)
if err != nil {
log.Println("UpdateTable: Values of updateTableParams:", updateTableParams)
log.Println("Error unmarshaling table from json string format:", err)
response.WriteHeader(http.StatusBadRequest)
return
}
newTable, err := controllers.UpdateTable(sqlHandler, updateTableParams)
if err != nil {
log.Println("Error updating table by id from database:", err)
response.WriteHeader(http.StatusInternalServerError)
return
}
log.Println("SQL result: Updated table record is:", newTable)
response.WriteHeader(http.StatusOK)
}
+18
View File
@@ -0,0 +1,18 @@
package models
type (
Table struct {
Id int64 `db:"id" json:"id"`
Name int64 `db:"name" json:"name"`
MaxPeople int64 `db:"max_people" json:"max_people"`
Room string `db:"room" json:"room"`
}
Booking struct {
Id int64 `db:"id" json:"id"`
IdTables int64 `db:"id_tables" json:"id_tables"`
ReservationDatetime string `db:"reservation" json:"reservation"`
TimeReserve string `db:"time_reserve" json:"time_reserve"`
WhoBooked string `db:"who_booked" json:"who_booked"`
}
)
+35
View File
@@ -0,0 +1,35 @@
package routes
import (
"github.com/gorilla/mux"
"go_project/internal/handlers"
"go_project/pkg/behaivor"
"go_project/pkg/information"
"net/http"
)
func DefaultRouter() *mux.Router {
router := mux.NewRouter()
declareRoutes(router)
http.Handle("/", router)
err := router.Walk(information.AboutRoute)
behaivor.Anxiety("Error walking routes", err)
return router
}
var declareRoutes = func(router *mux.Router) {
// End points for Table record
router.HandleFunc("/tables", handlers.GetAllTables).Methods("GET")
router.HandleFunc("/table/{id:[0-9]+}", handlers.GetTableById).Methods("GET")
router.HandleFunc("/table", handlers.CreateTable).Methods("POST")
router.HandleFunc("/table", handlers.UpdateTable).Methods("PUT")
router.HandleFunc("/table/{id:[0-9]+}", handlers.DeleteTableById).Methods("DELETE")
// End points for Booking record
router.HandleFunc("/bookings", handlers.GetAllBookings).Methods("GET")
router.HandleFunc("/booking/{id:[0-9]+}", handlers.GetBookingById).Methods("GET")
router.HandleFunc("/booking", handlers.CreateBooking).Methods("POST")
router.HandleFunc("/booking", handlers.UpdateBooking).Methods("PUT")
router.HandleFunc("/booking/{id:[0-9]+}", handlers.DeleteBookingById).Methods("DELETE")
}
+66
View File
@@ -0,0 +1,66 @@
package schema
import (
"github.com/jmoiron/sqlx"
)
type schema struct {
create string
drop string
}
func MustCreate(db *sqlx.DB) {
db.MustExec(defaultSchema.create)
}
func MustDrop(db *sqlx.DB) {
db.MustExec(defaultSchema.drop)
}
// Type time_reserve stores the reserve time reserve by time
// Type TIME(0) has minimal accuracy, counts down by seconds
// Type SMALLDATETIME has format'YYYYMMDD hh:mm', for example '20090212 12:30', counts down by minuts
var defaultSchema = schema{
create: `
CREATE TABLE IF NOT EXISTS Tables (
id INTEGER
CONSTRAINT PK_Tables PRIMARY KEY AUTOINCREMENT,
name INTEGER NOT NULL UNIQUE,
max_people INTEGER NOT NULL,
room TEXT NOT NULL,
CONSTRAINT CHK_max_people CHECK (
max_people > 0
AND max_people < 11
)
);
CREATE TABLE IF NOT EXISTS Bookings (
id INTEGER
CONSTRAINT PK_Bookings PRIMARY KEY AUTOINCREMENT,
id_tables INTEGER,
reservation TEXT NOT NULL,
time_reserve TEXT NOT NULL,
who_booked TEXT NOT NULL,
CONSTRAINT FK_Bookings_Tables FOREIGN KEY (id_tables) REFERENCES Tables (id),
CONSTRAINT CHK_min_max_time_reserve CHECK (time_reserve BETWEEN "00:30" AND "12:00")
);
CREATE TRIGGER IF NOT EXISTS CHK_Bookings_Insert
BEFORE INSERT ON Bookings
BEGIN
SELECT RAISE(
FAIL,
"Reservation of table is not possible due to the existing reservation"
)
FROM (
SELECT
datetime(NEW.reservation) AS new_res_dt,
datetime(reservation) AS res_dt,
datetime(reservation, time_reserve) AS reserved_until,
id_tables
FROM Bookings
)
WHERE
(new_res_dt >= res_dt AND new_res_dt < reserved_until)
AND id_tables = NEW.id_tables;
END;
`,
}
+5
View File
@@ -0,0 +1,5 @@
DROP TABLE IF EXISTS Tables;
DROP TABLE IF EXISTS Bookings;
DROP TRIGGER IF EXISTS CHK_Bookings_Insert;
+33
View File
@@ -0,0 +1,33 @@
CREATE TABLE IF NOT EXISTS Tables (
-- column-def:
id INTEGER -- column-constraint:
CONSTRAINT PK_Tables PRIMARY KEY AUTOINCREMENT,
--
name INTEGER NOT NULL UNIQUE,
--
max_people INTEGER NOT NULL,
--
room TEXT NOT NULL,
-- table-constraint:
CONSTRAINT CHK_max_people CHECK (
max_people > 0
AND max_people < 11
)
);
CREATE TABLE IF NOT EXISTS Bookings (
-- column-def:
id INTEGER -- column-constraint:
CONSTRAINT PK_Bookings PRIMARY KEY AUTOINCREMENT,
--
id_tables INTEGER,
--
reservation TEXT NOT NULL,
--
time_reserve TEXT NOT NULL,
--
who_booked TEXT NOT NULL,
-- table-constraint:
CONSTRAINT FK_Bookings_Tables FOREIGN KEY (id_tables) REFERENCES Tables (id),
CONSTRAINT CHK_min_max_time_reserve CHECK (time_reserve BETWEEN "00:30" AND "12:00")
);
+21
View File
@@ -0,0 +1,21 @@
CREATE TRIGGER IF NOT EXISTS CHK_Bookings_Insert BEFORE INSERT ON Bookings BEGIN
SELECT
RAISE (
FAIL,
"Reservation of table is not possible due to the existing reservation"
)
FROM
(
SELECT
datetime (NEW.reservation) AS new_res_dt,
datetime (reservation) AS res_dt,
datetime (reservation, time_reserve) AS reserved_until
FROM
Bookings
)
WHERE
new_res_dt >= res_dt
OR new_res_dt < reserved_until;
--
END;
+7
View File
@@ -0,0 +1,7 @@
INSERT INTO
Tables (name, max_people, room)
VALUES
(1, 5, "Red room"),
(4, 4, "Red room"),
(2, 7, "Green room"),
(3, 10, "Game hall");
+144
View File
@@ -0,0 +1,144 @@
-- reservation format "YYYY-MM-DDTHH:MM"
-- time_reserve "HH:MM"
-- fail check time_reserve
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T00:00", "00:00", "");
-- fail check time_reserve
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T00:00", "00:29", "");
-- fail check time_reserve
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T00:00", "12:01", "");
-- success check time_reserve
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T00:00", "00:30", "");
-- success until 02:30
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T01:00", "01:30", "");
-- fail check begin
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T00:30", "01:00", "");
-- fail check middle
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T00:30", "01:30", "");
-- fail check end
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T00:30", "02:00", "");
-- fail check begin
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T01:00", "00:30", "");
-- fail check middle
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T01:30", "00:30", "");
-- fail check end
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T02:00", "00:30", "");
-- success
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T02:30", "00:30", "");
-- success
INSERT INTO
Bookings (
id_tables,
reservation,
time_reserve,
who_booked
)
VALUES
(1, "2000-01-01T03:00", "00:30", "");
+10
View File
@@ -0,0 +1,10 @@
SELECT -- Incorrect table reservations
Left.reservation,
datetime (Left.reservation, Left.time_reserve) AS reserved_until,
Right.reservation
from
Bookings AS Left
LEFT JOIN Bookings AS Right ON Left.id != Right.id
AND Left.id_tables = Right.id_tables
WHERE
datetime (Right.reservation) BETWEEN datetime (Left.reservation) AND datetime (Left.reservation, Left.time_reserve)
+10
View File
@@ -0,0 +1,10 @@
package behaivor
import "log"
// Crash the program if an error exists. Anxiety before panic
func Anxiety(prefixMessage string, err error) {
if err != nil {
log.Fatalf("%v:\n\t%v", prefixMessage, err)
}
}
+27
View File
@@ -0,0 +1,27 @@
package file
import (
"go_project/pkg/behaivor"
"os"
)
// Verification of the existence of a program with an emergency termination in case of an error
func MustIsNotExist(filename string) (result bool) {
_, err := os.Stat(filename)
if err != nil {
result = os.IsNotExist(err)
} else {
return false
}
if result == false {
behaivor.Anxiety("Error checking the existence of the file", err)
}
return true
}
// Create a file with truncated data
func MustCreate(filename string) {
file, err := os.Create(filename)
behaivor.Anxiety("File creating error", err)
file.Close()
}
+53
View File
@@ -0,0 +1,53 @@
package information
import (
"bytes"
"github.com/gorilla/mux"
"log"
"strings"
)
// Following prints all of the registered routes
func AboutRoute(route *mux.Route, _ *mux.Router, _ []*mux.Route) error {
pathTemplate, err := route.GetPathTemplate()
var buffer bytes.Buffer
if err == nil {
buffer.WriteString("\n\t")
buffer.WriteString("ROUTE: ")
buffer.WriteString(pathTemplate)
}
pathRegexp, err := route.GetPathRegexp()
if err == nil {
buffer.WriteString("\n\t")
buffer.WriteString("Path regexp: ")
buffer.WriteString(pathRegexp)
}
queriesTemplates, err := route.GetQueriesTemplates()
if err == nil {
buffer.WriteString("\n\t")
buffer.WriteString("Queries templates: ")
buffer.WriteString(strings.Join(queriesTemplates, ","))
}
queriesRegexps, err := route.GetQueriesRegexp()
if err == nil {
buffer.WriteString("\n\t")
buffer.WriteString("Queries regexps: ")
buffer.WriteString(strings.Join(queriesRegexps, ","))
}
methods, err := route.GetMethods()
if err == nil {
buffer.WriteString("\n\t")
buffer.WriteString("Methods: ")
buffer.WriteString(strings.Join(methods, ","))
}
if len := buffer.Len(); len != 0 {
log.Println(buffer.String())
}
return nil
}
+16
View File
@@ -0,0 +1,16 @@
package marshaling
import (
"encoding/json"
"go_project/pkg/behaivor"
"io"
"net/http"
)
// NOTE: Not used in this project
func MustUnmarshalBody(r *http.Request, x interface{}) {
body, err := io.ReadAll(r.Body)
behaivor.Anxiety("Error reading the request body", err)
err = json.Unmarshal([]byte(body), x)
behaivor.Anxiety("Error unmarshaling the request body", err)
}