commit 51e2e789d03d217cc924227d38e8ac4974e841f1 Author: user Date: Sun Jul 12 13:47:34 2026 +0400 Initial commit diff --git a/cmd/001_run_server.sh b/cmd/001_run_server.sh new file mode 100755 index 0000000..981d0b2 --- /dev/null +++ b/cmd/001_run_server.sh @@ -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 + diff --git a/cmd/011_POST_Tables.sh b/cmd/011_POST_Tables.sh new file mode 100755 index 0000000..1df867b --- /dev/null +++ b/cmd/011_POST_Tables.sh @@ -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 diff --git a/cmd/012_PUT_Tables.sh b/cmd/012_PUT_Tables.sh new file mode 100755 index 0000000..b4d3307 --- /dev/null +++ b/cmd/012_PUT_Tables.sh @@ -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 + diff --git a/cmd/013_GET_Tables.sh b/cmd/013_GET_Tables.sh new file mode 100755 index 0000000..92ce3d8 --- /dev/null +++ b/cmd/013_GET_Tables.sh @@ -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 diff --git a/cmd/014_DELETE_Tables.sh b/cmd/014_DELETE_Tables.sh new file mode 100755 index 0000000..1660cab --- /dev/null +++ b/cmd/014_DELETE_Tables.sh @@ -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 diff --git a/cmd/021_POST_Booking.sh b/cmd/021_POST_Booking.sh new file mode 100755 index 0000000..68a7210 --- /dev/null +++ b/cmd/021_POST_Booking.sh @@ -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 diff --git a/cmd/022_PUT_Booking.sh b/cmd/022_PUT_Booking.sh new file mode 100755 index 0000000..6812296 --- /dev/null +++ b/cmd/022_PUT_Booking.sh @@ -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 diff --git a/cmd/023_GET_Booking.sh b/cmd/023_GET_Booking.sh new file mode 100755 index 0000000..99647cc --- /dev/null +++ b/cmd/023_GET_Booking.sh @@ -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 diff --git a/cmd/024_DELETE_Booking.sh b/cmd/024_DELETE_Booking.sh new file mode 100755 index 0000000..813ac3b --- /dev/null +++ b/cmd/024_DELETE_Booking.sh @@ -0,0 +1,4 @@ +clear + +# Delete one record of Booking by id 1 +curl -X DELETE localhost:8080/booking/1 --silent diff --git a/cmd/SQLiteDatabase.db b/cmd/SQLiteDatabase.db new file mode 100755 index 0000000..411e076 Binary files /dev/null and b/cmd/SQLiteDatabase.db differ diff --git a/cmd/cmd b/cmd/cmd new file mode 100755 index 0000000..0f1fd0a Binary files /dev/null and b/cmd/cmd differ diff --git a/cmd/cmd.exe b/cmd/cmd.exe new file mode 100755 index 0000000..6f30c3a Binary files /dev/null and b/cmd/cmd.exe differ diff --git a/cmd/main.go b/cmd/main.go new file mode 100755 index 0000000..54b30e2 --- /dev/null +++ b/cmd/main.go @@ -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") +} diff --git a/go.mod b/go.mod new file mode 100755 index 0000000..5d5fb65 --- /dev/null +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100755 index 0000000..0ba724f --- /dev/null +++ b/go.sum @@ -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= diff --git a/internal/client/ui/index.html b/internal/client/ui/index.html new file mode 100755 index 0000000..4e1550c --- /dev/null +++ b/internal/client/ui/index.html @@ -0,0 +1,110 @@ + + + + + + Table Booking + + + + +
+

Table and Booking Management

+
+ +
+ +
+

Manage Tables

+ + + + + + + + + + + + + +
IDName (Number)Max PeopleRoomActions
+ +

Create/Update Table

+
+ + + + + + + + + + + + + +
+ +

Find Table by ID

+
+ + + +
+
+ + +
+

Manage Bookings

+ + + + + + + + + + + + + + +
IDTable IDReservation DateTimeTime ReserveWho BookedActions
+ +

Create/Update Booking

+
+ + + + + + + + + + + + + + + + +
+ + +

Find Booking by ID

