Skip to content

Commit

Permalink
Merge pull request #184 from COS301-SE-2024/fix/backend/ViewRooms-Fix
Browse files Browse the repository at this point in the history
Fix/backend/view rooms fix
  • Loading branch information
Rethakgetse-Manaka authored Jul 10, 2024
2 parents 3112b04 + 5b2aacc commit bf84ce3
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 49 deletions.
Binary file added documentation/SRS-Docs/SRS.pdf
Binary file not shown.
89 changes: 85 additions & 4 deletions documentation/occupi-docs/pages/api-documentation/api-usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -808,10 +808,11 @@ If there are any errors during the process, appropriate error messages are retur
This endpoint is used to view all rooms in the Occupi system.
Upon a successful request, a list of all rooms is returned.
If there are any errors during the process, appropriate error messages are returned.
Pass floorNo as a query parameter to get rooms on a specific floor. If floorNo is not provided, all rooms on floor 0 are returned.

- **URL**

`/api/view-rooms`
`/api/view-rooms?floorNo=0`

- **Method**

Expand All @@ -822,9 +823,7 @@ If there are any errors during the process, appropriate error messages are retur
- **Content**

```json copy
{
"floorNo": "string"
}
{}
```

**Success Response**
Expand Down Expand Up @@ -950,6 +949,88 @@ If there are any errors during the process, appropriate error messages are retur
- **Code:** 500
- **Content:** `{ "status": 500, "message": "Failed to update user details", "error": {"code":"INTERNAL_SERVER_ERROR","details":null,"message":"Failed to update user details"} }`

### GetUsers

This endpoint is used to view all users in the Occupi system.
Upon a successful request, a list of all users is returned.
If there are any errors during the process, appropriate error messages are returned.

- **URL**

`/api/get-users`

- **Method**

`GET`

- **Request Body**

- **Content**

```json copy
{}
```

**Success Response**

- **Code:** 200

- **Content:** `{ "status": 200, "message": "Successfully fetched users!", "data": {"list of all users"} }`

**Error Response**

- **Code:** 400

- **Content:** `{ "status": 400, "message": "Invalid request payload", "error": {"code":"BAD_REQUEST","details":null,"message":"Invalid request payload"}, }`

**Error Response**

- **Code:** 500

- **Content:** `{ "status": 500, "message": "Failed to get users", "error": {"code":"INTERNAL_SERVER_ERROR","details":null,"message":"Failed to get users"} }`

### FilterUsers

This endpoint is used to filter users in the Occupi system.
The client needs to provide the filter criteria.
Upon a successful request, a list of users that match the filter criteria is returned.
If there are any errors during the process, appropriate error messages are returned.

- **URL**

`/api/filter-users?DepartmentNo=?`

- **Method**

`GET`

- **Request Body**

- **Content**

```json copy
{}
```

**Success Response**

- **Code:** 200

- **Content:** `{ "status": 200, "message": "Successfully fetched users!", "data": {"list of all users"} }`

**Error Response**

- **Code:** 400

- **Content:** `{ "status": 400, "message": "Invalid request payload", "error": {"code":"BAD_REQUEST","details":null,"message":"Expected Department Number"}, }`

**Error Response**

- **Code:** 500

- **Content:** `{ "status": 500, "message": "Failed to get users", "error": {"code":"INTERNAL_SERVER_ERROR","details":null,"message":"Failed to get users"} }`





Expand Down
57 changes: 57 additions & 0 deletions occupi-backend/pkg/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,63 @@ func UpdateUserDetails(ctx *gin.Context, appsession *models.AppSession, user mod
return true, nil
}

// Filters Users based on the filter provided
func FilterUsers(ctx *gin.Context, appsession *models.AppSession, filter models.FilterUsers) ([]models.UserDetails, error) {
collection := appsession.DB.Database("Occupi").Collection("Users")
if collection == nil {
logrus.Error("Failed to get collection")
return nil, errors.New("failed to get collection")
}

filterMap := bson.M{}
AddFieldToUpdateMap(filterMap, "role", filter.Role)
AddFieldToUpdateMap(filterMap, "status", filter.Status)
AddFieldToUpdateMap(filterMap, "departmentNo", filter.DepartmentNo)
cursor, err := collection.Find(ctx, filterMap)
if err != nil {
logrus.Error(err)
return nil, err
}
defer cursor.Close(ctx)

var users []models.UserDetails
for cursor.Next(ctx) {
var user models.UserDetails
if err := cursor.Decode(&user); err != nil {
logrus.Error(err)
return nil, err
}
users = append(users, user)
}
return users, nil
}

