Пользовательский кодек Tapir

Я застрял в каком-то месте, использую scala, tapir и circe.

sealed abstract class S1Error extends Product with Serializable
object S1Error {
  final case class SError(error: SMError) extends S1Error
}
sealed abstract class SMError(message: String)
object SMError {
  final case class SVError(message: String) extends SMError(message)
}

Для ошибки тапира, но я использую это

val schemaVersionError: EndpointOutput.StatusMapping[SError] = statusMappingValueMatcher(
      StatusCode.BadRequest,
      jsonBody[SError]
        .description("XXXX.")
    ) {
      case SMError(SVError(_)) => true
      case _                                  => false
    }

Теперь из-за этой структуры я получаю результат API

{
    "error": {
        "SVError": {
            "message": "XXXXG"
        }
    }
}

где в идеале я хотел бы получить ответ как

"message": "XXXXG"

Не могу изменить структуру ошибки. Есть ли способ обернуть эту ошибку с помощью специального кодека, чтобы получить требуемый результат.

1 ответ

Решение

Кодек Tapir является производным от декодера и кодировщика Circe.

Вы видите способ кодирования классов case по умолчанию с помощью circe.

Circe предоставляет возможность кодировать классы случаев так, как вы описали с помощью deriveUnwrappedEncoderот https://github.com/circe/circe-generic-extras. К сожалению, он не компилируется дляSMError (вероятно, механизм деривации запутан вашей иерархией абстрактных классов).

Вы можете просто создать кодировщик вручную:

sealed abstract class S1Error extends Product with Serializable

object S1Error {
  final case class SError(error: SMError) extends S1Error

  implicit val encoder: Encoder[SError] = Encoder[SMError].contramap(_.error)
  // or you can use deriveUnwrappedEncoder from circe-generic-extras:
  // implicit val encoder: Encoder[SError] = deriveUnwrappedEncoder
}

//I also needed to make message a field in SMError
sealed abstract class SMError(val message: String)
object SMError {
  final case class SVError(override val message: String) extends SMError(message)

  implicit val encoder: Encoder[SMError] = Encoder.encodeJsonObject.contramap{s => JsonObject("message" -> s.message.asJson)}
}

Ответ теперь выглядит так:

{
    message": "XXXXG"
}
Другие вопросы по тегам