package controllers import javax.inject._ import play.api.mvc._ import play.api.libs.json.{JsValue, JsLookupResult} import models.{Daily} import models.actions.{AuthenticatedUserAction, AuthenticationRequest} import models.exceptions.{ConflictException, NotFoundException, InvalidRequestBodyException, InvalidQueryParameterException} import scala.concurrent.TimeoutException import org.bson.types.ObjectId /** * This controller handles all the Daily endpoints. */ @Singleton class DailyController @Inject()(val controllerComponents: ControllerComponents, authenticatedUserAction: AuthenticatedUserAction) extends BaseController { /** * Create an Action to fetch all the Dailies in the DB. */ def getAll() = authenticatedUserAction { println("DailyController:getAll") try { val result: Seq[Daily] = Daily.getAllDailiesAsync() val jsonResult: JsValue = Daily.toJson(result) Ok(jsonResult) } catch { case _: TimeoutException => BadRequest("Request timed out") case _: Throwable => BadRequest("Exception raised") } } /** * 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) = authenticatedUserAction { 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, questionId: String) = authenticatedUserAction { implicit request: AuthenticationRequest[AnyContent] => 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), new ObjectId(questionId), request.jwt) 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 create a Daily. */ def create() = authenticatedUserAction { implicit request: AuthenticationRequest[AnyContent] => println("DailyController:create") try { 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") } } /** * Create an Action to like a Daily. */ def like() = authenticatedUserAction { implicit request: AuthenticationRequest[AnyContent] => println("DailyController:like") try { val (dailyId, likerId) = fetchLikeRequestBody(request.body) Daily.likeAsync(dailyId, likerId, request.jwt) 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") } } /** * Create an Action to unlike a Daily. */ def unlike() = authenticatedUserAction { implicit request: AuthenticationRequest[AnyContent] => println("DailyController:unlike") try { val (dailyId, likerId) = fetchLikeRequestBody(request.body) Daily.unlikeAsync(dailyId, likerId, request.jwt) 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) } }