diff --git a/admin/app/Application.scala b/admin/app/Application.scala
index b84ca05..6b0bf54 100644
--- a/admin/app/Application.scala
+++ b/admin/app/Application.scala
@@ -27,6 +27,7 @@ class PiezoAdminComponents(context: Context) extends BuiltInComponentsFromContex
lazy val schedulerFactory: WorkerSchedulerFactory = new WorkerSchedulerFactory()
lazy val jobFormHelper: JobFormHelper = wire[JobFormHelper]
+ lazy val monitoringTeams: MonitoringTeams = wire[MonitoringTeams]
lazy val triggers: Triggers = wire[Triggers]
lazy val jobs: Jobs = wire[Jobs]
diff --git a/admin/app/com/lucidchart/piezo/admin/controllers/Jobs.scala b/admin/app/com/lucidchart/piezo/admin/controllers/Jobs.scala
index d15b579..47bd9fc 100644
--- a/admin/app/com/lucidchart/piezo/admin/controllers/Jobs.scala
+++ b/admin/app/com/lucidchart/piezo/admin/controllers/Jobs.scala
@@ -14,6 +14,7 @@ import scala.jdk.CollectionConverters._
import scala.collection.mutable
import scala.Some
import scala.io.Source
+import com.lucidchart.piezo.admin.models.MonitoringTeams
trait ImportResult {
val jobKey: Option[JobKey]
@@ -35,14 +36,14 @@ trait ImportResult {
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) extends AbstractController(cc) with Logging with ErrorLogging with play.api.i18n.I18nSupport {
+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))
val triggerMonitoringPriorityModel = logExceptions(new TriggerMonitoringModel(properties))
val jobFormHelper = new JobFormHelper()
- val triggerFormHelper = new TriggerFormHelper(scheduler)
+ val triggerFormHelper = new TriggerFormHelper(scheduler, monitoringTeams)
// Allow up to 1M
private val maxFormSize = 1024 * 1024
diff --git a/admin/app/com/lucidchart/piezo/admin/controllers/TriggerFormHelper.scala b/admin/app/com/lucidchart/piezo/admin/controllers/TriggerFormHelper.scala
index 0388e8a..33cb7d0 100644
--- a/admin/app/com/lucidchart/piezo/admin/controllers/TriggerFormHelper.scala
+++ b/admin/app/com/lucidchart/piezo/admin/controllers/TriggerFormHelper.scala
@@ -2,6 +2,7 @@ package com.lucidchart.piezo.admin.controllers
import com.lucidchart.piezo.TriggerMonitoringPriority
import com.lucidchart.piezo.TriggerMonitoringPriority.TriggerMonitoringPriority
+import com.lucidchart.piezo.admin.models.MonitoringTeams
import com.lucidchart.piezo.admin.utils.CronHelper
import java.text.ParseException
import org.quartz._
@@ -11,7 +12,7 @@ import play.api.data.format.Formats.parsing
import play.api.data.format.Formatter
import play.api.data.validation.{Constraint, Constraints, Invalid, Valid, ValidationError}
-class TriggerFormHelper(scheduler: Scheduler) extends JobDataHelper {
+class TriggerFormHelper(scheduler: Scheduler, monitoringTeams: MonitoringTeams) extends JobDataHelper {
private def simpleScheduleFormApply(repeatCount: Int, repeatInterval: Int): SimpleScheduleBuilder = {
SimpleScheduleBuilder
@@ -161,6 +162,12 @@ class TriggerFormHelper(scheduler: Scheduler) extends JobDataHelper {
fields => {
scheduler.checkExists(fields._1.getJobKey)
},
+ )
+ .verifying(
+ "Team is required if monitoring is on",
+ fields => {
+ !monitoringTeams.teamsDefined || fields._2 == TriggerMonitoringPriority.Off || fields._4.nonEmpty
+ }
),
)
}
diff --git a/admin/app/com/lucidchart/piezo/admin/controllers/Triggers.scala b/admin/app/com/lucidchart/piezo/admin/controllers/Triggers.scala
index d8b4f5c..8d4e4b6 100644
--- a/admin/app/com/lucidchart/piezo/admin/controllers/Triggers.scala
+++ b/admin/app/com/lucidchart/piezo/admin/controllers/Triggers.scala
@@ -15,13 +15,14 @@ import scala.util.Try
import play.api.Logging
import play.api.i18n.I18nSupport
-class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponents) extends AbstractController(cc) with Logging with ErrorLogging with play.api.i18n.I18nSupport {
+class Triggers(schedulerFactory: WorkerSchedulerFactory, 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 triggerHistoryModel = logExceptions(new TriggerHistoryModel(properties))
val triggerMonitoringPriorityModel = logExceptions(new TriggerMonitoringModel(properties))
- val triggerFormHelper = new TriggerFormHelper(scheduler)
+ val triggerFormHelper = new TriggerFormHelper(scheduler, monitoringTeams)
def firesFirst(time: Date)(trigger1: Trigger, trigger2: Trigger): Boolean = {
val time1 = trigger1.getFireTimeAfter(time)
@@ -165,12 +166,12 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent
Ok(
com.lucidchart.piezo.admin.views.html.editTrigger(
TriggerHelper.getTriggersByGroup(scheduler),
+ monitoringTeams.value,
newTriggerForm,
formNewAction,
false,
false
)
- (request, implicitly)
)
}
}
@@ -200,22 +201,24 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent
Ok(
com.lucidchart.piezo.admin.views.html.editTrigger(
TriggerHelper.getTriggersByGroup(scheduler),
+ monitoringTeams.value,
editTriggerForm,
formNewAction,
false,
isTemplate
- )(request, implicitly)
+ )
)
}
else {
Ok(
com.lucidchart.piezo.admin.views.html.editTrigger(
TriggerHelper.getTriggersByGroup(scheduler),
+ monitoringTeams.value,
editTriggerForm,
formEditAction(group, name),
true,
isTemplate
- )(request, implicitly)
+ )
)
}
}
@@ -231,6 +234,7 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent
BadRequest(
com.lucidchart.piezo.admin.views.html.editTrigger(
TriggerHelper.getTriggersByGroup(scheduler),
+ monitoringTeams.value,
formWithErrors,
formEditAction(group, name),
true,
@@ -258,6 +262,7 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent
BadRequest(
com.lucidchart.piezo.admin.views.html.editTrigger(
TriggerHelper.getTriggersByGroup(scheduler),
+ monitoringTeams.value,
formWithErrors,
formNewAction,
false,
@@ -284,12 +289,13 @@ class Triggers(schedulerFactory: WorkerSchedulerFactory, cc: ControllerComponent
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")
- )(request, implicitly)
+ )
)
}
}
diff --git a/admin/app/com/lucidchart/piezo/admin/models/MonitoringTeams.scala b/admin/app/com/lucidchart/piezo/admin/models/MonitoringTeams.scala
new file mode 100644
index 0000000..973c3c6
--- /dev/null
+++ b/admin/app/com/lucidchart/piezo/admin/models/MonitoringTeams.scala
@@ -0,0 +1,28 @@
+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
+
+class MonitoringTeams(configuration: Configuration) {
+ private 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
+ ).toOption
+ ).getOrElse(Seq.empty)
+
+ def teamsDefined: Boolean = value.nonEmpty
+}
+object MonitoringTeams {
+ def empty: MonitoringTeams = new MonitoringTeams(Configuration.empty)
+}
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 be423e8..e3e519f 100644
--- a/admin/app/com/lucidchart/piezo/admin/views/editTrigger.scala.html
+++ b/admin/app/com/lucidchart/piezo/admin/views/editTrigger.scala.html
@@ -1,15 +1,16 @@
@(
triggersByGroup: scala.collection.mutable.Buffer[(String, scala.collection.immutable.List[org.quartz.TriggerKey])],
+monitoringTeams: Seq[String],
triggerForm: Form[(org.quartz.Trigger, com.lucidchart.piezo.TriggerMonitoringPriority.Value, Int, Option[String])],
formAction: play.api.mvc.Call,
existing: Boolean,
isTemplate: Boolean,
errorMessage: Option[String] = None,
-scripts: List[String] = List[String]("js/jobData.js", "js/typeAhead.js")
+scripts: List[String] = List[String]("js/jobData.js", "js/typeAhead.js", "js/triggerMonitoring.js")
)(
implicit
request: play.api.mvc.Request[AnyContent],
-messagesProvider: play.api.i18n.MessagesProvider
+messagesProvider: play.api.i18n.MessagesProvider,
)
@import com.lucidchart.piezo.TriggerMonitoringPriority
@@ -67,12 +68,18 @@
@triggerForm.errors.filter(_.key == "").map(_.message).m
}
}
@helper.select(triggerForm("triggerMonitoringPriority"), TriggerMonitoringPriority.values.map(tp => tp.name -> tp.name), Symbol("_label") -> "Monitoring Priority", Symbol("labelClass") -> "col-sm-2 text-right", Symbol("inputDivClass") -> "col-sm-4", Symbol("class") -> "form-control", Symbol("value") -> triggerForm.data.get("triggerMonitoringPriority").getOrElse(TriggerMonitoringPriority.Low), Symbol("placeholder") -> TriggerMonitoringPriority.Low)
- @helper.input(triggerForm("triggerMaxErrorTime"), Symbol("_label") -> "Monitoring - Max Seconds Between Successes", Symbol("labelClass") -> "col-sm-2 text-right", Symbol("inputDivClass") -> "col-sm-4", Symbol("placeholder") -> "", Symbol("value") -> triggerForm.data.get("triggerMaxErrorTime").getOrElse(300)) { (id, name, value, args) =>
-
- }
- @helper.input(triggerForm("triggerMonitoringTeam"), Symbol("_label") -> "Monitoring team", Symbol("labelClass") -> "col-sm-2 text-right", Symbol("inputDivClass") -> "col-sm-4", Symbol("placeholder") -> "", Symbol("value") -> triggerForm.data.get("triggerMonitoringTeam").getOrElse(None)) { (id, name, value, args) =>
-
- }
+
+ @helper.input(triggerForm("triggerMaxErrorTime"), Symbol("_label") -> "Monitoring - Max Seconds Between Successes", Symbol("labelClass") -> "col-sm-2 text-right", Symbol("inputDivClass") -> "col-sm-4", Symbol("placeholder") -> "", Symbol("value") -> triggerForm.data.get("triggerMaxErrorTime").getOrElse(300)) { (id, name, value, args) =>
+
+ }
+ @if(monitoringTeams.nonEmpty) {
+ @helper.select(triggerForm("triggerMonitoringTeam"), monitoringTeams.map(mt => mt -> mt), Symbol("_default") -> "Select team", Symbol("_label") -> "Monitoring team", Symbol("labelClass") -> "col-sm-2 text-right", Symbol("inputDivClass") -> "col-sm-4", Symbol("class") -> "form-control", Symbol("value") -> triggerForm.data.get("triggerMonitoringTeam").getOrElse(""))
+ } else {
+ @helper.input(triggerForm("triggerMonitoringTeam"), Symbol("_label") -> "Monitoring team", Symbol("labelClass") -> "col-sm-2 text-right", Symbol("inputDivClass") -> "col-sm-4", Symbol("placeholder") -> "", Symbol("value") -> triggerForm.data.get("triggerMonitoringTeam").getOrElse(None)) { (id, name, value, args) =>
+
+ }
+ }
+
@helper.input(triggerForm("description"), Symbol("_label") -> "Description", Symbol("labelClass") -> "col-sm-2 text-right", Symbol("inputDivClass") -> "col-sm-10", Symbol("placeholder") -> "Description", Symbol("value")-> triggerForm.data.get("description").getOrElse("")) { (id, name, value, args) =>
diff --git a/admin/conf/application.conf b/admin/conf/application.conf
index 962802b..7907dbd 100644
--- a/admin/conf/application.conf
+++ b/admin/conf/application.conf
@@ -46,3 +46,14 @@ com.lucidchart.piezo.heartbeatFile="/tmp/piezo/workerHeartbeatFile"
com.lucidchart.piezo.admin.production=false
healthCheck.worker.minutesBetween=5
play.application.loader=com.lucidchart.piezo.admin.PiezoAdminApplicationLoader
+
+# Monitoring teams
+# ~~~~~
+# Path to a JSON file that fills the "Monitoring Team" dropdown on editTrigger
+# in the admin UI with a predefined set of team names. File format:
+# [
+# {"name": "team1"},
+# {"name": "team2"}
+# ]
+# If this is left blank, monitoring team will be a freeform input.
+# com.lucidchart.piezo.admin.monitoringTeams.path = "/etc/piezo/teams.json"
diff --git a/admin/public/js/triggerMonitoring.js b/admin/public/js/triggerMonitoring.js
new file mode 100644
index 0000000..f829b64
--- /dev/null
+++ b/admin/public/js/triggerMonitoring.js
@@ -0,0 +1,14 @@
+(function () {
+ var setMonitoringFieldVisibility = function() {
+ var priority = $('#triggerMonitoringPriority option:selected').val();
+ if (priority === 'Off') {
+ $('#triggerMonitoringDetails').hide();
+ } else {
+ $('#triggerMonitoringDetails').show();
+ }
+ };
+
+ $('#triggerMonitoringPriority').on('change', setMonitoringFieldVisibility);
+
+ setMonitoringFieldVisibility();
+})();
diff --git a/admin/test/com/lucidchart/piezo/admin/controllers/JobsService.scala b/admin/test/com/lucidchart/piezo/admin/controllers/JobsService.scala
index e283d33..2dfbc9d 100644
--- a/admin/test/com/lucidchart/piezo/admin/controllers/JobsService.scala
+++ b/admin/test/com/lucidchart/piezo/admin/controllers/JobsService.scala
@@ -1,7 +1,6 @@
package com.lucidchart.piezo.admin.controllers
import org.specs2.mutable._
-
import play.api.test._
import play.api.test.Helpers._
import com.lucidchart.piezo.jobs.monitoring.HeartBeat
@@ -14,8 +13,8 @@ import com.lucidchart.piezo.util.DummyClassGenerator
import play.api.mvc.{Result, AnyContentAsEmpty}
import java.util.Properties
import play.api.Configuration
-
import scala.concurrent.Future
+import com.lucidchart.piezo.admin.models.MonitoringTeams
/**
* Add your spec here.
@@ -39,7 +38,7 @@ class JobsService extends Specification {
properties.load(propertiesStream)
schedulerFactory.initialize(properties)
- val jobsController = new Jobs(schedulerFactory, jobView, Helpers.stubControllerComponents())
+ val jobsController = new Jobs(schedulerFactory, jobView, Helpers.stubControllerComponents(), MonitoringTeams.empty)
val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/jobs/missinggroup/missingname")
val missingJob: Future[Result] = jobsController.getJob("missinggroup", "missingname")(request)
@@ -57,8 +56,7 @@ class JobsService extends Specification {
val scheduler = schedulerFactory.getScheduler()
createJob(scheduler)
-
- val jobsController = new Jobs(schedulerFactory, jobView, Helpers.stubControllerComponents())
+ val jobsController = new Jobs(schedulerFactory, jobView, Helpers.stubControllerComponents(), MonitoringTeams.empty)
val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/jobs/" + jobGroup + "/" + jobName)
val validJob: Future[Result] = jobsController.getJob(jobGroup, jobName)(request)
diff --git a/admin/test/com/lucidchart/piezo/admin/controllers/TriggersService.scala b/admin/test/com/lucidchart/piezo/admin/controllers/TriggersService.scala
index 0c42bb7..4c3030d 100644
--- a/admin/test/com/lucidchart/piezo/admin/controllers/TriggersService.scala
+++ b/admin/test/com/lucidchart/piezo/admin/controllers/TriggersService.scala
@@ -1,7 +1,6 @@
package com.lucidchart.piezo.admin.controllers
import org.specs2.mutable._
-
import play.api.test._
import play.api.test.Helpers._
import com.lucidchart.piezo.WorkerSchedulerFactory
@@ -9,49 +8,49 @@ import TestUtil._
import java.util.Properties
import play.api.mvc.{AnyContentAsEmpty, Result}
import scala.concurrent.Future
+import com.lucidchart.piezo.admin.models.MonitoringTeams
/**
- * Add your spec here.
- * You can mock out a whole application including requests, plugins etc.
- * For more information, consult the wiki.
- */
+ * Add your spec here. You can mock out a whole application including requests, plugins etc. For more information,
+ * consult the wiki.
+ */
class TriggersService extends Specification {
- "Triggers" should {
-
- "send 404 on a non-existent trigger request" in {
- val schedulerFactory: WorkerSchedulerFactory = new WorkerSchedulerFactory()
-
- val propertiesStream = getClass().getResourceAsStream("/quartz_test.properties")
- val properties = new Properties
- properties.load(propertiesStream)
- schedulerFactory.initialize(properties)
-
- val triggersController = new Triggers(schedulerFactory, Helpers.stubControllerComponents())
- val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/triggers/missinggroup/missingname")
- val missingTrigger: Future[Result] = triggersController.getTrigger("missinggroup", "missingname")(request)
-
- status(missingTrigger) must equalTo(NOT_FOUND)
- contentType(missingTrigger) must beSome.which(_ == "text/html")
- contentAsString(missingTrigger) must contain ("Trigger missinggroup missingname not found")
- }
-
- "send valid trigger details" in {
- val schedulerFactory: WorkerSchedulerFactory = new WorkerSchedulerFactory()
- val propertiesStream = getClass().getResourceAsStream("/quartz_test.properties")
- val properties = new Properties
- properties.load(propertiesStream)
- schedulerFactory.initialize(properties)
- val scheduler = schedulerFactory.getScheduler()
- createJob(scheduler)
-
- val triggersController = new Triggers(schedulerFactory, Helpers.stubControllerComponents())
- val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/triggers/" + jobGroup + "/" + jobName)
- val validTrigger: Future[Result] = triggersController.getTrigger(triggerGroup, triggerName)(request)
-
- status(validTrigger) must equalTo(OK)
- contentType(validTrigger) must beSome.which(_ == "text/html")
- contentAsString(validTrigger) must contain (triggerGroup)
- contentAsString(validTrigger) must contain (triggerName)
- }
- }
- }
+ "Triggers" should {
+
+ "send 404 on a non-existent trigger request" in {
+ val schedulerFactory: WorkerSchedulerFactory = new WorkerSchedulerFactory()
+
+ val propertiesStream = getClass().getResourceAsStream("/quartz_test.properties")
+ val properties = new Properties
+ properties.load(propertiesStream)
+ schedulerFactory.initialize(properties)
+
+ val triggersController = new Triggers(schedulerFactory, Helpers.stubControllerComponents(), MonitoringTeams.empty)
+ val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/triggers/missinggroup/missingname")
+ val missingTrigger: Future[Result] = triggersController.getTrigger("missinggroup", "missingname")(request)
+
+ status(missingTrigger) must equalTo(NOT_FOUND)
+ contentType(missingTrigger) must beSome.which(_ == "text/html")
+ contentAsString(missingTrigger) must contain("Trigger missinggroup missingname not found")
+ }
+
+ "send valid trigger details" in {
+ val schedulerFactory: WorkerSchedulerFactory = new WorkerSchedulerFactory()
+ val propertiesStream = getClass().getResourceAsStream("/quartz_test.properties")
+ val properties = new Properties
+ properties.load(propertiesStream)
+ schedulerFactory.initialize(properties)
+ val scheduler = schedulerFactory.getScheduler()
+ createJob(scheduler)
+
+ val triggersController = new Triggers(schedulerFactory, Helpers.stubControllerComponents(), MonitoringTeams.empty)
+ val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, "/triggers/" + jobGroup + "/" + jobName)
+ val validTrigger: Future[Result] = triggersController.getTrigger(triggerGroup, triggerName)(request)
+
+ status(validTrigger) must equalTo(OK)
+ contentType(validTrigger) must beSome.which(_ == "text/html")
+ contentAsString(validTrigger) must contain(triggerGroup)
+ contentAsString(validTrigger) must contain(triggerName)
+ }
+ }
+}