Commit 94bfe152 authored by Enrico Bollen's avatar Enrico Bollen 💬

Merge branch 'dev' into 'master'

Dev

See merge request !33
parents f4e7774a ecb358bc
Pipeline #1854 failed with stages
in 54 seconds
......@@ -73,6 +73,9 @@ production:
before_script:
- gem install dpl
image: ruby:latest
environment:
name: "Production"
url: "https://go-hard-or-go-home.herokuapp.com/"
script:
- dpl --provider=heroku --app=$HEROKU_APP_PRODUCTION --api-key=$HEROKU_API_KEY
only:
......@@ -81,6 +84,9 @@ production:
staging:
stage: deploy
image: ruby:latest
environment:
name: "Staging"
url: "https://go-hard-or-go-home-staging.herokuapp.com/"
before_script:
- gem install dpl
script:
......
......@@ -15,8 +15,6 @@ coverage:
build:
go build -o . ./cmd/main.go
clean:
# Need to install reflex
# go get github.com/cespare/reflex
watch:
......
# Good2Go – Roombooking
# Good2Go – RoomBooking
[Error Tracking](https://git.coco.study/fvitt/good2go/-/error_tracking)
[Coverage Report](https://fvitt.pages.coco.study/good2go/master/coverage)
......
......@@ -10,7 +10,7 @@ RestAPI Module
## Components
The api module is seperated into three major components
The api module is separated into three major components
### Router
......
package controllers
import "time"
// CreateAppointment creates an appointment.
func CreateAppointment() {
}
// GetAppointment finds a single appointment by startTime
func GetAppointment(startTime time.Time) {
}
// GetAllAppointments returns all the appointments
func GetAllAppointments() {
}
// UpdateAppointment updates a single appointment
func UpdateAppointment() {
}
// DeleteAppointment deletes a single appointment
func DeleteAppointment() {
}
package controllers
// CreateBuilding Creates a single new building
func CreateBuilding() {
// Reservered for future use
}
// GetBuilding gets a single building
func GetBuilding() {
// Reservered for future use
}
// GetAllBuildings Returns all the buildings
func GetAllBuildings() {
// Reservered for future use
}
// UpdateBuilding Updates a single building
func UpdateBuilding() {
// Reservered for future use
}
// DeleteBuilding Deletes a single building
func DeleteBuilding(id int) {
// Reservered for future use
}
package controllers
import (
m "git.coco.study/fvitt/good2go/internal/model"
)
// RoomController Controller to handle all types of controller stuff
type RoomController struct{}
// CreateRoom *drumroll* creates a room
func CreateRoom(roomNo int, capacity int) m.Room {
return m.Building.AddRoom(roomNo, capacity)
}
// GetRoom gets a single room by id
func GetRoom(roomNo int) (room *m.Room, err error) {
return m.Building.GetRoomByNo(roomNo)
}
// GetAllRooms returns all the rooms
func GetAllRooms() []*m.Room {
return m.Building.GetRooms()
}
// UpdateRoom updates a single room
func UpdateRoom(roomNo int, capacity int) (err error) {
return m.Building.UpdateRoomCapacity(roomNo, capacity)
}
// DeleteRoom deletes a room by id
func DeleteRoom(roomNo int) {
m.Building.DeleteRoom(roomNo)
}
package controllers
import (
"reflect"
"testing"
"git.coco.study/fvitt/good2go/internal/model"
)
var initialRoomAmount = 0
func isRoom(r *model.Room) bool {
if reflect.TypeOf(r.Capacity).Name() != "int" {
return false
}
if reflect.TypeOf(r.Number).Name() != "int" {
return false
}
return true
}
func TestCreateRooms(t *testing.T) {
room := CreateRoom(420, 69)
if room.Number != 420 {
t.Errorf("Returned room didnt have the correct room number.")
}
if room.Capacity != 69 {
t.Errorf("Returned room didnt have the correct capacity.")
}
}
func TestGetRoom(t *testing.T) {
CreateRoom(666, 66)
room, err := GetRoom(666)
if err != nil {
t.Error(err)
}
if room.Capacity != 66 {
t.Errorf("first element in getAllRooms[] doesnt seem to be a room")
}
if room.Number != 666 {
t.Errorf("Created room didnt have the correct room number")
}
}
func TestGetAllRooms(t *testing.T) {
rooms := GetAllRooms()
initialRoomAmount = len(rooms)
if initialRoomAmount > 0 && !isRoom(rooms[0]) {
t.Errorf("first element in getAllRooms[] doesnt seem to be a room")
}
}
func TestUpdateSingleRoom(t *testing.T) {
// GIVEN
CreateRoom(34, 34)
// WHEN
updateErr := UpdateRoom(34, 90)
if updateErr != nil {
t.Error(updateErr)
}
// THEN
room, err := GetRoom(34)
if err != nil {
t.Errorf("Could not find created room")
}
if room.Capacity != 90 {
t.Error("Updated room didnt have the correct capacity")
}
}
func TestDeleteRoom(t *testing.T) {
// GIVEN
CreateRoom(100, 100)
_, err := GetRoom(100)
if err != nil {
t.Error(err.Error())
}
// WHEN
DeleteRoom(100)
// THEN
_, err = GetRoom(100)
if err == nil {
t.Error("Deleted room was not deleted")
}
}
......@@ -9,7 +9,7 @@ import (
"github.com/gorilla/mux"
m "git.coco.study/fvitt/good2go/api/rest/middlewares"
h "git.coco.study/fvitt/good2go/api/rest/subrouters"
h "git.coco.study/fvitt/good2go/api/rest/routes"
)
func getPort() string {
......@@ -26,18 +26,19 @@ func Init() {
// INIT ALL THE THINGS
r := mux.NewRouter()
// Add all the middlewares
// Add all the middleware
r.Use(m.LogRequests)
r.Use(m.SentryHandler)
// Register all the Routers
h.RoomRouter.AttachRouter(r)
h.AppointmentRouter.AttachRouter(r)
h.RoomRouter.AttachRouter(r)
h.HomeRouter.AttachRouter(r)
// Create a new server
srv := &http.Server{
Handler: r,
Addr: "0.0.0.0:" + getPort(),
Addr: "0.0.0.0:8080",
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 5 * time.Second,
ReadTimeout: 5 * time.Second,
......
......@@ -8,7 +8,7 @@ import (
// LogRequests logs requests
func LogRequests(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("[REST-API] " + r.Method + " request on: " + r.RequestURI)
log.Println("[RESTAPI] " + r.Method + " request on: " + r.RequestURI)
next.ServeHTTP(w, r)
})
}
package restapi
import (
sentryhttp "github.com/getsentry/sentry-go/http"
)
var SentryHandler = sentryhttp.New(sentryhttp.Options{}).Handle
package restapi
import (
"encoding/json"
"fmt"
"html"
"net/http"
"github.com/gorilla/mux"
......@@ -18,19 +21,61 @@ var (
var name = "Home"
func createHome(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Why would you want to create a new /? This one is perfectly fine!"))
_, err := w.Write([]byte("Why would you want to create a new /? This one is perfectly fine!"))
if err != nil {
fmt.Println(err)
}
}
func getHome(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from " + name))
_, err := w.Write([]byte("Hello from " + name))
if err != nil {
fmt.Println(err)
}
}
func deleteHome(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("What are you thinking, you cant delete /, are you crazy!?!?"))
func min(a, b int) int {
if a < b {
return a
}
return b
}
func limitString(input string, maxLength int) string {
output := ""
runes := []rune(input)
length := min(len(input), maxLength)
for i := 0; i < length; i++ {
output += string(runes[i])
}
return output
}
func updateHome(res http.ResponseWriter, req *http.Request) {
var r struct {
name string
}
parseBodyErr := json.NewDecoder(req.Body).Decode(&r)
if parseBodyErr != nil {
http.Error(res, parseBodyErr.Error(), http.StatusBadRequest)
return
}
name = limitString(html.EscapeString(r.name), 20)
_, err := res.Write([]byte("Updated home: new name " + name + "!?!?"))
if err != nil {
fmt.Println(err)
}
}
func updateHome(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("What are you thinking, you cant delete /, are you crazy!?!?"))
func deleteHome(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte("What are you thinking, you cant delete /, are you crazy!?!?"))
if err != nil {
fmt.Println(err)
}
}
func (a homeRouter) AttachRouter(r *mux.Router) {
......
......@@ -2,12 +2,14 @@ package restapi
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
c "git.coco.study/fvitt/good2go/api/rest/controllers"
u "git.coco.study/fvitt/good2go/api/rest/utils"
model "git.coco.study/fvitt/good2go/internal/model"
"git.coco.study/fvitt/good2go/internal/model"
s "git.coco.study/fvitt/good2go/internal/services"
"github.com/gorilla/mux"
)
......@@ -19,58 +21,54 @@ var (
)
func createRoom(res http.ResponseWriter, req *http.Request) {
// Decode the body
var r model.Room
err := json.NewDecoder(req.Body).Decode(&r)
if err != nil {
http.Error(res, err.Error(), http.StatusBadRequest)
return
}
room, err := s.RoomService.CreateRoom(&r)
if err != nil {
http.Error(res, err.Error(), http.StatusBadRequest)
return
}
room := c.CreateRoom(r.Number, r.Capacity)
u.SendJSON(res).Encode(room)
err = u.SendJSON(res).Encode(room)
if err != nil {
fmt.Println(err)
}
}
func getRoom(res http.ResponseWriter, req *http.Request) {
var roomNo int
roomNo, parseErr := strconv.Atoi(mux.Vars(req)["roomNo"])
roomNumberString := mux.Vars(req)["roomNumber"]
if parseErr == nil {
roomNumber, _ := strconv.Atoi(roomNumberString)
room, roomErr := c.GetRoom(roomNo)
room, roomErr := s.RoomService.GetRoomByNumber(roomNumber)
if roomErr == nil {
u.SendJSON(res).Encode(room)
} else {
http.Error(res, roomErr.Error(), http.StatusNotFound)
}
if roomErr == nil {
u.SendJSON(res).Encode(room)
} else {
http.Error(res, parseErr.Error(), http.StatusBadRequest)
http.Error(res, roomErr.Error(), http.StatusNotFound)
}
}
func getAllRooms(res http.ResponseWriter, req *http.Request) {
rooms := c.GetAllRooms()
u.SendJSON(res).Encode(rooms)
rooms := s.RoomService.GetAllRooms()
err := u.SendJSON(res).Encode(rooms)
if err != nil {
fmt.Println(err)
}
}
func updateRoom(res http.ResponseWriter, req *http.Request) {
// Parse to integer
roomNo, parseURLError := strconv.Atoi(mux.Vars(req)["roomNo"])
if parseURLError != nil {
http.Error(res, parseURLError.Error(), http.StatusBadRequest)
return
}
roomNumberString := mux.Vars(req)["roomNumber"]
roomNumber, _ := strconv.Atoi(roomNumberString)
// Parse the body
var r model.Room
parseBodyErr := json.NewDecoder(req.Body).Decode(&r)
if parseBodyErr != nil {
......@@ -78,9 +76,13 @@ func updateRoom(res http.ResponseWriter, req *http.Request) {
return
}
err := c.UpdateRoom(roomNo, r.Capacity)
room, err := s.RoomService.UpdateRoomCapacity(roomNumber, r.Capacity)
if err == nil {
getRoom(res, req)
err := u.SendJSON(res).Encode(room)
if err != nil {
fmt.Println(err)
}
} else {
http.Error(res, err.Error(), http.StatusNotFound)
}
......@@ -88,35 +90,35 @@ func updateRoom(res http.ResponseWriter, req *http.Request) {
func deleteRoom(res http.ResponseWriter, req *http.Request) {
// get the roomNo from the url variables
var roomNo int
roomNo, urlError := strconv.Atoi(mux.Vars(req)["roomNo"])
if urlError != nil {
http.Error(res, urlError.Error(), http.StatusBadRequest)
return
}
roomNumberString := mux.Vars(req)["roomNumber"]
roomNumber, _ := strconv.Atoi(roomNumberString)
c.DeleteRoom(roomNo)
err := s.RoomService.DeleteRoom(roomNumber)
if err != nil {
http.Error(res, err.Error(), http.StatusBadRequest)
log.Fatal(err)
}
res.Write([]byte("deleted room with number " + roomNumberString))
res.WriteHeader(http.StatusOK)
}
// AttachRouter initialize Router
func (a roomRouter) AttachRouter(router *mux.Router) {
r := router.PathPrefix("/rooms").Subrouter()
//todo addd single room route
multipleR := router.PathPrefix("/rooms").Subrouter().StrictSlash(true)
singleR := router.PathPrefix("/room").Subrouter().StrictSlash(true)
// CREATE
r.HandleFunc("/", createRoom).Methods("POST")
singleR.HandleFunc("/", createRoom).Methods("POST")
// READ
r.HandleFunc("/{roomNo}", getRoom).Methods("GET")
r.HandleFunc("/", getAllRooms).Methods("GET")
singleR.HandleFunc("/{roomNumber}", getRoom).Methods("GET")
multipleR.HandleFunc("/", getAllRooms).Methods("GET")
// UPDATE
r.HandleFunc("/{roomNo}", updateRoom).Methods("PUT")
singleR.HandleFunc("/{roomNumber}", updateRoom).Methods("PUT")
// DELETE
r.HandleFunc("/{roomNo}", deleteRoom).Methods("DELETE")
singleR.HandleFunc("/{roomNumber}", deleteRoom).Methods("DELETE")
}
package restapi
import (
"net/http"
"github.com/gorilla/mux"
)
type appointmentRouter struct{}
var (
// AppointmentRouter handles appointment requests
AppointmentRouter = &appointmentRouter{}
)
func createAppointment(res http.ResponseWriter, req *http.Request) {
}
func getAppointment(res http.ResponseWriter, req *http.Request) {
}
func getAllAppointments(res http.ResponseWriter, req *http.Request) {
}
func updateAppointment(res http.ResponseWriter, req *http.Request) {
}
func deleteAppointment(res http.ResponseWriter, req *http.Request) {
}
// AttachRouter Creates and attaches a subrouter to the given routers
func (a appointmentRouter) AttachRouter(router *mux.Router) {
r := router.PathPrefix("/appointments").Subrouter()
// CREATE
r.HandleFunc("/", createAppointment).Methods("POST")
// READ
r.HandleFunc("/{id}", getAppointment).Methods("GET")
r.HandleFunc("/", getAllAppointments).Methods("GET")
// UPDATE
r.HandleFunc("/{id}", updateAppointment).Methods("PUT")
// DELETE
r.HandleFunc("/{id}", deleteAppointment).Methods("DELETE")
}
package main
import (
"fmt"
"log"
"time"