diff --git a/src/main/scala/net/yoshinorin/qualtet/domains/search/SearchService.scala b/src/main/scala/net/yoshinorin/qualtet/domains/search/SearchService.scala index 34c20bde..5aa82414 100644 --- a/src/main/scala/net/yoshinorin/qualtet/domains/search/SearchService.scala +++ b/src/main/scala/net/yoshinorin/qualtet/domains/search/SearchService.scala @@ -7,7 +7,7 @@ import cats.implicits.* import net.yoshinorin.qualtet.config.SearchConfig import net.yoshinorin.qualtet.infrastructure.db.Executer import net.yoshinorin.qualtet.message.Fail.UnprocessableEntity -import net.yoshinorin.qualtet.http.{Error => Err} +import net.yoshinorin.qualtet.http.ProblemDetailsError import net.yoshinorin.qualtet.types.Points import net.yoshinorin.qualtet.syntax.* @@ -27,11 +27,11 @@ class SearchService[F[_]: Monad]( private[search] def extractQueryStringsFromQuery(query: Map[String, List[String]]): List[String] = query.getOrElse("q", List()).map(_.trim.toLower) // TODO: move constant values - private[search] def accumurateQueryStringsErrors(queryStrings: List[String]): Seq[Err] = { - val validator: (Boolean, Err) => Option[Err] = (cond, err) => { if cond then Some(err) else None } + private[search] def accumurateQueryStringsErrors(queryStrings: List[String]): Seq[ProblemDetailsError] = { + val validator: (Boolean, ProblemDetailsError) => Option[ProblemDetailsError] = (cond, err) => { if cond then Some(err) else None } val isEmptyError = validator( queryStrings.isEmpty, - Err( + ProblemDetailsError( code = "SEARCH_QUERY_REQUIRED", message = "Search query required." ) @@ -39,7 +39,7 @@ class SearchService[F[_]: Monad]( val tooManySearchWordsError = validator( queryStrings.sizeIs > searchConfig.maxWords, - Err( + ProblemDetailsError( code = "TOO_MANY_SEARCH_WORDS", message = s"Search words must be less than ${searchConfig.maxWords}. You specified ${queryStrings.size}." ) @@ -49,21 +49,21 @@ class SearchService[F[_]: Monad]( List( validator( q.hasIgnoreChars, - Err( + ProblemDetailsError( code = "INVALID_CHARS_INCLUDED", message = s"Contains unusable chars in ${q}" ) ), validator( q.length < searchConfig.minWordLength, - Err( + ProblemDetailsError( code = "SEARCH_CHAR_LENGTH_TOO_SHORT", message = s"${q} is too short. You must be more than ${searchConfig.minWordLength} chars in one word." ) ), validator( q.length > searchConfig.maxWordLength, - Err( + ProblemDetailsError( code = "SEARCH_CHAR_LENGTH_TOO_LONG", message = s"${q} is too long. You must be less than ${searchConfig.maxWordLength} chars in one word." ) diff --git a/src/main/scala/net/yoshinorin/qualtet/http/ResponseProblemDetails.scala b/src/main/scala/net/yoshinorin/qualtet/http/ResponseProblemDetails.scala index 2ed8d7b7..e9d8f081 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/ResponseProblemDetails.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/ResponseProblemDetails.scala @@ -10,19 +10,19 @@ final case class ResponseProblemDetails( status: Int, detail: String, instance: String, - errors: Option[Seq[Error]] = None + errors: Option[Seq[ProblemDetailsError]] = None ) object ResponseProblemDetails { given codecResponseProblemDetails: JsonValueCodec[ResponseProblemDetails] = JsonCodecMaker.make } -final case class Error( +final case class ProblemDetailsError( code: String, message: String ) -object Error { - given codecError: JsonValueCodec[Error] = JsonCodecMaker.make - given codecErrors: JsonValueCodec[Option[Seq[Error]]] = JsonCodecMaker.make +object ProblemDetailsError { + given codecProblemDetailsError: JsonValueCodec[ProblemDetailsError] = JsonCodecMaker.make + given codecProblemDetailsErrors: JsonValueCodec[Option[Seq[ProblemDetailsError]]] = JsonCodecMaker.make } diff --git a/src/main/scala/net/yoshinorin/qualtet/message/Fail.scala b/src/main/scala/net/yoshinorin/qualtet/message/Fail.scala index 2de6cc36..56823bea 100644 --- a/src/main/scala/net/yoshinorin/qualtet/message/Fail.scala +++ b/src/main/scala/net/yoshinorin/qualtet/message/Fail.scala @@ -1,6 +1,6 @@ package net.yoshinorin.qualtet.message -import net.yoshinorin.qualtet.http.{Error => Err} +import net.yoshinorin.qualtet.http.ProblemDetailsError sealed trait Fail extends Exception @@ -9,31 +9,31 @@ object Fail { final case class NotFound( title: String = "Not Found", detail: String, - errors: Option[Seq[Err]] = None + errors: Option[Seq[ProblemDetailsError]] = None ) extends Fail final case class Unauthorized( title: String = "Unauthorized", detail: String = "Unauthorized", // NOTE: no-need to set error details for secure reason. set default value instead. - errors: Option[Seq[Err]] = None + errors: Option[Seq[ProblemDetailsError]] = None ) extends Fail final case class UnprocessableEntity( title: String = "Unprocessable Entity", detail: String, - errors: Option[Seq[Err]] = None + errors: Option[Seq[ProblemDetailsError]] = None ) extends Fail final case class BadRequest( title: String = "Bad Request", detail: String, - errors: Option[Seq[Err]] = None + errors: Option[Seq[ProblemDetailsError]] = None ) extends Fail final case class Forbidden( title: String = "Forbidden", detail: String, - errors: Option[Seq[Err]] = None + errors: Option[Seq[ProblemDetailsError]] = None ) extends Fail final case class InternalServerError( diff --git a/src/test/scala/net/yoshinorin/qualtet/domains/search/SearchServiceSpec.scala b/src/test/scala/net/yoshinorin/qualtet/domains/search/SearchServiceSpec.scala index d77cb80c..6b12f6b6 100644 --- a/src/test/scala/net/yoshinorin/qualtet/domains/search/SearchServiceSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/domains/search/SearchServiceSpec.scala @@ -4,7 +4,7 @@ import net.yoshinorin.qualtet.domains.contents.{Path, RequestContent} import net.yoshinorin.qualtet.domains.robots.Attributes import net.yoshinorin.qualtet.fixture.Fixture.* import net.yoshinorin.qualtet.Modules.* -import net.yoshinorin.qualtet.http.{Error => Err} +import net.yoshinorin.qualtet.http.ProblemDetailsError import org.scalatest.wordspec.AnyWordSpec import org.scalatest.BeforeAndAfterAll import net.yoshinorin.qualtet.message.Fail.UnprocessableEntity @@ -100,7 +100,7 @@ class SearchServiceSpec extends AnyWordSpec with BeforeAndAfterAll { ) ) assert( - result === List(Err(message = "Search query required.", code = "SEARCH_QUERY_REQUIRED")) + result === List(ProblemDetailsError(message = "Search query required.", code = "SEARCH_QUERY_REQUIRED")) ) } @@ -118,13 +118,13 @@ class SearchServiceSpec extends AnyWordSpec with BeforeAndAfterAll { ) assert( result === List( - Err(message = "a is too short. You must be more than 4 chars in one word.", code = "SEARCH_CHAR_LENGTH_TOO_SHORT"), - Err(message = "abc is too short. You must be more than 4 chars in one word.", code = "SEARCH_CHAR_LENGTH_TOO_SHORT"), - Err(message = "Contains unusable chars in d.", code = "INVALID_CHARS_INCLUDED"), - Err(message = "d. is too short. You must be more than 4 chars in one word.", code = "SEARCH_CHAR_LENGTH_TOO_SHORT"), - Err(message = "Contains unusable chars in 1234567890123456", code = "INVALID_CHARS_INCLUDED"), - Err(message = "1234567890123456 is too long. You must be less than 15 chars in one word.", code = "SEARCH_CHAR_LENGTH_TOO_LONG"), - Err(message = "Search words must be less than 3. You specified 7.", code = "TOO_MANY_SEARCH_WORDS") + ProblemDetailsError(message = "a is too short. You must be more than 4 chars in one word.", code = "SEARCH_CHAR_LENGTH_TOO_SHORT"), + ProblemDetailsError(message = "abc is too short. You must be more than 4 chars in one word.", code = "SEARCH_CHAR_LENGTH_TOO_SHORT"), + ProblemDetailsError(message = "Contains unusable chars in d.", code = "INVALID_CHARS_INCLUDED"), + ProblemDetailsError(message = "d. is too short. You must be more than 4 chars in one word.", code = "SEARCH_CHAR_LENGTH_TOO_SHORT"), + ProblemDetailsError(message = "Contains unusable chars in 1234567890123456", code = "INVALID_CHARS_INCLUDED"), + ProblemDetailsError(message = "1234567890123456 is too long. You must be less than 15 chars in one word.", code = "SEARCH_CHAR_LENGTH_TOO_LONG"), + ProblemDetailsError(message = "Search words must be less than 3. You specified 7.", code = "TOO_MANY_SEARCH_WORDS") ) ) }