diff --git a/elastic4play/app/org/elastic4play/JsonFormat.scala b/elastic4play/app/org/elastic4play/JsonFormat.scala index fe64d8230..885a2939a 100644 --- a/elastic4play/app/org/elastic4play/JsonFormat.scala +++ b/elastic4play/app/org/elastic4play/JsonFormat.scala @@ -4,6 +4,8 @@ import java.util.Date import scala.util.{Failure, Success, Try} +import scala.reflect.ClassTag + import play.api.libs.json._ import org.elastic4play.controllers.JsonFormat.inputValueFormat @@ -14,38 +16,31 @@ object JsonFormat { private val dateWrites: Writes[Date] = Writes[Date](d => JsNumber(d.getTime)) implicit val dateFormat: Format[Date] = Format(dateReads, dateWrites) - private val invalidFormatAttributeErrorWrites = Writes[InvalidFormatAttributeError] { ifae => - Json.writes[InvalidFormatAttributeError].writes(ifae) + - ("type" -> JsString("InvalidFormatAttributeError")) + - ("message" -> JsString(ifae.toString)) - } - private val unknownAttributeErrorWrites = Writes[UnknownAttributeError] { uae => - Json.writes[UnknownAttributeError].writes(uae) + - ("type" -> JsString("UnknownAttributeError")) + - ("message" -> JsString(uae.toString)) - } - private val updateReadOnlyAttributeErrorWrites = Writes[UpdateReadOnlyAttributeError] { uroae => - Json.writes[UpdateReadOnlyAttributeError].writes(uroae) + - ("type" -> JsString("UpdateReadOnlyAttributeError")) + - ("message" -> JsString(uroae.toString)) - } - private val missingAttributeErrorWrites = Writes[MissingAttributeError] { mae => - Json.writes[MissingAttributeError].writes(mae) + - ("type" -> JsString("MissingAttributeError")) + - ("message" -> JsString(mae.toString)) + private def attributeErrorWrites[E <: AttributeError](underlying: OWrites[E]): OWrites[E] = { + OWrites.transform(underlying) { (origErr, obj) => + obj + ("message" -> JsString(origErr.toString)) + } } - implicit val attributeCheckingExceptionWrites: OWrites[AttributeCheckingError] = OWrites[AttributeCheckingError] { ace => - Json.obj( - "tableName" -> ace.tableName, - "type" -> "AttributeCheckingError", - "errors" -> JsArray(ace.errors.map { - case e: InvalidFormatAttributeError => invalidFormatAttributeErrorWrites.writes(e) - case e: UnknownAttributeError => unknownAttributeErrorWrites.writes(e) - case e: UpdateReadOnlyAttributeError => updateReadOnlyAttributeErrorWrites.writes(e) - case e: MissingAttributeError => missingAttributeErrorWrites.writes(e) - }) - ) + private[elastic4play] implicit val invalidFormatAttributeErrorWrites: OWrites[InvalidFormatAttributeError] = attributeErrorWrites[InvalidFormatAttributeError](Json.writes[InvalidFormatAttributeError]) + + private[elastic4play] implicit val unknownAttributeErrorWrites: OWrites[UnknownAttributeError] = attributeErrorWrites[UnknownAttributeError](Json.writes[UnknownAttributeError]) + + private[elastic4play] implicit val updateReadOnlyAttributeErrorWrites: OWrites[UpdateReadOnlyAttributeError] = attributeErrorWrites[UpdateReadOnlyAttributeError](Json.writes[UpdateReadOnlyAttributeError]) + + private[elastic4play] implicit val missingAttributeErrorWrites: OWrites[MissingAttributeError] = attributeErrorWrites(Json.writes[MissingAttributeError]) + + implicit val attributeCheckingExceptionWrites: OWrites[AttributeCheckingError] = { + val pkgName = classOf[AttributeError].getPackage.getName + '.' + + implicit def errWrites: OWrites[AttributeError] = Json.configured(JsonConfiguration( + discriminator = "type", + typeNaming = JsonNaming(_.stripPrefix(pkgName)) + )).writes + + val discriminator = "type" -> JsString("AttributeCheckingError") + + Json.writes[AttributeCheckingError].transform(_ + discriminator) } implicit def tryWrites[A](implicit aWrites: Writes[A]): Writes[Try[A]] = Writes[Try[A]] { diff --git a/elastic4play/test/org/elastic4play/JsonFormatSpec.scala b/elastic4play/test/org/elastic4play/JsonFormatSpec.scala new file mode 100644 index 000000000..498cc0563 --- /dev/null +++ b/elastic4play/test/org/elastic4play/JsonFormatSpec.scala @@ -0,0 +1,98 @@ +package org.elastic4play + +import play.api.libs.json._ + +import org.elastic4play.controllers.JsonInputValue + +final class JsonFormatSpec extends org.specs2.mutable.Specification { + "Play JSON format for Elastic".title + + "Format for attribute errors" should { + val invalidFormatAttrErr = InvalidFormatAttributeError( + name = "Test 1", + format = "Foo", + value = JsonInputValue(JsString("Input"))) + + val invalidFormatAttrErrJson = Json.obj( + "name" -> "Test 1", + "format" -> "Foo", + "value" -> Json.obj("type" -> "JsonInputValue", "value" -> "Input"), + "message" -> "Invalid format for Test 1: JsonInputValue(\"Input\"), expected Foo") + + "support InvalidFormatAttributeError" in { + implicit def w: OWrites[InvalidFormatAttributeError] = + JsonFormat.invalidFormatAttributeErrorWrites + + Json.toJsObject(invalidFormatAttrErr) must_=== invalidFormatAttrErrJson + } + + val unknownAttrErr = UnknownAttributeError( + name = "Test 2", + value = JsString("Bar")) + + val unknownAttrErrJson = Json.obj( + "name" -> "Test 2", + "value" -> "Bar", + "message" -> "Unknown attribute Test 2: \"Bar\"") + + "support UnknownAttributeError" in { + implicit def w: OWrites[UnknownAttributeError] = + JsonFormat.unknownAttributeErrorWrites + + Json.toJsObject(unknownAttrErr) must_=== unknownAttrErrJson + } + + val updateRoAttrErr = UpdateReadOnlyAttributeError("Lorem") + + val updateRoAttrErrJson = Json.obj( + "name" -> "Lorem", + "message" -> "Attribute Lorem is read-only") + + "support UpdateReadOnlyAttributeError" in { + implicit def w: OWrites[UpdateReadOnlyAttributeError] = + JsonFormat.updateReadOnlyAttributeErrorWrites + + Json.toJsObject(updateRoAttrErr) must_=== updateRoAttrErrJson + } + + val missingAttrErr = MissingAttributeError("Ipsum") + + val missingAttrErrJson = Json.obj( + "name" -> "Ipsum", + "message" -> "Attribute Ipsum is missing") + + "support MissingAttributeError" in { + implicit def w: OWrites[MissingAttributeError] = + JsonFormat.missingAttributeErrorWrites + + Json.toJsObject(missingAttrErr) must_=== missingAttrErrJson + } + + "support AttributeCheckingError" in { + import JsonFormat.attributeCheckingExceptionWrites + + val errors = Seq(invalidFormatAttrErr, unknownAttrErr, updateRoAttrErr, missingAttrErr) + + val errorsJson = Seq( + (invalidFormatAttrErrJson + ("type" -> JsString("InvalidFormatAttributeError"))), + (unknownAttrErrJson + ("type" -> JsString("UnknownAttributeError"))), + (updateRoAttrErrJson + ("type" -> JsString("UpdateReadOnlyAttributeError"))), + (missingAttrErrJson + ("type" -> JsString("MissingAttributeError")))) + + Json.toJsObject(AttributeCheckingError( + tableName = "Table 1", + errors = errors)) must_=== Json.obj( + "tableName" -> "Table 1", + "type" -> "AttributeCheckingError", + "errors" -> errorsJson) and { + // Reverse errors to make sure the order is preserved + Json.toJsObject(AttributeCheckingError( + tableName = "Table 2", + errors = errors.reverse)) must_=== Json.obj( + "tableName" -> "Table 2", + "type" -> "AttributeCheckingError", + "errors" -> errorsJson.reverse) + } + } + } +}