app/repositories/ContentAPI.scala (106 lines of code) (raw):
package repositories
import java.net.URI
import com.amazonaws.auth.{AWSCredentialsProvider, AWSCredentialsProviderChain, STSAssumeRoleSessionCredentialsProvider}
import com.amazonaws.auth.profile.ProfileCredentialsProvider
import com.gu.contentapi.client.{GuardianContentClient, IAMSigner}
import com.gu.contentapi.client.model._
import play.api.Logging
import services.Config
import scala.annotation.tailrec
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration._
import scala.language.postfixOps
import scala.util.{Failure, Success, Try}
object ContentAPI extends Logging {
private val previewApiClient = new DraftContentApiClass(Config().capiKey, Config().capiPreviewIAMUrl)
val capiPreviewCredentials: AWSCredentialsProvider = {
new AWSCredentialsProviderChain(
new ProfileCredentialsProvider("capi"),
new STSAssumeRoleSessionCredentialsProvider.Builder(Config().capiPreviewRole, "capi").build()
)
}
val signer = new IAMSigner(
credentialsProvider = capiPreviewCredentials,
awsRegion = Config().aws.region
)
def countOccurencesOfTagInContents(contentIds: List[String], apiTagId: String)(implicit ec: ExecutionContext): Int = {
if (contentIds.nonEmpty) {
val builder = new StringBuilder()
var pageSize = 0
var total = 0
for (id <- contentIds) {
// ~2048 chars is the max sensible amount for a URL.
// Rather than faff about subtracting our URL + query string junk from 2048 we'll just stay well below the max (1500)
// Also, CAPI maxes out at 50 items per query
if (pageSize >= 50 || builder.length + id.length > 1500) {
total += countTags(builder.toString, pageSize, apiTagId)
builder.setLength(0)
pageSize = 0
}
if (builder.nonEmpty) {
builder.append(',')
}
builder.append(id)
pageSize += 1
}
total += countTags(builder.toString, pageSize, apiTagId)
total
} else 0
}
private def countTags(ids: String, pageSize: Int, apiTagId: String)(implicit ec: ExecutionContext): Int = {
val response = previewApiClient.getResponse(SearchQuery()
.ids(ids)
.pageSize(pageSize)
.showTags("all")
)
val contentWithTag = response.map(_.results.filter{ c => c.tags.exists(_.id == apiTagId)})
val contentWithTagCount = contentWithTag.map(_.length)
Await.result(contentWithTagCount, 5 seconds)
}
def getTag(apiTagId: String)(implicit ec: ExecutionContext) = {
try {
val response = previewApiClient.getResponse(ItemQuery(apiTagId))
Await.result(response.map(_.tag), 5 seconds)
} catch {
case ContentApiError(404, _, _) =>
logger.debug(s"No tag found for id $apiTagId")
None
}
}
def countContentWithTag(apiTagId: String, page: Int = 1, count: Int = 0)(implicit ec: ExecutionContext): Int = {
val response = previewApiClient.getResponse(SearchQuery().tag(apiTagId).pageSize(1))
val resultPage = Await.result(response, 5 seconds)
resultPage.total
}
@tailrec
def getContentIdsForTag(apiTagId: String, page: Int = 1, ids: List[String] = Nil)(implicit ec: ExecutionContext): List[String] = {
logger.debug(s"Loading page $page of contentent ids for tag $apiTagId")
val response = previewApiClient.getResponse(SearchQuery().tag(apiTagId).pageSize(100).page(page))
val resultPage = Await.result(response, 5 seconds)
val allIds = ids ::: resultPage.results.map(_.id).toList
if (page >= resultPage.pages) {
allIds
} else {
getContentIdsForTag(apiTagId, page + 1, allIds)
}
}
@tailrec
def getDraftContentIdsForSection(apiSectionId: String, page: Int = 1, ids: List[String] = Nil)(implicit ec: ExecutionContext): List[String] = {
logger.debug(s"Loading page $page of contentent ids for section $apiSectionId")
val response = previewApiClient.getResponse(SearchQuery().section(apiSectionId).pageSize(100).page(page))
val resultPage = Await.result(response, 5 seconds)
val allIds = ids ::: resultPage.results.map(_.id).toList
if (page >= resultPage.pages) {
allIds
} else {
getDraftContentIdsForSection(apiSectionId, page + 1, allIds)
}
}
def shutdown(): Unit = {
previewApiClient.shutdown()
}
}
class DraftContentApiClass(override val apiKey: String, apiUrl: String) extends GuardianContentClient(apiKey) {
override val targetUrl = apiUrl
override def get(url: String, headers: Map[String, String])(implicit context: ExecutionContext): Future[HttpResponse] = {
val headersWithAuth = ContentAPI.signer.addIAMHeaders(headers, URI.create(url))
super.get(url, headersWithAuth)
}
}