app/controllers/FaciaContentApiProxy.scala (95 lines of code) (raw):

package controllers import java.net.{URI, URLEncoder} import org.apache.pekko.actor.ActorSystem import story_packages.auth.PanDomainAuthActions import com.amazonaws.auth.{AWSCredentialsProviderChain, STSAssumeRoleSessionCredentialsProvider} import com.amazonaws.auth.profile.ProfileCredentialsProvider import com.gu.contentapi.client.{IAMEncoder, IAMSigner} import story_packages.metrics.FaciaToolMetrics import story_packages.model.Cached import play.api.libs.ws.WSClient import play.api.mvc._ import conf.ApplicationConfiguration import story_packages.switchboard.SwitchManager import story_packages.util.ContentUpgrade.rewriteBody import scala.concurrent.ExecutionContext.Implicits.global class FaciaContentApiProxy(config: ApplicationConfiguration, components: ControllerComponents, wsClient: WSClient) extends StoryPackagesBaseController(config, components, wsClient) with PanDomainAuthActions { implicit class string2encodings(s: String) { lazy val urlEncoded = URLEncoder.encode(s, "utf-8") } private val previewSigner = { val capiPreviewCredentials = new AWSCredentialsProviderChain( new ProfileCredentialsProvider("capi"), new STSAssumeRoleSessionCredentialsProvider.Builder(config.contentApi.previewRole, "capi").build() ) new IAMSigner( credentialsProvider = capiPreviewCredentials, awsRegion = config.aws.region ) } private def getPreviewHeaders(url: String): Seq[(String,String)] = previewSigner.addIAMHeaders(headers = Map.empty, URI.create(url)).toSeq def capiPreview(path: String) = APIAuthAction.async { request => FaciaToolMetrics.ProxyCount.increment() val queryString = IAMEncoder.encodeParams(request.queryString) val contentApiHost: String = if (SwitchManager.getStatus("facia-tool-draft-content")) config.contentApi.contentApiDraftHost else config.contentApi.contentApiLiveHost val url = s"$contentApiHost/$path?$queryString${config.contentApi.key.map(key => s"&api-key=$key").getOrElse("")}" Logger.info(s"Proxying preview API query to: $url") wsClient.url(url).withHttpHeaders(getPreviewHeaders(url): _*).get().map { response => Cached(60) { Ok(rewriteBody(response.body)).as("application/javascript") } } } def capiLive(path: String) = APIAuthAction.async { request => FaciaToolMetrics.ProxyCount.increment() val queryString = request.queryString.filter(_._2.exists(_.nonEmpty)).map { p => "%s=%s".format(p._1, p._2.head.urlEncoded) }.mkString("&") val contentApiHost = config.contentApi.contentApiLiveHost val url = s"$contentApiHost/$path?$queryString${config.contentApi.key.map(key => s"&api-key=$key").getOrElse("")}" Logger.info(s"Proxying live API query to: $url") wsClient.url(url).get().map { response => Cached(60) { Ok(rewriteBody(response.body)).as("application/javascript") } } } def http(url: String) = APIAuthAction.async { request => FaciaToolMetrics.ProxyCount.increment() Logger.info(s"Proxying http request to: $url") wsClient.url(url).get().map { response => Cached(60) { Ok(response.body).as("text/html") } } } def json(url: String) = APIAuthAction.async { request => FaciaToolMetrics.ProxyCount.increment() Logger.info(s"Proxying json request to: $url") wsClient.url(url).withHttpHeaders(getPreviewHeaders(url): _*).get().map { response => Cached(60) { Ok(rewriteBody(response.body)).as("application/json") } } } def ophan(path: String) = APIAuthAction.async { request => FaciaToolMetrics.ProxyCount.increment() val paths = request.queryString.get("path").map(_.mkString("path=", "&path=", "")).getOrElse("") val queryString = request.queryString.filterNot(_._1 == "path").filter(_._2.exists(_.nonEmpty)).map { p => "%s=%s".format(p._1, p._2.head.urlEncoded) }.mkString("&") val ophanApiHost = config.ophanApi.host.get val ophanKey = config.ophanApi.key.map(key => s"&api-key=$key").getOrElse("") val url = s"$ophanApiHost/$path?$queryString&$paths&$ophanKey" Logger.info(s"Proxying ophan request to: $url") wsClient.url(url).get().map { response => Cached(60) { Ok(response.body).as("application/json") } } } }