<commit_before>package com.anchortab.model

import net.liftweb._
  import common._
  import mongodb._
  import util.Helpers._
  import json._

import org.joda.time._

import com.ecwid.mailchimp._
  import method.list._

import org.bson.types.ObjectId

/**
 * The ServiceWrapper trait is the common interface for all wrappers around external email
 * subscription services. The only two functions these wrappers are required to expose are
 * a subscribe and an unsubscribe. The mechanics and individual settings of how they handle
 * those operations are irrelevent to the parent interface.
**/
sealed trait ServiceWrapper {
  def credentialsValid_? : Boolean
  def subscribeEmail(email:String) : Box[Boolean]
  def unsubscribeEmail(email:String) : Box[Boolean]

  def wrapperIdentifier: String
}
object ServiceWrapper {
  val typeHints = ShortTypeHints(List(
    classOf[MailChimpServiceWrapper],
    classOf[ConstantContactServiceWrapper],
    classOf[CampaignMonitorServiceWrapper]
  ))
}

case class CampaignMonitorServiceWrapper(userId: ObjectId, listId: String) extends ServiceWrapper with Loggable {
  import com.anchortab.campaignmonitor._

  protected def updateCredentials(userId: ObjectId, accessToken: String, refreshToken: String, expiresAt: DateTime) = {
    //TODO
  }

  protected def doWithNewAccessCredentials[T](userId: ObjectId, accessToken: String, refreshToken: String, doSomething: (String, String)=>Box[T]): Box[T] = {
    for {
      newToken <- CampaignMonitor.refreshToken(accessToken, refreshToken)
      expiresAt = (new DateTime()).plusSeconds(newToken.expires_in)
      updateCredentialsUnit = updateCredentials(userId, accessToken, refreshToken, expiresAt)
      resultOfSomething <- doSomething(newToken.access_token, newToken.refresh_token)
    } yield {
      resultOfSomething
    }
  }

  protected def withAccessCredentials[T](doSomething: (String, String)=>Box[T]): Box[T] = {
    {
      for {
        user <- (User.find(userId):Box[User])
        credentials <- user.credentialsFor(CampaignMonitor.serviceIdentifier)
        accessToken <- credentials.serviceCredentials.get("accessToken")
        refreshToken <- credentials.serviceCredentials.get("refreshToken")
        expiresAt <- credentials.serviceCredentials.get("expiresAt")
      } yield {
        if (DateTime.parse(expiresAt) isBeforeNow) {
          doWithNewAccessCredentials(userId, accessToken, refreshToken, doSomething)
        } else {
          doSomething(accessToken, refreshToken)
        }
      }
    } match {
      // We unwrap fulls.
      case Full(t) => t
      case fail: Failure => fail
      case _ => Empty
    }
  }

  val credentialsValid_? = true

  def subscribeEmail(email: String) = {
    val result = withAccessCredentials { (accessToken, refreshToken) =>
      CampaignMonitor.addSubscriber(accessToken, refreshToken, listId, email)
    }

    result.map(thing => true)
  }

  def unsubscribeEmail(email: String) = {
    val result = withAccessCredentials { (accessToken, refreshToken) =>
      CampaignMonitor.removeSubscriber(accessToken, refreshToken, listId, email)
    }

    result.map(thing => true)
  }

  def wrapperIdentifier = {
    "Campaign Monitor - " + userId.toString
  }
}

