├── 1-car-washing-system.md ├── 2-meeting-room-booking-system.md ├── 3-car-rental-reservation-system.md ├── 4-bike-rental-service.md ├── 5-sports-facility-booking-platform.md └── README.md /1-car-washing-system.md: -------------------------------------------------------------------------------- 1 | # Car Wash Booking System 2 | 3 | ## Story 4 | In a small town, there was a car wash run by an old man named Mr. Joe. People said his car wash had a special power – it made dreams come true. 5 | 6 | One sunny day, a little girl named Lily visited the car wash with her dad. Lily had a big dream of becoming an artist, but she was shy and unsure if she was any good. 7 | 8 | As Mr. Joe washed their car, he noticed Lily staring at the colorful soap bubbles. He smiled kindly and asked her about her dreams. Lily hesitated at first but then shared her passion for painting. 9 | 10 | Mr. Joe listened intently, then handed Lily a brush and some paints. He told her to paint whatever she liked on the car windows. Lily's eyes lit up with excitement. 11 | 12 | With shaky hands, Lily began to paint. She poured her heart onto the windows, creating a beautiful masterpiece. When she finished, she stepped back to admire her work, feeling proud. 13 | 14 | From that day on, Lily's confidence grew. She painted everywhere she could, and her talent blossomed. Years later, Lily became a famous artist, known for her vibrant paintings. 15 | 16 | And whenever she passed by the magic car wash, now run by Mr. Joe's grandchildren, she couldn't help but smile, remembering the day her dreams took flight. 17 | 18 | --- 19 | 20 | 21 | You have been assigned the task of building the backend for a Car Washing System. The main focus of this assignment is to implement Error Handling, CRUD operations, Authentication & Authorization, Transaction & Rollback (If necessary) 22 | 23 | ## **Technology Stack:** 24 | 25 | * Use TypeScript as the programming language. 26 | * Use Express.js as the web framework. 27 | * Use Mongoose as the Object Data Modeling (ODM) and validation library for MongoDB 28 | 29 | ## Main Part: (50 Marks) 30 | 31 | ### User Model 32 | 33 | * `name`: Full name of the user. 34 | * `email`: Email address used for communication and login. 35 | * `password`: Securely hashed password for authentication. 36 | * `phone`: Contact phone number for notifications and updates. 37 | * `role`**:** The role of the user, which can be one of the following: `admin`, `user`. 38 | * `address`: Complete physical address for service delivery or correspondence. 39 | 40 | ### Service Model 41 | 42 | * `name`: Title of the service. 43 | * `description`: Brief description of what the service entails. 44 | * `price`: Cost of the service in the local currency. 45 | * `duration`**:** Duration of the service in minutes. 46 | * `isDeleted`: Indicates whether the service is marked as deleted (false means it is not deleted). 47 | 48 | ### Slot Model 49 | 50 | * `service`: Reference to the specific service being booked. 51 | * `date`: Date of the booking. 52 | * `startTime`: Start time of the slot. 53 | * `endTime`: Approximate end time of the slot. 54 | * `isBooked`: Status of the slot (available, booked, canceled). 55 | 56 | ### Booking Model 57 | 58 | * `customer`: Reference to the user who made the booking. 59 | * `service`: Reference to the booked service. 60 | * `slot`: Reference to the booking slot. 61 | * `vehicleType`: Type of the vehicle (enum: `car`, `truck`, `SUV`, `van`, `motorcycle`, `bus`, `electricVehicle`, `hybridVehicle`, `bicycle`, `tractor`). 62 | * `vehicleBrand`: Brand or manufacturer of the vehicle. 63 | * `vehicleModel`: Model or variant of the vehicle. 64 | * `manufacturingYear`: Manufacturing year of the vehicle. 65 | * `registrationPlate`: Unique registration number assigned to the vehicle. 66 | 67 | ## API Endpoints 68 | 69 | ### User Routes 70 | 71 | ### 1\. User Sign Up 72 | 73 | **Route**: `/api/auth/signup` (**POST**) 74 | 75 | **Request Body:** 76 | 77 | ```json 78 | { 79 | "name": "Programming Hero", 80 | "email": "web@programming-hero.com", 81 | "password": "ph-password", 82 | "phone": "1234567890", 83 | "role": "admin", //role can be user or admin 84 | "address": "123 Main Street, City, Country" 85 | } 86 | ``` 87 | 88 | **Response**: 89 | 90 | ```json 91 | { 92 | "success": true, 93 | "statusCode": 200, 94 | "message": "User registered successfully", 95 | "data": { 96 | "_id": "60629b8e8cfcd926384b6e5e", 97 | "name": "Programming Hero", 98 | "email": "web@programming-hero.com", 99 | "phone": "1234567890", 100 | "role": "admin", 101 | "address": "123 Main Street, City, Country", 102 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 103 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 104 | } 105 | } 106 | ``` 107 | 108 | ### 109 | 110 | ### 2\. User Login 111 | 112 | **Route**: `/api/auth/login`(**POST**) 113 | 114 | **Request Body:** 115 | 116 | ```json 117 | { 118 | "email": "web@programming-hero.com", 119 | "password": "ph-password", 120 | } 121 | ``` 122 | 123 | 124 | 125 | **Response:** 126 | 127 | ```json 128 | { 129 | "success": true, 130 | "statusCode": 200, 131 | "message": "User logged in successfully", 132 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2MDYyOWI4ZThjZmNkOTI2Mzg0YjZlNWUiLCJuYW1lIjoiUHJvZ3JhbW1pbmcgSGVyb3MiLCJlbWFpbCI6IndlYkBwcm9ncmFtbWluZy1oZXJvLmNvbSIsInBob25lIjoiMTIzNDU2Nzg5MCIsInJvbGUiOiJhZG1pbiIsImFkZHJlc3MiOiIxMjMgTWFpbiBTdHJlZXQsIENpdHksIENvdW50cnkiLCJpYXQiOjE2MjQ1MTY2MTksImV4cCI6MTYyNDUyMDYxOX0.kWrEphO6lE9P5tvzrNBwx0sNogNuXpdyG-YoN9fB1W8", 133 | "data": { 134 | "_id": "60629b8e8cfcd926384b6e5e", 135 | "name": "Programming Hero", 136 | "email": "web@programming-hero.com", 137 | "phone": "1234567890", 138 | "role": "admin", 139 | "address": "123 Main Street, City, Country", 140 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 141 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 142 | } 143 | } 144 | ``` 145 | 146 | ### 147 | 148 | ### **3\. Create Service (Only Accessible by Admin)** 149 | 150 | **Route:** `/api/services`(**POST**) 151 | 152 | **Request Headers:** 153 | 154 | ```javascript 155 | Authorization: 156 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 157 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 158 | 159 | You must include "Bearer" at the beginning of the token! Do not copy and apply directly from the module. If you blindly follow the modules, you will be a copy master, not a developer. 160 | ``` 161 | 162 | **Request Body:** 163 | 164 | ```json 165 | { 166 | "name": "Car Wash", 167 | "description": "Professional car washing service", 168 | "price": 50, 169 | "duration": 60, // Duration in minutes 170 | "isDeleted": false 171 | } 172 | ``` 173 | 174 | **Response Body:** 175 | 176 | ```json 177 | { 178 | "success": true, 179 | "statusCode": 200, 180 | "message": "Service created successfully", 181 | "data": { 182 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 183 | "name": "Car Wash", 184 | "description": "Professional car washing service", 185 | "price": 50, 186 | "duration": 60, 187 | "isDeleted": false, 188 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 189 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 190 | } 191 | } 192 | ``` 193 | 194 | ### 195 | 196 | ### **4\. Get a Service** 197 | 198 | **Route:** `/api/services/:id`(**GET**) 199 | 200 | **Response:** 201 | 202 | ```json 203 | { 204 | "success": true, 205 | "statusCode": 200, 206 | "message": "Service retrieved successfully", 207 | "data": { 208 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 209 | "name": "Car Wash", 210 | "description": "Professional car washing service", 211 | "price": 50, 212 | "duration": 60, 213 | "isDeleted": false, 214 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 215 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 216 | } 217 | } 218 | ``` 219 | 220 | 221 | 222 | ### **5\. Get All Services** 223 | 224 | **Route:** `/api/services`(**GET**) 225 | 226 | **Response:** 227 | 228 | ```json 229 | { 230 | "success": true, 231 | "statusCode": 200, 232 | "message": "Services retrieved successfully", 233 | "data": [ 234 | { 235 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 236 | "name": "Car Wash", 237 | "description": "Professional car washing service", 238 | "price": 50, 239 | "duration": 60, 240 | "isDeleted": false, 241 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 242 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 243 | }, 244 | { 245 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 246 | "name": "Oil Change", 247 | "description": "Regular engine oil change service", 248 | "price": 30, 249 | "duration": 30, 250 | "isDeleted": false, 251 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 252 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 253 | }, 254 | { 255 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 256 | "name": "Tire Rotation", 257 | "description": "Rotation of vehicle tires", 258 | "price": 20, 259 | "duration": 45, 260 | "isDeleted": false, 261 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 262 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 263 | } 264 | ] 265 | } 266 | ``` 267 | 268 | 269 | 270 | ### **6\. Update Services (Only Accessible by Admin)** 271 | 272 | **Route:** `/api/services/:id`(**PUT**) 273 | 274 | **Request Headers:** 275 | 276 | ```javascript 277 | Authorization: 278 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 279 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 280 | 281 | You must include "Bearer" at the beginning of the token! 282 | ``` 283 | 284 | **Request Body:** 285 | 286 | ```json 287 | { 288 | "price": 700 // You can include any attribute(s) of the service collection that you want to update, one or more. 289 | } 290 | ``` 291 | 292 | **Response Body:** 293 | 294 | ```json 295 | { 296 | "success": true, 297 | "statusCode": 200, 298 | "message": "Service updated successfully", 299 | "data": { 300 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 301 | "name": "Car Wash", 302 | "description": "Professional car washing service", 303 | "price": 700, 304 | "duration": 60, 305 | "isDeleted": false, 306 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 307 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 308 | } 309 | } 310 | ``` 311 | 312 | 313 | 314 | ### **7\. Delete a Service (Only Accessible by Admin)** 315 | 316 | **Route:** `/api/services/:id`(**DELETE**) \[SOFT DELETE \] 317 | 318 | **Request Headers:** 319 | 320 | ```javascript 321 | Authorization: 322 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 323 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 324 | 325 | You must include "Bearer" at the beginning of the token! 326 | ``` 327 | 328 | **Response Body:** 329 | 330 | ```json 331 | { 332 | "success": true, 333 | "statusCode": 200, 334 | "message": "Service deleted successfully", 335 | "data": { 336 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 337 | "name": "Car Wash", 338 | "description": "Professional car washing service", 339 | "price": 50, 340 | "duration": 60, 341 | "isDeleted": true, 342 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 343 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 344 | } 345 | } 346 | ``` 347 | 348 | 349 | 350 | ### 8.**Create Slot (Only Accessible by Admin)** 351 | 352 | **Route:** `/api/services/slots`(**POST**) 353 | 354 | **Request Headers:** 355 | 356 | ```javascript 357 | Authorization: 358 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 359 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 360 | 361 | You must include "Bearer" at the beginning of the token! 362 | ``` 363 | 364 | **Request Body:** 365 | 366 | ```json 367 | { 368 | "service": "60d9c4e4f3b4b544b8b8d1c5", 369 | "date": "2024-06-15", 370 | "startTime": "09:00", 371 | "endTime": "14:00" 372 | } 373 | ``` 374 | 375 | **Response Body:** 376 | 377 | ```json 378 | { 379 | "success": true, 380 | "statusCode": 200, 381 | "message": "Slots created successfully", 382 | "data": [ 383 | { 384 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 385 | "service": "60d9c4e4f3b4b544b8b8d1c5", 386 | "date": "2024-06-15", 387 | "startTime": "09:00", 388 | "endTime": "10:00", //look at the starting point 389 | "isBooked": "available", 390 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 391 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 392 | }, 393 | { 394 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 395 | "service": "60d9c4e4f3b4b544b8b8d1c5", 396 | "date": "2024-06-15", 397 | "startTime": "10:00", 398 | "endTime": "11:00", 399 | "isBooked": "available", 400 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 401 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 402 | }, 403 | { 404 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 405 | "service": "60d9c4e4f3b4b544b8b8d1c5", 406 | "date": "2024-06-15", 407 | "startTime": "11:00", 408 | "endTime": "12:00", 409 | "isBooked": "available", 410 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 411 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 412 | }, 413 | { 414 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 415 | "service": "60d9c4e4f3b4b544b8b8d1c5", 416 | "date": "2024-06-15", 417 | "startTime": "12:00", 418 | "endTime": "13:00", 419 | "isBooked": "available", 420 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 421 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 422 | }, 423 | { 424 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 425 | "service": "60d9c4e4f3b4b544b8b8d1c5", 426 | "date": "2024-06-15", 427 | "startTime": "13:00", 428 | "endTime": "14:00", //look at the ending point 429 | "isBooked": "available", 430 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 431 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 432 | } 433 | ] 434 | } 435 | ``` 436 | 437 | 438 | 439 | ### **Hints for creating slots:** 440 | 441 | 442 | 443 | 1. **Retrieve Service Duration**: Assume the service duration is provided or retrieved from the database. For this example, we'll use a service duration of 60 minutes. 444 | 2. **Parse Request Body**: Extract the necessary information from the request body: 445 | * Start time: "09:00" 446 | * End time: "14:00" 447 | * Service duration: 60 minutes 448 | 3. **Calculate the Total Duration**: 449 | * Convert the start time and end time to minutes. 450 | * Calculate the total duration between the start and end times in minutes. 451 | 4. **Generate Slot Time Intervals**: 452 | * Determine the number of slots by dividing the total duration by the service duration. 453 | * Generate start and end times for each slot. 454 | 5. **Example Calculation** 455 | 1. **Step-by-Step Breakdown**: 456 | 2. **Service Duration**: 60 minutes 457 | 3. **Start Time and End Time**: 458 | * Start Time: "09:00" 459 | * End Time: "14:00" 460 | 4. **Convert Times to Minutes**: 461 | * "09:00" → 9 \* 60 = 540 minutes. 462 | * "14:00" → 14 \* 60 = 840 minutes. 463 | 5. **Calculate Total Duration**: 464 | * Total Duration: 840 minutes - 540 minutes = 300 minutes 465 | 6. **Number of Slots**: 466 | * Number of Slots: 300 minutes / 60 minutes per slot = 5 slots 467 | 7. **Generate Slot Time Intervals**: 468 | * Slot 1: Start Time: "09:00", End Time: "10:00" 469 | * Slot 2: Start Time: "10:00", End Time: "11:00" 470 | * Slot 3: Start Time: "11:00", End Time: "12:00" 471 | * Slot 4: Start Time: "12:00", End Time: "13:00" 472 | * Slot 5: Start Time: "13:00", End Time: "14:00" 473 | 474 | 475 | 476 | ### **9\. Get available slots** 477 | 478 | **Route:** `/api/slots/availability`(**GET**) 479 | 480 | **Query Parameters:** 481 | 482 | * `date`: (Optional) The specific date for which available slots are requested (format: YYYY-MM-DD). 483 | * `serviceId`: (Optional) ID of the service for which available slots are requested. 484 | 485 | 486 | 487 | **Request Example:** 488 | 489 | ```plain 490 | GET /api/slots/availability?date=2024-06-15&serviceId=60d9c4e4f3b4b544b8b8d1c5 491 | ``` 492 | 493 | 494 | 495 | **Response:** 496 | 497 | ```json 498 | { 499 | "success": true, 500 | "statusCode": 200, 501 | "message": "Available slots retrieved successfully", 502 | "data": [ 503 | { 504 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 505 | "service": { 506 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 507 | "name": "Car Wash", 508 | "description": "Professional car washing service", 509 | "price": 700, 510 | "duration": 60, 511 | "isDeleted": false, 512 | "createdAt": "2024-06-15T12:00:00Z", 513 | "updatedAt": "2024-06-15T12:00:00Z" 514 | }, 515 | "date": "2024-06-15", 516 | "startTime": "09:00", 517 | "endTime": "10:00", 518 | "isBooked": "available", 519 | "createdAt": "2024-06-15T12:00:00Z", 520 | "updatedAt": "2024-06-15T12:00:00Z" 521 | }, 522 | { 523 | "_id": "60d9c4e4f3b4b544b8b8d1c9", 524 | "service": { 525 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 526 | "name": "Car Wash", 527 | "description": "Professional car washing service", 528 | "price": 700, 529 | "duration": 60, 530 | "isDeleted": false, 531 | "createdAt": "2024-06-15T12:00:00Z", 532 | "updatedAt": "2024-06-15T12:00:00Z" 533 | }, 534 | "date": "2024-06-15", 535 | "startTime": "10:00", 536 | "endTime": "11:00", 537 | "isBooked": "canceled", 538 | "createdAt": "2024-06-15T12:00:00Z", 539 | "updatedAt": "2024-06-15T12:00:00Z" 540 | } 541 | ] 542 | } 543 | ``` 544 | 545 | 546 | 547 | ### **10\. Book a Service (Only Accessible by User)** 548 | 549 | **Route:** `/api/bookings`(**POST**) 550 | 551 | **Request Headers:** 552 | 553 | ```javascript 554 | Authorization: 555 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 556 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 557 | 558 | You must include "Bearer" at the beginning of the token! 559 | ``` 560 | 561 | **Request Body:** 562 | 563 | ```json 564 | { 565 | "serviceId": "60d9c4e4f3b4b544b8b8d1c5", 566 | "slotId": "60d9c4e4f3b4b544b8b8d1c6", 567 | "vehicleType": "car", 568 | "vehicleBrand": "Toyota", 569 | "vehicleModel": "Camry", 570 | "manufacturingYear": 2020, 571 | "registrationPlate": "ABC123" 572 | } 573 | ``` 574 | 575 | **Response Body:** 576 | 577 | ```json 578 | { 579 | "success": true, 580 | "statusCode": 200, 581 | "message": "Booking successful", 582 | "data": { 583 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 584 | "customer": { 585 | "_id": "123456789012345678901234", 586 | "name": "John Doe", 587 | "email": "johndoe@example.com", 588 | "phone": "1234567890", 589 | "address": "123 Main Street, City, Country" 590 | }, 591 | "service": { 592 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 593 | "name": "Car Wash", 594 | "description": "Exterior and interior car cleaning", 595 | "price": 50, 596 | "duration": 30, 597 | "isDeleted": false 598 | }, 599 | "slot": { 600 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 601 | "service": "60d9c4e4f3b4b544b8b8d1c5", 602 | "date": "2024-06-15", 603 | "startTime": "09:00", 604 | "endTime": "10:00", 605 | "isBooked": "booked" // Updated to "booked" 606 | }, 607 | "vehicleType": "car", 608 | "vehicleBrand": "Toyota", 609 | "vehicleModel": "Camry", 610 | "manufacturingYear": 2020, 611 | "registrationPlate": "ABC123", 612 | "createdAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 613 | "updatedAt": "2024-06-15T12:00:00Z", // For this, ensure that your model includes the option to enable timestamps 614 | } 615 | } 616 | ``` 617 | 618 | 619 | 620 | ### **11\. Get All Bookings (Only Accessible by Admin)** 621 | 622 | **Route:** `/api/bookings`(**GET**) 623 | 624 | **Request Headers:** 625 | 626 | ```javascript 627 | Authorization: 628 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 629 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 630 | 631 | You must include "Bearer" at the beginning of the token! 632 | 633 | 634 | ``` 635 | 636 | **Response Body:** 637 | 638 | ```json 639 | { 640 | "success": true, 641 | "statusCode": 200, 642 | "message": "All bookings retrieved successfully", 643 | "data": [ 644 | { 645 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 646 | "customer": { 647 | "_id": "123456789012345678901234", 648 | "name": "John Doe", 649 | "email": "johndoe@example.com", 650 | "phone": "1234567890", 651 | "address": "123 Main Street, City, Country" 652 | }, 653 | "service": { 654 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 655 | "name": "Car Wash", 656 | "description": "Exterior and interior car cleaning", 657 | "price": 50, 658 | "duration": 30, 659 | "isDeleted": false 660 | }, 661 | "slot": { 662 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 663 | "service": "60d9c4e4f3b4b544b8b8d1c5", 664 | "date": "2024-06-15", 665 | "startTime": "09:00", 666 | "endTime": "09:30", 667 | "isBooked": "booked" 668 | }, 669 | "vehicleType": "car", 670 | "vehicleBrand": "Toyota", 671 | "vehicleModel": "Camry", 672 | "manufacturingYear": 2020, 673 | "registrationPlate": "ABC123", 674 | "createdAt": "2024-06-15T12:00:00Z", 675 | "updatedAt": "2024-06-15T12:00:00Z" 676 | }, 677 | { 678 | "_id": "60d9c4e4f3b4b544b8b8d1c8", 679 | "customer": { 680 | "_id": "234567890123456789012345", 681 | "name": "Jane Smith", 682 | "email": "janesmith@example.com", 683 | "phone": "0987654321", 684 | "address": "456 Oak Street, City, Country" 685 | }, 686 | "service": { 687 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 688 | "name": "Car Wash", 689 | "description": "Exterior and interior car cleaning", 690 | "price": 50, 691 | "duration": 30, 692 | "isDeleted": false 693 | }, 694 | "slot": { 695 | "_id": "60d9c4e4f3b4b544b8b8d1c9", 696 | "service": "60d9c4e4f3b4b544b8b8d1c5", 697 | "date": "2024-06-15", 698 | "startTime": "10:00", 699 | "endTime": "10:30", 700 | "isBooked": "canceled" 701 | }, 702 | "vehicleType": "car", 703 | "vehicleBrand": "Honda", 704 | "vehicleModel": "Accord", 705 | "manufacturingYear": 2018, 706 | "registrationPlate": "XYZ456", 707 | "createdAt": "2024-06-15T13:00:00Z", 708 | "updatedAt": "2024-06-15T13:30:00Z" 709 | } 710 | ], 711 | 712 | // aditional bookings 713 | } 714 | ``` 715 | 716 | 717 | 718 | ### **12\. Get User's Bookings (Only Accessible by User)** 719 | 720 | **Route:** `/api/my-bookings`(**GET**) 721 | 722 | **Request Headers:** 723 | 724 | ```javascript 725 | Authorization: 726 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 727 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 728 | 729 | You must include "Bearer" at the beginning of the token! 730 | 731 | 732 | ``` 733 | 734 | **Response Body:** 735 | 736 | ```json 737 | { 738 | "success": true, 739 | "statusCode": 200, 740 | "message": "User bookings retrieved successfully", 741 | "data": [ 742 | { 743 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 744 | "service": { 745 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 746 | "name": "Car Wash", 747 | "description": "Exterior and interior car cleaning", 748 | "price": 50, 749 | "duration": 30, 750 | "isDeleted": false 751 | }, 752 | "slot": { 753 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 754 | "service": "60d9c4e4f3b4b544b8b8d1c5", 755 | "date": "2024-06-15", 756 | "startTime": "09:00", 757 | "endTime": "09:30", 758 | "isBooked": "booked" 759 | }, 760 | "vehicleType": "car", 761 | "vehicleBrand": "Toyota", 762 | "vehicleModel": "Camry", 763 | "manufacturingYear": 2020, 764 | "registrationPlate": "ABC123", 765 | "createdAt": "2024-06-15T12:00:00Z", 766 | "updatedAt": "2024-06-15T12:00:00Z" 767 | } 768 | ] 769 | } 770 | ``` 771 | 772 | ## 773 | 774 | ## Bonus Part: 775 | 776 | ### **1\. No Data Found:** 777 | 778 | When retrieving data, if the database collection is empty or no matching data is found, return the message: "No data found." 779 | 780 | ```elixir 781 | { 782 | "success": false, 783 | "statusCode": 404, 784 | "message": "No Data Found", 785 | "data":[] 786 | } 787 | ``` 788 | 789 | ### **2.Error Handling:** 790 | 791 | Implement proper error handling throughout the application. Use global error handling `middleware` to catch and handle errors, providing appropriate error responses with error messages. 792 | 793 | 794 | 795 | **Error Response Object Should include the following properties:** 796 | 797 | * success → false 798 | * message → Error Type → Validation Error, Cast Error, Duplicate Entry 799 | * errorMessages 800 | * stack 801 | 802 | 803 | 804 | **Sample Error Response** 805 | 806 | ```swift 807 | { 808 | "success": false, 809 | "message": "E11000 duplicate key error collection: univerity-management.students index: email_1 dup key: { email: \\"user2@gmail.com\\" }", 810 | "errorMessages": [ 811 | { 812 | "path": "", 813 | "message": "E11000 duplicate key error collection: univerity-management.students index: email_1 dup key: { email: \\"user2@gmail.com\\" }" 814 | } 815 | ], 816 | "stack": "MongoServerError: E11000 duplicate key error collection: univerity-management.students index: email_1 dup key: { email: \\"user2@gmail.com\\" }\\n at H:\\\\next-level-development\\\\university-management-auth-service\\\\node_modules\\\\mongodb\\\\src\\\\operations\\\\insert.ts:85:25\\n at H:\\\\next-level-development\\\\university-management-auth-service\\\\node_modules\\\\mongodb\\\\src\\\\cmap\\\\connection_pool.ts:574:11\\n at H:\\\\next-level-development\\\\university-writeOrBuffer (node:internal/streams/writable:391:12)" 817 | } 818 | ``` 819 | 820 | ### 821 | 822 | ### **3\. Not Found Route:** 823 | 824 | Implement a global "Not Found" handler for unmatched routes. When a route is not found, respond with a generic message: "Not Found.” 825 | 826 | ```json 827 | { 828 | "success": false, 829 | "statusCode": 404, 830 | "message": "Not Found", 831 | } 832 | ``` 833 | ### **4\. Authentication Middleware:** 834 | 835 | Implement an Authentication Middleware to authenticate your application. Ensures that only user and admin can access their own accessible routes. 836 | 837 | ```json 838 | { 839 | "success": false, 840 | "statusCode": 401, 841 | "message": "You have no access to this route", 842 | } 843 | ``` 844 | 845 | ### **5\. Zod Validation:** 846 | The API employs Zod for input validation, ensuring data consistency. When validation fails, a 400 Bad Request status code is returned, accompanied by detailed error messages specifying the erroneous fields and reasons. 847 | 848 | * * * 849 | 850 | ### 851 | -------------------------------------------------------------------------------- /2-meeting-room-booking-system.md: -------------------------------------------------------------------------------- 1 | # Meeting Room Booking System for Co-working spaces 2 | 3 | ## Story: 4 | 5 | Suppose you have an agency where you offer co-working spaces for meetings and discussions. To streamline the booking process, you've decided to develop a web application for managing room reservations. Starting with the backend, you've crafted schemas based on the following models: 6 | 7 | 1. **User Model** 8 | 2. **Room Model** 9 | 3. **Slot** 10 | 4. **Booking Model** 11 | 12 | With these models in place, you empower both admins and users to interact seamlessly with the booking system: 13 | 14 | * **Admin Actions:** Administrators have the privilege of creating, updating, and deleting rooms. They can specify details like the room's name, room number, floor number, capacity, price per slot, and available amenities. Additionally, admins are responsible for creating time slots for each room. They set the date, start time, and end time for these slots, ensuring that users have a range of options to choose from. Through the web interface, admins can effortlessly manage the co-working space inventory and slot availability, ensuring accurate and up-to-date information for users. 15 | * **User Interactions:** On the user side, individuals can create bookings by selecting from the available time slots for their desired meeting times. They input the date and select specific slots for their sessions, along with their preferred room selection. The system automatically calculates the total amount based on the number of slots selected and the price per slot. Users receive real-time feedback on the availability of rooms and slots, ensuring smooth booking experiences without conflicts. 16 | 17 | As the development progresses, you implement robust validation and error handling mechanisms to enhance the application's reliability. Users receive informative messages in case of booking conflicts or validation errors, guiding them towards successful interactions with the platform. 18 | 19 | 20 | 21 | ## Technology Stack: 22 | 23 | * Use TypeScript as the programming language. 24 | * Use Express.js as the web framework. 25 | * Use Mongoose as the Object Data Modeling (ODM) and validation library for MongoDB 26 | 27 | ## Main Part: (50 Marks) 28 | 29 | ## Models: 30 | 31 | ### User Model: 32 | 33 | * `name`: The name of the user. 34 | * `email`: The contact email address. 35 | * `password`: The account password. 36 | * `phone`: The contact phone number. 37 | * `address`: The physical address. 38 | * `role`: The role of the user, can be `user` or `admin`. 39 | 40 | ### Room Model: 41 | 42 | * `name`: The name of the meeting room. 43 | * `roomNo` : The unique number of the room. 44 | * `floorNo` : The level of the meeting room where it is located. 45 | * `capacity`: The maximum number of people the room can accommodate. 46 | * `pricePerSlot`: The individual cost of a single slot. 47 | * `amenities`: An array of amenities available in the room (e.g., "Projector", "Whiteboard"). Don't use enum. 48 | * `isDeleted`: Boolean to indicates whether the room has been marked as deleted (false means it is not deleted). 49 | 50 | ### Slot Model 51 | 52 | * `room` : Reference to the specific room being booked. 53 | * `date`: Date of the booking. 54 | * `startTime`: Start time of the slot. 55 | * `endTime`: End time of the slot. 56 | * `isBooked`: Boolean to indicate whether the slot has been marked as booked (false means it is not booked). 57 | 58 | ### Booking Model: 59 | 60 | * `room`: Identifier for the booked room (a reference to room model). 61 | * `slots`: An array containing the slot IDs (a reference to the booking slots). 62 | * `user`: Identifier for the user who booked the room (a reference to the user model). 63 | * `date`: Date of the booking. 64 | * `totalAmount` : The total amount of the bill is calculated based on the selected number of slots. 65 | * `isConfirmed`: Indicates the booking status, whether it's `confirmed`, `unconfirmed`, or `canceled`. 66 | * `isDeleted`: Boolean to indicates whether the booking has been marked as deleted (false means it is not deleted). 67 | 68 | 69 | 70 | ### User Routes 71 | 72 | 73 | 74 | 1. **User Sign Up** 75 | - _*Route:*_ `/api/auth/signup` (POST) 76 | - **Request Body:** 77 | 78 | ```json 79 | { 80 | "name": "Programming Hero", 81 | "email": "web@programming-hero.com", 82 | "password": "ph-password", 83 | "phone": "1234567890", 84 | "role": "admin", //role can be user or admin 85 | "address": "123 Main Street, City, Country" 86 | } 87 | 88 | ``` 89 | 90 | - **Response:** 91 | 92 | ```json 93 | { 94 | "success": true, 95 | "statusCode": 200, 96 | "message": "User registered successfully", 97 | "data": { 98 | "_id": "60629b8e8cfcd926384b6e5e", 99 | "name": "Programming Hero", 100 | "email": "web@programming-hero.com", 101 | "phone": "1234567890", 102 | "role": "admin", 103 | "address": "123 Main Street, City, Country" 104 | } 105 | } 106 | ``` 107 | 108 | **2\. User Login** 109 | 110 | - _*Route:*_ `/api/auth/login` (POST) 111 | - **Request Body:** 112 | 113 | ```json 114 | { 115 | "email": "web@programming-hero.com", 116 | "password": "ph-password", 117 | } 118 | ``` 119 | 120 | - Response: 121 | 122 | ```json 123 | { 124 | "success": true, 125 | "statusCode": 200, 126 | "message": "User logged in successfully", 127 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2MDYyOWI4ZThjZmNkOTI2Mzg0YjZlNWUiLCJuYW1lIjoiUHJvZ3JhbW1pbmcgSGVyb3MiLCJlbWFpbCI6IndlYkBwcm9ncmFtbWluZy1oZXJvLmNvbSIsInBob25lIjoiMTIzNDU2Nzg5MCIsInJvbGUiOiJhZG1pbiIsImFkZHJlc3MiOiIxMjMgTWFpbiBTdHJlZXQsIENpdHksIENvdW50cnkiLCJpYXQiOjE2MjQ1MTY2MTksImV4cCI6MTYyNDUyMDYxOX0.kWrEphO6lE9P5tvzrNBwx0sNogNuXpdyG-YoN9fB1W8", 128 | "data": { 129 | "_id": "60629b8e8cfcd926384b6e5e", 130 | "name": "Programming Hero", 131 | "email": "web@programming-hero.com", 132 | "phone": "1234567890", 133 | "role": "admin", 134 | "address": "123 Main Street, City, Country" 135 | } 136 | } 137 | ``` 138 | 139 | ### 140 | 141 | ### Room Routes 142 | 143 | 144 | 145 | **3\. Create Room (Only Accessible by Admin)** 146 | 147 | - _*Route:*_ `/api/rooms` (POST) 148 | - **Request Headers:** 149 | 150 | ```plain 151 | Authorization: 152 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 153 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 154 | 155 | You must include "Bearer" at the beginning of the token! Do not copy and apply directly from the module. If you blindly follow the modules, you will be a copy master, not a developer. 156 | ``` 157 | 158 | - _*Request Body:*_ 159 | 160 | ```json 161 | { 162 | "name": "Conference Room", 163 | "roomNo": 201, 164 | "floorNo": 1, 165 | "capacity": 20, 166 | "pricePerSlot": 100, 167 | "amenities": ["Projector", "Whiteboard"] 168 | } 169 | 170 | 171 | ``` 172 | 173 | - _*Response:*_ 174 | 175 | ```json 176 | { 177 | "success": true, 178 | "statusCode": 200, 179 | "message": "Room added successfully", 180 | "data": { 181 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 182 | "name": "Conference Room", 183 | "roomNo": 201, 184 | "floorNo": 1, 185 | "capacity": 20, 186 | "pricePerSlot": 100, 187 | "amenities": ["Projector", "Whiteboard"], 188 | "isDeleted": false 189 | } 190 | } 191 | 192 | 193 | ``` 194 | 195 | **4\. Get a Room** 196 | 197 | - _*Route:*_ `/api/rooms/:id` (GET) 198 | - _*Response:*_ 199 | 200 | ```json 201 | { 202 | "success": true, 203 | "statusCode": 200, 204 | "message": "Room retrieved successfully", 205 | "data": { 206 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 207 | "name": "Conference Room", 208 | "roomNo": 201, 209 | "floorNo": 1, 210 | "capacity": 20, 211 | "pricePerSlot": 100, 212 | "amenities": ["Projector", "Whiteboard"], 213 | "isDeleted": false 214 | } 215 | } 216 | 217 | 218 | ``` 219 | 220 | **5\. Get All Rooms** 221 | 222 | - _*Route:*_ `/api/rooms` (GET) 223 | - **Response:** 224 | 225 | ```json 226 | { 227 | "success": true, 228 | "statusCode": 200, 229 | "message": "Rooms retrieved successfully", 230 | "data": [ 231 | { 232 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 233 | "name": "Conference Room", 234 | "roomNo": 201, 235 | "floorNo": 1, 236 | "capacity": 20, 237 | "pricePerSlot": 100, 238 | "amenities": ["Projector", "Whiteboard"], 239 | "isDeleted": false 240 | }, 241 | { 242 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 243 | "name": "Meeting Room", 244 | "roomNo": 301, 245 | "floorNo": 2, 246 | "capacity": 10, 247 | "pricePerSlot": 200, 248 | "amenities": ["Whiteboard"], 249 | "isDeleted": false 250 | } 251 | // Other available rooms 252 | ] 253 | } 254 | ``` 255 | 256 | 257 | 258 | **6\. Update Room (Only Accessible by Admin)** 259 | 260 | - _*Route:*_ `/api/rooms/:id` (PUT) 261 | - **Request Headers:** 262 | 263 | ```plain 264 | Authorization: 265 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 266 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 267 | 268 | You must include "Bearer" at the beginning of the token! Do not copy and apply directly from the module. If you blindly follow the modules, you will be a copy master, not a developer. 269 | ``` 270 | 271 | - **Request Body:** 272 | 273 | ```json 274 | { 275 | "pricePerSlot": 200 //we can update any field dynamically, (e.g., name, roomNo, floorNo, capacity, pricePerSlot, amenities).. 276 | } 277 | ``` 278 | 279 | - **Response:** 280 | 281 | ```json 282 | { 283 | "success": true, 284 | "statusCode": 200, 285 | "message": "Room updated successfully", 286 | "data": { 287 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 288 | "name": "Conference Room", 289 | "roomNo": 201, 290 | "floorNo": 1, 291 | "capacity": 20, 292 | "pricePerSlot": 200, 293 | "amenities": ["Projector", "Whiteboard"], 294 | "isDeleted": false 295 | } 296 | } 297 | ``` 298 | 299 | **7\. Delete a Room (Soft Delete, Only Accessible by Admin)** 300 | 301 | - _*Route:*_ `/api/rooms/:id` (DELETE) 302 | - **Request Headers:** 303 | 304 | ```plain 305 | Authorization: 306 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 307 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 308 | 309 | You must include "Bearer" at the beginning of the token! Do not copy and apply directly from the module. If you blindly follow the modules, you will be a copy master, not a developer. 310 | ``` 311 | 312 | - **Response:** 313 | 314 | ```json 315 | { 316 | "success": true, 317 | "statusCode": 200, 318 | "message": "Room deleted successfully", 319 | "data": { 320 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 321 | "name": "Conference Room", 322 | "roomNo": 201, 323 | "floorNo": 1, 324 | "capacity": 20, 325 | "pricePerSlot": 200, 326 | "amenities": ["Projector", "Whiteboard"], 327 | "isDeleted": true 328 | } 329 | } 330 | ``` 331 | 332 | ### 333 | 334 | ### Slot Routes 335 | 336 | 337 | 338 | 8\. **Create Slot (Only Accessible by Admin)** 339 | 340 | - _*Route:*_ `/api/slots`(**POST**) 341 | 342 | **Request Headers:** 343 | 344 | ```javascript 345 | Authorization: 346 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 347 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 348 | 349 | You must include "Bearer" at the beginning of the token! Do not copy and apply directly from the module. If you blindly follow the modules, you will be a copy master, not a developer. 350 | ``` 351 | 352 | **Request Body:** 353 | 354 | ```json 355 | { 356 | "room": "60d9c4e4f3b4b544b8b8d1c5", 357 | "date": "2024-06-15", 358 | "startTime": "09:00", 359 | "endTime": "14:00" 360 | } 361 | ``` 362 | 363 | **Response Body:** 364 | 365 | ```json 366 | { 367 | "success": true, 368 | "statusCode": 200, 369 | "message": "Slots created successfully", 370 | "data": [ 371 | { 372 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 373 | "room": "60d9c4e4f3b4b544b8b8d1c5", 374 | "date": "2024-06-15", 375 | "startTime": "09:00", 376 | "endTime": "10:00", 377 | "isBooked": false 378 | }, 379 | { 380 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 381 | "room": "60d9c4e4f3b4b544b8b8d1c5", 382 | "date": "2024-06-15", 383 | "startTime": "10:00", 384 | "endTime": "11:00", 385 | "isBooked": false 386 | }, 387 | { 388 | "_id": "60d9c4e4f3b4b544b8b8d1c8", 389 | "room": "60d9c4e4f3b4b544b8b8d1c5", 390 | "date": "2024-06-15", 391 | "startTime": "11:00", 392 | "endTime": "12:00", 393 | "isBooked": false 394 | }, 395 | { 396 | "_id": "60d9c4e4f3b4b544b8b8d1c9", 397 | "room": "60d9c4e4f3b4b544b8b8d1c5", 398 | "date": "2024-06-15", 399 | "startTime": "12:00", 400 | "endTime": "13:00", 401 | "isBooked": false 402 | }, 403 | { 404 | "_id": "60d9c4e4f3b4b544b8b8d1ca", 405 | "room": "60d9c4e4f3b4b544b8b8d1c5", 406 | "date": "2024-06-15", 407 | "startTime": "13:00", 408 | "endTime": "14:00", 409 | "isBooked": false 410 | } 411 | ] 412 | } 413 | 414 | 415 | ``` 416 | 417 | 418 | 419 | **Hints for creating slots:** 420 | 421 | 1. **Retrieve Slot Duration**: Assume the slot duration is provided or retrieved from the database. For this example, we'll use a slot duration of 60 minutes. 422 | 2. **Parse Request Body**: Extract the necessary information from the request body: 423 | * Start time: "09:00" 424 | * End time: "14:00" 425 | * Slot duration: 60 minutes 426 | 3. **Calculate the Total Duration**: 427 | * Convert the start time and end time to minutes since midnight. 428 | * Calculate the total duration between the start and end times in minutes. 429 | 4. **Generate Slot Time Intervals**: 430 | * Determine the number of slots by dividing the total duration by the service duration. 431 | * Generate start and end times for each slot. 432 | 5. **Example Calculation - Step-by-Step Breakdown**: 433 | 1. **Slot Duration**: 60 minutes 434 | 2. **Start Time and End Time**: 435 | * Start Time: "09:00" 436 | * End Time: "14:00" 437 | 3. **Convert Times to Minutes**: 438 | * "09:00" → 9 \* 60 = 540 minutes since midnight 439 | * "14:00" → 14 \* 60 = 840 minutes since midnight 440 | 4. **Calculate Total Duration**: 441 | * Total Duration: 840 minutes - 540 minutes = 300 minutes 442 | 5. **Number of Slots**: 443 | * Number of Slots: 300 minutes / 60 minutes per slot = 5 slots 444 | 6. **Generate Slot Time Intervals**: 445 | * Slot 1: Start Time: "09:00", End Time: "10:00" 446 | * Slot 2: Start Time: "10:00", End Time: "11:00" 447 | * Slot 3: Start Time: "11:00", End Time: "12:00" 448 | * Slot 4: Start Time: "12:00", End Time: "13:00" 449 | * Slot 5: Start Time: "13:00", End Time: "14:00" 450 | 451 | 452 | 453 | **9\. Get available slots** 454 | 455 | **Route:** `/api/slots/availability`(**GET**) 456 | 457 | **Query Parameters:** 458 | 459 | * `date`: The specific date for which available slots are requested (format: YYYY-MM-DD). 460 | * `roomId`: ID of the room for which available slots are requested. 461 | 462 | 463 | 464 | > Special Remarks 465 | 466 | If we hit `/api/slots/availability` without any query params then we should get all the slots that are not booked ( isBooked: false) 467 | 468 | 469 | 470 | **Request endpoint example** 471 | 472 | `/api/slots/availability?date=2024-06-15&roomId=60d9c4e4f3b4b544b8b8d1c5` 473 | 474 | or 475 | 476 | `/api/slots/availability` 477 | 478 | 479 | 480 | **Response:** 481 | 482 | ```json 483 | { 484 | "success": true, 485 | "statusCode": 200, 486 | "message": "Available slots retrieved successfully", 487 | "data": [ 488 | { 489 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 490 | "room": { 491 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 492 | "name": "Conference Room", 493 | "roomNo": 201, 494 | "floorNo": 1, 495 | "capacity": 20, 496 | "pricePerSlot": 100, 497 | "amenities": ["Projector", "Whiteboard"], 498 | "isDeleted": false 499 | }, 500 | "date": "2024-06-15", 501 | "startTime": "09:00", 502 | "endTime": "10:00", 503 | "isBooked": false 504 | }, 505 | { 506 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 507 | "room": { 508 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 509 | "name": "Conference Room", 510 | "roomNo": 201, 511 | "floorNo": 1, 512 | "capacity": 20, 513 | "pricePerSlot": 100, 514 | "amenities": ["Projector", "Whiteboard"], 515 | "isDeleted": false 516 | }, 517 | "date": "2024-06-15", 518 | "startTime": "10:00", 519 | "endTime": "11:00", 520 | "isBooked": false 521 | } 522 | ] 523 | } 524 | 525 | 526 | 527 | ``` 528 | 529 | ### 530 | 531 | ### Booking Routes 532 | 533 | 534 | 535 | **10\. Create a Booking (Only Accessible by Authenticated User)** 536 | 537 | - _*Route:*_ `/api/bookings` (POST) 538 | - **Request Headers:** 539 | 540 | ```javascript 541 | Authorization: 542 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 543 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 544 | 545 | You must include "Bearer" at the beginning of the token! Do not copy and apply directly from the module. If you blindly follow the modules, you will be a copy master, not a developer. 546 | ``` 547 | 548 | - **Request Body:** 549 | 550 | ```json 551 | { 552 | "date": "2024-06-15", 553 | "slots": ["60d9c4e4f3b4b544b8b8d1c6", "60d9c4e4f3b4b544b8b8d1c7"], 554 | "room": "60d9c4e4f3b4b544b8b8d1c5", 555 | "user": "60d9c4e4f3b4b544b8b8d1c4" 556 | } 557 | ``` 558 | 559 | - **Response:** 560 | 561 | ```json 562 | { 563 | "success": true, 564 | "statusCode": 200, 565 | "message": "Booking created successfully", 566 | "data": { 567 | "_id": "60d9c4e4f3b4b544b8b8d1c9", 568 | "date": "2024-06-15", 569 | "slots": [ 570 | { 571 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 572 | "room": "60d9c4e4f3b4b544b8b8d1c5", 573 | "date": "2024-06-15", 574 | "startTime": "09:00", 575 | "endTime": "10:00", 576 | "isBooked": true 577 | }, 578 | { 579 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 580 | "room": "60d9c4e4f3b4b544b8b8d1c5", 581 | "date": "2024-06-15", 582 | "startTime": "10:00", 583 | "endTime": "11:00", 584 | "isBooked": true 585 | } 586 | ], 587 | "room": { 588 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 589 | "name": "Conference Room", 590 | "roomNo": 201, 591 | "floorNo": 1, 592 | "capacity": 20, 593 | "pricePerSlot": 100, 594 | "amenities": ["Projector", "Whiteboard"], 595 | "isDeleted": false 596 | }, 597 | "user": { 598 | "_id": "60d9c4e4f3b4b544b8b8d1c4", 599 | "name": "John Doe", 600 | "email": "john.doe@example.com", 601 | "phone": "1234567890", 602 | "address": "123 Main St, Anytown, USA", 603 | "role": "user" 604 | }, 605 | "totalAmount": 200, 606 | "isConfirmed": "unconfirmed", 607 | "isDeleted": false 608 | } 609 | } 610 | 611 | 612 | 613 | ``` 614 | 615 | 616 | 617 | 618 | 619 | **11\. Get All Bookings (Only Accessible by Admin)** 620 | 621 | - _*Route:*_ `/api/bookings` (GET) 622 | - **Request Headers:** 623 | 624 | ```javascript 625 | Authorization: 626 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 627 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 628 | 629 | You must include "Bearer" at the beginning of the token! Do not copy and apply directly from the module. If you blindly follow the modules, you will be a copy master, not a developer. 630 | ``` 631 | 632 | - **Response:** 633 | 634 | ```json 635 | { 636 | "success": true, 637 | "statusCode": 200, 638 | "message": "All bookings retrieved successfully", 639 | "data": [ 640 | { 641 | "_id": "60d9c4e4f3b4b544b8b8d1c9", 642 | "date": "2024-06-15", 643 | "slots": [ 644 | { 645 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 646 | "room": "60d9c4e4f3b4b544b8b8d1c5", 647 | "date": "2024-06-15", 648 | "startTime": "09:00", 649 | "endTime": "10:00", 650 | "isBooked": true 651 | }, 652 | { 653 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 654 | "room": "60d9c4e4f3b4b544b8b8d1c5", 655 | "date": "2024-06-15", 656 | "startTime": "10:00", 657 | "endTime": "11:00", 658 | "isBooked": true 659 | } 660 | ], 661 | "room": { 662 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 663 | "name": "Conference Room", 664 | "roomNo": 201, 665 | "floorNo": 1, 666 | "capacity": 20, 667 | "pricePerSlot": 100, 668 | "amenities": ["Projector", "Whiteboard"], 669 | "isDeleted": false 670 | }, 671 | "user": { 672 | "_id": "60d9c4e4f3b4b544b8b8d1c4", 673 | "name": "John Doe", 674 | "email": "john.doe@example.com", 675 | "phone": "1234567890", 676 | "address": "123 Main St, Anytown, USA", 677 | "role": "user" 678 | }, 679 | "totalAmount": 200, 680 | "isConfirmed": "unconfirmed", 681 | "isDeleted": false 682 | }, 683 | // other bookings ( If any ) 684 | ] 685 | } 686 | 687 | 688 | ``` 689 | 690 | **12\. Get User's Bookings (Only Accessible by Authenticated User)** 691 | 692 | - _*Route:*_ `/api/my-bookings`(**GET**) 693 | - **Request Headers:** 694 | 695 | ```javascript 696 | Authorization: 697 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 698 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 699 | 700 | You must include "Bearer" at the beginning of the token! 701 | ``` 702 | 703 | - **Response:** 704 | 705 | ```json 706 | { 707 | "success": true, 708 | "statusCode": 200, 709 | "message": "User bookings retrieved successfully", 710 | "data": [ 711 | { 712 | "_id": "60d9c4e4f3b4b544b8b8d1ca", 713 | "date": "2024-06-15", 714 | "slots": [ 715 | { 716 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 717 | "room": "60d9c4e4f3b4b544b8b8d1c5", 718 | "date": "2024-06-15", 719 | "startTime": "09:00", 720 | "endTime": "10:00", 721 | "isBooked": true 722 | }, 723 | { 724 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 725 | "room": "60d9c4e4f3b4b544b8b8d1c5", 726 | "date": "2024-06-15", 727 | "startTime": "10:00", 728 | "endTime": "11:00", 729 | "isBooked": true 730 | } 731 | ], 732 | "room": { 733 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 734 | "name": "Conference Room", 735 | "roomNo": 201, 736 | "floorNo": 1, 737 | "capacity": 20, 738 | "pricePerSlot": 100, 739 | "amenities": ["Projector", "Whiteboard"], 740 | "isDeleted": false 741 | }, 742 | "totalAmount": 200, 743 | "isConfirmed": "unconfirmed", 744 | "isDeleted": false 745 | } 746 | ] 747 | } 748 | 749 | 750 | ``` 751 | 752 | 753 | 754 | **13\. Update Booking (Only Accessible by Admin)** 755 | 756 | - _*Route:*_ `/api/bookings/:id` (PUT) 757 | - **Request Headers:** 758 | 759 | ```javascript 760 | Authorization: 761 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 762 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 763 | 764 | You must include "Bearer" at the beginning of the token! Do not copy and apply directly from the module. If you blindly follow the modules, you will be a copy master, not a developer. 765 | ``` 766 | 767 | - **Request Body:** 768 | 769 | ```json 770 | { 771 | "isConfirmed": "confirmed" 772 | } 773 | ``` 774 | 775 | **Response:** 776 | 777 | ```json 778 | { 779 | "success": true, 780 | "statusCode": 200, 781 | "message": "Booking updated successfully", 782 | "data": { 783 | "_id": "60d9c4e4f3b4b544b8b8d1ca", 784 | "date": "2024-06-15", 785 | "slots": ["60d9c4e4f3b4b544b8b8d1c6", "60d9c4e4f3b4b544b8b8d1c7"], 786 | "totalAmount": 200, 787 | "room": "60d9c4e4f3b4b544b8b8d1c5", 788 | "user": "60d9c4e4f3b4b544b8b8d1c4", 789 | "isConfirmed": "confirmed", 790 | "isDeleted": false 791 | } 792 | } 793 | 794 | 795 | ``` 796 | 797 | 798 | 799 | **14\. Delete Booking (Soft Delete, Only Accessible by Admin)** 800 | 801 | - _*Route:*_ `/api/bookings/:id` (DELETE) 802 | - **Request Headers:** 803 | 804 | ```javascript 805 | Authorization: 806 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 807 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 808 | 809 | You must include "Bearer" at the beginning of the token! Do not copy and apply directly from the module. If you blindly follow the modules, you will be a copy master, not a developer. 810 | ``` 811 | 812 | - **Response:** 813 | 814 | ```json 815 | { 816 | "success": true, 817 | "statusCode": 200, 818 | "message": "Booking deleted successfully", 819 | "data": { 820 | "_id": "60d9c4e4f3b4b544b8b8d1ca", 821 | "date": "2024-06-15", 822 | "slots": ["60d9c4e4f3b4b544b8b8d1c6", "60d9c4e4f3b4b544b8b8d1c7"], 823 | "totalAmount": 200, 824 | "room": "60d9c4e4f3b4b544b8b8d1c5", 825 | "user": "60d9c4e4f3b4b544b8b8d1c4", 826 | "isConfirmed": "confirmed", 827 | "isDeleted": true 828 | } 829 | } 830 | 831 | 832 | ``` 833 | 834 | ## Bonus Part: (10 Marks) 835 | 836 | ### **1\. No Data Found:** 837 | 838 | When retrieving data, if the database collection is empty or no matching data is found, return the message: "No data found." 839 | 840 | ```json 841 | { 842 | "success": false, 843 | "statusCode": 404, 844 | "message": "No Data Found", 845 | "data":[] 846 | } 847 | ``` 848 | 849 | ### **2\. Error Handling:** 850 | 851 | Implement proper error handling throughout the application. Use global error handling `middleware` to catch and handle errors, providing appropriate error responses with error messages. 852 | 853 | 854 | 855 | **Error Response Object Should include the following properties:** 856 | 857 | * success → false 858 | * message → Error Type → Validation Error, Cast Error, Duplicate Entry 859 | * errorMessages 860 | * stack 861 | 862 | 863 | 864 | **Sample Error Response** 865 | 866 | ```json 867 | { 868 | "success": false, 869 | "message": "E11000 duplicate key error collection: univerity-management.students index: email_1 dup key: { email: \\"user2@gmail.com\\" }", 870 | "errorMessages": [ 871 | { 872 | "path": "", 873 | "message": "E11000 duplicate key error collection: univerity-management.students index: email_1 dup key: { email: \\"user2@gmail.com\\" }" 874 | } 875 | ], 876 | "stack": "MongoServerError: E11000 duplicate key error collection: univerity-management.students index: email_1 dup key: { email: \\"user2@gmail.com\\" }\\n at H:\\\\next-level-development\\\\university-management-auth-service\\\\node_modules\\\\mongodb\\\\src\\\\operations\\\\insert.ts:85:25\\n at H:\\\\next-level-development\\\\university-management-auth-service\\\\node_modules\\\\mongodb\\\\src\\\\cmap\\\\connection_pool.ts:574:11\\n at H:\\\\next-level-development\\\\university-writeOrBuffer (node:internal/streams/writable:391:12)" 877 | } 878 | ``` 879 | 880 | ### 881 | 882 | ### **3\. Not Found Route:** 883 | 884 | Implement a global "Not Found" handler for unmatched routes. When a route is not found, respond with a generic message: "Not Found.” 885 | 886 | ```json 887 | { 888 | "success": false, 889 | "statusCode": 404, 890 | "message": "Not Found", 891 | } 892 | ``` 893 | 894 | ### **4\. Authentication Middleware:** 895 | 896 | Implement an Authentication Middleware to authenticate your application. Ensures that only user and admin can access their own accessible routes. 897 | 898 | ```json 899 | { 900 | "success": false, 901 | "statusCode": 401, 902 | "message": "You have no access to this route", 903 | } 904 | ``` 905 | 906 | ### **5\. Zod Validation:** 907 | The API employs Zod for input validation, ensuring data consistency. When validation fails, a 400 Bad Request status code is returned, accompanied by detailed error messages specifying the erroneous fields and reasons. 908 | -------------------------------------------------------------------------------- /3-car-rental-reservation-system.md: -------------------------------------------------------------------------------- 1 | # Car Rental Reservation System 2 | 3 | ## Assignment Name: Car Rental Reservation System Backend 4 | 5 | You have been assigned the task of building the backend for a Car Renting System. The main focus of this assignment is to implement error handling, CRUD operations, authentication, and authorization, Transaction & Rollback (If necessary) 6 | 7 | ## Story 8 | 9 | Imagine you own a car rental business. To keep track of your cars and manage customer rentals, you've decided to built a web app with a database designed based on the following models: 10 | 11 | - User Model 12 | - Car Model 13 | - Booking Model 14 | 15 | With these models as the foundation, you can build a powerful web app for your car rental business. This app will allow both admins and users to interact smoothly with the booking system: 16 | 17 | Both users and administrators need to register and log in to the car rental web app before performing any actions. This ensures a secure and controlled environment for managing rentals. 18 | 19 | **Admin Actions:** 20 | 21 | **Car Management**: Admins can create new car entries in the system, specifying details like name, color, features, etc. They can also update existing car information to keep things accurate. Additionally, admins can perform "soft deletes" on cars that are no longer available for rent. This keeps a record of the car but removes it from active listings.  22 | 23 | **Booking Oversight**:  Admins have a comprehensive view of all ongoing and past bookings within the system. This allows them to monitor rental activity and identify any potential issues. 24 | 25 | 26 | **Ride Cost Calculation**: For completed rentals (where the end time has been entered by admin), admins can calculate the total cost using startTime , endTime and pricePerHour to ensure accurate billing. 27 | 28 | **User’s Actions:** 29 | 30 | **Book a Ride**: Users can select their pick-up entering carId and startTime to book the perfect car for their needs. 31 | 32 | **Rental Histor**y: They can easily access their booking history, allowing them to review past rentals. 33 | 34 | --- 35 | 36 | 37 | ## Technology Stack: 38 | 39 | * Use TypeScript as the programming language. 40 | * Use Express.js as the web framework. 41 | * Use Mongoose as the Object Data Modeling (ODM) and validation library for MongoDB 42 | 43 | ## Main Part: (50 Marks) 44 | 45 | 46 | 47 | ## **Model:** 48 | 49 | ### **User Model:** 50 | 51 | * **name**: The name of the entity or user. 52 | * **email**: The contact email address. 53 | * **role**: There will be two types of users: 54 | * user 55 | * admin 56 | * **password**: The account password. 57 | * **phone**: The contact phone number. 58 | * **address**: The physical address. 59 | 60 | 61 | 62 | ### **Car Model:** 63 | 64 | * **name**: The name of the car. 65 | * **description**: A brief description of what the car entails. 66 | * **color:** The color of the car. 67 | * **isElectric**: Boolean to indicate if the car is electric. 68 | * **status**: The availability of the car. By default, the status will be `available.` 69 | * **features**: An array listing the features of the car (e.g., \["Bluetooth", "AC", "Sunroof"\]). 70 | * **pricePerHour**: The cost per hour of the booking in the local currency. 71 | * **isDeleted**: Indicates whether the car has been marked as deleted (false means it is not deleted). 72 | 73 | 74 | 75 | ### **Booking Model:** 76 | 77 | * **date**: The date of the booking. 78 | * **user**: Identifier for the user. **_(reference to user model)_** 79 | * **car**: Identifier for the booked car. **_(reference to car model)_** 80 | * **startTime**: The start time of the booking. The time will be in 24hr format. 81 | * **endTime:** The end time of the booking. The time will be in 24hr format. 82 | * **totalCost**: The total cost will be calculated using `startTime`, `endTime` and `pricePerHour` data. By default totalCost will be `0`. 83 | 84 | 85 | 86 | ## API Endpoints 87 | 88 | ### 1\. Sign Up 89 | 90 | **Route**: `/api/auth/signup` (**POST**) 91 | 92 | **Request Body:** 93 | 94 | ```json 95 | { 96 | "name": "John Doe", 97 | "email": "johndoe@example.com", 98 | "role": "user", // role can be user or admin 99 | "password": "password123", 100 | "phone": "1234567890", 101 | "address": "123 Main St, City, Country" 102 | 103 | } 104 | ``` 105 | 106 | **Response**: 107 | 108 | ```json 109 | { 110 | "success": true, 111 | "statusCode": 201, 112 | "message": "User registered successfully", 113 | "data": { 114 | "_id": "6071f0fbf98b210012345678", 115 | "name": "John Doe", 116 | "email": "johndoe@example.com", 117 | "role": "user", 118 | "phone": "1234567890", 119 | "address": "123 Main St, City, Country", 120 | "createdAt": "2024-06-10T12:00:00.000Z", 121 | "updatedAt": "2024-06-10T12:00:00.000Z" 122 | } 123 | } 124 | ``` 125 | 126 | ### 127 | 128 | ### 2\. Sign In 129 | 130 | **Route**: `/api/auth/signin`(**POST**) 131 | 132 | **Request Body:** 133 | 134 | ```json 135 | { 136 | "email": "johndoe@example.com", 137 | "password": "password123" 138 | } 139 | ``` 140 | 141 | **Response:** 142 | 143 | ```json 144 | { 145 | "success": true, 146 | "statusCode": 200, 147 | "message": "User logged in successfully", 148 | "data": { 149 | "_id": "6071f0fbf98b210012345678", 150 | "name": "John Doe", 151 | "email": "johndoe@example.com", 152 | "role": "user", 153 | "phone": "1234567890", 154 | "address": "123 Main St, City, Country", 155 | "createdAt": "2024-06-10T12:00:00.000Z", 156 | "updatedAt": "2024-06-10T12:00:00.000Z" 157 | }, 158 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... (your JWT token)" 159 | } 160 | ``` 161 | 162 | ### 3\. Create a Car (Only accessible to the Admin) 163 | 164 | **Route**: `/api/cars`(**POST**) 165 | 166 | **Request Headers:** 167 | 168 | ```javascript 169 | Authorization: 170 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 171 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 172 | 173 | You must include "Bearer" at the beginning of the token! Do not copy and apply directly from the module. If you blindly follow the modules, you will be a copy master, not a developer. 174 | ``` 175 | 176 | 177 | 178 | **Request Body:** 179 | 180 | ```json 181 | { 182 | "name": "Tesla Model 3", 183 | "description": "An electric car with advanced technology and performance.", 184 | "color": "White", 185 | "isElectric": true, 186 | "features": ["AC", "Bluetooth", "Long Range Battery"], 187 | "pricePerHour": 500 188 | } 189 | ``` 190 | 191 | **Response:** 192 | 193 | ```json 194 | { 195 | "success": true, 196 | "statusCode": 201, 197 | "message": "Car created successfully", 198 | "data": { 199 | "_id": "608a6d8d03a1b40012abcdef", 200 | "name": "Tesla Model 3", 201 | "description": "An electric car with advanced technology and performance.", 202 | "color": "White", 203 | "isElectric": true, 204 | "features": ["AC", "Bluetooth", "Long Range Battery"], 205 | "pricePerHour": 500, 206 | "status": "available", 207 | "isDeleted": false, 208 | "createdAt": "2024-04-28T12:00:00.000Z", 209 | "updatedAt": "2024-04-28T12:00:00.000Z" 210 | } 211 | } 212 | ``` 213 | 214 | ### 4\. Get All Cars 215 | 216 | **Route**: `/api/cars`(**GET**) 217 | 218 | **Response Body:** 219 | 220 | ```json 221 | { 222 | "success": true, 223 | "statusCode": 200, 224 | "message": "Cars retrieved successfully", 225 | "data": [ 226 | { 227 | "_id": "608a6d8d03a1b40012abcdef", 228 | "name": "Tesla Model 3", 229 | "description": "An electric car with advanced technology and performance.", 230 | "color": "White", 231 | "isElectric": true, 232 | "features": ["AC", "Bluetooth", "Long Range Battery"], 233 | "pricePerHour": 500, 234 | "status": "available", 235 | "isDeleted": false, 236 | "createdAt": "2024-04-28T12:00:00.000Z", 237 | "updatedAt": "2024-04-28T12:00:00.000Z" 238 | }, 239 | // more data 240 | ] 241 | } 242 | ``` 243 | 244 | 245 | 246 | ### 5\. Get A Car 247 | 248 | **Route**: `/api/cars/:id`(**GET**) 249 | 250 | **Response Body:** 251 | 252 | ```json 253 | { 254 | "success": true, 255 | "statusCode": 200, 256 | "message": "A Car retrieved successfully", 257 | "data": { 258 | "_id": "608a6d8d03a1b40012abcdef", 259 | "name": "Tesla Model 3", 260 | "description": "An electric car with advanced technology and performance.", 261 | "color": "White", 262 | "isElectric": true, 263 | "features": ["AC", "Bluetooth", "Long Range Battery"], 264 | "pricePerHour": 500, 265 | "status": "available", 266 | "isDeleted": false, 267 | "createdAt": "2024-04-28T12:00:00.000Z", 268 | "updatedAt": "2024-04-28T12:00:00.000Z" 269 | } 270 | } 271 | ``` 272 | 273 | 274 | 275 | ### **6\. Update A Car (Only Accessible to the Admin)** 276 | 277 | **Route:** `/api/cars/:id`(**PUT**) 278 | 279 | **Request Headers:** 280 | 281 | ```javascript 282 | Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 283 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 284 | 285 | You must include "Bearer" at the beginning of the token! 286 | ``` 287 | 288 | **Request Body:** 289 | 290 | ```json 291 | { 292 | "color": "Black", // we will have to update all the fields including name, description, color, isElectric, features, pricePerHour, etc. 293 | } 294 | ``` 295 | 296 | **Response Body:** 297 | 298 | ```json 299 | { 300 | "success": true, 301 | "statusCode": 200, 302 | "message": "Car updated successfully", 303 | "data": { 304 | "_id": "608a6d8d03a1b40012abcdef", 305 | "name": "Tesla Model 3", 306 | "description": "An electric car with advanced technology and performance.", 307 | "color": "Black", 308 | "isElectric": true, 309 | "features": ["AC", "Bluetooth", "Long Range Battery"], 310 | "pricePerHour": 500, 311 | "status": "available", 312 | "isDeleted": false, 313 | "createdAt": "2024-04-28T12:00:00.000Z", 314 | "updatedAt": "2024-04-29T12:00:00.000Z" 315 | } 316 | } 317 | ``` 318 | 319 | ### 320 | 321 | ### **7\. Delete A Car (Only Accessible to the Admin)** 322 | 323 | **Route:** `/api/cars/:id`(**DELETE**) \[SOFT DELETE\] 324 | 325 | **Request Headers:** 326 | 327 | ```javascript 328 | Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 329 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 330 | 331 | You must include "Bearer" at the beginning of the token! 332 | ``` 333 | 334 | **Response Body:** 335 | 336 | ```json 337 | { 338 | "success": true, 339 | "statusCode": 200, 340 | "message": "Car Deleted successfully", 341 | "data": { 342 | "_id": "608a6d8d03a1b40012abcdef", 343 | "name": "Tesla Model 3", 344 | "description": "An electric car with advanced technology and performance.", 345 | "color": "Black", 346 | "isElectric": true, 347 | "features": ["AC", "Bluetooth", "Long Range Battery"], 348 | "pricePerHour": 500, 349 | "status": "available", 350 | "isDeleted": true, 351 | "createdAt": "2024-04-28T12:00:00.000Z", 352 | "updatedAt": "2024-05-29T12:00:00.000Z" 353 | } 354 | } 355 | ``` 356 | 357 | 358 | 359 | ### **8\. Get All Bookings (Accessible to the Admin)** 360 | 361 | **Route:** `/api/bookings`(**GET**) 362 | 363 | **Query Parameters:** 364 | 365 | * `carId`: ID of the car for which availability needs to be checked. 366 | * `date`: The specific date for which availability needs to be checked (format: YYYY-MM-DD). 367 | 368 | 369 | 370 | Example Request: 371 | 372 | `/api/bookings?carId=608a6d8d03a1b40012abcdef&date=2024-06-15` 373 | 374 | 375 | 376 | **Response Body:** 377 | 378 | ```json 379 | { 380 | "success": true, 381 | "statusCode": 200, 382 | "message": "Bookings retrieved successfully", 383 | "data": [ 384 | { 385 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 386 | "date": "2024-06-15", 387 | "startTime": "13:00", 388 | "endTime": null, // it will be null by default, when booked. It will be updated by the admin, when the car is returned. 389 | "user": { 390 | "_id": "6071f0fbf98b210012345688", 391 | "name": "Tom", 392 | "email": "tom@example.com", 393 | "role": "user", 394 | "phone": "1234567890", 395 | "address": "123 Main St, City, Country", 396 | }, 397 | "car": { 398 | "_id": "608a6d8d03a1b40012abcdef", 399 | "name": "Tesla Model 3", 400 | "description": "An electric car with advanced technology and performance.", 401 | "color": "White", 402 | "isElectric": true, 403 | "features": ["AC", "Bluetooth", "Long Range Battery"], 404 | "pricePerHour": 500, 405 | "status": "unavailable", 406 | "isDeleted": false, 407 | "createdAt": "2024-04-28T12:00:00.000Z", 408 | "updatedAt": "2024-04-28T12:00:00.000Z" 409 | }, 410 | "totalCost": 0, // it will be 0 by default, when booked. It will be calculate and update by the admin, when the car is returned. 411 | "createdAt": "2024-04-28T12:00:00.000Z", 412 | "updatedAt": "2024-05-29T12:00:00.000Z" 413 | }, 414 | //...additional bookings... 415 | ] 416 | } 417 | ``` 418 | 419 | 420 | 421 | ### **9\. Book a Car (Only Accessible to the User)** 422 | 423 | **Route:** `/api/bookings`(**POST**) 424 | 425 | **Request Headers:** 426 | 427 | ```javascript 428 | Authorization: 429 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 430 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 431 | 432 | You must include "Bearer" at the beginning of the token! 433 | ``` 434 | 435 | 436 | 437 | **Request Body:** 438 | 439 | ```json 440 | { 441 | "carId": "60d9c4e4f3b4b544b8b8d1c7", 442 | "date": "2024-06-15", 443 | "startTime": "13:00", 444 | } 445 | ``` 446 | 447 | 448 | 449 | **Response Body:** 450 | 451 | ```json 452 | { 453 | "success": true, 454 | "statusCode": 200, 455 | "message": "Car booked successfully", 456 | "data": { 457 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 458 | "date": "2024-06-15", 459 | "startTime": "13:00", 460 | "endTime": null, // it will be null by default, when booked. It will be updated by the admin, when the car is returned. 461 | "user": { 462 | "_id": "6071f0fbf98b210012345688", 463 | "name": "Tom", 464 | "email": "tom@example.com", 465 | "role": "user", 466 | "phone": "1234567890", 467 | "address": "123 Main St, City, Country", 468 | }, 469 | "car": { 470 | "_id": "608a6d8d03a1b40012abcdef", 471 | "name": "Tesla Model 3", 472 | "description": "An electric car with advanced technology and performance.", 473 | "color": "White", 474 | "isElectric": true, 475 | "features": ["AC", "Bluetooth", "Long Range Battery"], 476 | "pricePerHour": 500, 477 | "status": "unavailable", 478 | "isDeleted": false, 479 | "createdAt": "2024-04-28T12:00:00.000Z", 480 | "updatedAt": "2024-04-28T12:00:00.000Z" 481 | }, 482 | "totalCost": 0, // it will be 0 by default, when booked. It will be calculate and update by the admin, when the car is returned. 483 | "createdAt": "2024-04-28T12:00:00.000Z", 484 | "updatedAt": "2024-05-29T12:00:00.000Z" 485 | } 486 | } 487 | ``` 488 | 489 | 490 | 491 | ### **10\. Get User's Bookings (Only Accessible To the User)** 492 | 493 | **Route:** `/api/bookings/my-bookings`(**GET**) 494 | 495 | **Request Headers:** 496 | 497 | ```javascript 498 | Authorization: 499 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 500 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 501 | 502 | You must include "Bearer" at the beginning of the token! 503 | 504 | 505 | ``` 506 | 507 | **Response Body:** 508 | 509 | ```json 510 | { 511 | "success": true, 512 | "statusCode": 200, 513 | "message": "My Bookings retrieved successfully", 514 | "data": [ 515 | { 516 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 517 | "date": "2024-06-15", 518 | "startTime": "13:00", 519 | "endTime": "15:00", 520 | "user": { 521 | "_id": "6071f0fbf98b210012345688", 522 | "name": "Tom", 523 | "email": "tom@example.com", 524 | "role": "user", 525 | "phone": "1234567890", 526 | "address": "123 Main St, City, Country", 527 | }, 528 | "car": { 529 | "_id": "608a6d8d03a1b40012abcdef", 530 | "name": "Tesla Model 3", 531 | "description": "An electric car with advanced technology and performance.", 532 | "color": "White", 533 | "isElectric": true, 534 | "features": ["AC", "Bluetooth", "Long Range Battery"], 535 | "pricePerHour": 500, 536 | "status":"unavailable", 537 | "isDeleted": false, 538 | "createdAt": "2024-04-28T12:00:00.000Z", 539 | "updatedAt": "2024-04-28T12:00:00.000Z" 540 | }, 541 | "totaCost":1000, 542 | "createdAt": "2024-04-28T12:00:00.000Z", 543 | "updatedAt": "2024-05-29T12:00:00.000Z" 544 | }, 545 | // ...additional bookings... 546 | ] 547 | } 548 | ``` 549 | 550 | ## 551 | 552 | ## **11\. Return The Car (Only Accessible To Admin)** 553 | 554 | 555 | **Route:** `/api/cars/return`(PUT) 556 | 557 | **Request Headers:** 558 | 559 | ```javascript 560 | Authorization: 561 | Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmF 562 | tZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 563 | 564 | You must include "Bearer" at the beginning of the token! 565 | ``` 566 | 567 | **Request Body:** 568 | 569 | ```json 570 | { 571 | "bookingId": "60d9c4e4f3b4b544b8b8d1c7", 572 | "endTime": "15:00" 573 | } 574 | ``` 575 | 576 | 577 | 578 | **Response Body:** 579 | 580 | ```json 581 | { 582 | "success": true, 583 | "statusCode": 200, 584 | "message": "Car returned successfully", 585 | "data": { 586 | "_id": "60d9c4e4f3b4b544b8b8d1c7", 587 | "date": "2024-06-15", 588 | "startTime": "13:00", 589 | "endTime": "15:00", 590 | "user": { 591 | "_id": "6071f0fbf98b210012345688", 592 | "name": "Tom", 593 | "email": "tom@example.com", 594 | "role": "user", 595 | "phone": "1234567890", 596 | "address": "123 Main St, City, Country", 597 | }, 598 | "car": { 599 | "_id": "608a6d8d03a1b40012abcdef", 600 | "name": "Tesla Model 3", 601 | "description": "An electric car with advanced technology and performance.", 602 | "color": "White", 603 | "isElectric": true, 604 | "features": ["AC", "Bluetooth", "Long Range Battery"], 605 | "pricePerHour": 500, 606 | "status": "available" // The status of the car is updated to "available" indicating it is now ready for booking, following its return. 607 | "isDeleted": false, 608 | "createdAt": "2024-04-28T12:00:00.000Z", 609 | "updatedAt": "2024-04-28T12:00:00.000Z" 610 | }, 611 | "totalCost":1000, //Calculated using the start time, end time, and price per hour. 612 | "createdAt": "2024-04-28T12:00:00.000Z", 613 | "updatedAt": "2024-05-29T12:00:00.000Z" 614 | } 615 | } 616 | 617 | 618 | ``` 619 | 620 | ## Hints for `totalCost` calculation: 621 | 622 | * **Convert Times to Hours:** The `startTime` and `endTime` are in `24 hour` format. Convert them to hours for calculating. 623 | * **Calculate Duration:** Subtract `startTime` from `endTime` to find the total duration in hours. 624 | * **Multiply by Price per Hour:** Once you have the duration in hours, multiply it by the`pricePerHour` to get the total cost. 625 | 626 | 627 | 628 | ## Bonus Part: 629 | 630 | ### **1\. No Data Found:** 631 | 632 | When retrieving data, if the database collection is empty or no matching data is found, return the message: "No data found." 633 | 634 | ```elixir 635 | { 636 | "success": false, 637 | "statusCode": 404, 638 | "message": "No Data Found", 639 | "data": [] 640 | } 641 | ``` 642 | 643 | ### **2\. Error Handling:** 644 | 645 | Implement proper error handling throughout the application. Use global error handling `middleware` to catch and handle errors, providing appropriate error responses with status codes and error messages. 646 | 647 | 648 | 649 | **Error Response Object Should include the following properties:** 650 | 651 | * success → false 652 | * message → Error Type → Validation Error, Cast Error, Duplicate Entry 653 | * errorMessages 654 | * stack 655 | 656 | 657 | 658 | #### **Sample Error Response** 659 | 660 | ```json 661 | { 662 | "success": false, 663 | "message": "E11000 duplicate key error collection: univerity-management.students index: 664 | email_1 dup key: { email: \\"user2@gmail.com\\" }", 665 | "errorMessages": [ 666 | { 667 | "path": "", 668 | "message": "E11000 duplicate key error collection: univerity-management.students 669 | index: email_1 dup key: { email: \\"user2@gmail.com\\" }" 670 | } 671 | ], 672 | "stack": "MongoServerError: E11000 duplicate key error collection: univerity- 673 | management.students index: email_1 dup key: { email: \\"user2@gmail.com\\" }\\n 674 | at H:\\\\next-level-development\\\\university-management-auth- 675 | service\\\\node_modules\\\\mongodb\\\\src\\\\operations\\\\insert.ts:85:25\\n 676 | at H:\\\\next-level-development\\\\university-management-auth- 677 | service\\\\node_modules\\\\mongodb\\\\src\\\\cmap\\\\connection_pool.ts:574:11\\n 678 | at H:\\\\next-level-development\\\\university-writeOrBuffer 679 | (node:internal/streams/writable:391:12)" 680 | } 681 | ``` 682 | 683 | 684 | ### **3\. Not Found Route:** 685 | 686 | Implement a global "Not Found" handler for unmatched routes. When a route is not found, respond with a generic message: "Not Found.” 687 | 688 | ```json 689 | { 690 | "success": false, 691 | "statusCode": 404, 692 | "message": "Not Found", 693 | } 694 | ``` 695 | 696 | 697 | ### **4\. Authentication Middleware:** 698 | 699 | Implement an Authentication Middleware to authenticate your application. Ensures that only user and admin can access their own accessible routes. 700 | 701 | ```json 702 | { 703 | "success": false, 704 | "statusCode": 401, 705 | "message": "You have no access to this route", 706 | } 707 | ``` 708 | 709 | ### **5\. Zod Validation:** 710 | The API employs Zod for input validation, ensuring data consistency. When validation fails, a 400 Bad Request status code is returned, accompanied by detailed error messages specifying the erroneous fields and reasons. 711 | 712 | 713 | -------------------------------------------------------------------------------- /4-bike-rental-service.md: -------------------------------------------------------------------------------- 1 | # Bike rental service for tourists or locals. 2 | 3 | * * * 4 | 5 | ## Assignment Name: Bike Rental Reservation System Backend 6 | 7 | You have been assigned the task of building the backend for a Bike Renting System. The main focus of this assignment is to implement error handling, CRUD operations, authentication, and authorization, Transaction & Rollback (If necessary) 8 | --- 9 | 10 | ### Story 11 | 12 | --- 13 | 14 | In the vibrant coastal town of Cox's Bazar, famous for its long Inani beach, tourism was thriving. The town, with its scenic beauty and lively atmosphere, was a favorite spot for tourists and locals alike. Seeing an opportunity, Jhankar Mahbub, a local entrepreneur with a passion for technology, decided to start a bike rental service to cater to both tourists and locals. 15 | 16 | Jhankar Mahbub envisioned a seamless bike rental system where users could easily rent bikes online. He wanted a backend system that would handle user registrations, bike availability, and booking management efficiently. To achieve this, he planned to hire a developer to build the backend for the Bike Rental Reservation System. 17 | 18 | **Models:** 19 | 20 | 1. **User Model** 21 | 2. **Bike Model** 22 | 3. **Booking Model** 23 | 24 | Jhankar's bike rental service quickly became popular. Tourists and locals loved the convenience of booking bikes online and exploring Cox's Bazar. With a robust backend system in place, Jhankar could efficiently manage his business, and his customers enjoyed a seamless rental experience. 25 | 26 | --- 27 | 28 | 29 | ### Technology Stack: 30 | 31 | - **Programming Language**: TypeScript 32 | - **Web Framework**: Express.js 33 | - **ODM & Validation Library**: Zod, Mongoose for MongoDB 34 | 35 | ### Main Part: (50 Marks) 36 | 37 | ### Models: 38 | 39 | 1. **User Model**: 40 | - **name**: The name of the user. 41 | - **email**: The contact email address. 42 | - **password**: The account password. 43 | - **phone**: The contact phone number. 44 | - **address**: The physical address. 45 | - **role**: admin / user 46 | 2. **Bike Model**: 47 | - **name**: The name of the bike model. 48 | - **description**: A brief description of the bike. 49 | - **pricePerHour**: The rental price per hour. 50 | - **isAvailable**: Indicates if the bike is available for rental (default: true). 51 | - **cc**: The engine capacity of the bike in cubic centimeters. 52 | - **year**: The manufacturing year of the bike. 53 | - **model**: The model of the bike. 54 | - **brand**: The brand of the bike. 55 | 3. **Booking Model**: 56 | - **userId**: Reference to the User model. 57 | - **bikeId**: Reference to the Bike model. 58 | - **startTime**: The start time of the rental. 59 | - **returnTime**: The return time of the rental. 60 | - **totalCost**: The total cost of the rental. 61 | - **isReturned**: Indicates if the bike has been returned (default: false). 62 | 63 | ### API Endpoints: 64 | 65 | ## **User Routes**: 66 | 67 | 1. **Sign Up** 68 | - **Route**: /api/auth/signup (POST) 69 | - **Request Body**: 70 | 71 | ```json 72 | { 73 | "name": "John Doe", 74 | "email": "john@example.com", 75 | "password": "password123", 76 | "phone": "1234567890", 77 | "address": "123 Main St, Anytown", 78 | "role" : "admin" 79 | } 80 | 81 | ``` 82 | 83 | - **Response**: 84 | 85 | ```json 86 | { 87 | "success": true, 88 | "statusCode": 201, 89 | "message": "User registered successfully", 90 | "data": { 91 | "_id": "60d9c4e4f3b4b544b8b8d1f5", 92 | "name": "John Doe", 93 | "email": "john@example.com", 94 | "phone": "1234567890", 95 | "address": "123 Main St, Anytown", 96 | "role" : "admin", 97 | "createdAt": "2024-06-10T13:26:51.289Z", 98 | "updatedAt": "2024-06-10T13:26:51.289Z", 99 | "__v": 0 100 | } 101 | } 102 | 103 | ``` 104 | 105 | 2. **User Login** 106 | - **Route**: /api/auth/login (POST) 107 | - **Request Body**: 108 | 109 | ```json 110 | { 111 | "email": "john@example.com", 112 | "password": "password123" 113 | } 114 | 115 | ``` 116 | 117 | - **Response**: 118 | 119 | ```json 120 | { 121 | "success": true, 122 | "statusCode": 200, 123 | "message": "User logged in successfully", 124 | "token": "jwt_token", 125 | "data": { 126 | "_id": "60d9c4e4f3b4b544b8b8d1c3", 127 | "name": "John Doe", 128 | "email": "john@example.com", 129 | "phone": "1234567890", 130 | "address": "123 Main St, Anytown", 131 | "role" : "admin" 132 | } 133 | } 134 | 135 | ``` 136 | 137 | 3. **Get Profile** 138 | - **Route**: /api/users/me (GET) 139 | - **Request Headers**: Authorization: Bearer jwt_token 140 | - **Response**: 141 | 142 | ```json 143 | { 144 | "success": true, 145 | "statusCode": 200, 146 | "message": "User profile retrieved successfully", 147 | "data": { 148 | "_id": "6666ff917181b8e5ffe04f91", 149 | "name": "admin", 150 | "email": "admin@gmail.com", 151 | "phone": "1234567890", 152 | "address": "123 Main St, Anytown", 153 | "role": "admin", 154 | "createdAt": "2024-06-10T13:28:49.260Z", 155 | "updatedAt": "2024-06-10T13:28:49.260Z", 156 | "__v": 0 157 | } 158 | } 159 | 160 | ``` 161 | 162 | 4. **Update Profile** 163 | - **Route**: /api/users/me (PUT) 164 | - **Request Headers**: Authorization: Bearer jwt_token 165 | - **Request Body**: 166 | 167 | ```json 168 | { 169 | "name": "John Updated", 170 | "phone": "0987654321" 171 | } 172 | 173 | ``` 174 | 175 | - **Response**: 176 | 177 | ```json 178 | { 179 | "success": true, 180 | "statusCode": 200, 181 | "message": "Profile updated successfully", 182 | "data": { 183 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 184 | "name": "John Updated", 185 | "email": "john@example.com", 186 | "phone": "0987654321", 187 | "address": "123 Main St, Anytown", 188 | "role" : "admin" 189 | } 190 | } 191 | 192 | ``` 193 | 194 | 195 | ## **Bike Routes**: 196 | 197 | 1. **Create Bike (Admin Only)** 198 | - **Route**: /api/bikes (POST) 199 | - **Request Headers**: Authorization: Bearer jwt_token 200 | - **Request Body**: 201 | 202 | ```json 203 | { 204 | "name": "Mountain Bike", 205 | "description": "A durable mountain bike for rough terrains.", 206 | "pricePerHour": 15, 207 | "cc": 250, 208 | "year": 2022, 209 | "model": "X1", 210 | "brand": "Yamaha" 211 | } 212 | 213 | ``` 214 | 215 | - **Response**: 216 | 217 | ```json 218 | { 219 | "success": true, 220 | "statusCode": 200, 221 | "message": "Bike added successfully", 222 | "data": { 223 | "_id": "60d9c4e4f3b4b544b8b8d1c4", 224 | "name": "Mountain Bike", 225 | "description": "A durable mountain bike for rough terrains.", 226 | "pricePerHour": 15, 227 | "isAvailable": true, 228 | "cc": 250, 229 | "year": 2022, 230 | "model": "X1", 231 | "brand": "Yamaha" 232 | } 233 | } 234 | 235 | ``` 236 | 237 | 2. **Get All Bikes** 238 | - **Route**: /api/bikes (GET) 239 | - **Response**: 240 | 241 | ```json 242 | { 243 | "success": true, 244 | "statusCode": 200, 245 | "message": "Bikes retrieved successfully", 246 | "data": [ 247 | { 248 | "_id": "bike_id", 249 | "name": "Mountain Bike", 250 | "description": "A durable mountain bike for rough terrains.", 251 | "pricePerHour": 15, 252 | "isAvailable": true, 253 | "cc": 250, 254 | "year": 2022, 255 | "model": "X1", 256 | "brand": "Yamaha" 257 | }, 258 | ...other bikes... 259 | ] 260 | } 261 | 262 | ``` 263 | 264 | 3. **Update Bike (Admin Only)** 265 | - **Route**: /api/bikes/:id (PUT) 266 | - **Request Headers**: Authorization: Bearer jwt_token 267 | - **Request Body**: 268 | 269 | ```json 270 | { 271 | "pricePerHour": 20 272 | } 273 | 274 | ``` 275 | 276 | - **Response**: 277 | 278 | ```json 279 | { 280 | "success": true, 281 | "statusCode": 200, 282 | "message": "Bike updated successfully", 283 | "data": { 284 | "_id": "bike_id", 285 | "name": "Mountain Bike", 286 | "description": "A durable mountain bike for rough terrains.", 287 | "pricePerHour": 20, // Updated price per hour 288 | "isAvailable": true, 289 | "cc": 250, 290 | "year": 2022, 291 | "model": "X1", 292 | "brand": "Yamaha" 293 | } 294 | } 295 | 296 | ``` 297 | 298 | 4. **Delete Bike (Admin Only)** 299 | - **Route**: /api/bikes/:id (DELETE) 300 | - **Request Headers**: Authorization: Bearer jwt_token 301 | - **Response**: 302 | 303 | ```json 304 | { 305 | "success": true, 306 | "statusCode": 200, 307 | "message": "Bike deleted successfully", 308 | "data": { 309 | "_id": "bike_id", 310 | "name": "Mountain Bike", 311 | "description": "A durable mountain bike for rough terrains.", 312 | "pricePerHour": 20, 313 | "isAvailable": false, 314 | "cc": 250, 315 | "year": 2022, 316 | "model": "X1", 317 | "brand": "Yamaha" 318 | } 319 | } 320 | 321 | ``` 322 | 323 | 324 | ## **Rental Routes**: 325 | 326 | 1. **Create Rental** 327 | - **Route**: /api/rentals (POST) 328 | - **Request Headers**: Authorization: Bearer jwt_token 329 | - User information should be extracted from the token 330 | - **Bike's availability status should be updated to false** 331 | - **Request Body**: 332 | 333 | ```json 334 | { 335 | "bikeId": "60d9c4e4f3b4b544b8b8d1c4", 336 | "startTime": "2024-06-10T09:00:00Z" 337 | } 338 | 339 | ``` 340 | 341 | - **Response**: 342 | 343 | ```json 344 | { 345 | "success": true, 346 | "statusCode": 200, 347 | "message": "Rental created successfully", 348 | "data": { 349 | "_id": "60d9c4e4f3b4b544b8b8d1c4", 350 | "userId": "60d9c4e4f3b4b544b8b8d1c3", 351 | "bikeId": "60d9c4e4f3b4b544b8b8d1c4", 352 | "startTime": "2024-06-10T09:00:00Z", 353 | "returnTime": null, 354 | "totalCost": 0, 355 | "isReturned": false 356 | } 357 | } 358 | ``` 359 | 360 | **Important Note:** Upon creating a rental, ensure the bike's isAvailable status is set to false to indicate that it is currently rented out and not available for other users to rent. 361 | 362 | 1. **Return Bike (Admin Only)** 363 | - **Route**: /api/rentals/:id/return (PUT) 364 | - **Request Headers**: Authorization: Bearer jwt_token 365 | - **Request Body**: Not needed 366 | - **Bike's availability status should be updated to true** 367 | - **Hints**: The cost should be calculated based on the start and return time of the rental. For example, if the start time is "2024-06-10T09:00:00Z" and the return time is "2024-06-10T18:00:00Z" (current time), the total rental duration is 9 hours. If the price per hour is $15, the total cost will be 9 * 15 = $135. 368 | - **Response**: 369 | 370 | ```json 371 | { 372 | "success": true, 373 | "statusCode": 200, 374 | "message": "Bike returned successfully", 375 | "data": { 376 | "_id": "60d9c4e4f3b4b544b8b8d1c4", 377 | "userId": "60d9c4e4f3b4b544b8b8d1c3", 378 | "bikeId": "60d9c4e4f3b4b544b8b8d1c4", 379 | "startTime": "2024-06-10T09:00:00Z", 380 | "returnTime": "2024-06-10T18:00:00Z",// Current time when returning the bike 381 | "totalCost": 135, // Calculated based on rental duration 382 | "isReturned": true 383 | } 384 | } 385 | 386 | ``` 387 | 388 | 2. **Get All Rentals for User (My rentals)** 389 | - **Route**: /api/rentals (GET) 390 | - **Request Headers**: Authorization: Bearer jwt_token 391 | - **Response**: 392 | 393 | ```json 394 | { 395 | "success": true, 396 | "statusCode": 200, 397 | "message": "Rentals retrieved successfully", 398 | "data": [ 399 | { 400 | "_id": "60d9c4e4f3b4b544b8b8d1c4", 401 | "userId": "60d9c4e4f3b4b544b8b8d1c3", 402 | "bikeId": "60d9c4e4f3b4b544b8b8d1c4", 403 | "startTime": "2024-06-10T09:00:00Z", 404 | "returnTime": "2024-06-10T18:00:00Z", 405 | "totalCost": 135, 406 | "isReturned": true 407 | }, 408 | ...other rentals... 409 | ] 410 | } 411 | 412 | ``` 413 | 414 | 415 | ### Bonus Part: (10 Marks) 416 | 417 | ### Middleware and Error Handling: 418 | 419 | 1. **No Data Found**: 420 | - When finding data, if the database collection is empty or does not match any data, return a generic message: "No data found." 421 | - **Response**: 422 | 423 | ```json 424 | { 425 | "success": false, 426 | "message": "No Data Found", 427 | "data": [] 428 | } 429 | 430 | ``` 431 | 432 | 2. **Error Handling**: 433 | - Implement proper error handling throughout the application. Use global error handling middleware to catch and handle errors, providing appropriate error responses error messages. 434 | - Error Response Object should include the following properties: 435 | - **success**: false 436 | - **message**: Error Type (e.g., Validation Error, Cast Error, Duplicate Entry) 437 | - **errorMessages**: 438 | 439 | ```json 440 | [ 441 | { 442 | "path": "", 443 | "message": "Error message" 444 | } 445 | ] 446 | 447 | ``` 448 | 449 | - **Sample Error Response**: 450 | 451 | ```json 452 | { 453 | "success": false, 454 | "message": "E11000 duplicate key error collection:: email_1 dup key: { email: \\"user2@gmail.com\\" }", 455 | "errorMessages": [ 456 | { 457 | "path": "", 458 | "message": "E11000 duplicate key error index: email_1 dup key: { email: \\"user2@gmail.com\\" }" 459 | } 460 | ], 461 | "stack": "error stack" 462 | } 463 | 464 | ``` 465 | 466 | 3. **Not Found Route**: 467 | - Implement a global "Not Found" handler for unmatched routes. When a route is not found, respond with a generic message: "Not Found." 468 | - **Response**: 469 | 470 | ```json 471 | { 472 | "success": false, 473 | "statusCode": 404, 474 | "message": "Not Found" 475 | } 476 | 477 | ``` 478 | 479 | ### **4\. Authentication Middleware:** 480 | 481 | Implement an Authentication Middleware to authenticate your application. Ensures that only user and admin can access their own accessible routes. 482 | 483 | ```json 484 | { 485 | "success": false, 486 | "statusCode": 401, 487 | "message": "You have no access to this route", 488 | } 489 | ``` 490 | 491 | ### **5\. Zod Validation:** 492 | The API employs Zod for input validation, ensuring data consistency. When validation fails, a 400 Bad Request status code is returned, accompanied by detailed error messages specifying the erroneous fields and reasons. 493 | 494 | 495 | -------------------------------------------------------------------------------- /5-sports-facility-booking-platform.md: -------------------------------------------------------------------------------- 1 | # Sports Facility Booking Platform 2 | 3 | ## Story 4 | Once upon a time in a bustling city, there was Alex, a sports lover with a big dream. He wanted to create a simple way for people to book sports facilities hassle-free. With his trusty team of developers, they set out to make this dream a reality. 5 | 6 | Using TypeScript, Express.js, and Mongoose, they built the backbone of their platform. They created models for users, facilities, and bookings, making sure to include all the necessary details. 7 | 8 | Next came the fun part—designing the API endpoints. Users could sign up, log in, and book facilities with ease. Admins had extra powers to manage facilities and view all bookings. 9 | 10 | But it wasn't all smooth sailing. They encountered challenges along the way, like handling errors and ensuring security. However, with determination and teamwork, they overcame each obstacle. 11 | 12 | After months of hard work, their platform was finally ready. People could now book sports facilities with just a few clicks. Alex and his team celebrated their success, proud of what they had accomplished together. And so, their simple idea turned into a solution that made a big difference in the world of sports. 13 | 14 | --- 15 | 16 | 17 | You are tasked with developing the backend for a sports facility booking platform. This assignment focuses on implementing the following key functionalities: Error Handling, CRUD operations, Authentication & Authorization, and Transaction & Rollback if needed. 18 | 19 | 20 | 21 | ## Technology Stack: 22 | 23 | * **Programming Language**: TypeScript 24 | * **Web Framework**: Express.js 25 | * **ODM & Validation Library**: Mongoose for MongoDB 26 | 27 | 28 | 29 | ## Main Requirements (50 Marks) 30 | 31 | 32 | 33 | ### Models: 34 | 35 | 36 | 37 | **User Model:** 38 | 39 | * `name`: The name of the user. 40 | * `email`: The contact email address. 41 | * `password`: The account password (must be hashed). 42 | * `phone`: The contact phone number. 43 | * `role`: The role of the user (can be 'admin' or 'user'). 44 | * `address`: The physical address. 45 | 46 | 47 | 48 | **Facility Model:** 49 | 50 | * `name`: The title of the facility. 51 | * `description`: A brief description of the facility. 52 | * `pricePerHour`: The cost of booking the facility per hour. 53 | * `location`: The physical location of the facility. 54 | * `isDeleted`: Boolean indicating if the facility is marked as deleted (false means not deleted). 55 | 56 | 57 | 58 | **Booking Model:** 59 | 60 | * `date`: The date of the booking. 61 | * `startTime`: The start time of the booking. 62 | * `endTime`: The end time of the booking. 63 | * `user`: Reference to the user who made the booking. 64 | * `facility`: Reference to the booked facility. 65 | * `payableAmount`: The calculated amount payable for the booking. 66 | * `isBooked`: Status of the booking (confirmed, unconfirmed, or canceled). 67 | 68 | 69 | 70 | ### Payable Amount Calculation: 71 | 72 | 73 | 74 | Formula: 75 | 76 | 77 | 78 | ```plain 79 | payableAmount = (endTime - startTime) * pricePerHour 80 | ``` 81 | 82 | 83 | 84 | Assume `endTime` and `startTime` are datetime objects and `pricePerHour` is a numeric value representing the cost per hour. 85 | 86 | 87 | 88 | **Example:** 89 | 90 | * Facility's price per hour: 20 TK 91 | * Booking start time: 10:00 AM 92 | * Booking end time: 1:00 PM 93 | 94 | 95 | 96 | Calculation: 97 | 98 | * Duration: 3 hours 99 | * Payable amount: 3 hours \* 20 TK/hour = 60 TK 100 | 101 | 102 | 103 | ### API Endpoints 104 | 105 | 106 | 107 | #### User Routes 108 | 109 | 110 | 111 | 1. **User Sign Up** 112 | * **Route**: `POST /api/auth/signup` 113 | * **Request Body**: 114 | 115 | ```json 116 | { 117 | "name": "Programming Hero", 118 | "email": "web@programming-hero.com", 119 | "password": "programming-hero", 120 | "phone": "01322901105", 121 | "role": "admin", // or 'user' 122 | "address": "Level-4, 34, Awal Centre, Banani, Dhaka" 123 | } 124 | ``` 125 | 126 | * **Response:** 127 | ```json 128 | { 129 | "success": true, 130 | "statusCode": 200, 131 | "message": "User registered successfully", 132 | "data": { 133 | "_id": "60d9c4e4f3b4b544b8b8d1c4", 134 | "name": "Programming Hero", 135 | "email": "web@programming-hero.com", 136 | "role": "admin", 137 | "phone": "01322901105", 138 | "address": "Level-4, 34, Awal Centre, Banani, Dhaka" 139 | } 140 | } 141 | ``` 142 | 143 | 2. **User Login** 144 | * **Route**: `POST /api/auth/login` 145 | * **Request Body**: 146 | 147 | ```json 148 | { 149 | "email": "web@programming-hero.com", 150 | "password": "programming-hero" 151 | } 152 | ``` 153 | 154 | * **Response:** 155 | ```json 156 | { 157 | "success": true, 158 | "statusCode": 200, 159 | "message": "User logged in successfully", 160 | "token": "JWT_TOKEN", 161 | "data": { 162 | "_id": "60d9c4e4f3b4b544b8b8d1c4", 163 | "name": "Programming Hero", 164 | "email": "web@programming-hero.com", 165 | "role": "admin", 166 | "phone": "01322901105", 167 | "address": "Level-4, 34, Awal Centre, Ban Myeni, Dhaka" 168 | } 169 | } 170 | ``` 171 | 172 | 173 | 174 | 175 | 3. **Create a Facility (Admin Only)** 176 | * **Route**: `POST /api/facility` 177 | * **Headers**: 178 | 179 | ```plain 180 | Authorization: Bearer JWT_TOKEN 181 | ``` 182 | 183 | ```json 184 | { 185 | "name": "Tennis Court", 186 | "description": "Outdoor tennis court with synthetic surface.", 187 | "pricePerHour": 30, 188 | "location": "456 Sports Ave, Springfield" 189 | } 190 | ``` 191 | 192 | ```json 193 | { 194 | "success": true, 195 | "statusCode": 200, 196 | "message": "Facility added successfully", 197 | "data": { 198 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 199 | "name": "Tennis Court", 200 | "description": "Outdoor tennis court with synthetic surface.", 201 | "pricePerHour": 30, 202 | "location": "456 Sports Ave, Springfield", 203 | "isDeleted": false 204 | } 205 | } 206 | ``` 207 | 208 | 209 | 210 | 211 | 4. **Update a Facility (Admin Only)** 212 | * **Route**: `PUT /api/facility/:id` 213 | * **Headers**: 214 | 215 | ```plain 216 | Authorization: Bearer JWT_TOKEN 217 | ``` 218 | 219 | ```json 220 | { 221 | "name": "Updated Tennis Court", 222 | "description": "Updated outdoor tennis court with synthetic surface.", 223 | "pricePerHour": 35, 224 | "location": "789 Sports Ave, Springfield" 225 | } 226 | ``` 227 | 228 | * **Response** 229 | ```json 230 | { 231 | "success": true, 232 | "statusCode": 200, 233 | "message": "Facility updated successfully", 234 | "data": { 235 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 236 | "name": "Updated Tennis Court", 237 | "description": "Updated outdoor tennis court with synthetic surface.", 238 | "pricePerHour": 35, 239 | "location": "789 Sports Ave, Springfield", 240 | "isDeleted": false 241 | } 242 | } 243 | ``` 244 | 245 | 5. **Delete a Facility - Soft Delete (Admin Only)** 246 | * **Route**: `DELETE /api/facility/:id` 247 | * **Headers**: 248 | 249 | ```plain 250 | Authorization: Bearer JWT_TOKEN 251 | ``` 252 | 253 | * **Response**: 254 | 255 | ```json 256 | { 257 | "success": true, 258 | "statusCode": 200, 259 | "message": "Facility deleted successfully", 260 | "data": { 261 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 262 | "name": "Updated Tennis Court", 263 | "description": "Updated outdoor tennis court with synthetic surface.", 264 | "pricePerHour": 35, 265 | "location": "789 Sports Ave, Springfield", 266 | "isDeleted": true 267 | } 268 | } 269 | 270 | ``` 271 | 272 | **6\. Get All Facilities** 273 | 274 | * **Route**: `GET /api/facility` 275 | * **Response**: 276 | 277 | ```json 278 | { 279 | "success": true, 280 | "statusCode": 200, 281 | "message": "Facilities retrieved successfully", 282 | "data": [ 283 | { 284 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 285 | "name": "Tennis Court", 286 | "description": "Outdoor tennis court with synthetic surface.", 287 | "pricePerHour": 30, 288 | "location": "456 Sports Ave, Springfield", 289 | "isDeleted": false 290 | } 291 | ] 292 | } 293 | ``` 294 | 295 | 296 | 297 | #### Booking Routes 298 | 299 | 300 | ### 7\. Check Availability 301 | 302 | Check the availability of time slots for booking on a specific date. 303 | 304 | * **Route**: `GET /api/check-availability` 305 | 306 | #### Query Parameters 307 | 308 | * **date** (`string`, optional): The date for which availability is to be checked. Format: `YYYY-MM-DD`. If not provided, today's date will be used by default. 309 | 310 | #### Response 311 | 312 | * **success** (`boolean`): Indicates whether the request was successful. 313 | * **statusCode** (`number`): HTTP status code of the response. 314 | * **message** (`string`): Descriptive message indicating the outcome of the request. 315 | * **data** (`Array` of `Object`): Array containing information about available time slots. 316 | 317 | ##### Time Slot Object 318 | 319 | * **startTime** (`string`): The start time of the available slot. 320 | * **endTime** (`string`): The end time of the available slot. 321 | 322 | #### Example Request 323 | 324 | ```sql 325 | GET /api/check-availability?date=2024-06-15 326 | ``` 327 | 328 | #### Example Response 329 | 330 | ```json 331 | { 332 | "success": true, 333 | "statusCode": 200, 334 | "message": "Availability checked successfully", 335 | "data": [ 336 | { 337 | "startTime": "08:00", 338 | "endTime": "10:00" 339 | }, 340 | { 341 | "startTime": "14:00", 342 | "endTime": "16:00" 343 | } 344 | ] 345 | } 346 | ``` 347 | 348 | 349 | 350 | **Hints**: 351 | 352 | * Parse the optional `date` query parameter from the request URL. If not provided, use today's date. 353 | * Retrieve bookings for the specified date from your database using Mongoose. 354 | * Define a function to find available time range based on the bookings retrieved. Compare booked time range to the total available time slots for the day. 355 | * Return a response containing the available time slots in the specified format. Use `res.json()` to send the response. 356 | 357 | 358 | 359 | 360 | 361 | **8\. Create a Booking (User Only)** 362 | 363 | * **Route**: `POST /api/bookings` 364 | * **Headers**: 365 | 366 | ```plain 367 | Authorization: Bearer JWT_TOKEN 368 | ``` 369 | 370 | ```json 371 | { 372 | "facility": "60d9c4e4f3b4b544b8b8d1c5", 373 | "date": "2024-06-15", 374 | "startTime": "10:00", 375 | "endTime": "13:00" 376 | } 377 | ``` 378 | * **Response:** 379 | ```json 380 | { 381 | "success": true, 382 | "statusCode": 200, 383 | "message": "Booking created successfully", 384 | "data": { 385 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 386 | "facility": "60d9c4e4f3b4b544b8b8d1c5", 387 | "date": "2024-06-15", 388 | "startTime": "10:00", 389 | "endTime": "13:00", 390 | "user": "60d9c4e4f3b4b544b8b8d1c4", 391 | "payableAmount": 90, 392 | "isBooked": "confirmed" 393 | } 394 | } 395 | ``` 396 | 397 | `If the facility is unavailable during the requested time slot, an error response is returned.` 398 | 399 | **9\. View All Bookings (Admin Only)** 400 | 401 | * **Route**: `GET /api/bookings` 402 | * **Headers**: 403 | 404 | ```plain 405 | Authorization: Bearer JWT_TOKEN 406 | ``` 407 | 408 | * **Response:** 409 | ```json 410 | { 411 | "success": true, 412 | "statusCode": 200, 413 | "message": "Bookings retrieved successfully", 414 | "data": [ 415 | { 416 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 417 | "facility": { 418 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 419 | "name": "Tennis Court", 420 | "description": "Outdoor tennis court with professional-grade surface.", 421 | "pricePerHour": 30, 422 | "location": "123 Main Street", 423 | "isDeleted": false 424 | }, 425 | "date": "2024-06-15", 426 | "startTime": "10:00", 427 | "endTime": "13:00", 428 | "user": { 429 | "_id": "60d9c4e4f3b4b544b8b8d1c4", 430 | "name": "Programming Hero", 431 | "email": "programming.hero@example.com", 432 | "phone": "+1234567890", 433 | "role": "user", 434 | "address": "456 Elm Street" 435 | }, 436 | "payableAmount": 90, 437 | "isBooked": " confirmed" 438 | } 439 | ] 440 | } 441 | ``` 442 | 443 | **10\. View Bookings by User (User Only)** 444 | 445 | * **Route**: `GET /api/bookings/user` 446 | * **Headers**: 447 | 448 | ```plain 449 | Authorization: Bearer JWT_TOKEN 450 | ``` 451 | 452 | ```json 453 | { 454 | "success": true, 455 | "statusCode": 200, 456 | "message": "Bookings retrieved successfully", 457 | "data": [ 458 | { 459 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 460 | "facility": { 461 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 462 | "name": "Tennis Court", 463 | "description": "Outdoor tennis court with professional-grade surface.", 464 | "pricePerHour": 30, 465 | "location": "123 Main Street", 466 | "isDeleted": false 467 | }, 468 | "date": "2024-06-15", 469 | "startTime": "10:00", 470 | "endTime": "13:00", 471 | "user": "60d9c4e4f3b4b544b8b8d1c4", 472 | "payableAmount": 90, 473 | "isBooked": " confirmed" 474 | } 475 | ] 476 | } 477 | ``` 478 | 479 | **11\. Cancel a Booking (User Only)** 480 | 481 | * **Route**: `DELETE /api/bookings/:id` 482 | * **Headers**: 483 | 484 | ```plain 485 | Authorization: Bearer JWT_TOKEN 486 | ``` 487 | 488 | ```json 489 | { 490 | "success": true, 491 | "statusCode": 200, 492 | "message": "Booking cancelled successfully", 493 | "data": { 494 | "_id": "60d9c4e4f3b4b544b8b8d1c6", 495 | "facility": { 496 | "_id": "60d9c4e4f3b4b544b8b8d1c5", 497 | "name": "Tennis Court", 498 | "description": "Outdoor tennis court with professional-grade surface.", 499 | "pricePerHour": 30, 500 | "location": "123 Main Street", 501 | "isDeleted": false 502 | }, 503 | "date": "2024-06-15", 504 | "startTime": "10:00", 505 | "endTime": "13:00", 506 | "user": "60d9c4e4f3b4b544b8b8d1c4", 507 | "payableAmount": 90, 508 | "isBooked": "canceled" 509 | } 510 | } 511 | ``` 512 | 513 | 514 | 515 | ## Bonus Part (10 Marks): 516 | 517 | ### **1\. No Data Found:** 518 | 519 | When retrieving data, if the database collection is empty or no matching data is found, return the message: "No data found." 520 | 521 | ```json 522 | { 523 | "success": false, 524 | "statusCode": 404, 525 | "message": "No Data Found", 526 | "data":[] 527 | } 528 | ``` 529 | 530 | ### **2.Error Handling:** 531 | 532 | Implement proper error handling throughout the application. Use global error handling `middleware` to catch and handle errors, providing appropriate error responses with error messages. 533 | 534 | **Error Response Object Should include the following properties:** 535 | 536 | * success → false 537 | * message → Error Type → Validation Error, Cast Error, Duplicate Entry 538 | * errorMessages 539 | * stack 540 | 541 | 542 | 543 | **Sample Error Response** 544 | 545 | ```json 546 | { 547 | "success": false, 548 | "message": "E11000 duplicate key error collection: univerity-management.students index: email_1 dup key: { email: \\"user2@gmail.com\\" }", 549 | "errorMessages": [ 550 | { 551 | "path": "", 552 | "message": "E11000 duplicate key error collection: project index: email_1 dup key: { email: \\"user2@gmail.com\\" }" 553 | } 554 | ], 555 | "stack": "MongoServerError: E11000 duplicate key error collection: project index: email_1 dup key: { email: \\"user2@gmail.com\\" }\\n at H:\\\\next-level-development\\\\project-management-auth-service\\\\node_modules\\\\mongodb\\\\src\\\\operations\\\\insert.ts:85:25\\n at H:\\\\next-level-development\\\\university-management-auth-service\\\\node_modules\\\\mongodb\\\\src\\\\cmap\\\\connection_pool.ts:574:11\\n at H:\\\\next-level-development\\\\university-writeOrBuffer (node:internal/streams/writable:391:12)" 556 | } 557 | ``` 558 | 559 | ### 560 | 561 | ### **3\. Not Found Route:** 562 | 563 | Implement a global "Not Found" handler for unmatched routes. When a route is not found, respond with a generic message: "Not Found.” 564 | 565 | ```json 566 | { 567 | "success": false, 568 | "statusCode": 404, 569 | "message": "Not Found", 570 | } 571 | ``` 572 | 573 | ### **4\. Authentication Middleware:** 574 | 575 | Implement an Authentication Middleware to authenticate your application. Ensures that only user and admin can access their own accessible routes. 576 | 577 | ```json 578 | { 579 | "success": false, 580 | "statusCode": 401, 581 | "message": "You have no access to this route", 582 | } 583 | ``` 584 | 585 | ### **5\. Zod Validation:** 586 | The API employs Zod for input validation, ensuring data consistency. When validation fails, a 400 Bad Request status code is returned, accompanied by detailed error messages specifying the erroneous fields and reasons. 587 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![Assignment-3](https://t4.ftcdn.net/jpg/03/06/88/29/360_F_306882952_XYkVbLIWubBrEyg5vvjcKisffe8CsuZG.jpg) 3 | 4 |

