app/controllers/ExplainerReindexController.scala (70 lines of code) (raw):
package controllers
import com.gu.atom.data.DynamoDataStore
import com.gu.atom.publish.{AtomReindexer, PreviewAtomReindexer, PublishedAtomReindexer}
import com.gu.contentatom.thrift.{ContentAtomEvent, EventType}
import config.Config
import db.ExplainerDBAPI
import play.api.Logging
import play.api.libs.json.Json
import play.api.libs.ws.WSClient
import play.api.mvc._
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Success, Try}
class ExplainerReindexController(
val wsClient: WSClient,
explainerDB: ExplainerDBAPI,
previewDataStore: DynamoDataStore,
publishedDataStore: DynamoDataStore,
previewReindexer: PreviewAtomReindexer,
publishedReindexer: PublishedAtomReindexer,
config: Config,
val controllerComponents: ControllerComponents
)(implicit ec: ExecutionContext) extends BaseController with Logging {
var lastPublished: Int = 0
var lastPreview: Int = 0
// Copy-pasted from the atom-maker library
object ApiKeyAction extends ActionBuilder[Request, AnyContent] {
lazy val apiKey = config.reindexApiKey
def invokeBlock[A](request: Request[A], block: (Request[A] => Future[Result])) = {
if(request.getQueryString("api").contains(apiKey))
block(request)
else
Future.successful(Unauthorized(""))
}
override def parser: BodyParser[AnyContent] = controllerComponents.parsers.defaultBodyParser
override protected def executionContext: ExecutionContext = controllerComponents.executionContext
}
def reindex(stack: String): Action[AnyContent] = ApiKeyAction.async { req =>
stack match {
case "preview" => run(previewDataStore, previewReindexer).andThen(updateState(true)) map displayResult recover displayError(stack)
case "published" => run(publishedDataStore, publishedReindexer).andThen(updateState(false)) map displayResult recover displayError(stack)
case x => Future.successful(BadRequest(s"$x is not a valid stack identifier"))
}
}
def status(stack: String): Action[AnyContent] = ApiKeyAction { req =>
stack match {
case "preview" => displayResult(lastPreview)
case "published" => displayResult(lastPublished)
case x => BadRequest(s"$x is not a valid stack identifier")
}
}
private def run(datastore: DynamoDataStore, reindexer: AtomReindexer): Future[Int] = {
explainerDB.listAtoms(datastore).fold(
apiError => Future.failed(new RuntimeException(apiError.msg)),
{ atoms =>
val timestamp = System.currentTimeMillis
val events = atoms.map(ContentAtomEvent(_, EventType.Update, timestamp))
reindexer.startReindexJob(events.iterator, events.size).execute
}
)
}
private def updateState(isPreview: Boolean): PartialFunction[Try[Int], _] = {
case Success(x) => if (isPreview) lastPreview = x else lastPublished = x
}
private def displayResult(result: Int): Result =
Ok(Json.parse(s"""{ "status": "completed", "documentsIndexed": $result, "documentsExpected": $result }"""))
private def displayError(stack: String): PartialFunction[Throwable, Result] = {
case x: Throwable =>
logger.error(s"Failed to reindex $stack explainer atoms", x)
InternalServerError(Json.parse(s"""{ "status": "failed", "documentsIndexed": 0, "documentsExpected": 0 }"""))
}
}