in membership-attribute-service/app/controllers/AttributeController.scala [94:187]
private def lookup(
endpointDescription: String,
onSuccessMember: Attributes => Result,
onSuccessSupporter: Attributes => Result,
onNotFound: Result,
sendAttributesIfNotFound: Boolean = false,
requiredScopes: List[AccessScope],
metricName: String,
useBatchedMetrics: Boolean = false,
): Action[AnyContent] = {
AuthorizeForScopes(requiredScopes).async { implicit request =>
import request.logPrefix
val future = {
if (endpointDescription == "membership" || endpointDescription == "features") {
DeprecatedRequestLogger.logDeprecatedRequest(request)
}
val user = request.user
// execute futures outside of the for comprehension so they are executed in parallel rather than in sequence
val futureSupporterAttributes = getSupporterProductDataAttributes(user.identityId)(request)
val futureOneOffContribution = getLatestOneOffContributionDate(user.identityId, user)
val futureMobileSubscriptionStatus = getLatestMobileSubscription(user.identityId)
(for {
// Fetch one-off data independently of zuora data so that we can handle users with no zuora record
(fromWhere: String, supporterAttributes: Option[Attributes]) <- futureSupporterAttributes
latestOneOffDate: Option[LocalDate] <- futureOneOffContribution
latestMobileSubscription: Option[MobileSubscriptionStatus] <- futureMobileSubscriptionStatus
supporterOrStaffAttributes: Option[Attributes] = maybeAllowAccessToDigipackForGuardianEmployees(
// transforming to Option here because type of failure is no longer relevant at this point
request.user,
supporterAttributes,
user.identityId,
)
allProductAttributes: Option[Attributes] = supporterOrStaffAttributes.map(
addOneOffAndMobile(_, latestOneOffDate, latestMobileSubscription),
)
} yield {
val result = allProductAttributes match {
case Some(attrs @ Attributes(_, Some(tier), _, _, _, _, _, _, _, _, _, _, _, _)) =>
logger.info(
s"${user.identityId} is a $tier member - $endpointDescription - $attrs found via $fromWhere",
)
onSuccessMember(attrs).withHeaders(
"X-Gu-Membership-Tier" -> tier,
"X-Gu-Membership-Is-Paid-Tier" -> attrs.isPaidTier.toString,
)
case Some(attrs) =>
attrs.DigitalSubscriptionExpiryDate.foreach { date =>
logger.info(s"${user.identityId} is a digital subscriber expiring $date")
}
attrs.PaperSubscriptionExpiryDate.foreach { date =>
logger.info(s"${user.identityId} is a paper subscriber expiring $date")
}
attrs.GuardianWeeklySubscriptionExpiryDate.foreach { date =>
logger.info(
s"${user.identityId} is a Guardian Weekly subscriber expiring $date",
)
}
attrs.GuardianPatronExpiryDate.foreach { date =>
logger.info(s"${user.identityId} is a Guardian Patron expiring $date")
}
attrs.RecurringContributionPaymentPlan.foreach { paymentPlan =>
logger.info(s"${user.identityId} is a regular $paymentPlan contributor")
}
logger.info(s"${user.identityId} supports the guardian - $attrs - found via $fromWhere")
onSuccessSupporter(attrs)
case None if sendAttributesIfNotFound =>
val attr = addOneOffAndMobile(Attributes(user.identityId), latestOneOffDate, latestMobileSubscription)
logger.info(s"${user.identityId} does not have zuora attributes - $attr - found via $fromWhere")
Ok(Json.toJson(attr))
case _ =>
onNotFound
}
addGuIdentityHeaders.fromUser(result, user)
}).recover { case e =>
// This branch indicates a serious error to be investigated ASAP, because it likely means we could not
// serve from either Zuora or DynamoDB cache. Likely multi-system outage in progress or logic error.
val errMsg = scrub"Failed to serve entitlements either from cache or directly. Urgently notify Retention team: $e"
metrics.incrementCount(s"$endpointDescription-failed-to-serve-entitlements")
logger.error(errMsg, e)
InternalServerError("failed to serve entitlements")
}
}
if (useBatchedMetrics) {
batchedMetrics.incrementCount(metricName)
future
} else {
metrics.measureDuration(metricName)(future)
}
}
}