diff --git a/backend-services/feed-service/app/controllers/DailyController.scala b/backend-services/feed-service/app/controllers/DailyController.scala index 4d640bc89d56f77aca2afdd841d26c2cd1ea607a..2ae8ee008eef2e63f94126cb0e0d0596864736ca 100644 --- a/backend-services/feed-service/app/controllers/DailyController.scala +++ b/backend-services/feed-service/app/controllers/DailyController.scala @@ -2,10 +2,10 @@ package controllers import javax.inject._ import play.api.mvc._ -import play.api.libs.json.JsValue +import play.api.libs.json.{JsValue, JsLookupResult} import models.{Daily} -import models.exceptions.{ConflictException, NotFoundException} +import models.exceptions.{ConflictException, NotFoundException, InvalidRequestBodyException, InvalidQueryParameterException} import scala.concurrent.TimeoutException import org.bson.types.ObjectId @@ -34,32 +34,42 @@ class DailyController @Inject()(val controllerComponents: ControllerComponents) /** * Create an Action to fetch the user's Dailies in the DB. + * + * @param userId The ID of the user to get the dailies for. */ def getUserDailies(userId: String) = Action { println("DailyController:getUserDailies") try { + if (!ObjectId.isValid(userId)) throw new InvalidQueryParameterException("Invalid query parameter ID format: userId") + val result: Seq[Daily] = Daily.getUserDailiesAsync(new ObjectId(userId)) val jsonResult: JsValue = Daily.toJson(result) Ok(jsonResult) } catch { case _: TimeoutException => BadRequest("Request timed out") + case ex: InvalidQueryParameterException => BadRequest(ex.getMessage()) case _: Throwable => BadRequest("Exception raised") } } /** * Create an Action to fetch the user's Feed. + * + * @param userId The ID of the user to get the feed for. */ def getUserFeed(userId: String) = Action { println("DailyController:getUserFeed") try { + if (!ObjectId.isValid(userId)) throw new InvalidRequestBodyException("Invalid query parameter ID format: userId") + val result: Seq[Daily] = Daily.getUserFeedAsync(new ObjectId(userId)) val jsonResult: JsValue = Daily.toJson(result) Ok(jsonResult) } catch { case _: TimeoutException => BadRequest("Request timed out") + case ex: InvalidQueryParameterException => BadRequest(ex.getMessage()) case _: Throwable => BadRequest("Exception raised") } } @@ -67,16 +77,18 @@ class DailyController @Inject()(val controllerComponents: ControllerComponents) /** * Create an Action to create a Daily. */ - def create(userId: String, questionId: String, content: String) = Action { + def create() = Action { implicit request: Request[AnyContent] => println("DailyController:create") try { - // Dummy data - val result: Daily = Daily.createDailyAsync(new ObjectId(userId), new ObjectId(questionId), content) + val (userId, questionId, content) = fetchCreateRequestBody(request.body) + + val result: Daily = Daily.createDailyAsync(userId, questionId, content) val jsonResult: JsValue = Daily.toJson(result) Ok(jsonResult) } catch { case _: TimeoutException => BadRequest("Request timed out") + case ex: InvalidRequestBodyException => BadRequest(ex.getMessage()) case _: Throwable => BadRequest("Exception raised") } } @@ -84,15 +96,17 @@ class DailyController @Inject()(val controllerComponents: ControllerComponents) /** * Create an Action to like a Daily. */ - def like(dailyId: String, likerId: String) = Action { + def like() = Action { implicit request: Request[AnyContent] => println("DailyController:like") try { - // Dummy data - Daily.likeAsync(new ObjectId(dailyId), new ObjectId(likerId)) - Ok("Updated") + val (dailyId, likerId) = fetchLikeRequestBody(request.body) + + Daily.likeAsync(dailyId, likerId) + Ok("Daily liked.") } catch { case _: TimeoutException => BadRequest("Request timed out") + case ex: InvalidRequestBodyException => BadRequest(ex.getMessage()) case ex: ConflictException => BadRequest(ex.getMessage()) case ex: NotFoundException => BadRequest(ex.getMessage()) case _: Throwable => BadRequest("Exception raised") @@ -102,18 +116,89 @@ class DailyController @Inject()(val controllerComponents: ControllerComponents) /** * Create an Action to unlike a Daily. */ - def unlike(dailyId: String, likerId: String) = Action { + def unlike() = Action { implicit request: Request[AnyContent] => println("DailyController:unlike") try { - // Dummy data - Daily.unlikeAsync(new ObjectId(dailyId), new ObjectId(likerId)) - Ok("Updated") + val (dailyId, likerId) = fetchLikeRequestBody(request.body) + + Daily.unlikeAsync(dailyId, likerId) + Ok("Daily unliked.") } catch { case _: TimeoutException => BadRequest("Request timed out") + case ex: InvalidRequestBodyException => BadRequest(ex.getMessage()) case ex: ConflictException => BadRequest(ex.getMessage()) case ex: NotFoundException => BadRequest(ex.getMessage()) case _: Throwable => BadRequest("Exception raised") } } + + /** + * Fetch the needed values from the request body for the creating a Daily endpoint. + * + * @param requestBody The request's body. + */ + def fetchCreateRequestBody(requestBody: AnyContent): (ObjectId, ObjectId, String) = { + if (!requestBody.asJson.isDefined) throw new InvalidRequestBodyException("Request body must be in JSON format.") + + val bodyJson = requestBody.asJson.get + + val userId: ObjectId = fetchJsonBodyObjectId(bodyJson, "userId") + val questionId: ObjectId = fetchJsonBodyObjectId(bodyJson, "questionId") + val content: String = fetchJsonBodyString(bodyJson, "content") + + (userId, questionId, content) + } + + /** + * Fetch the needed values from the request body for the liking/unliking a Daily endpoint. + * + * @param requestBody The request's body. + */ + def fetchLikeRequestBody(requestBody: AnyContent): (ObjectId, ObjectId) = { + if (!requestBody.asJson.isDefined) throw new InvalidRequestBodyException("Request body must be in JSON format.") + + val bodyJson = requestBody.asJson.get + + val dailyId: ObjectId = fetchJsonBodyObjectId(bodyJson, "dailyId") + val likerId: ObjectId = fetchJsonBodyObjectId(bodyJson, "likerId") + + (dailyId, likerId) + } + + /** + * Fetch the value of the given field name from the JSON. + * + * @param bodyJson The JSON. + * @param fieldName The field name. + */ + def fetchJsonBodyValue(bodyJson: JsValue, fieldName: String): JsValue = { + val value: JsLookupResult = (bodyJson \ fieldName) + if (!value.isDefined) throw new InvalidRequestBodyException("Missing parameter: " + fieldName) + value.get + } + + /** + * Fetch the String value of the field name from the JSON. + * + * @param bodyJson The JSON. + * @param fieldName The field name. + */ + def fetchJsonBodyString(bodyJson: JsValue, fieldName: String): String = { + fetchJsonBodyValue(bodyJson, fieldName).as[String] + } + + /** + * Fetch the ObjectId value of the field name from the JSON. + * + * @param bodyJson The JSON. + * @param fieldName The field name. + * + * @throws InvalidRequestBodyException if the value is not a valid ID. + */ + def fetchJsonBodyObjectId(bodyJson: JsValue, fieldName: String): ObjectId = { + val value: String = fetchJsonBodyValue(bodyJson, fieldName).as[String] + if (!ObjectId.isValid(value)) throw new InvalidRequestBodyException("Invalid ID format: " + fieldName) + new ObjectId(value) + } } diff --git a/backend-services/feed-service/app/models/exceptions/InvalidObjectIdValueException.scala b/backend-services/feed-service/app/models/exceptions/InvalidObjectIdValueException.scala new file mode 100644 index 0000000000000000000000000000000000000000..582699b04c3ba49a911bd3ba544071d98b87afd2 --- /dev/null +++ b/backend-services/feed-service/app/models/exceptions/InvalidObjectIdValueException.scala @@ -0,0 +1,3 @@ +package models.exceptions + +case class InvalidObjectIdValueException(message: String) extends Exception(message) diff --git a/backend-services/feed-service/app/models/exceptions/InvalidQueryParameterException.scala b/backend-services/feed-service/app/models/exceptions/InvalidQueryParameterException.scala new file mode 100644 index 0000000000000000000000000000000000000000..7acff65ebbbea9e3a2b960db92535a160a270826 --- /dev/null +++ b/backend-services/feed-service/app/models/exceptions/InvalidQueryParameterException.scala @@ -0,0 +1,3 @@ +package models.exceptions + +case class InvalidQueryParameterException(message: String) extends Exception(message) diff --git a/backend-services/feed-service/app/models/exceptions/InvalidRequestBodyException.scala b/backend-services/feed-service/app/models/exceptions/InvalidRequestBodyException.scala new file mode 100644 index 0000000000000000000000000000000000000000..744142c69e270ad8edff0ecdad6880502552fd7e --- /dev/null +++ b/backend-services/feed-service/app/models/exceptions/InvalidRequestBodyException.scala @@ -0,0 +1,3 @@ +package models.exceptions + +case class InvalidRequestBodyException(message: String) extends Exception(message) diff --git a/backend-services/feed-service/conf/routes b/backend-services/feed-service/conf/routes index d826e757c1a1fdb726b6ad6c7408f616f7413c5e..e0476f4814c7d61632f58fd9bfe31f75305873e3 100644 --- a/backend-services/feed-service/conf/routes +++ b/backend-services/feed-service/conf/routes @@ -15,8 +15,8 @@ GET /daily/users controllers.DailyController.getUserDailies(userId: St GET /feed controllers.DailyController.getUserFeed(userId: String) -POST /daily/create controllers.DailyController.create(userId: String, questionId: String, content: String) +POST /daily/create controllers.DailyController.create() -PUT /daily/like controllers.DailyController.like(dailyId: String, likerId: String) +PUT /daily/like controllers.DailyController.like() -PUT /daily/unlike controllers.DailyController.unlike(dailyId: String, likerId: String) +PUT /daily/unlike controllers.DailyController.unlike()