private def lookup()

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)
      }
    }
  }