diff --git a/app/Components.scala b/app/Components.scala index 2e081ee4b50..60b7d60f9ff 100644 --- a/app/Components.scala +++ b/app/Components.scala @@ -79,7 +79,8 @@ class AppComponents(context: Context, val config: ApplicationConfiguration) val collectionService = new CollectionService(frontsApi, containerService) val faciaPressQueue = new FaciaPressQueue(config) val faciaPressTopic = new FaciaPressTopic(config) - val faciaPress = new FaciaPress(faciaPressQueue, faciaPressTopic, configAgent) + val eventBridge = new EventBridge(config) + val faciaPress = new FaciaPress(faciaPressQueue, faciaPressTopic, eventBridge, configAgent) val updateActions = new UpdateActions(faciaApiIO, frontsApi, config, configAgent, structuredLogger) val updateManager = new UpdateManager(updateActions, configAgent, s3FrontsApi) val cloudwatch = new CloudWatch(config, awsEndpoints) @@ -93,7 +94,7 @@ class AppComponents(context: Context, val config: ApplicationConfiguration) val defaults = new DefaultsController(acl, isDev, this) val faciaCapiProxy = new FaciaContentApiProxy(capi, this) val faciaTool = new FaciaToolController(acl, frontsApi, collectionService, faciaApiIO, updateActions, breakingNewsUpdate, - structuredLogger, faciaPress, faciaPressQueue, faciaPressTopic, configAgent, s3FrontsApi, this) + structuredLogger, faciaPress, faciaPressQueue, faciaPressTopic, configAgent, s3FrontsApi, eventBridge, this) val front = new FrontController(acl, structuredLogger, updateManager, press, this) val pandaAuth = new PandaAuthController(this) val status = new StatusController(this) diff --git a/app/conf/Configuration.scala b/app/conf/Configuration.scala index c762b343457..b6d5482e86f 100644 --- a/app/conf/Configuration.scala +++ b/app/conf/Configuration.scala @@ -234,6 +234,7 @@ class ApplicationConfiguration(val playConfiguration: PlayConfiguration, val isP lazy val stsRoleToAssume = getString("faciatool.sts.role.to.assume").getOrElse(stsRoleToAssumeFromProperties) lazy val frontPressUpdateTable = frontPressedDynamoTable lazy val userDataTable = userTable + lazy val eventBridgeBusName = getMandatoryString("eventbridge.bus_name") } object media { diff --git a/app/controllers/FaciaToolController.scala b/app/controllers/FaciaToolController.scala index 80a62404b99..52933ea5c06 100644 --- a/app/controllers/FaciaToolController.scala +++ b/app/controllers/FaciaToolController.scala @@ -32,6 +32,7 @@ class FaciaToolController( val FaciaPressTopic: FaciaPressTopic, val configAgent: ConfigAgent, val s3FrontsApi: S3FrontsApi, + val eventBridge: EventBridge, val deps: BaseFaciaControllerComponents )(implicit ec: ExecutionContext) extends BaseFaciaController(deps) with BreakingNewsEditCollectionsCheck with ModifyCollectionsPermissionsCheck with Logging { @@ -248,12 +249,14 @@ class FaciaToolController( def pressLivePath(path: String) = AccessAPIAuthAction { request => faciaPressQueue.enqueue(PressJob(FrontPath(path), Live, forceConfigUpdate = Option(true))) FaciaPressTopic.publish(PressJob(FrontPath(path), Live, forceConfigUpdate = Option(true))) + eventBridge.putEvent(path, Live) NoCache(Ok) } def pressDraftPath(path: String) = AccessAPIAuthAction { request => faciaPressQueue.enqueue(PressJob(FrontPath(path), Draft, forceConfigUpdate = Option(true))) FaciaPressTopic.publish(PressJob(FrontPath(path), Draft, forceConfigUpdate = Option(true))) + eventBridge.putEvent(path, Draft) NoCache(Ok) } diff --git a/app/services/EventBridge.scala b/app/services/EventBridge.scala new file mode 100644 index 00000000000..bca7d21ae86 --- /dev/null +++ b/app/services/EventBridge.scala @@ -0,0 +1,35 @@ +package services + +import com.amazonaws.services.eventbridge.AmazonEventBridgeAsyncClient +import com.amazonaws.services.eventbridge.model.{PutEventsRequest, PutEventsRequestEntry, PutEventsResult} +import com.gu.facia.api.models.faciapress.PressType +import conf.ApplicationConfiguration +import play.api.libs.json.Json + +import java.util.Date +import scala.concurrent.Future +import scala.util.Try + +class EventBridge(config: ApplicationConfiguration) { + val client = AmazonEventBridgeAsyncClient.asyncBuilder().build() + + def putEvent(path: String, pressType: PressType): Future[PutEventsResult] = { + val requestEntry = new PutEventsRequestEntry() + .withTime(new Date()) + .withEventBusName(config.faciatool.eventBridgeBusName) // if we don't define this then we use the default event bus, can be referenced by name or arn + .withSource("facia-tool") // could be used in the EventPattern to trigger only some Rules + .withDetailType("front-path") // maybe superfluous? "used to decide what fields to expect in the event detail" + .withDetail(EventBridgeDetail(path, pressType).toJsonString) + + val request = new PutEventsRequest() + .withEntries(requestEntry) + + Future.fromTry(Try(client.putEvents(request))) + } +} + +case class EventBridgeDetail(`front-path`: String, pressType: PressType) { + def toJsonString: String = { + Json.toJson(this)(Json.writes[EventBridgeDetail]).toString + } +} diff --git a/app/services/FaciaPress.scala b/app/services/FaciaPress.scala index aa1107093ce..54b037c1029 100644 --- a/app/services/FaciaPress.scala +++ b/app/services/FaciaPress.scala @@ -73,7 +73,7 @@ class FaciaPressTopic(val config: ApplicationConfiguration) { } -class FaciaPress(val faciaPressQueue: FaciaPressQueue, val faciaPressTopic: FaciaPressTopic, val configAgent: ConfigAgent) extends Logging { +class FaciaPress(val faciaPressQueue: FaciaPressQueue, val faciaPressTopic: FaciaPressTopic, val eventBridge: EventBridge, val configAgent: ConfigAgent) extends Logging { def press(pressCommand: PressCommand): Future[List[SendMessageResult]] = { configAgent.refreshAndReturn() flatMap { _ => val paths: Set[String] = for { @@ -93,6 +93,11 @@ class FaciaPress(val faciaPressQueue: FaciaPressQueue, val faciaPressTopic: Faci case Success(_) => logger.info(s"Published to the SNS topic, $pressType event: $event") } + eventBridge.putEvent(path, pressType).onComplete { // fire & forget (but log) + case Failure(error) => logger.error(s"Error publishing to the EventBridge, $pressType path: $path", error) + case Success(_) => logger.info(s"Published to the EventBridge, $pressType path: $path") + } + faciaPressQueue.enqueue(event) } result.onComplete { diff --git a/build.sbt b/build.sbt index 4c686bbfeee..a6b193b88b9 100644 --- a/build.sbt +++ b/build.sbt @@ -90,6 +90,7 @@ libraryDependencies ++= Seq( "com.amazonaws" % "aws-java-sdk-ssm" % awsVersion, "com.amazonaws" % "aws-java-sdk-sts" % awsVersion, "com.amazonaws" % "aws-java-sdk-dynamodb" % awsVersion, + "com.amazonaws" % "aws-java-sdk-eventbridge" % awsVersion, "com.gu" %% "content-api-models-scala" % capiModelsVersion, "com.gu" %% "content-api-models-json" % capiModelsVersion, "com.gu" %% "content-api-client-aws" % "0.6", diff --git a/conf/application.conf b/conf/application.conf index 37f8172ba99..0160a11ef18 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -127,6 +127,8 @@ PROD { faciatool.sns.tool_topic_arn="arn:aws:sns:eu-west-1:163592447864:facia-PROD-FrontsUpdateSNSTopic-kWN6oX2kvOmI" } +eventbridge.bus_name="cms-fronts-eventbridge-bus-CODE" + faciatool.show_test_containers=true include "local.conf"