func GetAllUsers(ctx *gin.Context, appsession *models.AppSession) ([]models.UserDetails, error) {
collection := appsession.DB.Database("Occupi").Collection("Users")
if collection == nil {
logrus.Error("Failed to get collection")
return nil, errors.New("failed to get collection")
}
cursor, err := collection.Find(ctx, bson.M{})
if err != nil {
logrus.Error(err)
return nil, err
}
defer cursor.Close(ctx)

var users []models.UserDetails
for cursor.Next(ctx) {
var user models.UserDetails
if err := cursor.Decode(&user); err != nil {
logrus.Error(err)
return nil, err
}
users = append(users, user)
}
return users, nil

}

// Checks if a user is an admin
func CheckIfUserIsAdmin(ctx *gin.Context, appsession *models.AppSession, email string) (bool, error) {
// check if database is nil
Expand Down
61 changes: 34 additions & 27 deletions occupi-backend/pkg/handlers/api_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package handlers
import (
"encoding/json"
"errors"
"io"
"net/http"
"reflect"

Expand Down Expand Up @@ -244,36 +243,14 @@ func UpdateUserDetails(ctx *gin.Context, appsession *models.AppSession) {
}

func ViewRooms(ctx *gin.Context, appsession *models.AppSession) {
var roomRequest map[string]interface{}
var room models.RoomRequest
if err := ctx.ShouldBindJSON(&roomRequest); err != nil && !errors.Is(err, io.EOF) {
HandleValidationErrors(ctx, err)
return
}

// Validate JSON
validatedData, err := utils.ValidateJSON(roomRequest, reflect.TypeOf(models.RoomRequest{}))
if err != nil {
ctx.JSON(http.StatusBadRequest, utils.ErrorResponse(http.StatusBadRequest, err.Error(), constants.BadRequestCode, err.Error(), nil))
return
}

// Convert validated JSON to RoomRequest struct
roomBytes, _ := json.Marshal(validatedData)
if err := json.Unmarshal(roomBytes, &room); err != nil {
ctx.JSON(http.StatusInternalServerError, utils.ErrorResponse(http.StatusInternalServerError, "Failed to get room", constants.InternalServerErrorCode, "Failed to check in", nil))
return
}

var floorNo string
if room.FloorNo == "" {
// Fetch floor number from query parameters
floorNo := ctx.Query("floorNo")
if floorNo == "" {
floorNo = "0"
} else {
floorNo = room.FloorNo
}

var rooms []models.Room
rooms, err = database.GetAllRooms(ctx, appsession, floorNo)
rooms, err := database.GetAllRooms(ctx, appsession, floorNo)
if err != nil {
ctx.JSON(http.StatusInternalServerError, utils.ErrorResponse(http.StatusInternalServerError, "Failed to get rooms", constants.InternalServerErrorCode, "Failed to get rooms", nil))
return
Expand All @@ -282,6 +259,36 @@ func ViewRooms(ctx *gin.Context, appsession *models.AppSession) {
ctx.JSON(http.StatusOK, utils.SuccessResponse(http.StatusOK, "Successfully fetched rooms!", rooms))
}

// func FilterUsers(ctx *gin.Context, appsession *models.AppSession) {
// var filterRequest models.FilterUsers
// if ctx.Query("DepartmentNo") == "" {
// ctx.JSON(http.StatusBadRequest, utils.ErrorResponse(http.StatusBadRequest, "Invalid request payload", constants.InvalidRequestPayloadCode, "Expected Department Number", nil))
// return
// }
// // Extract query parameters
// filterRequest.DepartmentNo = ctx.Query("DepartmentNo")

// // Get all users matching a filter
// users, err := database.FilterUsers(ctx, appsession, filterRequest)

// if err != nil {
// ctx.JSON(http.StatusInternalServerError, utils.ErrorResponse(http.StatusInternalServerError, "Failed to get users", constants.InternalServerErrorCode, "Failed to get users", nil))
// return
// }

// ctx.JSON(http.StatusOK, utils.SuccessResponse(http.StatusOK, "Successfully fetched users!", users))
// }

// func GetUsers(ctx *gin.Context, appsession *models.AppSession) {
// users, err := database.GetAllUsers(ctx, appsession)
// if err != nil {
// ctx.JSON(http.StatusInternalServerError, utils.ErrorResponse(http.StatusInternalServerError, "Failed to get users", constants.InternalServerErrorCode, "Failed to get users", nil))
// return
// }

// ctx.JSON(http.StatusOK, utils.SuccessResponse(http.StatusOK, "Successfully fetched users!", users))
// }

// Helper function to handle validation of requests
func HandleValidationErrors(ctx *gin.Context, err error) {
var ve validator.ValidationErrors
Expand Down
33 changes: 20 additions & 13 deletions occupi-backend/pkg/models/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import "time"

// structure of user
type User struct {
ID string `json:"_id" bson:"_id,omitempty"`
OccupiID string `json:"occupiId" bson:"occupiId"`
Password string `json:"password" bson:"password"`
Email string `json:"email" bson:"email"`
Role string `json:"role" bson:"role"`
OnSite bool `json:"onSite" bson:"onSite"`
IsVerified bool `json:"isVerified" bson:"isVerified"`
NextVerificationDate time.Time `json:"nextVerificationDate" bson:"nextVerificationDate"`
TwoFAEnabled bool `json:"twoFAEnabled" bson:"twoFAEnabled"`
ID string `json:"_id" bson:"_id,omitempty"`
OccupiID string `json:"occupiId" bson:"occupiId"`
Password string `json:"password" bson:"password"`
Email string `json:"email" bson:"email"`
Role string `json:"role" bson:"role"`
OnSite bool `json:"onSite" bson:"onSite"`
IsVerified bool `json:"isVerified" bson:"isVerified"`
NextVerificationDate time.Time `json:"nextVerificationDate" bson:"nextVerificationDate"`
TwoFAEnabled bool `json:"twoFAEnabled" bson:"twoFAEnabled"`
}

type UserDetails struct {
Expand All @@ -29,6 +29,13 @@ type UserDetails struct {
Security *Security `json:"security" bson:"security"`
Status string `json:"status" bson:"status"`
Position string `json:"position" bson:"position"`
DepartmentNo string `json:"departmentNo" bson:"departmentNo, omitempty"`
}

type FilterUsers struct {
Role string `json:"role" bson:"role, omitempty"`
Status string `json:"status" bson:"status, omitempty"`
DepartmentNo string `json:"departmentNo" bson:"departmentNo, omitempty"`
}

type Details struct {
Expand Down Expand Up @@ -109,7 +116,7 @@ type RoomRequest struct {
}

type ResetToken struct {
Email string `bson:"email"`
Token string `bson:"token"`
ExpireWhen time.Time `bson:"expireWhen"`
}
Email string `bson:"email"`
Token string `bson:"token"`
ExpireWhen time.Time `bson:"expireWhen"`
}
12 changes: 7 additions & 5 deletions occupi-backend/pkg/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ func OccupiRouter(router *gin.Engine, db *mongo.Client, cache *bigcache.BigCache
api.POST("/check-in", middleware.ProtectedRoute, func(ctx *gin.Context) { handlers.CheckIn(ctx, appsession) })
api.POST("/cancel-booking", middleware.ProtectedRoute, func(ctx *gin.Context) { handlers.CancelBooking(ctx, appsession) })
api.GET(("/view-bookings"), middleware.ProtectedRoute, func(ctx *gin.Context) { handlers.ViewBookings(ctx, appsession) })
api.GET("/view-rooms", middleware.ProtectedRoute, func(ctx *gin.Context) { handlers.ViewRooms(ctx, appsession) })
api.GET("/view-rooms", middleware.UnProtectedRoute, func(ctx *gin.Context) { handlers.ViewRooms(ctx, appsession) })
api.GET("/user-details", middleware.ProtectedRoute, func(ctx *gin.Context) { handlers.GetUserDetails(ctx, appsession) })
api.PUT("/update-user", middleware.ProtectedRoute, func(ctx *gin.Context) { handlers.UpdateUserDetails(ctx, appsession) })
// api.GET("/filter-users", middleware.UnProtectedRoute, func(ctx *gin.Context) { handlers.FilterUsers(ctx, appsession) })
// api.GET("/get-users", middleware.UnProtectedRoute, func(ctx *gin.Context) { handlers.GetUsers(ctx, appsession) })
}
auth := router.Group("/auth")
{
Expand All @@ -68,11 +70,11 @@ func OccupiRouter(router *gin.Engine, db *mongo.Client, cache *bigcache.BigCache
auth.POST("/logout", middleware.ProtectedRoute, func(ctx *gin.Context) { handlers.Logout(ctx) })
// it's typically used by users who can't log in because they've forgotten their password.

auth.POST("/reset-password",middleware.UnProtectedRoute, func(ctx *gin.Context) { handlers.ResetPassword(ctx, appsession) })
auth.POST("/forgot-password", middleware.UnProtectedRoute, func(ctx *gin.Context) { handlers.ForgotPassword(ctx, appsession)})
auth.POST("/reset-password", middleware.UnProtectedRoute, func(ctx *gin.Context) { handlers.ResetPassword(ctx, appsession) })
auth.POST("/forgot-password", middleware.UnProtectedRoute, func(ctx *gin.Context) { handlers.ForgotPassword(ctx, appsession) })
auth.POST("/verify-2fa", middleware.UnProtectedRoute, func(ctx *gin.Context) { handlers.VerifyTwoFA(ctx, appsession) })
auth.POST("/verify-otp-enable-2fa", middleware.UnProtectedRoute, func(ctx *gin.Context) {
handlers.VerifyOTPAndEnable2FA(ctx, appsession)
auth.POST("/verify-otp-enable-2fa", middleware.UnProtectedRoute, func(ctx *gin.Context) {
handlers.VerifyOTPAndEnable2FA(ctx, appsession)
})

}
Expand Down

0 comments on commit bf84ce3

Please sign in to comment.