diff --git a/membership-attribute-service/app/controllers/AccountController.scala b/membership-attribute-service/app/controllers/AccountController.scala new file mode 100644 index 00000000..e50e51d6 --- /dev/null +++ b/membership-attribute-service/app/controllers/AccountController.scala @@ -0,0 +1,59 @@ +package controllers +import play.api.libs.concurrent.Execution.Implicits._ +import services.{IdentityAuthService, AuthenticationService} +import com.gu.memsub._ +import json.PaymentCardUpdateResultWriters._ +import com.gu.services.model.PaymentDetails +import configuration.Config +import models.ApiErrors._ +import play.api.data.Form +import play.api.data.Forms._ +import play.api.libs.json.Json +import play.api.mvc.Results._ +import play.filters.cors.CORSActionBuilder +import scalaz.std.scalaFuture._ +import scala.concurrent.Future +import models.AccountDetails._ +import scalaz.OptionT +import actions._ + + +class AccountController { + + lazy val authenticationService: AuthenticationService = IdentityAuthService + lazy val corsCardFilter = CORSActionBuilder(Config.mmaCardCorsConfig) + lazy val mmaCorsFilter = CORSActionBuilder(Config.mmaCorsConfig) + lazy val mmaAction = mmaCorsFilter andThen BackendFromCookieAction + lazy val mmaCardAction = corsCardFilter andThen BackendFromCookieAction + + def updateCard(implicit product: ProductFamily) = mmaCardAction.async { implicit request => + val updateForm = Form { single("stripeToken" -> nonEmptyText) } + val tp = request.touchpoint + + (for { + user <- OptionT(Future.successful(authenticationService.userId)) + sfUser <- OptionT(tp.contactRepo.get(user)) + subscription <- OptionT(tp.subService.get(sfUser)) + stripeCardToken <- OptionT(Future.successful(updateForm.bindFromRequest().value)) + updateResult <- OptionT(tp.paymentService.setPaymentCardWithStripeToken(subscription.accountId, stripeCardToken)) + } yield updateResult match { + case success: CardUpdateSuccess => Ok(Json.toJson(success)) + case failure: CardUpdateFailure => Forbidden(Json.toJson(failure)) + }).run.map(_.getOrElse(notFound)) + } + + def paymentDetails(implicit product: ProductFamily) = mmaAction.async { implicit request => + (for { + user <- OptionT(Future.successful(authenticationService.userId)) + contact <- OptionT(request.touchpoint.contactRepo.get(user)) + sub <- OptionT(request.touchpoint.subService.getEither(contact)) + details <- OptionT(request.touchpoint.paymentService.paymentDetails(sub).map[Option[PaymentDetails]](Some(_))) + } yield (contact, details).toResult).run.map(_ getOrElse Ok(Json.obj())) + } + + def membershipUpdateCard = updateCard(Membership) + def digitalPackUpdateCard = updateCard(Digipack) + + def membershipDetails = paymentDetails(Membership) + def digitalPackDetails = paymentDetails(Digipack) +} diff --git a/membership-attribute-service/app/controllers/AttributeController.scala b/membership-attribute-service/app/controllers/AttributeController.scala index 88f03fe9..b2144e95 100644 --- a/membership-attribute-service/app/controllers/AttributeController.scala +++ b/membership-attribute-service/app/controllers/AttributeController.scala @@ -1,7 +1,6 @@ package controllers - import com.gu.memsub._ -import com.gu.services.model.PaymentDetails +import com.typesafe.scalalogging.LazyLogging import configuration.Config import models.ApiError._ import models.ApiErrors._ @@ -9,32 +8,46 @@ import models.Features._ import actions._ import models._ import monitoring.CloudWatch -import play.api.data.Form -import play.api.data.Forms._ import play.api.libs.concurrent.Execution.Implicits._ import play.api.libs.json.Json -import play.api.mvc.Result -import play.filters.cors.CORSActionBuilder +import play.api.mvc.{Controller, Result} import _root_.services.{AuthenticationService, IdentityAuthService} -import models.AccountDetails._ +import play.filters.cors.CORSActionBuilder import scala.concurrent.Future -import scalaz.OptionT +import scalaz.{\/, EitherT} import scalaz.std.scalaFuture._ -import play.api.mvc.Results.{Ok, Forbidden} -import json.PaymentCardUpdateResultWriters._ +import scalaz.syntax.std.option._ -class AttributeController { - - lazy val authenticationService: AuthenticationService = IdentityAuthService - lazy val mmaCorsFilter = CORSActionBuilder(Config.mmaCorsConfig) + +class AttributeController extends Controller with LazyLogging { lazy val corsFilter = CORSActionBuilder(Config.corsConfig) - lazy val corsCardFilter = CORSActionBuilder(Config.mmaCardCorsConfig) lazy val backendAction = corsFilter andThen BackendFromCookieAction - lazy val mmaAction = mmaCorsFilter andThen BackendFromCookieAction - lazy val mmaCardAction = corsCardFilter andThen BackendFromCookieAction + lazy val authenticationService: AuthenticationService = IdentityAuthService lazy val metrics = CloudWatch("AttributesController") + def update = BackendFromCookieAction.async { implicit request => + + val result: EitherT[Future, String, Attributes] = for { + id <- EitherT(Future.successful(authenticationService.userId \/> "No user")) + contact <- EitherT(request.touchpoint.contactRepo.get(id).map(_ \/> s"No contact for $id")) + sub <- EitherT(request.touchpoint.membershipSubscriptionService.get(contact)(Membership).map(_ \/> s"No sub for $id")) + attributes = Attributes(id, sub.plan.tier.name, contact.regNumber) + res <- EitherT(request.touchpoint.attrService.set(attributes).map(\/.right)) + } yield attributes + + result.run.map(_.fold( + error => { + logger.error(s"Failed to update attributes - $error") + ApiErrors.badRequest(error) + }, + attributes => { + logger.info(s"${attributes.userId} -> ${attributes.tier}") + Ok(Json.obj("updated" -> true)) + } + )) + } + private def lookup(endpointDescription: String, onSuccess: Attributes => Result, onNotFound: Result = notFound) = backendAction.async { request => authenticationService.userId(request).map[Future[Result]] { id => request.touchpoint.attrService.get(id).map { @@ -52,41 +65,5 @@ class AttributeController { } def membership = lookup("membership", identity[Attributes]) - - def features = lookup("features", - onSuccess = Features.fromAttributes, - onNotFound = Features.unauthenticated - ) - - def membershipUpdateCard = updateCard(Membership) - def digitalPackUpdateCard = updateCard(Digipack) - - def updateCard(implicit product: ProductFamily) = mmaCardAction.async { implicit request => - val updateForm = Form { single("stripeToken" -> nonEmptyText) } - val tp = request.touchpoint - - (for { - user <- OptionT(Future.successful(authenticationService.userId)) - sfUser <- OptionT(tp.contactRepo.get(user)) - subscription <- OptionT(tp.subService.get(sfUser)) - stripeCardToken <- OptionT(Future.successful(updateForm.bindFromRequest().value)) - updateResult <- OptionT(tp.paymentService.setPaymentCardWithStripeToken(subscription.accountId, stripeCardToken)) - } yield updateResult match { - case success: CardUpdateSuccess => Ok(Json.toJson(success)) - case failure: CardUpdateFailure => Forbidden(Json.toJson(failure)) - }).run.map(_.getOrElse(notFound)) - } - - def membershipDetails = paymentDetails(Membership) - def digitalPackDetails = paymentDetails(Digipack) - - def paymentDetails(implicit product: ProductFamily) = mmaAction.async { implicit request => - (for { - user <- OptionT(Future.successful(authenticationService.userId)) - contact <- OptionT(request.touchpoint.contactRepo.get(user)) - sub <- OptionT(request.touchpoint.subService.getEither(contact)) - details <- OptionT(request.touchpoint.paymentService.paymentDetails(sub).map[Option[PaymentDetails]](Some(_))) - - } yield (contact, details).toResult).run.map(_ getOrElse Ok(Json.obj())) - } + def features = lookup("features", onSuccess = Features.fromAttributes, onNotFound = Features.unauthenticated) } diff --git a/membership-attribute-service/conf/routes b/membership-attribute-service/conf/routes index cae7babd..7758596d 100644 --- a/membership-attribute-service/conf/routes +++ b/membership-attribute-service/conf/routes @@ -3,15 +3,16 @@ GET /healthcheck controllers.HealthCh GET /user-attributes/me/membership controllers.AttributeController.membership GET /user-attributes/me/features controllers.AttributeController.features +POST /user-attributes/me/update controllers.AttributeController.update -GET /user-attributes/me/mma-digitalpack controllers.AttributeController.digitalPackDetails -GET /user-attributes/me/mma-membership controllers.AttributeController.membershipDetails +GET /user-attributes/me/mma-digitalpack controllers.AccountController.digitalPackDetails +GET /user-attributes/me/mma-membership controllers.AccountController.membershipDetails -POST /user-attributes/me/membership-update-card controllers.AttributeController.membershipUpdateCard -OPTIONS /user-attributes/me/membership-update-card controllers.AttributeController.membershipUpdateCard +POST /user-attributes/me/membership-update-card controllers.AccountController.membershipUpdateCard +OPTIONS /user-attributes/me/membership-update-card controllers.AccountController.membershipUpdateCard -POST /user-attributes/me/digitalpack-update-card controllers.AttributeController.digitalPackUpdateCard -OPTIONS /user-attributes/me/digitalpack-update-card controllers.AttributeController.digitalPackUpdateCard +POST /user-attributes/me/digitalpack-update-card controllers.AccountController.digitalPackUpdateCard +OPTIONS /user-attributes/me/digitalpack-update-card controllers.AccountController.digitalPackUpdateCard POST /salesforce-hook controllers.SalesforceHookController.createAttributes POST /stripe-hook controllers.StripeHookController.process