membership-attribute-service/app/services/SupporterRatePlanToAttributesMapper.scala (351 lines of code) (raw):
package services
import com.gu.memsub.subsv2.Catalog
import com.gu.monitoring.SafeLogger.LogPrefix
import com.gu.monitoring.SafeLogging
import configuration.Stage
import models.{Attributes, DynamoSupporterRatePlanItem}
import org.joda.time.LocalDate
import services.MembershipTier._
import services.SupporterRatePlanToAttributesMapper.productRatePlanMappings
class SupporterRatePlanToAttributesMapper(stage: Stage) extends SafeLogging {
val productRatePlanIdMappings = productRatePlanMappings(stage.value)
def attributesFromSupporterRatePlans(identityId: String, supporterRatePlanItems: List[DynamoSupporterRatePlanItem])(implicit
logPrefix: LogPrefix,
): Option[Attributes] = {
val transformations: List[Attributes => Attributes] = supporterRatePlanItems
.filter(_.termEndDate.isAfter(LocalDate.now.minusDays(1)))
.sortWith((first, second) => first.termEndDate.isBefore(second.termEndDate))
.flatMap(transformationFor)
if (transformations.isEmpty) {
None
} else {
Some(transform(transformations, identityId))
}
}
private def transformationFor(ratePlanItem: DynamoSupporterRatePlanItem)(implicit logPrefix: LogPrefix): Option[Attributes => Attributes] =
productRatePlanIdMappings
.get(ratePlanItem.productRatePlanId)
.orElse(logUnsupportedRatePlanId(ratePlanItem))
.map(transformer => transformer.transform(_, ratePlanItem))
private def logUnsupportedRatePlanId(ratePlanItem: DynamoSupporterRatePlanItem)(implicit logPrefix: LogPrefix): Option[Nothing] = {
// this staff check is needed to tidy up the logs because we have not yet cancelled the staff in zuora
val staffPROD = "2c92a0f949efde7c0149f1f18162178e"
if (ratePlanItem.productRatePlanId != staffPROD)
logger.error(scrub"Unsupported product rate plan id: ${ratePlanItem.productRatePlanId}")
None
}
private def transform(transformations: List[Attributes => Attributes], identityId: String): Attributes =
transformations.foldLeft(Attributes(identityId)) { (attributes, transformation) => transformation(attributes) }
}
object SupporterRatePlanToAttributesMapper {
type Stage = String
type ProductRatePlanId = String
val digitalSubTransformer: AttributeTransformer = (attributes: Attributes, supporterRatePlanItem: DynamoSupporterRatePlanItem) =>
attributes.copy(
DigitalSubscriptionExpiryDate = Some(supporterRatePlanItem.termEndDate),
)
val supporterPlusTransformer: AttributeTransformer = (attributes: Attributes, supporterRatePlanItem: DynamoSupporterRatePlanItem) =>
attributes.copy(
SupporterPlusExpiryDate = Some(supporterRatePlanItem.termEndDate),
)
val supporterPlusV2Transformer: AttributeTransformer = supporterPlusTransformer
val tierThreeTransformer: AttributeTransformer =
(attributes: Attributes, supporterRatePlanItem: DynamoSupporterRatePlanItem) =>
attributes.copy(
SupporterPlusExpiryDate = Some(supporterRatePlanItem.termEndDate),
GuardianWeeklySubscriptionExpiryDate = Some(supporterRatePlanItem.termEndDate),
)
val guardianAdLiteTransformer: AttributeTransformer =
(attributes: Attributes, supporterRatePlanItem: DynamoSupporterRatePlanItem) =>
attributes.copy(
GuardianAdLiteExpiryDate = Some(supporterRatePlanItem.termEndDate),
)
val monthlyContributionTransformer: AttributeTransformer = (attributes: Attributes, item: DynamoSupporterRatePlanItem) =>
attributes.copy(
RecurringContributionPaymentPlan = Some("Monthly Contribution"),
RecurringContributionAcquisitionDate = Some(item.contractEffectiveDate),
)
val annualContributionTransformer: AttributeTransformer = (attributes: Attributes, item: DynamoSupporterRatePlanItem) =>
attributes.copy(
RecurringContributionPaymentPlan = Some("Annual Contribution"),
RecurringContributionAcquisitionDate = Some(item.contractEffectiveDate),
)
val paperTransformer: AttributeTransformer = (attributes: Attributes, supporterRatePlanItem: DynamoSupporterRatePlanItem) =>
attributes.copy(
PaperSubscriptionExpiryDate = Some(supporterRatePlanItem.termEndDate),
)
val paperPlusDigitalTransformer: AttributeTransformer = (attributes: Attributes, supporterRatePlanItem: DynamoSupporterRatePlanItem) =>
attributes.copy(
PaperSubscriptionExpiryDate = Some(supporterRatePlanItem.termEndDate),
DigitalSubscriptionExpiryDate = Some(supporterRatePlanItem.termEndDate),
)
val guardianWeeklyTransformer: AttributeTransformer = (attributes: Attributes, supporterRatePlanItem: DynamoSupporterRatePlanItem) =>
attributes.copy(
GuardianWeeklySubscriptionExpiryDate = Some(supporterRatePlanItem.termEndDate),
)
val guardianPatronTransformer: AttributeTransformer = (attributes: Attributes, supporterRatePlanItem: DynamoSupporterRatePlanItem) =>
attributes.copy(
GuardianPatronExpiryDate = Some(supporterRatePlanItem.termEndDate),
)
private val prodMappings: Map[List[ProductRatePlanId], AttributeTransformer] = Map(
List(Catalog.guardianPatronProductRatePlanId.get) -> guardianPatronTransformer,
List(
"8a12865b8219d9b401822106192b64dc",
"8a12865b8219d9b40182210618a464ba",
) -> supporterPlusTransformer,
List(
"8a128ed885fc6ded018602296ace3eb8",
"8a128ed885fc6ded01860228f77e3d5a",
) -> supporterPlusV2Transformer,
List(
"8a1299788ff2ec100190025fccc32bb1",
"8a1288a38ff2af980190025b32591ccc",
"8a128ab18ff2af9301900255d77979ac",
"8a1299788ff2ec100190024d1e3b1a09",
"8a129c2591f06a5d0191fa2edb383026",
"8a12891291f04b9d0191fa2ffbe10975",
"8a128dfb91f04b9a0191fa30ae2e1b7e",
"8a128dfb91f04b9a0191fa315d091c51",
) -> tierThreeTransformer,
List(
"8a1285e294443da501944b04cb692c9e",
) -> guardianAdLiteTransformer,
List(
"2c92a0fb4edd70c8014edeaa4eae220a",
"2c92a0fb4edd70c8014edeaa4e972204",
"2c92a00d71c96bac0171df3a5622740f",
"2c92a00d779932ef0177a65430d30ac1",
"2c92a00c77992ba70177a6596f710265",
"2c92a0ff73add07f0173b99f14390afc",
"2c92a00773adc09d0173b99e4ded7f45",
"2c92a0fb4edd70c8014edeaa4e8521fe",
) -> digitalSubTransformer,
List("2c92a0fc5aacfadd015ad24db4ff5e97") -> monthlyContributionTransformer,
List("2c92a0fc5e1dc084015e37f58c200eea") -> annualContributionTransformer,
List(
"2c92a00870ec598001710740cdd02fbd",
"2c92a00870ec598001710740d0d83017",
"2c92a00870ec598001710740d24b3022",
"2c92a00870ec598001710740ca532f69",
"2c92a00870ec598001710740c78d2f13",
"2c92a0fd6205707201621f9f6d7e0116",
"2c92a0fe5af9a6b9015b0fe1ecc0116c",
"2c92a0ff56fe33f00157040f9a537f4b",
"2c92a0fd56fe270b0157040e42e536ef",
"2c92a0fd56fe270b0157040dd79b35da",
"2c92a0fd5e1dcf0d015e3cb39d0a7ddb",
"2c92a0ff5af9b657015b0fea5b653f81",
"2c92a0fd5614305c01561dc88f3275be",
"2c92a0ff560d311b0156136f2afe5315",
"2c92a0fd560d13880156136b72e50f0c",
"2c92a0ff56fe33f001572334561765c1",
"2c92a0fd596d321a0159735a7b150e43",
// National Delivery
"8a12999f8a268c57018a27ebfd721883", // Sixday
"8a12999f8a268c57018a27ebe868150c", // Weekend
"8a12999f8a268c57018a27ebe31414a4", // Everyday
) -> paperTransformer,
List(
"2c92a00870ec598001710740ce702ff0",
"2c92a00870ec598001710740cf9e3004",
"2c92a00870ec598001710740c6672ee7",
"2c92a00870ec598001710740c4582ead",
"2c92a00870ec598001710740d3d03035",
"2c92a0fd6205707201621fa1350710e3",
"2c92a0fe56fe33ff0157040d4b824168",
"2c92a0fd56fe26b60157040cdd323f76",
"2c92a0fc56fe26ba0157040c5ea17f6a",
"2c92a0ff56fe33f50157040bbdcf3ae4",
"2c92a0ff6205708e01622484bb2c4613",
"2c92a0fd560d13880156136b8e490f8b",
"2c92a0ff560d311b0156136b9f5c3968",
"2c92a0ff560d311b0156136b697438a9",
"2c92a0fd560d132301560e43cf041a3c",
) -> paperPlusDigitalTransformer,
List(
"2c92a0fe6619b4b601661ab300222651",
"2c92a0ff67cebd140167f0a2f66a12eb",
"2c92a0086619bf8901661ab02752722f",
"2c92a0076dd9892e016df8503e7c6c48",
"2c92a0fe6619b4b901661aa8e66c1692",
"2c92a0ff67cebd0d0167f0a1a834234e",
"2c92a0fe6619b4b301661aa494392ee2",
"2c92a00e6dd988e2016df85387417498",
"2c92a0fd58cf57000158f30ae6d06f2a",
"2c92a0ff58bdf4eb0158f2ecc89c1034",
"2c92a0ff58bdf4ee0158f30905e82181",
"2c92a0fd5a5adc8b015a5c690d0d1ec6",
"2c92a0ff5a4b85e7015a4cf95d352a07",
"2c92a0ff5a5adca9015a611f77db4431",
"2c92a0fc5a2a49f0015a41f473da233a",
"2c92a0fe5a5ad344015a5c67b1144250",
"2c92a0ff59d9d540015a41a40b3e07d3",
"2c92a0fd5a5adc8b015a5c65074b7c41",
"2c92a0ff5a5adca7015a5c4af5963efa",
"2c92a0fe5a5ad349015a5c61d6e05d8d",
"2c92a0fe57d0a0c40157d74240de5543",
"2c92a0ff57d0a0b60157d741e722439a",
"2c92a0ff58bdf4eb0158f307eccf02af",
"2c92a0fc6ae918b6016b080950e96d75",
"2c92a0fc5b42d2c9015b6259f7f40040",
"2c92a0fd57d0a9870157d7412f19424f",
"2c92a0fe57d0a0c40157d74241005544",
"2c92a0ff58bdf4eb0158f307ed0e02be",
"2c92a0fd79ac64b00179ae3f9d474960",
"2c92a0086619bf8901661aaac94257fe",
"2c92a0ff79ac64e30179ae45669b3a83",
"2c92a0086619bf8901661ab545f51b21",
) -> guardianWeeklyTransformer,
List(
"8a129ce886834fa90186a20c3ee70b6a", // 2023 price rise annual
"8a1287c586832d250186a2040b1548fe", // 2023 price rise monthly
"2c92a0f94c547592014c69f5b0ff4f7e",
"2c92a0fb4c5481db014c69f4a1e03bbd",
"2c92a0fb4bb97034014bbbc562114fef",
"2c92a0fb4bb97034014bbbc562604ff7",
) -> memberTransformer(Supporter),
List(
"2c92a0fb4c5481dc014c69f95fce7240",
"2c92a0f94c54758b014c69f813bd39ec",
"2c92a0f9479fb46d0147d0155ca15595",
"2c92a0f9479fb46d0147d0155cb15596",
) -> memberTransformer(Partner),
List(
"2c92a0fb4c5481db014c69fb9118704b",
"2c92a0f94c547592014c69fb0c4274fc",
"2c92a0f9479fb46d0147d0155bf9557a",
"2c92a0f9479fb46d0147d0155c245581",
) -> memberTransformer(Patron),
List("single_contribution") -> singleContributionTransformer,
)
private val codeMappings: Map[List[ProductRatePlanId], AttributeTransformer] = Map(
List(Catalog.guardianPatronProductRatePlanId.get) -> guardianPatronTransformer,
List(
"8ad09fc281de1ce70181de3b251736a4",
"8ad09fc281de1ce70181de3b28ee3783",
) -> supporterPlusTransformer,
List(
"8ad08cbd8586721c01858804e3275376",
"8ad08e1a8586721801858805663f6fab",
) -> supporterPlusV2Transformer,
List(
"8ad097b48ff26452019001cebac92376",
"8ad081dd8ff24a9a019001d95e4e3574",
"8ad081dd8ff24a9a019001df2ce83657",
"8ad097b48ff26452019001e65bbf2ca8",
"8ad097b491daf9180191e0cdf34e185e",
"8ad097b491daf9180191e0cdba5f183c",
"8ad097b491daf9180191e0cd58e5180b",
"8ad081dd91dae1d30191e0ce082d18d3",
) -> tierThreeTransformer,
List(
"71a1bebf6be9444afad446c5ebaf0019",
) -> guardianAdLiteTransformer,
List(
"2c92c0f84bbfec8b014bc655f4852d9d",
"2c92c0f94bbffaaa014bc6a4212e205b",
"2c92c0f971c65dfe0171c6c1f86e603c",
"2c92c0f8778bf8f60177915b477714aa",
"2c92c0f8778bf8cd0177a610cdf230ae",
) -> digitalSubTransformer,
List("2c92c0f85a6b134e015a7fcd9f0c7855") -> monthlyContributionTransformer,
List("2c92c0f85e2d19af015e3896e824092c") -> annualContributionTransformer,
List(
"2c92c0f86fa49142016fa49ea442291b",
"2c92c0f86fa49142016fa49eb0a42a01",
"2c92c0f86fa49142016fa49ea0d028b6",
"2c92c0f86fa49142016fa49e9b9a286f",
"2c92c0f86fa49142016fa49ea56a2938",
"2c92c0f861f9c26d0161fc434bfe004c",
"2c92c0f95aff3b56015b1045fb9332d2",
"2c92c0f8555ce5cf01556e7f01b81b94",
"2c92c0f8555ce5cf01556e7f01771b8a",
"2c92c0f9555cf10501556e84a70440e2",
"2c92c0f961f9cf300161fc4d2e3e3664",
"2c92c0f85aff3453015b1041dfd2317f",
"2c92c0f955c3cf0f0155c5d9df433bf7",
"2c92c0f955c3cf0f0155c5d9ddf13bc5",
"2c92c0f955c3cf0f0155c5d9e2493c43",
// National Delivery
"8ad096ca8992481d018992a363bd17ad", // Everyday
"8ad096ca8992481d018992a36256175e", // Weekend
"8ad096ca8992481d018992a35f60171b", // Sixday
) -> paperTransformer,
List(
"2c92c0f86fa49142016fa49eb1732a39",
"2c92c0f86fa49142016fa49ea90e2976",
"2c92c0f86fa49142016fa49eaecb29dd",
"2c92c0f86fa49142016fa49ea1af28c8",
"2c92c0f86fa49142016fa49eaa492988",
"2c92c0f961f9cf300161fc44f2661258",
"2c92c0f955a0b5bf0155b62623846fc8",
"2c92c0f95aff3b54015b1047efaa2ac3",
"2c92c0f855c3b8190155c585a95e6f5a",
"2c92c0f95aff3b53015b10469bbf5f5f",
"2c92c0f961f9cf300161fc4f71473a34",
"2c92c0f955c3cf0f0155c5d9e83a3cb7",
"2c92c0f95aff3b56015b104aa9a13ea5",
"2c92c0f85aff33ff015b1042d4ba0a05",
"2c92c0f85aff3453015b10496b5e3d17",
) -> paperPlusDigitalTransformer,
List(
"2c92c0f965f2122101660fb33ed24a45",
"2c92c0f967caee410167eff78e7b5244",
"2c92c0f965f2122101660fb81b745a06",
"2c92c0f96df75b5a016df81ba1c62609",
"2c92c0f965d280590165f16b1b9946c2",
"2c92c0f867cae0700167eff921734f7b",
"2c92c0f965dc30640165f150c0956859",
"2c92c0f96ded216a016df491134d4091",
"2c92c0f965f2122101660fbc75a16c38",
"2c92c0f878ac402c0178acb3a90a3620",
"2c92c0f965f212210165f69b94c92d66",
"2c92c0f878ac40300178acaa04bb401d",
) -> guardianWeeklyTransformer,
List(
"2c92c0f94c510a0d014c569ba8eb45f7",
"2c92c0f94c510a01014c569e2d857cfd",
"2c92c0f84b079582014b2754c07c0f7d",
"2c92c0f84b079582014b2754bfd70f6d",
) -> memberTransformer(Supporter),
List(
"2c92c0f94c510a0d014c569a93194575",
"2c92c0f84c510081014c569a18b04e84",
"2c92c0f945fee1c9014605749e450969",
"2c92c0f8471e22bb01471ffe9596366c",
) -> memberTransformer(Partner),
List(
"2c92c0f84c5100b6014c56908a63216d",
"2c92c0f94c510a04014c568d648d097d",
"2c92c0f845fed48301460578277167c3",
"2c92c0f9471e145d01471ffd7c304df9",
) -> memberTransformer(Patron),
List("single_contribution") -> singleContributionTransformer,
)
val productRatePlanMappings: Map[Stage, Map[ProductRatePlanId, AttributeTransformer]] =
Map(
"PROD" -> flatten(prodMappings),
"CODE" -> flatten(codeMappings),
)
private def flatten[A, B](map: Map[List[A], B]): Map[A, B] =
map.flatMap { case (list, value) =>
list.map(_ -> value)
}
private def singleContributionTransformer: AttributeTransformer = (attributes: Attributes, item: DynamoSupporterRatePlanItem) =>
attributes.copy(OneOffContributionDate = Some(item.contractEffectiveDate))
private def memberTransformer(tier: MembershipTier): AttributeTransformer = (attributes: Attributes, _: DynamoSupporterRatePlanItem) =>
attributes.copy(Tier = getMostValuableTier(tier, attributes.Tier))
trait AttributeTransformer {
def transform(attributes: Attributes, supporterRatePlanItem: DynamoSupporterRatePlanItem): Attributes
}
}
sealed abstract class MembershipTier(val name: String, val value: Int)
object MembershipTier {
case object Supporter extends MembershipTier("Supporter", 3)
case object Partner extends MembershipTier("Partner", 4)
case object Patron extends MembershipTier("Patron", 5)
private def fromString(name: String): Option[MembershipTier] =
List(Supporter, Partner, Patron).find(_.name == name)
def getMostValuableTier(newTier: MembershipTier, existingTier: Option[String]) =
if (existingTier.flatMap(fromString).exists(_.value > newTier.value))
existingTier
else
Some(newTier.name)
}