blob: 78d2289f37403cdc04acd2bb2172f00f98eb485f [file] [log] [blame]
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.googlesource.gerrit.plugins.analytics.wizard.model
import java.net.URL
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import enumeratum.EnumEntry.Snakecase
import enumeratum._
import scala.util.{Failure, Success, Try}
case class ETLConfig(aggregate: AggregationType,
projectPrefix: Option[String],
since: Option[LocalDate],
until: Option[LocalDate],
eventsUrl: Option[URL],
writeNotProcessedEventsTo: Option[URL],
emailAliasesPath: Option[String],
username: Option[String],
password: Option[String])
sealed trait AggregationType extends EnumEntry with Snakecase
object AggregationType extends Enum[AggregationType] {
val values = findValues
case object Email extends AggregationType
case object EmailHour extends AggregationType
case object EmailDay extends AggregationType
case object EmailMonth extends AggregationType
case object EmailYear extends AggregationType
}
object ETLConfig {
def fromRaw(raw: ETLConfigRaw): Either[ETLConfigValidationError, ETLConfig] = {
for {
s <- validateLocalDate("since", raw.since).right
u <- validateLocalDate("until", raw.until).right
w <- validateUrl("writeNotProcessedEventsTo", raw.writeNotProcessedEventsTo).right
eu <- validateUrl("eventsUrl", raw.eventsUrl).right
a <- validateAggregate(raw.aggregate).right
} yield
ETLConfig(
aggregate = a,
projectPrefix = Option(raw.projectPrefix),
since = s,
until = u,
eventsUrl = eu,
writeNotProcessedEventsTo = w,
emailAliasesPath = Option(raw.emailAliasesPath),
username = Option(raw.username),
password = Option(raw.password)
)
}
private def validateLocalDate(
parameter: String,
value: String): Either[LocalDateValidationError, Option[LocalDate]] = {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
Option(value)
.map { maybeDate =>
Try(LocalDate.parse(maybeDate, formatter)) match {
case Success(date) => Right(Some(date))
case Failure(exception) => Left(LocalDateValidationError(parameter, maybeDate, exception))
}
}
.getOrElse(Right(None))
}
private def validateUrl(parameter: String,
value: String): Either[UrlValidationError, Option[URL]] = {
Option(value)
.map { u =>
Try(new URL(u)) match {
case Success(url) => Right(Some(url))
case Failure(exception) => Left(UrlValidationError(parameter, u, exception))
}
}
.getOrElse(Right(None))
}
private def validateAggregate(
value: String): Either[AggregateValidationError, AggregationType] = {
val maybeAggregate =
AggregationType.withNameInsensitiveOption(Option(value).getOrElse("email"))
Either.cond(maybeAggregate.isDefined, maybeAggregate.get, AggregateValidationError(value))
}
}
sealed trait ETLConfigValidationError {
def message: String = s"Error validating '$parameter' parameter: $value. Exception: $cause"
def value: String
def parameter: String
def cause: Throwable
}
case class LocalDateValidationError(parameter: String, value: String, cause: Throwable)
extends ETLConfigValidationError
case class UrlValidationError(parameter: String, value: String, cause: Throwable)
extends ETLConfigValidationError
case class AggregateValidationError(value: String) extends ETLConfigValidationError {
val parameter = "aggregate"
val cause: Throwable = new Throwable(s"Value $value is not a valid aggregation type")
}