// This package contains the database interface and an in memory implementation. // // For the in-memory implementation see the "NewInMemoryStore()" function. package db import ( "errors" "fmt" "sync" "git.phc.dm.unipi.it/aziis98/posti-dm/server/util" ) // ErrAlreadyExists is the error thrown from Store functions when an entity already exists. var ErrAlreadyExists = errors.New(`object already exists in database`) // ErrDoesntExist is the error thrown from Store functions when an entity doesn't exist. var ErrDoesntExist = errors.New(`object doesn't exist in database`) const ( PermissionAdmin = "admin" PermissionModerator = "moderator" ) // Entities // type FrontendUser struct { // Anonymous bool `json:"anonymous,omitempty"` // ID string `json:"id,omitempty"` // Permissions []string `json:"permissions,omitempty"` // } // User represents a user in the database. type User struct { ID string `json:"id"` Permissions util.StringSet `json:"permissions"` // passwordSaltHash string } // Room represents a room in the database, a room has an id, a name and a collection of seatIDs of this room. type Room struct { ID string `json:"id"` SeatIDs []string `json:"seatIds"` // gridRows, gridCols int } // Version 1: // // type SeatState struct { // Occupied bool // Anonymous bool // UserID string // } // Version 2: // // type SeatState interface{ isSeatState() } // // type SeatNotOccupied struct{} // type SeatOccupiedByUser struct{ UserID string } // type SeatOccupiedByAnonymous struct{} // // func (s SeatNotOccupied) isSeatState() // func (s SeatOccupiedByUser) isSeatState() // func (s SeatOccupiedByAnonymous) isSeatState() // Seat represents a seat in the database, it belongs to a single room and can be free or occupied by some user (referenced by userID). type Seat struct { ID string `json:"id"` RoomID string `json:"roomId"` // OccupiedBy is an empty list or a singleton of a userID OccupiedBy []string `json:"occupiedBy"` // x, y, w, h int } // type FrontendSeat struct { // ID string `json:"id"` // RoomID string `json:"roomId"` // // OccupiedBy is an empty list or a singleton of a userID // OccupiedBy *FrontendUser `json:"occupiedBy"` // DiagramRect struct { // X int `json:"x"` // Y int `json:"y"` // Width int `json:"width"` // Height int `json:"height"` // } `json:"diagramRect"` // } // // Database Interfaces // // Store is the main interface for interacting with database implementations, for now the only implementation is "memDB". type Store interface { BeginTransaction() EndTransaction() CreateUser(user *User) error CreateRoom(room *Room) error CreateSeat(seat *Seat) error DeleteUser(user *User) error DeleteRoom(room *Room) error DeleteSeat(seat *Seat) error GetUser(userID string) (*User, error) GetRoom(roomID string) (*Room, error) GetSeat(seatID string) (*Seat, error) GetRooms() ([]string, error) GetRoomOccupiedSeats(roomID string) ([]string, error) GetRoomFreeSeats(roomID string) ([]string, error) GetUserSeats(userID string) (util.StringSet, error) OccupySeat(seatID, userID string) error FreeSeat(seatID string) error } // TODO: Create an SQLite implementation // NewInMemoryStore creates an instance of memDB hidden behind the Store interface. func NewInMemoryStore() Store { db := &memDB{ &sync.Mutex{}, make(map[string]*User), make(map[string]*Room), make(map[string]*Seat), } db.rooms["aula-stud"] = &Room{ ID: "aula-stud", SeatIDs: []string{}, } for i := 1; i <= 11; i++ { seatID := fmt.Sprintf(`aula-stud/posto-%d`, i) db.rooms["aula-stud"].SeatIDs = append(db.rooms["aula-stud"].SeatIDs, seatID) db.seats[seatID] = &Seat{ ID: seatID, RoomID: "aula-stud", OccupiedBy: []string{}, } } db.seats["aula-stud/posto-7"].OccupiedBy = []string{"aziis98"} db.seats["aula-stud/posto-1"].OccupiedBy = []string{"bachoseven"} db.users["aziis98"] = &User{ ID: "aziis98", Permissions: util.NewStringSet(PermissionAdmin), } db.users["bachoseven"] = &User{ ID: "bachoseven", Permissions: util.NewStringSet(PermissionAdmin), } return db }