3 Commits

Author SHA1 Message Date
tom
5f2cd56325 CHANGE: docs 2024-09-06 09:59:22 +02:00
tom
68608a995b CHANGE: changed API endpoints 2024-09-06 08:50:24 +02:00
tom
bc859085b8 CHANGE: moved DB funcitons into bookings added return for create booking 2024-09-06 08:50:03 +02:00
8 changed files with 265 additions and 26 deletions

27
API.apib Normal file
View File

@@ -0,0 +1,27 @@
#Group Bookings
## Bookings Collection [/time{?cardID}]
### List all bookings for specific card_id [GET]
+ Parameters
+ cardID:test_card (string) - the id of the rfid card
+ Response 200 (application/json)
[
{
"cradID": "test_card",
"readerID": "test_reader",
"bookingTyp": 2,
"loggedTime": "2024-09-05T08:37:53.117641Z",
"id": 5
},
{
"cradID": "test_card",
"readerID": "mytest",
"bookingTyp": 1,
"loggedTime": "2024-09-05T08:51:12.670827Z",
"id": 6
},
]

View File

@@ -2,39 +2,47 @@ package main
import (
"arbeitszeitmessung/models"
"database/sql"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"os"
"strconv"
_ "github.com/lib/pq"
)
var DB *sql.DB
func main() {
var err error
DB, err = OpenDatabase()
models.DB, err = OpenDatabase()
if err != nil {
log.Fatal(err)
}
defer DB.Close()
defer models.DB.Close()
http.HandleFunc("/time/new", timeCreateHandler)
http.HandleFunc("/time", timeHandler)
fmt.Println("Server is running at http://localhost:8080")
fmt.Printf("Server is running at http://localhost:8080 exposed to port %s\n", getEnv("EXPOSED_PORT", "8000"))
log.Fatal(http.ListenAndServe(":8080", nil))
}
func timeCreateHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "PUT":
createBooking(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func timeHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "PUT":
createBooking(w, r)
case "GET":
getBookings(w, r)
case "PUT":
updateBooking(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
@@ -43,7 +51,7 @@ func timeHandler(w http.ResponseWriter, r *http.Request) {
func createBooking(w http.ResponseWriter, r *http.Request) {
booking := (*models.Booking).FromUrlParams(nil, r.URL.Query())
if booking.Verify() {
err := booking.Insert(DB)
err := booking.Insert()
if errors.Is(models.SameBookingError{}, err) {
http.Error(w, "Booking already exists", http.StatusConflict)
return
@@ -61,7 +69,7 @@ func createBooking(w http.ResponseWriter, r *http.Request) {
func getBookings(w http.ResponseWriter, r *http.Request) {
card_id := r.URL.Query().Get("cardID")
bookings, err := GetBookingsByCardID(DB, card_id)
bookings, err := (*models.Booking).GetBookingsByCardID(nil, card_id)
if err != nil {
log.Println("Error getting bookings: ", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
@@ -69,7 +77,42 @@ func getBookings(w http.ResponseWriter, r *http.Request) {
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(bookings)
}
func updateBooking(w http.ResponseWriter, r *http.Request) {
_booking_id := r.URL.Query().Get("bookingID")
if _booking_id == "" {
http.Error(w, "Missing bookingID query parameter", http.StatusBadRequest)
return
}
booking_id, err := strconv.Atoi(_booking_id)
if err != nil {
http.Error(w, "Invalid bookingID query parameter", http.StatusBadRequest)
return
}
booking, err := (*models.Booking).GetBookingById(nil, booking_id)
if err != nil {
log.Println("Error getting booking: ", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
if card_id := r.URL.Query().Get("cardID"); card_id != "" {
booking.CardID = card_id
}
if reader_id := r.URL.Query().Get("readerID"); reader_id != "" {
booking.ReaderID = reader_id
}
if _booking_type := r.URL.Query().Get("bookingType"); _booking_type != "" {
booking_type, err := strconv.Atoi(_booking_type)
if err != nil {
http.Error(w, "Invalid booking_type query parameter", http.StatusInternalServerError)
return
}
booking.BookingType = booking_type
}
booking.Save()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(booking)
}
// func getBooking(w http.ResponseWriter, r *http.Request)

View File

@@ -2,6 +2,7 @@ package models
import (
"database/sql"
"fmt"
"log"
"net/url"
"strconv"
@@ -22,6 +23,8 @@ type Booking struct {
Id int `json:"id"`
}
var DB *sql.DB
func (b Booking) New(card_id string, reader_id string, booking_type int) Booking {
return Booking{
CardID: card_id,
@@ -48,24 +51,81 @@ func (b Booking) Verify() bool {
return true
}
func (b Booking) Insert(db *sql.DB) error {
if !checkLastBooking(b, db) {
func (b *Booking) Insert() error {
if !checkLastBooking(*b) {
return SameBookingError{}
}
stmt, err := db.Prepare((`INSERT INTO zeiten (card_id, reader_id, booking_type) VALUES ($1, $2, $3)`))
stmt, err := DB.Prepare((`INSERT INTO zeiten (card_id, reader_id, booking_type) VALUES ($1, $2, $3) RETURNING id, logged_time`))
if err != nil {
return err
}
_, err = stmt.Query(b.CardID, b.ReaderID, b.BookingType)
err = stmt.QueryRow(b.CardID, b.ReaderID, b.BookingType).Scan(&b.Id, &b.LoggedTime)
if err != nil {
return err
}
return nil
}
func checkLastBooking(b Booking, db *sql.DB) bool {
func (b *Booking) GetBookingById(booking_id int) (Booking, error) {
var booking Booking
qStr, err := DB.Prepare((`SELECT id, logged_time, card_id, reader_id, booking_type FROM zeiten WHERE id = $1`))
if err != nil {
return booking, err
}
err = qStr.QueryRow(booking_id).Scan(&booking.Id, &booking.LoggedTime, &booking.CardID, &booking.ReaderID, &booking.BookingType)
if err != nil {
return booking, err
}
if !booking.Verify() {
fmt.Printf("Booking verification failed")
return booking, nil
}
return booking, nil
}
func (b *Booking) GetBookingsByCardID(card_id string) ([]Booking, error) {
qStr, err := DB.Prepare((`SELECT id, logged_time, card_id, reader_id, booking_type FROM zeiten WHERE card_id = $1`))
if err != nil {
return nil, err
}
var bookings []Booking
rows, err := qStr.Query(card_id)
if err == sql.ErrNoRows {
return bookings, err
}
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var booking Booking
if err := rows.Scan(&booking.Id, &booking.LoggedTime, &booking.CardID, &booking.ReaderID, &booking.BookingType); err != nil {
return bookings, err
}
bookings = append(bookings, booking)
}
if err = rows.Err(); err != nil {
return bookings, err
}
return bookings, nil
}
func (b Booking) Save() {
qStr, err := DB.Prepare((`UPDATE "zeiten" SET "id" = $1, "card_id" = $2, "reader_id" = $3, "booking_type" = $4 WHERE "id" = $1;`))
if err != nil {
log.Fatalf("Error preparing query: %v", err)
return
}
_, err = qStr.Query(b.Id, b.CardID, b.ReaderID, b.BookingType)
if err != nil {
log.Fatalf("Error executing query: %v", err)
return
}
}
func checkLastBooking(b Booking) bool {
var booking_type int
stmt, err := db.Prepare((`SELECT booking_type FROM "zeiten" WHERE "card_id" = $1 ORDER BY "logged_time" DESC LIMIT 1;`))
stmt, err := DB.Prepare((`SELECT booking_type FROM "zeiten" WHERE "card_id" = $1 ORDER BY "logged_time" DESC LIMIT 1;`))
if err != nil {
log.Fatalf("Error preparing query: %v", err)
return false

View File

@@ -2,4 +2,4 @@ POSTGRES_USER=arbeit_zeit
POSTGRES_PASSWORD=password
POSTGRES_PATH=./database
POSTGRES_DB=arbeitszeitmessung
# EXPOSED_PORT=8080
EXPOSED_PORT=8000

View File

@@ -0,0 +1,35 @@
name: arbeitszeitmessung-dev
services:
db:
image: postgres:16
restart: unless-stopped
env_file:
- .env
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
PGDATA: /var/lib/postgresql/data/pg_data
volumes:
- ${POSTGRES_PATH}:/var/lib/postgresql/data
ports:
- 5432:5432
adminer:
image: adminer
restart: unless-stopped
ports:
- 8001:8080
backend:
build: ../Backend
image: git.letsstein.de/tom/arbeitszeit-backend
env_file:
- .env
environment:
POSTGRES_HOST: db
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASS: ${POSTGRES_PASSWORD}
EXPOSED_PORT: ${EXPOSED_PORT}
ports:
- 8000:8080

View File

@@ -12,16 +12,14 @@ services:
PGDATA: /var/lib/postgresql/data/pg_data
volumes:
- ${POSTGRES_PATH}:/var/lib/postgresql/data
ports:
- 5432:5432
adminer:
image: adminer
restart: unless-stopped
ports:
- 8000:8080
backend:
build: ../Backend
image: git.letsstein.de/tom/arbeitszeit-backend
env_file:
- .env
@@ -30,5 +28,8 @@ services:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASS: ${POSTGRES_PASSWORD}
EXPOSED_PORT: ${EXPOSED_PORT}
ports:
- 8001:8080
- ${EXPOSED_PORT}:8080
depends_on:
- db

View File

@@ -5,9 +5,81 @@ bis jetzt ein einfaches Backend mit PostgreSQL Datenbank und GO Webserver um Arb
## Installation
```bash
git clone https://git.letsstein.de/tom/arbeitszeitmessung
git clone https://git.letsstein.de/tom/arbeitszeitmessung arbeitszeitmessung
# cd in das Verzeichnis
cd Docker
cd arbeitszeitmessung/Docker
# .env Datei anpassen
docker compose up -d
```
## API
Nutzung der API
### Buchungen [/time]
#### [GET] Anfrage
Parameter: cardID (string)
Antwort: `200`
```json
[
{
"cradID": "test_card",
"readerID": "test_reader",
"bookingTyp": 2,
"loggedTime": "2024-09-05T08:37:53.117641Z",
"id": 5
},
{
"cradID": "test_card",
"readerID": "mytest",
"bookingTyp": 1,
"loggedTime": "2024-09-05T08:51:12.670827Z",
"id": 6
},
]
```
Antwort `500`
Serverfehler
#### [PUT] Anfrage
Parameter: id (int)
Body: (veränderte Parameter)
```json
{
"cradID": "test_card",
"readerID": "mytest",
"bookingTyp": 1,
"loggedTime": "2024-09-05T08:51:12.670827Z",
}
```
Antwort `200`
```json
{
"cradID": "test_card",
"readerID": "mytest",
"bookingTyp": 1,
"loggedTime": "2024-09-05T08:51:12.670827Z",
"id": 6
}
```
### Neue Buchung [/time/new]
#### [PUT] Anfrage
Parameter:
- cardID (string)
- readerID (string)
- bookingType (string)
Antwort `202` Akzeptiert und eingefügt
```json
{
"cradID": "test_card",
"readerID": "mytest",
"bookingTyp": 1,
"loggedTime": "2024-09-05T08:51:12.670827Z",
"id": 6
}
```
Antwort `409` Konflikt
Die vorherige Buchung am selben Tag hat den gleichen Buchungstyp

3
db.sql
View File

@@ -8,7 +8,8 @@ CREATE TABLE zeiten (
);
-- @block insert data
INSERT INTO zeiten (card_id, reader_id, booking_type)
VALUES ('test_card', 'test_reader', '2');
VALUES ('test_card', 'test_reader', '2')
RETURNING id, logged_time;
-- @block select
SELECT *
FROM zeiten;