diff --git a/admin/app/Application.scala b/admin/app/Application.scala index 94df120..a08fb41 100644 --- a/admin/app/Application.scala +++ b/admin/app/Application.scala @@ -1,6 +1,5 @@ package com.lucidchart.piezo.admin -import com.lucidchart.piezo.util.DummyClassGenerator import com.softwaremill.macwire._ import play.api.ApplicationLoader.Context import play.api._ @@ -12,10 +11,11 @@ import play.api.Mode import play.api.routing.Router import router.Routes import scala.concurrent.Future -import com.lucidchart.piezo.admin.models._ import com.lucidchart.piezo.admin.controllers._ +import com.lucidchart.piezo.admin.models.MonitoringTeams import com.lucidchart.piezo.WorkerSchedulerFactory import _root_.controllers.AssetsComponents + /** * Application loader that wires up the application dependencies using Macwire */ @@ -23,7 +23,10 @@ class PiezoAdminApplicationLoader extends ApplicationLoader { def load(context: Context): Application = new PiezoAdminComponents(context).application } -class PiezoAdminComponents(context: Context) extends BuiltInComponentsFromContext(context) with I18nComponents with AssetsComponents { +class PiezoAdminComponents(context: Context) + extends BuiltInComponentsFromContext(context) + with I18nComponents + with AssetsComponents { lazy val schedulerFactory: WorkerSchedulerFactory = new WorkerSchedulerFactory() lazy val jobFormHelper: JobFormHelper = wire[JobFormHelper] @@ -39,33 +42,40 @@ class PiezoAdminComponents(context: Context) extends BuiltInComponentsFromContex override val httpFilters: Seq[EssentialFilter] = { val ec = controllerComponents.executionContext Seq( - wire[RequestStatCollector] + new RequestStatCollector(ec), ) } val logger = Logger("com.lucidchart.piezo.Global") + override lazy val httpErrorHandler: HttpErrorHandler = + new DefaultHttpErrorHandler(environment, configuration, devContext.map(_.sourceMapper), Some(router)) { - override lazy val httpErrorHandler: HttpErrorHandler = new DefaultHttpErrorHandler(environment, configuration, devContext.map(_.sourceMapper), Some(router)) { /** - * Invoked when a handler or resource is not found. - * - * @param request The request that no handler was found to handle. - * @param message A message. - */ - override protected def onNotFound(request: RequestHeader, message: String): Future[Result] = { - logger.error("Request handler not found for URL: " + request.uri) - Future.successful(NotFound(com.lucidchart.piezo.admin.views.html.errors.notfound(None)(request))) - } + * Invoked when a handler or resource is not found. + * + * @param request + * The request that no handler was found to handle. + * @param message + * A message. + */ + override protected def onNotFound(request: RequestHeader, message: String): Future[Result] = { + logger.error("Request handler not found for URL: " + request.uri) + Future.successful(NotFound(com.lucidchart.piezo.admin.views.html.errors.notfound(None)(request))) + } - override def onServerError(request: RequestHeader, exception: Throwable): Future[Result] = { - logger.error("Error handling request for URL: " + request.uri, exception) - if(environment.mode == Mode.Dev) { - super.onServerError(request, exception) - } else { - Future.successful(InternalServerError(com.lucidchart.piezo.admin.views.html.errors.error(Option(exception.getMessage))(request))) + override def onServerError(request: RequestHeader, exception: Throwable): Future[Result] = { + logger.error("Error handling request for URL: " + request.uri, exception) + if (environment.mode == Mode.Dev) { + super.onServerError(request, exception) + } else { + Future.successful( + InternalServerError( + com.lucidchart.piezo.admin.views.html.errors.error(Option(exception.getMessage))(request), + ), + ) + } } } - } // set up logger LoggerConfigurator(context.environment.classLoader).foreach { _.configure(context.environment, context.initialConfiguration, Map.empty) diff --git a/admin/app/com/lucidchart/piezo/admin/controllers/ApplicationController.scala b/admin/app/com/lucidchart/piezo/admin/controllers/ApplicationController.scala index 027762c..fbf39c4 100644 --- a/admin/app/com/lucidchart/piezo/admin/controllers/ApplicationController.scala +++ b/admin/app/com/lucidchart/piezo/admin/controllers/ApplicationController.scala @@ -1,6 +1,5 @@ package com.lucidchart.piezo.admin.controllers -import play.api._ import play.api.mvc._ class ApplicationController(cc: ControllerComponents) extends AbstractController(cc) { diff --git a/admin/app/com/lucidchart/piezo/admin/controllers/HealthCheck.scala b/admin/app/com/lucidchart/piezo/admin/controllers/HealthCheck.scala index daf85c7..3cb30bb 100644 --- a/admin/app/com/lucidchart/piezo/admin/controllers/HealthCheck.scala +++ b/admin/app/com/lucidchart/piezo/admin/controllers/HealthCheck.scala @@ -19,7 +19,7 @@ class HealthCheck(configuration: Configuration, cc: ControllerComponents) extend 5 } - def main = cc.actionBuilder { implicit requests=> + def main = cc.actionBuilder { request => val workerHealth = areWorkersHealthy() val responseBody = Json.toJson(Map("HeartbeatTime" -> Json.toJson(workerHealth._2))) if(workerHealth._1) { diff --git a/admin/app/com/lucidchart/piezo/admin/controllers/JobDataHelper.scala b/admin/app/com/lucidchart/piezo/admin/controllers/JobDataHelper.scala index 71d1ade..4256ec5 100644 --- a/admin/app/com/lucidchart/piezo/admin/controllers/JobDataHelper.scala +++ b/admin/app/com/lucidchart/piezo/admin/controllers/JobDataHelper.scala @@ -2,7 +2,6 @@ package com.lucidchart.piezo.admin.controllers import org.quartz.JobDataMap import play.api.libs.json._ -import play.api.data.Form import play.api.data.Forms._ import play.api.data.Mapping diff --git a/admin/app/com/lucidchart/piezo/admin/controllers/JobFormHelper.scala b/admin/app/com/lucidchart/piezo/admin/controllers/JobFormHelper.scala index ed9eb06..61eb0f3 100644 --- a/admin/app/com/lucidchart/piezo/admin/controllers/JobFormHelper.scala +++ b/admin/app/com/lucidchart/piezo/admin/controllers/JobFormHelper.scala @@ -1,7 +1,6 @@ package com.lucidchart.piezo.admin.controllers import org.quartz._ -import play.api.Logger import play.api.data.Form import play.api.data.Forms._ import com.lucidchart.piezo.GeneratorClassLoader diff --git a/admin/app/com/lucidchart/piezo/admin/controllers/Jobs.scala b/admin/app/com/lucidchart/piezo/admin/controllers/Jobs.scala index 3ea4f98..b7679fa 100644 --- a/admin/app/com/lucidchart/piezo/admin/controllers/Jobs.scala +++ b/admin/app/com/lucidchart/piezo/admin/controllers/Jobs.scala @@ -3,11 +3,10 @@ package com.lucidchart.piezo.admin.controllers import com.lucidchart.piezo.admin.utils.JobUtils import com.lucidchart.piezo.admin.utils.JobDetailHelper._ import com.lucidchart.piezo.admin.views._ -import com.lucidchart.piezo.{JobHistoryModel, TriggerHistoryModel, TriggerMonitoringModel, WorkerSchedulerFactory} +import com.lucidchart.piezo.{JobHistoryModel, TriggerMonitoringModel, WorkerSchedulerFactory} import org.quartz._ import org.quartz.impl.matchers.GroupMatcher import play.api._ -import play.api.libs.functional.syntax._ import play.api.libs.json._ import play.api.mvc._ import scala.jdk.CollectionConverters._ @@ -23,20 +22,32 @@ trait ImportResult { def toJson = { Json.obj( "success" -> success, - "errorMessage" -> errorMessage - ) ++ jobKey.map { jk => + "errorMessage" -> errorMessage, + ) ++ jobKey + .map { jk => Json.obj( "jobName" -> jk.getName, - "jobGroup" -> jk.getGroup + "jobGroup" -> jk.getGroup, ) - }.getOrElse(Json.obj()) + } + .getOrElse(Json.obj()) } } -case class ImportSuccess(val jobKey: Option[JobKey], val errorMessage: String = "", val success: Boolean=true) extends ImportResult -case class ImportFailure(val jobKey: Option[JobKey], val errorMessage: String, val success: Boolean=false) extends ImportResult - -class Jobs(schedulerFactory: WorkerSchedulerFactory, jobView: html.job, cc: ControllerComponents, monitoringTeams: MonitoringTeams) extends AbstractController(cc) with Logging with ErrorLogging with play.api.i18n.I18nSupport { +case class ImportSuccess(val jobKey: Option[JobKey], val errorMessage: String = "", val success: Boolean = true) + extends ImportResult +case class ImportFailure(val jobKey: Option[JobKey], val errorMessage: String, val success: Boolean = false) + extends ImportResult + +class Jobs( + schedulerFactory: WorkerSchedulerFactory, + jobView: html.job, + cc: ControllerComponents, + monitoringTeams: MonitoringTeams, +) extends AbstractController(cc) + with Logging + with ErrorLogging + with play.api.i18n.I18nSupport { val scheduler = logExceptions(schedulerFactory.getScheduler()) val properties = schedulerFactory.props val jobHistoryModel = logExceptions(new JobHistoryModel(properties)) @@ -60,14 +71,22 @@ class Jobs(schedulerFactory: WorkerSchedulerFactory, jobView: html.job, cc: Cont def getIndex = Action { implicit request => val allJobs: List[JobKey] = getJobsByGroup().flatMap(_._2).toList - val jobHistories = allJobs.flatMap({ job => - jobHistoryModel.getJob(job).headOption - }).sortWith(_.start after _.start) - val triggeredJobs: List[JobKey] = TriggerHelper.getTriggersByGroup(scheduler).flatMap { case (group, triggerKeys) => - triggerKeys.map(triggerKey => scheduler.getTrigger(triggerKey).getJobKey) - }.toList + val jobHistories = allJobs + .flatMap { job => + jobHistoryModel.getJob(job).headOption + } + .sortWith(_.start after _.start) + val triggeredJobs: List[JobKey] = TriggerHelper + .getTriggersByGroup(scheduler) + .flatMap { case (group, triggerKeys) => + triggerKeys.map(triggerKey => scheduler.getTrigger(triggerKey).getJobKey) + } + .toList val untriggeredJobs: List[JobKey] = allJobs.filterNot(x => triggeredJobs.contains(x)) - Ok(com.lucidchart.piezo.admin.views.html.jobs(getJobsByGroup(), None, Some(jobHistories), untriggeredJobs, scheduler.getMetaData)(request)) + Ok( + com.lucidchart.piezo.admin.views.html + .jobs(getJobsByGroup(), None, Some(jobHistories), untriggeredJobs, scheduler.getMetaData)(request), + ) } def getJob(group: String, name: String) = Action { implicit request => @@ -94,11 +113,16 @@ class Jobs(schedulerFactory: WorkerSchedulerFactory, jobView: html.job, cc: Cont } val triggers = scheduler.getTriggersOfJob(jobKey).asScala.toList - val (resumableTriggers, pausableTriggers) = triggers.filter(_.isInstanceOf[CronTrigger]).map(_.getKey()).partition{ triggerKey => - scheduler.getTriggerState(triggerKey) == Trigger.TriggerState.PAUSED - } + val (resumableTriggers, pausableTriggers) = + triggers.filter(_.isInstanceOf[CronTrigger]).map(_.getKey()).partition { triggerKey => + scheduler.getTriggerState(triggerKey) == Trigger.TriggerState.PAUSED + } - Ok(jobView(getJobsByGroup(), jobDetail, history, Some(triggers), None, pausableTriggers, resumableTriggers)(request)) + Ok( + jobView(getJobsByGroup(), jobDetail, history, Some(triggers), None, pausableTriggers, resumableTriggers)( + request, + ), + ) } catch { case e: Exception => { val errorMsg = "Exception caught getting job " + group + " " + name + ". -- " + e.getLocalizedMessage() @@ -129,7 +153,7 @@ class Jobs(schedulerFactory: WorkerSchedulerFactory, jobView: html.job, cc: Cont } def getJobDetail(group: String, name: String) = Action { implicit request => - if(request.accepts(JSON)) { + if (request.accepts(JSON)) { val jobKey = new JobKey(name, group) if (scheduler.checkExists(jobKey)) { val jobDetail = scheduler.getJobDetail(jobKey) @@ -153,8 +177,8 @@ class Jobs(schedulerFactory: WorkerSchedulerFactory, jobView: html.job, cc: Cont val triggers = scheduler.getTriggersOfJob(jk).asScala.toList Json.toJson(scheduler.getJobDetail(jk))(jobDetailWrites(triggers, triggerMonitoringPriorityModel)) } - } - ) + }, + ), ) } else { UnsupportedMediaType @@ -166,14 +190,18 @@ class Jobs(schedulerFactory: WorkerSchedulerFactory, jobView: html.job, cc: Cont val submitEditMessage = "Save" def formEditAction(group: String, name: String): Call = routes.Jobs.putJob(group, name) - def getNewJobForm(templateGroup: Option[String] = None, templateName: Option[String] = None) = Action { implicit request => - //if (request.queryString.contains()) - templateGroup match { - case Some(group) => getEditJob(group, templateName.get, true) - case None => - val newJobForm = jobFormHelper.buildJobForm - Ok(com.lucidchart.piezo.admin.views.html.editJob(getJobsByGroup(), newJobForm, submitNewMessage, formNewAction, false)(request, implicitly)) - } + def getNewJobForm(templateGroup: Option[String] = None, templateName: Option[String] = None) = Action { + implicit request => + // if (request.queryString.contains()) + templateGroup match { + case Some(group) => getEditJob(group, templateName.get, true) + case None => + val newJobForm = jobFormHelper.buildJobForm + Ok( + com.lucidchart.piezo.admin.views.html + .editJob(getJobsByGroup(), newJobForm, submitNewMessage, formNewAction, false)(request, implicitly), + ) + } } @@ -183,8 +211,21 @@ class Jobs(schedulerFactory: WorkerSchedulerFactory, jobView: html.job, cc: Cont if (scheduler.checkExists(jobKey)) { val jobDetail = scheduler.getJobDetail(jobKey) val editJobForm = jobFormHelper.buildJobForm.fill(jobDetail) - if (isTemplate) Ok(com.lucidchart.piezo.admin.views.html.editJob(getJobsByGroup(), editJobForm, submitNewMessage, formNewAction, false)(request, implicitly)) - else Ok(com.lucidchart.piezo.admin.views.html.editJob(getJobsByGroup(), editJobForm, submitEditMessage, formEditAction(group, name), true)(request, implicitly)) + if (isTemplate) + Ok( + com.lucidchart.piezo.admin.views.html + .editJob(getJobsByGroup(), editJobForm, submitNewMessage, formNewAction, false)(request, implicitly), + ) + else + Ok( + com.lucidchart.piezo.admin.views.html.editJob( + getJobsByGroup(), + editJobForm, + submitEditMessage, + formEditAction(group, name), + true, + )(request, implicitly), + ) } else { val errorMsg = Some("Job %s %s not found".format(group, name)) NotFound(com.lucidchart.piezo.admin.views.html.trigger(mutable.Buffer(), None, None, errorMsg)(request)) @@ -194,65 +235,77 @@ class Jobs(schedulerFactory: WorkerSchedulerFactory, jobView: html.job, cc: Cont def getEditJobAction(group: String, name: String) = Action { implicit request => getEditJob(group, name, false) } def putJob(group: String, name: String) = Action { implicit request => - jobFormHelper.buildJobForm.bindFromRequest().fold( - formWithErrors => - BadRequest(html.editJob(getJobsByGroup(), formWithErrors, submitNewMessage, formNewAction, false)), - value => { - val jobDetail = JobUtils.cleanup(value) - scheduler.addJob(jobDetail, true) - Redirect(routes.Jobs.getJob(value.getKey.getGroup(), value.getKey.getName())) - .flashing("message" -> "Successfully edited job.", "class" -> "") - } - ) + jobFormHelper.buildJobForm + .bindFromRequest() + .fold( + formWithErrors => + BadRequest(html.editJob(getJobsByGroup(), formWithErrors, submitNewMessage, formNewAction, false)), + value => { + val jobDetail = JobUtils.cleanup(value) + scheduler.addJob(jobDetail, true) + Redirect(routes.Jobs.getJob(value.getKey.getGroup(), value.getKey.getName())) + .flashing("message" -> "Successfully edited job.", "class" -> "") + }, + ) } def parseJson(json: JsValue): List[ImportResult] = { def formErrorStr(formErrors: Seq[play.api.data.FormError]): String = { - formErrors.map { e => - (if(e.key.nonEmpty) s"${e.key}:" else "") + e.message - }.mkString(",") + formErrors + .map { e => + (if (e.key.nonEmpty) s"${e.key}:" else "") + e.message + } + .mkString(",") } json.as[List[JsObject]].map { jsObject => - jobFormHelper.buildJobForm.bind(jsObject, maxFormSize).fold ( e => { - val jobKey = for { - name <- (jsObject \ "name").validate[String] - group <- (jsObject \ "group").validate[String] - } yield { - new JobKey(name, group) - } - val errorMessage = formErrorStr(e.errors) - logger.error(errorMessage) - ImportFailure(jobKey.asOpt, "Job Import Error:" + errorMessage) - }, value => { - try { - val jobDetail = JobUtils.cleanup(value) - scheduler.addJob(jobDetail, false) - val triggersOpt = (jsObject \ "triggers").asOpt[List[JsObject]] - val triggersBinding = triggersOpt.map(_.map(triggerFormHelper.buildTriggerForm.bind(_, maxFormSize))).getOrElse(Nil) - if(triggersBinding.exists(b => b.hasErrors || b.hasGlobalErrors)) { - val errorMessage = formErrorStr(triggersBinding.filter(_.hasErrors).flatMap(_.errors)) + jobFormHelper.buildJobForm + .bind(jsObject, maxFormSize) + .fold( + e => { + val jobKey = for { + name <- (jsObject \ "name").validate[String] + group <- (jsObject \ "group").validate[String] + } yield { + new JobKey(name, group) + } + val errorMessage = formErrorStr(e.errors) logger.error(errorMessage) - ImportFailure(Some(jobDetail.getKey), "Trigger Import Error:"+errorMessage) - } else { - val triggers = triggersBinding.flatMap(_.value) - triggers.foreach { case TriggerFormValue(trigger, monitoringPriority, errorTime, monitoringTeam) => - scheduler.scheduleJob(trigger) - triggerMonitoringPriorityModel.setTriggerMonitoringRecord( - trigger.getKey, - monitoringPriority, - errorTime, - monitoringTeam, - ) + ImportFailure(jobKey.asOpt, "Job Import Error:" + errorMessage) + }, + value => { + try { + val jobDetail = JobUtils.cleanup(value) + scheduler.addJob(jobDetail, false) + val triggersOpt = (jsObject \ "triggers").asOpt[List[JsObject]] + val triggersBinding = + triggersOpt.map(_.map(triggerFormHelper.buildTriggerForm.bind(_, maxFormSize))).getOrElse(Nil) + if (triggersBinding.exists(b => b.hasErrors || b.hasGlobalErrors)) { + val errorMessage = formErrorStr(triggersBinding.filter(_.hasErrors).flatMap(_.errors)) + logger.error(errorMessage) + ImportFailure(Some(jobDetail.getKey), "Trigger Import Error:" + errorMessage) + } else { + val triggers = triggersBinding.flatMap(_.value) + triggers.foreach { case TriggerFormValue(trigger, monitoringPriority, errorTime, monitoringTeam) => + scheduler.scheduleJob(trigger) + triggerMonitoringPriorityModel.setTriggerMonitoringRecord( + trigger.getKey, + monitoringPriority, + errorTime, + monitoringTeam, + ) + } + ImportSuccess(Some(jobDetail.getKey)) + } + } catch { + case _: ObjectAlreadyExistsException => + logger.error( + s"Failed to add Job ${value.getKey.getGroup} - ${value.getKey.getGroup} because it already exists", + ) + ImportFailure(Some(value.getKey), "The Job Already Exists") } - ImportSuccess(Some(jobDetail.getKey)) - } - } catch { - case _: ObjectAlreadyExistsException => - logger.error(s"Failed to add Job ${value.getKey.getGroup} - ${value.getKey.getGroup} because it already exists") - ImportFailure(Some(value.getKey), "The Job Already Exists") - } - }) + }, + ) } } @@ -264,62 +317,75 @@ class Jobs(schedulerFactory: WorkerSchedulerFactory, jobView: html.job, cc: Cont } } } - jsonOpt.map { json => - val results = parseJson(json) - if(results.forall(_.success == true)) { - Created(Json.obj( - "count" -> results.size, - "failures" -> Json.arr() - )) - } else { - Created(Json.obj( - "count" -> results.size, - "failures" -> results.filter(_.success == false).map(_.toJson) - )) + jsonOpt + .map { json => + val results = parseJson(json) + if (results.forall(_.success == true)) { + Created( + Json.obj( + "count" -> results.size, + "failures" -> Json.arr(), + ), + ) + } else { + Created( + Json.obj( + "count" -> results.size, + "failures" -> results.filter(_.success == false).map(_.toJson), + ), + ) + } + } + .getOrElse { + BadRequest } - }.getOrElse { - BadRequest - } } def postJob = Action { implicit request => - jobFormHelper.buildJobForm.bindFromRequest().fold( - formWithErrors => - BadRequest(com.lucidchart.piezo.admin.views.html.editJob(getJobsByGroup(), formWithErrors, submitNewMessage, formNewAction, false)), - value => { - try { - val jobDetail = JobUtils.cleanup(value) - scheduler.addJob(jobDetail, false) - Redirect(routes.Jobs.getJob(value.getKey.getGroup(), value.getKey.getName())) - .flashing("message" -> "Successfully added job.", "class" -> "") - } catch { - case alreadyExists: ObjectAlreadyExistsException => - val form = jobFormHelper.buildJobForm.fill(value) - Ok(com.lucidchart.piezo.admin.views.html.editJob(getJobsByGroup(), - form, - submitNewMessage, - formNewAction, - false, - errorMessage = Some("Please provide unique group-name pair") - )(request, implicitly)) - } - } - ) + jobFormHelper.buildJobForm + .bindFromRequest() + .fold( + formWithErrors => + BadRequest( + com.lucidchart.piezo.admin.views.html + .editJob(getJobsByGroup(), formWithErrors, submitNewMessage, formNewAction, false), + ), + value => { + try { + val jobDetail = JobUtils.cleanup(value) + scheduler.addJob(jobDetail, false) + Redirect(routes.Jobs.getJob(value.getKey.getGroup(), value.getKey.getName())) + .flashing("message" -> "Successfully added job.", "class" -> "") + } catch { + case alreadyExists: ObjectAlreadyExistsException => + val form = jobFormHelper.buildJobForm.fill(value) + Ok( + com.lucidchart.piezo.admin.views.html.editJob( + getJobsByGroup(), + form, + submitNewMessage, + formNewAction, + false, + errorMessage = Some("Please provide unique group-name pair"), + )(request, implicitly), + ) + } + }, + ) } - def jobGroupTypeAhead(sofar: String) = Action { implicit request => + def jobGroupTypeAhead(sofar: String) = Action { request => val groups = scheduler.getJobGroupNames().asScala.toList - Ok(Json.obj("groups" -> groups.filter{ group => + Ok(Json.obj("groups" -> groups.filter { group => group.toLowerCase.contains(sofar.toLowerCase) })) } - def jobNameTypeAhead(group: String, sofar: String) = Action { implicit request => + def jobNameTypeAhead(group: String, sofar: String) = Action { request => val jobs = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(group)).asScala.toSet Ok(Json.obj("jobs" -> jobs.filter(_.getName.toLowerCase.contains(sofar.toLowerCase)).map(_.getName))) } - } diff --git a/admin/app/com/lucidchart/piezo/admin/controllers/TriggerFormHelper.scala b/admin/app/com/lucidchart/piezo/admin/controllers/TriggerFormHelper.scala index 6cdb8d1..364cbee 100644 --- a/admin/app/com/lucidchart/piezo/admin/controllers/TriggerFormHelper.scala +++ b/admin/app/com/lucidchart/piezo/admin/controllers/TriggerFormHelper.scala @@ -11,8 +11,14 @@ import play.api.data.Forms._ import play.api.data.format.Formats.parsing import play.api.data.format.Formatter import play.api.data.validation.{Constraint, Constraints, Invalid, Valid, ValidationError} +import scala.language.existentials -case class TriggerFormValue(trigger: Trigger, priority: TriggerMonitoringPriority, maxErrorTime: Int, monitoringTeam: Option[String]) +case class TriggerFormValue( + trigger: Trigger, + priority: TriggerMonitoringPriority, + maxErrorTime: Int, + monitoringTeam: Option[String], +) class TriggerFormHelper(scheduler: Scheduler, monitoringTeams: MonitoringTeams) extends JobDataHelper { private def simpleScheduleFormApply(repeatCount: Int, repeatInterval: Int): SimpleScheduleBuilder = { @@ -33,7 +39,7 @@ class TriggerFormHelper(scheduler: Scheduler, monitoringTeams: MonitoringTeams) private def cronScheduleFormUnapply(cron: CronScheduleBuilder) = { val cronTrigger = cron.build().asInstanceOf[CronTrigger] - Some((cronTrigger.getCronExpression())) + Some(cronTrigger.getCronExpression()) } private def triggerFormApply( @@ -55,7 +61,7 @@ class TriggerFormHelper(scheduler: Scheduler, monitoringTeams: MonitoringTeams) .withIdentity(name, group) .withDescription(description) .withSchedule(triggerType match { - case "cron" => cron.get + case "cron" => cron.get case "simple" => simple.get }) .forJob(jobName, jobGroup) @@ -86,8 +92,8 @@ class TriggerFormHelper(scheduler: Scheduler, monitoringTeams: MonitoringTeams) ), ] = { val trigger = value.trigger - val (triggerType: String, simple, cron) = trigger match { - case cron: CronTrigger => ("cron", None, Some(cron.getScheduleBuilder)) + val (triggerType: String, simple, cron) = (trigger: @unchecked) match { + case cron: CronTrigger => ("cron", None, Some(cron.getScheduleBuilder)) case simple: SimpleTrigger => ("simple", Some(simple.getScheduleBuilder), None) } val description = if (trigger.getDescription() == null) "" else trigger.getDescription() @@ -167,8 +173,9 @@ class TriggerFormHelper(scheduler: Scheduler, monitoringTeams: MonitoringTeams) .verifying( "A valid team is required if monitoring is on", fields => { - !monitoringTeams.teamsDefined || fields.priority == TriggerMonitoringPriority.Off || fields.monitoringTeam.exists(monitoringTeams.value.contains[String]) - } + !monitoringTeams.teamsDefined || fields.priority == TriggerMonitoringPriority.Off || fields.monitoringTeam + .exists(monitoringTeams.value.contains[String]) + }, ), ) } diff --git a/admin/app/com/lucidchart/piezo/admin/controllers/Triggers.scala b/admin/app/com/lucidchart/piezo/admin/controllers/Triggers.scala index 8ef213d..02a4015 100644 --- a/admin/app/com/lucidchart/piezo/admin/controllers/Triggers.scala +++ b/admin/app/com/lucidchart/piezo/admin/controllers/Triggers.scala @@ -5,18 +5,19 @@ import java.util.Date import org.quartz.Trigger.TriggerState import org.quartz._ import org.quartz.impl.triggers.{CronTriggerImpl, SimpleTriggerImpl} -import play.api._ import play.api.libs.json._ import play.api.mvc._ -import com.lucidchart.piezo.admin.models._ +import com.lucidchart.piezo.admin.models.MonitoringTeams import scala.collection.mutable import scala.jdk.CollectionConverters._ import scala.util.Try import play.api.Logging -import play.api.i18n.I18nSupport class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponents, monitoringTeams: MonitoringTeams) - extends AbstractController(cc) with Logging with ErrorLogging with play.api.i18n.I18nSupport { + extends AbstractController(cc) + with Logging + with ErrorLogging + with play.api.i18n.I18nSupport { val scheduler = logExceptions(schedulerFactory.getScheduler()) val properties = schedulerFactory.props @@ -33,20 +34,24 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent else trigger1.getPriority > trigger2.getPriority } - def getIndex = Action { implicit request => - val now = new Date() - val allTriggers: List[Trigger] = TriggerHelper.getTriggersByGroup(scheduler).flatMap { case (group, triggerKeys) => - triggerKeys.map(triggerKey => scheduler.getTrigger(triggerKey)) - }.toList.sortWith(firesFirst(now)) - Ok( - com.lucidchart.piezo.admin.views.html.triggers( + def getIndex = Action { implicit request => + val now = new Date() + val allTriggers: List[Trigger] = TriggerHelper + .getTriggersByGroup(scheduler) + .flatMap { case (group, triggerKeys) => + triggerKeys.map(triggerKey => scheduler.getTrigger(triggerKey)) + } + .toList + .sortWith(firesFirst(now)) + Ok( + com.lucidchart.piezo.admin.views.html.triggers( TriggerHelper.getTriggersByGroup(scheduler), None, allTriggers, - scheduler.getMetaData - )(request) - ) - } + scheduler.getMetaData, + )(request), + ) + } def getTrigger(group: String, name: String) = Action { implicit request => val triggerKey = new TriggerKey(name, group) @@ -70,11 +75,18 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent } val (triggerMonitoringPriority, triggerMaxErrorTime, triggerMonitoringTeam) = Try { - triggerMonitoringPriorityModel.getTriggerMonitoringRecord( - triggerDetail.getKey - ).map { triggerMonitoringRecord => - (triggerMonitoringRecord.priority, triggerMonitoringRecord.maxSecondsInError, triggerMonitoringRecord.monitoringTeam) - }.getOrElse((TriggerMonitoringPriority.Low, 300, None)) + triggerMonitoringPriorityModel + .getTriggerMonitoringRecord( + triggerDetail.getKey, + ) + .map { triggerMonitoringRecord => + ( + triggerMonitoringRecord.priority, + triggerMonitoringRecord.maxSecondsInError, + triggerMonitoringRecord.monitoringTeam, + ) + } + .getOrElse((TriggerMonitoringPriority.Low, 300, None)) }.getOrElse { logger.error("Failed to get trigger monitoring info") (TriggerMonitoringPriority.Low, 300, None) @@ -89,8 +101,8 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent Some(triggerMonitoringPriority), triggerMaxErrorTime, triggerMonitoringTeam, - triggerState = Some(scheduler.getTriggerState(triggerKey)) - )(request) + triggerState = Some(scheduler.getTriggerState(triggerKey)), + )(request), ) } catch { case e: Exception => { @@ -101,8 +113,8 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent mutable.Buffer(), None, None, - Some(errorMsg) - )(request) + Some(errorMsg), + )(request), ) } } @@ -123,8 +135,8 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent com.lucidchart.piezo.admin.views.html.trigger( TriggerHelper.getTriggersByGroup(scheduler), None, - None - )(request) + None, + )(request), ) } catch { case e: Exception => { @@ -135,8 +147,8 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent mutable.Buffer(), None, None, - Some(errorMsg) - )(request) + Some(errorMsg), + )(request), ) } } @@ -151,7 +163,7 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent jobGroup: String = "", jobName: String = "", templateGroup: Option[String] = None, - templateName: Option[String] = None + templateName: Option[String] = None, ) = Action { implicit request => templateGroup match { case Some(group) => getEditTrigger(group, templateName.get, true) @@ -161,7 +173,7 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent case "simple" => new DummySimpleTrigger(jobGroup, jobName) } val newTriggerForm = triggerFormHelper.buildTriggerForm.fill( - TriggerFormValue(dummyTrigger, TriggerMonitoringPriority.Low, 300, None) + TriggerFormValue(dummyTrigger, TriggerMonitoringPriority.Low, 300, None), ) Ok( com.lucidchart.piezo.admin.views.html.editTrigger( @@ -170,8 +182,8 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent newTriggerForm, formNewAction, false, - false - ) + false, + ), ) } } @@ -185,17 +197,24 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent } else { val triggerDetail: Trigger = scheduler.getTrigger(triggerKey) val (triggerMonitoringPriority, triggerMaxErrorTime, triggerMonitoringTeam) = Try { - triggerMonitoringPriorityModel.getTriggerMonitoringRecord( - triggerDetail.getKey, - ).map { triggerMonitoringRecord => - (triggerMonitoringRecord.priority, triggerMonitoringRecord.maxSecondsInError, triggerMonitoringRecord.monitoringTeam) - }.getOrElse((TriggerMonitoringPriority.Low, 300, None)) + triggerMonitoringPriorityModel + .getTriggerMonitoringRecord( + triggerDetail.getKey, + ) + .map { triggerMonitoringRecord => + ( + triggerMonitoringRecord.priority, + triggerMonitoringRecord.maxSecondsInError, + triggerMonitoringRecord.monitoringTeam, + ) + } + .getOrElse((TriggerMonitoringPriority.Low, 300, None)) }.getOrElse { logger.error("Failed to get trigger monitoring info") (TriggerMonitoringPriority.Low, 300, None) } val editTriggerForm = triggerFormHelper.buildTriggerForm.fill( - TriggerFormValue(triggerDetail, triggerMonitoringPriority, triggerMaxErrorTime, triggerMonitoringTeam) + TriggerFormValue(triggerDetail, triggerMonitoringPriority, triggerMaxErrorTime, triggerMonitoringTeam), ) if (isTemplate) { Ok( @@ -205,11 +224,10 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent editTriggerForm, formNewAction, false, - isTemplate - ) + isTemplate, + ), ) - } - else { + } else { Ok( com.lucidchart.piezo.admin.views.html.editTrigger( TriggerHelper.getTriggersByGroup(scheduler), @@ -217,8 +235,8 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent editTriggerForm, formEditAction(group, name), true, - isTemplate - ) + isTemplate, + ), ) } } @@ -229,88 +247,92 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent } def putTrigger(group: String, name: String) = Action { implicit request => - triggerFormHelper.buildTriggerForm.bindFromRequest().fold( - formWithErrors => - BadRequest( - com.lucidchart.piezo.admin.views.html.editTrigger( - TriggerHelper.getTriggersByGroup(scheduler), - monitoringTeams.value, - formWithErrors, - formEditAction(group, name), - true, - false - ) - ), - value => { - val TriggerFormValue(trigger, triggerMonitoringPriority, triggerMaxErrorTime, triggerMonitoringTeam) = value - scheduler.rescheduleJob(trigger.getKey(), trigger) - triggerMonitoringPriorityModel.setTriggerMonitoringRecord( - trigger.getKey, - triggerMonitoringPriority, - triggerMaxErrorTime, - triggerMonitoringTeam - ) - Redirect(routes.Triggers.getTrigger(trigger.getKey.getGroup(), trigger.getKey.getName())) - .flashing("message" -> "Successfully added trigger.", "class" -> "") - } - ) - } - - def postTrigger() = Action { implicit request => - triggerFormHelper.buildTriggerForm.bindFromRequest().fold( - formWithErrors => - BadRequest( - com.lucidchart.piezo.admin.views.html.editTrigger( - TriggerHelper.getTriggersByGroup(scheduler), - monitoringTeams.value, - formWithErrors, - formNewAction, - false, - false - ) - ), - value => { - val TriggerFormValue(trigger, triggerMonitoringPriority, triggerMaxErrorTime, triggerMonitoringTeam) = value - try { - scheduler.scheduleJob(trigger) + triggerFormHelper.buildTriggerForm + .bindFromRequest() + .fold( + formWithErrors => + BadRequest( + com.lucidchart.piezo.admin.views.html.editTrigger( + TriggerHelper.getTriggersByGroup(scheduler), + monitoringTeams.value, + formWithErrors, + formEditAction(group, name), + true, + false, + ), + ), + value => { + val TriggerFormValue(trigger, triggerMonitoringPriority, triggerMaxErrorTime, triggerMonitoringTeam) = value + scheduler.rescheduleJob(trigger.getKey(), trigger) triggerMonitoringPriorityModel.setTriggerMonitoringRecord( trigger.getKey, triggerMonitoringPriority, triggerMaxErrorTime, - triggerMonitoringTeam + triggerMonitoringTeam, ) Redirect(routes.Triggers.getTrigger(trigger.getKey.getGroup(), trigger.getKey.getName())) .flashing("message" -> "Successfully added trigger.", "class" -> "") - } catch { - case alreadyExists: ObjectAlreadyExistsException => - val form = triggerFormHelper.buildTriggerForm.fill( - TriggerFormValue(trigger, triggerMonitoringPriority, triggerMaxErrorTime, triggerMonitoringTeam) + }, + ) + } + + def postTrigger() = Action { implicit request => + triggerFormHelper.buildTriggerForm + .bindFromRequest() + .fold( + formWithErrors => + BadRequest( + com.lucidchart.piezo.admin.views.html.editTrigger( + TriggerHelper.getTriggersByGroup(scheduler), + monitoringTeams.value, + formWithErrors, + formNewAction, + false, + false, + ), + ), + value => { + val TriggerFormValue(trigger, triggerMonitoringPriority, triggerMaxErrorTime, triggerMonitoringTeam) = value + try { + scheduler.scheduleJob(trigger) + triggerMonitoringPriorityModel.setTriggerMonitoringRecord( + trigger.getKey, + triggerMonitoringPriority, + triggerMaxErrorTime, + triggerMonitoringTeam, ) - Ok( - com.lucidchart.piezo.admin.views.html.editTrigger( - TriggerHelper.getTriggersByGroup(scheduler), - monitoringTeams.value, - form, - formNewAction, - false, - false, - errorMessage = Some("Please provide unique group-name pair") + Redirect(routes.Triggers.getTrigger(trigger.getKey.getGroup(), trigger.getKey.getName())) + .flashing("message" -> "Successfully added trigger.", "class" -> "") + } catch { + case alreadyExists: ObjectAlreadyExistsException => + val form = triggerFormHelper.buildTriggerForm.fill( + TriggerFormValue(trigger, triggerMonitoringPriority, triggerMaxErrorTime, triggerMonitoringTeam), ) - ) - } - } - ) + Ok( + com.lucidchart.piezo.admin.views.html.editTrigger( + TriggerHelper.getTriggersByGroup(scheduler), + monitoringTeams.value, + form, + formNewAction, + false, + false, + errorMessage = Some("Please provide unique group-name pair"), + ), + ) + } + }, + ) } - def triggerGroupTypeAhead(sofar: String) = Action { implicit request => + def triggerGroupTypeAhead(sofar: String) = Action { request => val groups = scheduler.getTriggerGroupNames().asScala.toList - Ok(Json.obj("groups" -> groups.filter{ group => + Ok(Json.obj("groups" -> groups.filter { group => group.toLowerCase.contains(sofar.toLowerCase) })) } - def triggerJob(group: String, name: String) = Action { implicit request => + def triggerJob(group: String, name: String) = Action { request => val jobKey = new JobKey(name, group) if (scheduler.checkExists(jobKey)) { @@ -332,33 +354,36 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent val triggerKey = new TriggerKey(name, group) val triggerExists = scheduler.checkExists(triggerKey) if (!triggerExists) { - NotFound(com.lucidchart.piezo.admin.views.html.trigger( - mutable.Buffer(), - None, - None, - errorMessage = Some(s"Trigger $group:$name not found"))(request) + NotFound( + com.lucidchart.piezo.admin.views.html + .trigger(mutable.Buffer(), None, None, errorMessage = Some(s"Trigger $group:$name not found"))(request), ) } else { - request.body.asJson.map { json => - (json \ "state").asOpt[String].map { state => - if(state.equalsIgnoreCase(TriggerState.PAUSED.toString)) { - scheduler.pauseTrigger(triggerKey) - } else { - val currentState = scheduler.getTriggerState(triggerKey).toString - if(currentState == TriggerState.ERROR.toString) { - val trigger = scheduler.getTrigger(triggerKey) - scheduler.rescheduleJob(triggerKey, trigger) - } else { - scheduler.resumeTrigger(triggerKey) + request.body.asJson + .map { json => + (json \ "state") + .asOpt[String] + .map { state => + if (state.equalsIgnoreCase(TriggerState.PAUSED.toString)) { + scheduler.pauseTrigger(triggerKey) + } else { + val currentState = scheduler.getTriggerState(triggerKey).toString + if (currentState == TriggerState.ERROR.toString) { + val trigger = scheduler.getTrigger(triggerKey) + scheduler.rescheduleJob(triggerKey, trigger) + } else { + scheduler.resumeTrigger(triggerKey) + } + } + Ok(Json.obj("state" -> scheduler.getTriggerState(triggerKey).toString)) + } + .getOrElse { + BadRequest("Missing parameter [state]") } - } - Ok(Json.obj("state" -> scheduler.getTriggerState(triggerKey).toString)) - }.getOrElse { - BadRequest("Missing parameter [state]") } - }.getOrElse { - BadRequest("Expecting Json data") - } + .getOrElse { + BadRequest("Expecting Json data") + } } } @@ -372,10 +397,10 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent private class DummyCronTrigger(jobGroup: String, jobName: String) extends CronTriggerImpl with DummyTrigger { override def getCronExpression() = "7 7 7 * * ?" - override def getJobKey() = { new JobKey(jobName, jobGroup)} + override def getJobKey() = { new JobKey(jobName, jobGroup) } } private class DummySimpleTrigger(jobGroup: String, jobName: String) extends SimpleTriggerImpl with DummyTrigger { - override def getJobKey() = { new JobKey(jobName, jobGroup)} + override def getJobKey() = { new JobKey(jobName, jobGroup) } } } diff --git a/admin/app/com/lucidchart/piezo/admin/models/MonitoringTeams.scala b/admin/app/com/lucidchart/piezo/admin/models/MonitoringTeams.scala index 4cbe6fc..b23bfcb 100644 --- a/admin/app/com/lucidchart/piezo/admin/models/MonitoringTeams.scala +++ b/admin/app/com/lucidchart/piezo/admin/models/MonitoringTeams.scala @@ -1,10 +1,8 @@ package com.lucidchart.piezo.admin.models import play.api.Configuration -import java.nio.file.Files import play.api.libs.json.Json import scala.util.Try -import java.io.File import java.io.FileInputStream import play.api.libs.json.JsArray import play.api.Logging @@ -18,19 +16,21 @@ object MonitoringTeams extends Logging { def apply(configuration: Configuration): MonitoringTeams = { val path = configuration.getOptional[String]("com.lucidchart.piezo.admin.monitoringTeams.path") - val value = path.flatMap { p => - Try { - Json.parse(new FileInputStream(p)) - .as[JsArray] - .value - .map(entry => (entry \ "name").as[String]) - .toSeq - }.recoverWith { - case NonFatal(e) => + val value = path + .flatMap { p => + Try { + Json + .parse(new FileInputStream(p)) + .as[JsArray] + .value + .map(entry => (entry \ "name").as[String]) + .toSeq + }.recoverWith { case NonFatal(e) => logger.error(s"Error reading monitoring teams from $p", e) Failure(e) - }.toOption - }.getOrElse(Seq.empty) + }.toOption + } + .getOrElse(Seq.empty) MonitoringTeams(value) } diff --git a/admin/app/com/lucidchart/piezo/admin/utils/JobDetailHelper.scala b/admin/app/com/lucidchart/piezo/admin/utils/JobDetailHelper.scala index 24fb669..fcb031c 100644 --- a/admin/app/com/lucidchart/piezo/admin/utils/JobDetailHelper.scala +++ b/admin/app/com/lucidchart/piezo/admin/utils/JobDetailHelper.scala @@ -1,7 +1,6 @@ package com.lucidchart.piezo.admin.utils import play.api.libs.json._ import org.quartz._ -import scala.collection.JavaConverters._ import com.lucidchart.piezo.admin.controllers.{JobDataHelper, JobFormHelper, TriggerHelper} import com.lucidchart.piezo.TriggerMonitoringModel diff --git a/admin/app/com/lucidchart/piezo/admin/views/editJob.scala.html b/admin/app/com/lucidchart/piezo/admin/views/editJob.scala.html index 02cbe03..ebe7cac 100644 --- a/admin/app/com/lucidchart/piezo/admin/views/editJob.scala.html +++ b/admin/app/com/lucidchart/piezo/admin/views/editJob.scala.html @@ -12,9 +12,6 @@ messagesProvider: play.api.i18n.MessagesProvider ) -@import com.lucidchart.piezo.admin.controllers.{routes=>piezoRoutes} -@import com.lucidchart.piezo.admin.views -@import org.quartz._ @import com.lucidchart.piezo.admin.views.FormHelpers._ @com.lucidchart.piezo.admin.views.html.jobsLayout(jobsByGroup, None, scripts) { diff --git a/admin/app/com/lucidchart/piezo/admin/views/editTrigger.scala.html b/admin/app/com/lucidchart/piezo/admin/views/editTrigger.scala.html index 8efc8d0..b057e4a 100644 --- a/admin/app/com/lucidchart/piezo/admin/views/editTrigger.scala.html +++ b/admin/app/com/lucidchart/piezo/admin/views/editTrigger.scala.html @@ -15,9 +15,6 @@ @import com.lucidchart.piezo.TriggerMonitoringPriority @import com.lucidchart.piezo.admin.controllers.{routes=>piezoRoutes} -@import com.lucidchart.piezo.admin.views -@import java.net.URLEncoder -@import org.quartz._ @import com.lucidchart.piezo.admin.views.FormHelpers._ @com.lucidchart.piezo.admin.views.html.triggersLayout(triggersByGroup, None, scripts) { diff --git a/admin/app/com/lucidchart/piezo/admin/views/errors/error.scala.html b/admin/app/com/lucidchart/piezo/admin/views/errors/error.scala.html index cd4447e..30488f2 100644 --- a/admin/app/com/lucidchart/piezo/admin/views/errors/error.scala.html +++ b/admin/app/com/lucidchart/piezo/admin/views/errors/error.scala.html @@ -4,11 +4,10 @@ implicit request: RequestHeader ) -@import com.lucidchart.piezo.admin.views @com.lucidchart.piezo.admin.views.html.main("Piezo Error") {
@errorMessage.getOrElse("Unknown Error")
@message.getOrElse("The page you requested could not be located.")