case class ConstantContactServiceWrapper(username:String, implicit val accessToken:String, listId:Long) extends ServiceWrapper with Loggable {
  import com.anchortab.constantcontact.model.Contacts._<commit_msg>Implement ServiceWrapper token refreshing for CM.<commit_after>package com.anchortab.model

import net.liftweb._
  import common._
  import mongodb._
    import BsonDSL._
  import util.Helpers._
  import json._
    import Extraction._

import org.joda.time._

import com.ecwid.mailchimp._
  import method.list._

import org.bson.types.ObjectId

/**
 * The ServiceWrapper trait is the common interface for all wrappers around external email
 * subscription services. The only two functions these wrappers are required to expose are
 * a subscribe and an unsubscribe. The mechanics and individual settings of how they handle
 * those operations are irrelevent to the parent interface.
**/
sealed trait ServiceWrapper {
  def credentialsValid_? : Boolean
  def subscribeEmail(email:String) : Box[Boolean]
  def unsubscribeEmail(email:String) : Box[Boolean]

  def wrapperIdentifier: String
}
object ServiceWrapper {
  val typeHints = ShortTypeHints(List(
    classOf[MailChimpServiceWrapper],
    classOf[ConstantContactServiceWrapper],
    classOf[CampaignMonitorServiceWrapper]
  ))
}

case class CampaignMonitorServiceWrapper(userId: ObjectId, listId: String) extends ServiceWrapper with Loggable {
  import com.anchortab.campaignmonitor._

  protected def updateCredentials(userId: ObjectId, accessToken: String, refreshToken: String, expiresAt: DateTime) = {
    implicit val formats = User.formats

    User.update(
      (
        ("_id" -> userId) ~
        ("serviceCredentials.serviceName" -> CampaignMonitor.serviceIdentifier)
      ),
      ("$set" -> (
        ("serviceCredentials.$.serviceCredentials.accessToken" -> accessToken) ~
        ("serviceCredentials.$.serviceCredentials.refreshToken" -> refreshToken) ~
        ("serviceCredentials.$.serviceCredentials.expiresAt" -> decompose(expiresAt))
      ))
    )
  }

  protected def doWithNewAccessCredentials[T](userId: ObjectId, accessToken: String, refreshToken: String, doSomething: (String, String)=>Box[T]): Box[T] = {
    val tokenRefreshResult = for {
      newToken <- CampaignMonitor.refreshToken(accessToken, refreshToken)
      expiresAt = (new DateTime()).plusSeconds(newToken.expires_in)
      updateCredentialsUnit = updateCredentials(userId, accessToken, refreshToken, expiresAt)
      resultOfSomething <- doSomething(newToken.access_token, newToken.refresh_token)
    } yield {
      resultOfSomething
    }

    tokenRefreshResult match {
      case result @ Full(_) =>
        logger.info("Refreshed CM token.")
        result

      case fail @ Failure(msg, _, _) =>
        logger.error("Error refreshing CM token: " + msg)
        fail

      case Empty =>
        logger.error("Empty refreshing CM token.")
        Empty
    }
  }

  protected def withAccessCredentials[T](doSomething: (String, String)=>Box[T]): Box[T] = {
    {
      for {
        user <- (User.find(userId):Box[User])
        credentials <- user.credentialsFor(CampaignMonitor.serviceIdentifier)
        accessToken <- credentials.serviceCredentials.get("accessToken")
        refreshToken <- credentials.serviceCredentials.get("refreshToken")
        expiresAt <- credentials.serviceCredentials.get("expiresAt")
      } yield {
        if (DateTime.parse(expiresAt) isBeforeNow) {
          doWithNewAccessCredentials(userId, accessToken, refreshToken, doSomething)
        } else {
          doSomething(accessToken, refreshToken)
        }
      }
    } match {
      // We unwrap fulls.
      case Full(t) => t

      case fail @ Failure(msg, _, _) =>
        logger.error("Returning a failure from Campaign Monitor: " + msg)
        fail

      case _ => Empty
    }
  }

  val credentialsValid_? = true

  def subscribeEmail(email: String) = {
    val result = withAccessCredentials { (accessToken, refreshToken) =>
      CampaignMonitor.addSubscriber(accessToken, refreshToken, listId, email)
    }

    result.map(thing => true)
  }

  def unsubscribeEmail(email: String) = {
    val result = withAccessCredentials { (accessToken, refreshToken) =>
      CampaignMonitor.removeSubscriber(accessToken, refreshToken, listId, email)
    }

    result.map(thing => true)
  }

  def wrapperIdentifier = {
    "Campaign Monitor - " + userId.toString
  }
}

case class ConstantContactServiceWrapper(username:String, implicit val accessToken:String, listId:Long) extends ServiceWrapper with Loggable {
  import com.anchortab.constantcontact.model.Contacts._
{'fraction_non_alphanumeric': 0.09504395464390368, 'fraction_numerical': 0.0, 'mean_word_length': 3.7090581883623277, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '10677', 'n_tokens_mistral': 2179, 'n_tokens_neox': 2162, 'n_words': 523}