5 | Assignment 3 🚀 6 |

7 |

8 | (Batch-3) 9 |

10 | 11 | 🎉 **Welcome to Assignment 3 !!** 12 | 13 | ## 🛠️ Technology You Can Use 14 | 15 | - **Backend Development:** 16 | - Node.js 17 | - Express.js 18 | - Mongoose 19 | - TypeScript 20 | 21 | - **Package Management:** 22 | - Any npm packages that are required for your project 23 | 24 | - **Additional Technologies:** 25 | - Feel free to incorporate any additional technologies that you deem necessary for your project. 26 | 27 | --- 28 | 29 | ## 📤 **Submission Guidelines:** 30 | 31 | - Submit a well-documented codebase with clear comments. 32 | - Make sure to add a README file that explains how to set up and use the application. In the README, include details like the project name, live URL, features, technology used, and other important information. Try to make it look professional by doing some research and making it appealing. 33 | 34 | ## What You Need to Provide: 35 | 36 | 1. Live Deployment Link (Server) 37 | 38 | 2. GitHub Repository Links (Server) 39 | 40 | 3. Project Overview Video. (A brief video providing an overview of the project.) 41 | 42 | 43 | ## Assignment Number based on the Last Digit of PH Student ID: 44 | 45 | | The last Digit of PH Student ID | Assignment Number (Design No) | 46 | | ------------------------------- | ------------------------------ | 47 | | 0, 1 | [SET-1](./1-car-washing-system.md) | 48 | | 2, 3 | [SET-2](./2-meeting-room-booking-system.md) | 49 | | 4, 5 | [SET-3](./3-car-rental-reservation-system.md) | 50 | | 6, 7 | [SET-4](./4-bike-rental-service.md) | 51 | | 8, 9 | [SET-5](./5-sports-facility-booking-platform.md) | 52 | 53 | ### ⏰ **Deadline:** 54 | 55 | - 60 Marks: June 15, 2024, 11.59 PM (4 days) 56 | - 50 Marks: June 22, 2024, 11.59 PM (7 days) 57 | - After June 22, 30 Marks Deadline 58 | 59 | ## 🚫 **Important Note:** 60 | 61 | Plagiarism will not be tolerated. Ensure that the code you submit is your work. Any instances of plagiarism will result in 0 Marks. 62 | 63 | --- 64 | 65 | By following these instructions, you'll be well-equipped to complete Assignment 3 successfully. Good luck! 🍀 66 | 67 | 68 | --------------------------------------------------------------------------------