+
+ + + +
+
+
+ + + + diff --git a/internal/client/ui/script.js b/internal/client/ui/script.js new file mode 100755 index 0000000..91d4fee --- /dev/null +++ b/internal/client/ui/script.js @@ -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 = ` + ${table.id} + ${table.name} + ${table.max_people} + ${table.room} + + + + + `; + 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 = ` + ${booking.id} + ${booking.id_tables} + ${booking.reservation} + ${booking.time_reserve} + ${booking.who_booked} + + + + + `; + 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(); +}); + diff --git a/internal/client/ui/styles.css b/internal/client/ui/styles.css new file mode 100755 index 0000000..635c772 --- /dev/null +++ b/internal/client/ui/styles.css @@ -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; +} + diff --git a/internal/configuration/globalVariabales.go b/internal/configuration/globalVariabales.go new file mode 100755 index 0000000..a8fcc96 --- /dev/null +++ b/internal/configuration/globalVariabales.go @@ -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 +} diff --git a/internal/connection/sqlite.go b/internal/connection/sqlite.go new file mode 100755 index 0000000..d8ced94 --- /dev/null +++ b/internal/connection/sqlite.go @@ -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) +} diff --git a/internal/controllers/bookingController.go b/internal/controllers/bookingController.go new file mode 100755 index 0000000..6701415 --- /dev/null +++ b/internal/controllers/bookingController.go @@ -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(¤tBooking.Id, ¤tBooking.IdTables, ¤tBooking.ReservationDatetime, ¤tBooking.TimeReserve, ¤tBooking.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 +} diff --git a/internal/controllers/tableController.go b/internal/controllers/tableController.go new file mode 100755 index 0000000..5078cdf --- /dev/null +++ b/internal/controllers/tableController.go @@ -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(¤tTable.Id, ¤tTable.Name, ¤tTable.MaxPeople, ¤tTable.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 +} diff --git a/internal/handlers/bookingHandler.go b/internal/handlers/bookingHandler.go new file mode 100755 index 0000000..f351496 --- /dev/null +++ b/internal/handlers/bookingHandler.go @@ -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) +} diff --git a/internal/handlers/tableHandler.go b/internal/handlers/tableHandler.go new file mode 100755 index 0000000..45bec7f --- /dev/null +++ b/internal/handlers/tableHandler.go @@ -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) +} diff --git a/internal/models/models.go b/internal/models/models.go new file mode 100755 index 0000000..e34f00b --- /dev/null +++ b/internal/models/models.go @@ -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"` + } +) diff --git a/internal/routes/routes.go b/internal/routes/routes.go new file mode 100755 index 0000000..674db48 --- /dev/null +++ b/internal/routes/routes.go @@ -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") +} diff --git a/internal/schema/defaultSchema.go b/internal/schema/defaultSchema.go new file mode 100755 index 0000000..5384962 --- /dev/null +++ b/internal/schema/defaultSchema.go @@ -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; +`, +} diff --git a/migrations/001_drop-all.sql b/migrations/001_drop-all.sql new file mode 100755 index 0000000..9c9fb61 --- /dev/null +++ b/migrations/001_drop-all.sql @@ -0,0 +1,5 @@ +DROP TABLE IF EXISTS Tables; + +DROP TABLE IF EXISTS Bookings; + +DROP TRIGGER IF EXISTS CHK_Bookings_Insert; diff --git a/migrations/002_create-tables.sql b/migrations/002_create-tables.sql new file mode 100755 index 0000000..eb0529d --- /dev/null +++ b/migrations/002_create-tables.sql @@ -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") +); diff --git a/migrations/003_create-triggers.sql b/migrations/003_create-triggers.sql new file mode 100755 index 0000000..5785cee --- /dev/null +++ b/migrations/003_create-triggers.sql @@ -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; diff --git a/migrations/004_insert-into-tables.sql b/migrations/004_insert-into-tables.sql new file mode 100755 index 0000000..4e2080a --- /dev/null +++ b/migrations/004_insert-into-tables.sql @@ -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"); diff --git a/migrations/005_insert-into-bookings.sql b/migrations/005_insert-into-bookings.sql new file mode 100755 index 0000000..741028b --- /dev/null +++ b/migrations/005_insert-into-bookings.sql @@ -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", ""); diff --git a/migrations/100_select-incorrect-reservations.sql b/migrations/100_select-incorrect-reservations.sql new file mode 100755 index 0000000..57505f0 --- /dev/null +++ b/migrations/100_select-incorrect-reservations.sql @@ -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) diff --git a/pkg/behaivor/behaivor.go b/pkg/behaivor/behaivor.go new file mode 100755 index 0000000..50b5cc0 --- /dev/null +++ b/pkg/behaivor/behaivor.go @@ -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) + } +} diff --git a/pkg/file/file.go b/pkg/file/file.go new file mode 100755 index 0000000..46f1de2 --- /dev/null +++ b/pkg/file/file.go @@ -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() +} diff --git a/pkg/information/routes.go b/pkg/information/routes.go new file mode 100755 index 0000000..00ff096 --- /dev/null +++ b/pkg/information/routes.go @@ -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 +} diff --git a/pkg/marshaling/marshaling.go b/pkg/marshaling/marshaling.go new file mode 100755 index 0000000..3d9a00b --- /dev/null +++ b/pkg/marshaling/marshaling.go @@ -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) +}