app/com/gu/memsub/subsv2/reads/CatJsonReads.scala (78 lines of code) (raw):
package com.gu.memsub.subsv2.reads
import com.gu.memsub.Subscription.{ProductId, ProductRatePlanChargeId, ProductRatePlanId}
import com.gu.memsub._
import com.gu.memsub.subsv2._
import com.gu.memsub.subsv2.reads.CommonReads._
import play.api.libs.functional.syntax._
import play.api.libs.json._
import scalaz.std.list._
import scalaz.syntax.traverse._
object CatJsonReads {
implicit val catalogZuoraPlanChargeReads: Reads[ZuoraCharge] = (
(__ \ "id").read[String].map(ProductRatePlanChargeId) and
(__ \ "pricingSummary").read[PricingSummary] and
(__ \ "billingPeriod").readNullable[ZBillingPeriod] and
(__ \ "specificBillingPeriod").readNullable[Int] and
(__ \ "model").read[String] and
(__ \ "name").read[String] and
(__ \ "type").read[String] and
(__ \ "endDateCondition").read[EndDateCondition] and
(__ \ "upToPeriods").readNullable[Int] and
(__ \ "upToPeriodsType").readNullable[UpToPeriodsType]
) (ZuoraCharge.apply(_, _, _, _, _, _, _, _, _, _))
implicit val ProductReads = new Reads[Benefit] {
override def reads(json: JsValue): JsResult[Benefit] = json match {
case JsString(id) => Benefit.fromId(id).fold[JsResult[Benefit]](JsError(s"Bad product $id"))(e => JsSuccess(e))
case a => JsError(s"Malformed product JSON, needed a string but got $a")
}
}
implicit val catalogZuoraPlanBenefitReads: Reads[(ProductRatePlanChargeId, Benefit)] = (
(__ \ "id").read[String].map(ProductRatePlanChargeId) and
(__ \ "ProductType__c").read[Benefit]
) (_ -> _)
implicit val listOfProductsReads = new Reads[Map[ProductRatePlanChargeId, Benefit]] {
override def reads(json: JsValue): JsResult[Map[ProductRatePlanChargeId, Benefit]] = json match {
case JsArray(vals) => vals
.map(_.validate[(ProductRatePlanChargeId, Benefit)])
.filter(_.isSuccess).toList // bad things are happening here, we're chucking away errors
.sequence[JsResult, (ProductRatePlanChargeId, Benefit)]
.map(_.toMap)
case _ => JsError("No valid benefits found")
}
}
implicit val statusReads: Reads[Status] = new Reads[Status] {
override def reads(json: JsValue): JsResult[Status] = json match {
case JsString("Expired") => JsSuccess(Status.legacy)
case JsString("Active") => JsSuccess(Status.current)
case JsString("NotStarted") => JsSuccess(Status.upcoming)
case a => JsError(s"Unknown status $a")
}
}
def catalogZuoraPlanReads(productType: Option[String], pid: ProductId): Reads[CatalogZuoraPlan] =
(json: JsValue) => {
((__ \ "id").read[String].map(ProductRatePlanId) and
(__ \ "name").read[String] and
(__ \ "description").readNullable[String].map(_.mkString) and
Reads.pure(pid) and
(__ \ "Saving__c").readNullable[String] and
(__ \ "productRatePlanCharges").read[List[ZuoraCharge]](niceListReads(catalogZuoraPlanChargeReads)) and
(__ \ "productRatePlanCharges").read[Map[ProductRatePlanChargeId, Benefit]](listOfProductsReads) and
(__ \ "status").read[Status] and
(__ \ "FrontendId__c").readNullable[String].map(_.flatMap(FrontendId.get)) and
Reads.pure(productType)
) (CatalogZuoraPlan.apply _).reads(json)
}
implicit val catalogZuoraPlanListReads: Reads[List[CatalogZuoraPlan]] =
(json: JsValue) =>
json \ "products" match {
case JsDefined(JsArray(products)) =>
products.toList.map { product =>
val productId = (product \ "id").as[String]
val productType = (product \ "ProductType__c").asOpt[String]
val reads = catalogZuoraPlanReads(productType, ProductId(productId))
(product \ "productRatePlans").validate[List[CatalogZuoraPlan]](niceListReads(reads))
}
.filter(_.isSuccess).sequence[JsResult, List[CatalogZuoraPlan]].map(_.flatten)
case a => JsError(s"No product array found, got $a")
}
}