Use parameters passed by UI to customise docker-compose

Docker compose will be dynamically created based on the paramenters passed from the UI.

The possible configurations are:
- aggregation time
- data time frame
- project prefix
- credentials

Feature: Issue 9907
Change-Id: I4366b4d054a51c3e5fff9274c21580ce6a0e10f3
diff --git a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetup.scala b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetup.scala
index fc53340..10a0f52 100644
--- a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetup.scala
+++ b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetup.scala
@@ -17,6 +17,8 @@
 import java.nio.charset.StandardCharsets
 import java.nio.file.{Files, Path}
 
+import com.googlesource.gerrit.plugins.analytics.wizard.model.ETLConfig
+
 trait ConfigWriter {
   def write(outputPath: Path, out: String)
 }
@@ -27,8 +29,10 @@
   }
 }
 
-case class AnalyticDashboardSetup(name: String, dockerComposeYamlPath: Path, gerritLocalUrl: URL)(
-    implicit val writer: ConfigWriter) {
+case class AnalyticDashboardSetup(name: String,
+                                  dockerComposeYamlPath: Path,
+                                  gerritLocalUrl: URL,
+                                  etlConfig: ETLConfig)(implicit val writer: ConfigWriter) {
 
   // Docker doesn't like container names with '/', hence the replace with '-'
   // Furthermore timestamp has been added to avoid conflicts among container names, i.e.:
@@ -36,6 +40,17 @@
   // would be potentially in conflict with another 'foo-bar' project's one
   private val sanitisedName =
     s"${name.replace("/", "-")}-${System.currentTimeMillis}"
+  private def analyticsArgs: String = {
+    val args = List(
+      Since(etlConfig.since.map(_.toString)),
+      Until(etlConfig.until.map(_.toString)),
+      ProjectPrefix(etlConfig.projectPrefix),
+      Aggregate(Some(etlConfig.aggregate.entryName)),
+      Password(etlConfig.password),
+      Username(etlConfig.username)
+    ).filter(_.value.isDefined) mkString " "
+    s"$args --writeNotProcessedEventsTo file:///tmp/failed-events -e gerrit/analytics"
+  }
   private val dockerComposeTemplate = {
     s"""
        |version: '3'
@@ -48,7 +63,7 @@
        |    environment:
        |      - ES_HOST=elasticsearch
        |      - GERRIT_URL=${gerritLocalUrl.getProtocol}://gerrit:${gerritLocalUrl.getPort}
-       |      - ANALYTICS_ARGS=--since 2000-06-01 --aggregate email_hour --writeNotProcessedEventsTo file:///tmp/failed-events -e gerrit/analytics
+       |      - ANALYTICS_ARGS=$analyticsArgs
        |    networks:
        |      - ek
        |    links:
@@ -102,3 +117,28 @@
 object AnalyticDashboardSetup {
   implicit val writer = new ConfigWriterImpl()
 }
+
+sealed trait AnalyticsOption {
+  val name: String
+  val value: Option[String]
+
+  override def toString: String = s"$name ${value.getOrElse("")}"
+}
+case class Since(value: Option[String]) extends AnalyticsOption {
+  val name = "--since"
+}
+case class Until(value: Option[String]) extends AnalyticsOption {
+  val name = "--until"
+}
+case class ProjectPrefix(value: Option[String]) extends AnalyticsOption {
+  val name = "--prefix"
+}
+case class Aggregate(value: Option[String]) extends AnalyticsOption {
+  val name = "--aggregate"
+}
+case class Password(value: Option[String]) extends AnalyticsOption {
+  val name = "--password"
+}
+case class Username(value: Option[String]) extends AnalyticsOption {
+  val name = "--username"
+}
diff --git a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticsWizardActions.scala b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticsWizardActions.scala
index 835fd77..a364bb3 100644
--- a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticsWizardActions.scala
+++ b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticsWizardActions.scala
@@ -58,7 +58,8 @@
             AnalyticDashboardSetup(
               input.dashboardName,
               dataPath.resolve(s"docker-compose.${input.dashboardName}.yaml"),
-              gerritLocalUrl
+              gerritLocalUrl,
+              etlConfig
             ).createDashboardSetupFile()
             Response.created(s"Dashboard configuration created for $encodedName!")
           case Failure(exception) =>
diff --git a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/model/ETLConfig.scala b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/model/ETLConfig.scala
index 2766d45..78d2289 100644
--- a/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/model/ETLConfig.scala
+++ b/src/main/scala/com/googlesource/gerrit/plugins/analytics/wizard/model/ETLConfig.scala
@@ -17,6 +17,7 @@
 import java.time.LocalDate
 import java.time.format.DateTimeFormatter
 
+import enumeratum.EnumEntry.Snakecase
 import enumeratum._
 
 import scala.util.{Failure, Success, Try}
@@ -31,7 +32,7 @@
                      username: Option[String],
                      password: Option[String])
 
-sealed trait AggregationType extends EnumEntry
+sealed trait AggregationType extends EnumEntry with Snakecase
 object AggregationType extends Enum[AggregationType] {
   val values = findValues
 
@@ -92,7 +93,7 @@
   private def validateAggregate(
       value: String): Either[AggregateValidationError, AggregationType] = {
     val maybeAggregate =
-      AggregationType.withNameInsensitiveOption(Option(value).getOrElse("email").replace("_", ""))
+      AggregationType.withNameInsensitiveOption(Option(value).getOrElse("email"))
     Either.cond(maybeAggregate.isDefined, maybeAggregate.get, AggregateValidationError(value))
   }
 }
diff --git a/src/test/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetupSpec.scala b/src/test/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetupSpec.scala
index 0f91888..74daff4 100644
--- a/src/test/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetupSpec.scala
+++ b/src/test/scala/com/googlesource/gerrit/plugins/analytics/wizard/AnalyticDashboardSetupSpec.scala
@@ -3,7 +3,11 @@
 import java.io.File
 import java.net.URL
 import java.nio.file.Path
+import java.time.LocalDate
+import java.time.format.DateTimeFormatter
 
+import com.googlesource.gerrit.plugins.analytics.wizard.model.AggregationType.{Email, EmailYear}
+import com.googlesource.gerrit.plugins.analytics.wizard.model.ETLConfig
 import org.scalatest.{FlatSpec, Matchers}
 
 class AnalyticDashboardSetupSpec extends FlatSpec with Matchers {
@@ -19,10 +23,56 @@
     implicit val writer = new MockWriter()
 
     val composeYamlFile = File.createTempFile(getClass.getName, ".yaml").toPath
-    val ads = AnalyticDashboardSetup("aProject",
-                                     composeYamlFile,
-                                     new URL("http://gerrit_local_ip_address:8080"))
+    val ads =
+      AnalyticDashboardSetup("aProject",
+                             composeYamlFile,
+                             new URL("http://gerrit_local_ip_address:8080"),
+                             ETLConfig(Email, None, None, None, None, None, None, None, None))
     ads.createDashboardSetupFile()
     gotFilename shouldBe Some(composeYamlFile)
   }
+
+  it should "create a config file with correct analytics args" in {
+    var gotFilename: Option[Path] = None
+    var dockerCompose: String     = ""
+    class MockWriter extends ConfigWriter {
+      override def write(filename: Path, out: String): Unit = {
+        gotFilename = Some(filename)
+        dockerCompose = out
+      }
+    }
+    implicit val writer = new MockWriter()
+
+    val dateSince       = "2018-10-10"
+    val localDateSince  = LocalDate.parse(dateSince, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
+    val dateUntil       = "2018-10-20"
+    val localDateUntil  = LocalDate.parse(dateUntil, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
+    val prefix          = "myProjectPrefix"
+    val password        = "myPassword"
+    val username        = "myUsername"
+    val composeYamlFile = File.createTempFile(getClass.getName, ".yaml").toPath
+    val ads =
+      AnalyticDashboardSetup(
+        "aProject",
+        composeYamlFile,
+        new URL("http://gerrit_local_ip_address:8080"),
+        ETLConfig(EmailYear,
+                  Some(prefix),
+                  Some(localDateSince),
+                  Some(localDateUntil),
+                  None,
+                  None,
+                  None,
+                  Some(username),
+                  Some(password))
+      )
+    ads.createDashboardSetupFile()
+    gotFilename shouldBe Some(composeYamlFile)
+    dockerCompose should include("--aggregate email_year")
+    dockerCompose should include(s"--until $dateUntil")
+    dockerCompose should include(s"--since $dateSince")
+    dockerCompose should include(s"--prefix $prefix")
+    dockerCompose should include(s"--password $password")
+    dockerCompose should include(s"--username $username")
+  }
 }