app/model/command/CreateTagCommand.scala (173 lines of code) (raw):
package model.command
import com.gu.tagmanagement.{EventType, SectionEvent, TagEvent}
import model.command.logic.{SponsorshipStatusCalculator, TagPathCalculator}
import model._
import org.apache.commons.lang3.StringUtils
import ai.x.play.json.Jsonx
import ai.x.play.json.Encoders.encoder
import ai.x.play.json.implicits.optionWithNull
import helpers.JodaDateTimeFormat._
import org.joda.time.{DateTime, DateTimeZone}
import play.api.libs.functional.syntax._
import play.api.libs.json.{Format, JsPath}
import repositories._
import CommandError._
import play.api.Logging
import services.KinesisStreams
import scala.concurrent.{Future, ExecutionContext}
import play.api.libs.json.OFormat
case class InlinePaidContentSponsorshipCommand(
validFrom: Option[DateTime],
validTo: Option[DateTime],
sponsorName: String,
sponsorLogo: Image,
highContrastSponsorLogo: Option[Image] = None,
sponsorLink: String,
aboutLink: Option[String] = None
) {
def createSponsorship(tagId: Long, createdSectionId: Option[Long]) = {
val status = SponsorshipStatusCalculator.calculateStatus(validFrom, validTo)
val sponsorship = Sponsorship(
id = Sequences.sponsorshipId.getNextId,
validFrom = validFrom,
validTo = validTo,
status = status,
sponsorshipType = "paidContent",
sponsorshipPackage = None,
sponsorName = sponsorName,
sponsorLogo = sponsorLogo,
highContrastSponsorLogo = highContrastSponsorLogo,
sponsorLink = sponsorLink,
aboutLink = aboutLink.flatMap{s => if(StringUtils.isNotBlank(s)) Some(s) else None },
tags = Some(List(tagId)),
sections = createdSectionId.map(List(_)),
targeting = None
)
SponsorshipRepository.updateSponsorship(sponsorship)
}
}
object InlinePaidContentSponsorshipCommand {
implicit val inlinePaidContentSponsorshipFormat: OFormat[InlinePaidContentSponsorshipCommand] = Jsonx.formatCaseClassUseDefaults[InlinePaidContentSponsorshipCommand]
}
case class CreateTagCommand(
`type`: String,
internalName: String,
externalName: String,
slug: String,
hidden: Boolean = false,
legallySensitive: Boolean = false,
comparableValue: String,
categories: Set[String] = Set(),
section: Option[Long] = None,
publication: Option[Long] = None,
description: Option[String] = None,
parents: Set[Long] = Set(),
references: List[Reference] = Nil,
podcastMetadata: Option[PodcastMetadata] = None,
contributorInformation: Option[ContributorInformation] = None,
publicationInformation: Option[PublicationInformation] = None,
isMicrosite: Boolean,
capiSectionId: Option[String] = None,
trackingInformation: Option[TrackingInformation] = None,
campaignInformation: Option[CampaignInformation] = None,
preCalculatedPath: Option[String] = None, //This is used so path isn't calculated
sponsorship: Option[InlinePaidContentSponsorshipCommand] = None,
paidContentInformation: Option[PaidContentInformation] = None,
createMicrosite: Boolean = false,
adBlockingLevel: Option[BlockingLevel] = None,
contributionBlockingLevel: Option[BlockingLevel] = None
) extends Command with Logging {
type T = Tag
def process()(implicit username: Option[String], ec: ExecutionContext): Future[Option[Tag]] = Future {
val tagId = Sequences.tagId.getNextId
logger.info(s"Create Tag command process started for tagid: $tagId")
val sectionId: Option[Long] = if(createMicrosite) {
val path = if(`type` == "PaidContent" && paidContentInformation.isDefined && paidContentInformation.get.paidContentType == "HostedContent") {
s"advertiser-content/$slug"
} else {slug}
val sectionPageId: Long = try { PathManager.registerPathAndGetPageId(path) } catch { case p: PathRegistrationFailed => PathInUse}
val nextSectionId = Sequences.sectionId.getNextId
val createdSection = Section(
id = nextSectionId,
sectionTagId = tagId,
name = externalName,
path = path,
wordsForUrl = path,
pageId = sectionPageId,
editions = Map(),
discriminator = Some("Navigation"),
isMicrosite = true,
activeSponsorships = Nil
)
val result = SectionRepository.updateSection(createdSection)
KinesisStreams.sectionUpdateStream.publishUpdate(createdSection.id.toString, SectionEvent(EventType.Update, createdSection.id, Some(createdSection.asThrift)))
SectionAuditRepository.upsertSectionAudit(SectionAudit.created(createdSection))
result.map(_.id)
} else {
section
}
val createdSectionId = if(createMicrosite) { sectionId } else { None }
val tagSubType: Option[String] = `type` match {
case "Tracking" => trackingInformation.map(_.trackingType)
case "Campaign" => campaignInformation.map(_.campaignType)
case _ => None
}
val calculatedPath = preCalculatedPath match {
case Some(path) => path
case None => TagPathCalculator.calculatePath(`type`, slug, sectionId, tagSubType)
// Note we don't pass the paid contnet subtype here as the path munging has now been applied to the created microsite
// so the standard section / slug logic is the desired logic
}
val pageId = try { PathManager.registerPathAndGetPageId(calculatedPath) } catch { case p: PathRegistrationFailed => PathInUse}
val createdSponsorship = sponsorship flatMap(_.createSponsorship(tagId, createdSectionId))
val createdSponsorshipActive = createdSponsorship.map(_.status == "active").getOrElse(false)
val tag = Tag(
id = tagId,
path = calculatedPath,
pageId = pageId,
`type`= `type`,
internalName = internalName,
externalName = externalName,
slug = slug,
hidden = hidden,
legallySensitive = legallySensitive,
comparableValue = comparableValue,
section = sectionId,
publication = publication,
categories = categories,
description = description,
parents = parents,
externalReferences = references,
podcastMetadata = podcastMetadata,
contributorInformation = contributorInformation,
publicationInformation = publicationInformation,
isMicrosite = isMicrosite || createMicrosite,
capiSectionId = capiSectionId,
trackingInformation = trackingInformation,
campaignInformation = campaignInformation,
activeSponsorships = if(createdSponsorshipActive) List(createdSponsorship.map(_.id).get) else Nil,
sponsorship = createdSponsorship.map(_.id),
paidContentInformation = paidContentInformation,
expired = createdSponsorship.map(_.status == "expired").getOrElse(false),
updatedAt = new DateTime(DateTimeZone.UTC).getMillis,
adBlockingLevel = adBlockingLevel,
contributionBlockingLevel = contributionBlockingLevel
)
val result = TagRepository.upsertTag(tag)
val thriftTag = tag.asThrift
val tagUpdateEvent = TagEvent(EventType.Update, tag.id, Some(thriftTag))
logger.info(s"Kiniesis producer publish tag-update event, tagEvent type: ${tagUpdateEvent.eventType}")
KinesisStreams.tagUpdateStream.publishUpdate(tag.id.toString, tagUpdateEvent)
TagAuditRepository.upsertTagAudit(TagAudit.created(tag))
if(createdSponsorshipActive) {
for (
sectionId <- createdSectionId;
sponsorship <- createdSponsorship
) {
SponsorshipOperations.addSponsorshipToSection(sponsorship.id, sectionId)
}
}
result
}
}
object CreateTagCommand {
implicit val createTagCommandFormat: OFormat[CreateTagCommand] = Jsonx.formatCaseClassUseDefaults[CreateTagCommand]
}