From 517fddb965a85d3e2c3e4177d0d43a0e06caf57e Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Mon, 23 May 2022 15:13:39 +0100 Subject: [PATCH] Update Scala to 2.13.8 (#109) --- .gitignore | 44 +++++++- .scalafix.conf | 21 ++++ .scalafmt.conf | 18 ++- .travis.yml | 13 ++- LICENSE | 2 +- build.sbt | 106 ++++++------------ docker-compose.yml | 41 ------- docker/docker-compose.yml | 78 ++++++------- project/Aliases.scala | 14 ++- project/Dependencies.scala | 96 ++++++++++++++++ project/Release.scala | 20 ++-- project/build.properties | 2 +- project/plugins.sbt | 16 +-- .../kamon-jmx-collector_2.12-1.0.0-RC1.jar | Bin 72070 -> 0 bytes .../kamon-jmx-collector_2.13-1.0.0-RC1.jar | Bin 0 -> 73584 bytes scheduler/src/main/resources/application.conf | 3 + .../scala/com/sky/kms/AkkaComponents.scala | 9 +- .../src/main/scala/com/sky/kms/Main.scala | 12 +- .../main/scala/com/sky/kms/SchedulerApp.scala | 15 +-- .../com/sky/kms/actors/PublisherActor.scala | 37 +++--- .../com/sky/kms/actors/SchedulingActor.scala | 35 +++--- .../com/sky/kms/actors/TerminatorActor.scala | 18 +-- .../main/scala/com/sky/kms/avro/package.scala | 13 ++- .../scala/com/sky/kms/config/config.scala | 5 + .../com/sky/kms/domain/ApplicationError.scala | 4 +- .../sky/kms/domain/PublishableMessage.scala | 13 ++- .../scala/com/sky/kms/domain/Schedule.scala | 25 +++-- .../com/sky/kms/domain/ScheduleEvent.scala | 12 ++ .../kms/domain/ScheduleQueueOfferResult.scala | 6 +- .../scala/com/sky/kms/domain/package.scala | 12 +- .../com/sky/kms/monitoring/Monitoring.scala | 4 +- .../src/main/scala/com/sky/kms/package.scala | 12 +- .../com/sky/kms/streams/ScheduleReader.scala | 27 +++-- .../streams/ScheduledMessagePublisher.scala | 26 ++--- scheduler/src/test/resources/logback-test.xml | 2 +- .../com/sky/kms/base/AkkaKafkaSpecBase.scala | 5 +- .../scala/com/sky/kms/base/AkkaSpecBase.scala | 5 +- .../com/sky/kms/base/AkkaStreamSpecBase.scala | 13 --- .../com/sky/kms/base/KafkaIntSpecBase.scala | 20 ++-- .../sky/kms/base/SchedulerIntSpecBase.scala | 7 +- .../scala/com/sky/kms/base/SpecBase.scala | 16 ++- .../sky/kms/e2e/SchedulerDeleteIntSpec.scala | 4 +- .../com/sky/kms/e2e/SchedulerIntSpec.scala | 54 +++++---- .../sky/kms/e2e/SchedulerResiliencySpec.scala | 18 +-- .../e2e/SchedulerSchemaEvolutionSpec.scala | 21 ++-- .../test/scala/com/sky/kms/e2e/package.scala | 16 +-- .../integration/ScheduleReaderIntSpec.scala | 53 +++++---- .../scala/com/sky/kms/unit/AvroSpec.scala | 2 +- .../com/sky/kms/unit/PublisherActorSpec.scala | 2 +- .../com/sky/kms/unit/ScheduleReaderSpec.scala | 28 +++-- .../unit/ScheduledMessagePublisherSpec.scala | 4 +- .../com/sky/kms/unit/SchedulerSpec.scala | 8 +- .../sky/kms/unit/SchedulingActorSpec.scala | 4 +- .../scala/com/sky/kms/utils/RandomPort.scala | 2 +- .../com/sky/kms/utils/TestActorSystem.scala | 9 +- .../scala/com/sky/kms/utils/TestConfig.scala | 2 +- .../com/sky/kms/utils/TestDataUtils.scala | 43 +++---- 57 files changed, 604 insertions(+), 493 deletions(-) create mode 100644 .scalafix.conf delete mode 100644 docker-compose.yml create mode 100644 project/Dependencies.scala delete mode 100644 scheduler/lib/kamon-jmx-collector_2.12-1.0.0-RC1.jar create mode 100644 scheduler/lib/kamon-jmx-collector_2.13-1.0.0-RC1.jar create mode 100644 scheduler/src/main/scala/com/sky/kms/domain/ScheduleEvent.scala delete mode 100644 scheduler/src/test/scala/com/sky/kms/base/AkkaStreamSpecBase.scala diff --git a/.gitignore b/.gitignore index 45770fc1..ea73961d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,45 @@ -# SBT +# bloop and metals +.bloop +.bsp + +# metals +project/metals.sbt +.metals + +# vs code +.vscode + +# scala 3 +.tasty + +# sbt +project/project/ +project/target/ target/ -# IDE +# eclipse +build/ +.classpath +.project +.settings +.worksheet +bin/ +.cache + +# intellij idea +*.log +*.iml +*.ipr +*.iws .idea -.pairs +# mac +.DS_Store + +# other? +.history +.scala_dependencies +.cache-main + +#general +*.class diff --git a/.scalafix.conf b/.scalafix.conf new file mode 100644 index 00000000..63629caa --- /dev/null +++ b/.scalafix.conf @@ -0,0 +1,21 @@ +rules = [ + RemoveUnused, + NoAutoTupling, + DisableSyntax, + LeakingImplicitClassVal, + NoValInForComprehension, + ProcedureSyntax +] + +OrganizeImports { + coalesceToWildcardImportThreshold = 8 + expandRelative = true + groupedImports = Merge + importSelectorsOrder = SymbolsFirst + removeUnused = false + groups = [ + "java." + "*" + "scala." + ] +} diff --git a/.scalafmt.conf b/.scalafmt.conf index 3857c45b..431d34fd 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,15 +1,11 @@ -style = defaultWithAlign - -align = most - -maxColumn = 120 +version = 3.4.3 +align.preset = most includeCurlyBraceInSelectChains = false - +maxColumn = 120 newlines.sometimesBeforeColonInMethodReturnType = false - -rewrite.rules = [SortImports, RedundantBraces, RedundantParens, PreferCurlyFors] - project.git = true - -spaces.beforeContextBoundColon = true +rewrite.rules = [SortImports, RedundantBraces, RedundantParens, PreferCurlyFors] +runner.dialect = scala213 +spaces.beforeContextBoundColon = Always +style = defaultWithAlign diff --git a/.travis.yml b/.travis.yml index d4390a54..b09b90e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: scala scala: - - 2.12.10 + - 2.13.8 jdk: - - openjdk12 + - openjdk17 # Avoid triggering a duplicate build for PRs branches: @@ -13,11 +13,14 @@ branches: # https://www.scala-sbt.org/1.x/docs/Travis-CI-with-sbt.html cache: directories: + - $HOME/.cache/coursier - $HOME/.ivy2/cache - - $HOME/.sbt/boot/ + - $HOME/.sbt + before_cache: - - find $HOME/.ivy2 -name "ivydata-*.properties" -delete - - find $HOME/.sbt -name "*.lock" -delete + - rm -fv $HOME/.ivy2/.sbt.ivy.lock + - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete + - find $HOME/.sbt -name "*.lock" -print -delete script: - sbt ++$TRAVIS_SCALA_VERSION ciBuild diff --git a/LICENSE b/LICENSE index 3d36aca4..eb31e348 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2017-2019, Sky CP Ltd +Copyright (c) 2017-2022, Sky CP Ltd All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/build.sbt b/build.sbt index 33d5c8b9..b826d5a9 100644 --- a/build.sbt +++ b/build.sbt @@ -2,110 +2,68 @@ import com.typesafe.sbt.packager.docker.Cmd import Aliases._ import Release._ -scalafmtVersion in ThisBuild := "1.2.0" -scalafmtOnCompile in ThisBuild := true +ThisBuild / scalafmtOnCompile := true +ThisBuild / semanticdbEnabled := true +ThisBuild / semanticdbVersion := scalafixSemanticdb.revision +ThisBuild / scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.6.0" -val kafkaVersion = "2.6.0" -val akkaVersion = "2.5.23" -val catsVersion = "1.6.1" -val refinedVersion = "0.9.8" -val pureConfigVersion = "0.11.1" -val kamonVersion = "2.4.2" +Global / onChangedBuildSource := ReloadOnSourceChanges -val dependencies = Seq( - "uk.sky" %% "kafka-topic-loader" % "1.5.3", - "com.typesafe.akka" %% "akka-actor" % akkaVersion, - "com.typesafe.akka" %% "akka-stream" % akkaVersion, - "com.typesafe.akka" %% "akka-slf4j" % akkaVersion, - "com.typesafe.akka" %% "akka-stream-kafka" % "1.0.4", - "com.typesafe.akka" %% "akka-stream-contrib" % "0.10", - "io.monix" %% "monix-execution" % "3.0.0-RC3", - "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2", - "org.typelevel" %% "cats-core" % catsVersion, - "org.typelevel" %% "cats-testkit" % catsVersion, - "ch.qos.logback" % "logback-classic" % "1.2.3" % Runtime, - "net.logstash.logback" % "logstash-logback-encoder" % "6.1" % Runtime, - "org.codehaus.janino" % "janino" % "3.0.13" % Runtime, - "com.github.pureconfig" %% "pureconfig" % pureConfigVersion, - "com.github.pureconfig" %% "pureconfig-cats" % pureConfigVersion, - "io.kamon" %% "kamon-core" % kamonVersion, - "io.kamon" %% "kamon-akka" % kamonVersion, - "io.kamon" %% "kamon-prometheus" % kamonVersion, - "eu.timepit" %% "refined" % refinedVersion, - "eu.timepit" %% "refined-pureconfig" % refinedVersion, - "eu.timepit" %% "refined-scalacheck" % refinedVersion, - "org.apache.kafka" %% "kafka" % kafkaVersion % Test, - "org.scalatest" %% "scalatest" % "3.0.8" % Test, - "com.typesafe.akka" %% "akka-testkit" % akkaVersion % Test, - "com.typesafe.akka" %% "akka-stream-testkit" % akkaVersion % Test, - "com.danielasfregola" %% "random-data-generator" % "2.7" % Test, - "com.47deg" %% "scalacheck-toolbox-datetime" % "0.2.5" % Test, - "org.mockito" % "mockito-all" % "1.10.19" % Test, - "org.zalando" %% "grafter" % "2.6.1" % Test, - "io.github.embeddedkafka" %% "embedded-kafka" % "2.6.0" % Test -) +Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oF") val commonSettings = Seq( organization := "com.sky", - scalaVersion := "2.12.10", - libraryDependencies += "com.sksamuel.avro4s" %% "avro4s-core" % "2.0.2" + scalaVersion := "2.13.8" +) + +val compilerSettings = Seq( + // Compiler option not provided by sbt-tpolecat + scalacOptions += "-Ymacro-annotations", + tpolecatScalacOptions ~= { opts => + opts.filterNot(Set(ScalacOptions.warnValueDiscard)) + } ) lazy val dockerSettings = Seq( - packageName in Docker := "kafka-message-scheduler", - dockerBaseImage := "alpine:3.13.2", - dockerRepository := Some("skyuk"), - dockerLabels := Map("maintainer" -> "Sky"), - dockerUpdateLatest := true, + Docker / packageName := "kafka-message-scheduler", + dockerBaseImage := "eclipse-temurin:17-jdk-alpine", + dockerRepository := Some("skyuk"), + dockerLabels := Map("maintainer" -> "Sky"), + dockerUpdateLatest := true, dockerCommands ++= Seq( Cmd("USER", "root"), - Cmd("RUN", "apk add --no-cache bash eudev openjdk11-jre") + Cmd("RUN", "apk add --no-cache bash") ) ) val buildInfoSettings = Seq( - buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion), + buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion), buildInfoPackage := "com.sky" ) lazy val scheduler = (project in file("scheduler")) .enablePlugins(BuildInfoPlugin, JavaAppPackaging, UniversalDeployPlugin, JavaAgent, DockerPlugin) .settings(commonSettings) + .settings(compilerSettings) .settings( - libraryDependencies ++= dependencies, - dependencyOverrides ++= Seq("org.apache.kafka" % "kafka-clients" % kafkaVersion, - "org.scalacheck" %% "scalacheck" % "1.13.5"), - resolvers ++= Seq( - "jitpack" at "https://jitpack.io", - Resolver.bintrayRepo("cakesolutions", "maven") - ), - addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full), - addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.10"), - scalacOptions ++= Seq( - "-language:implicitConversions", - "-language:postfixOps", - "-Xfatal-warnings", - "-Ywarn-dead-code", - "-Ywarn-unused", - "-deprecation", - "-Ypartial-unification", - "-encoding", - "utf-8" - ), - fork in run := true, - fork in Test := true, - javaAgents += "io.kamon" % "kanela-agent" % "1.0.7", + libraryDependencies ++= Dependencies.all, + addCompilerPlugin("org.typelevel" % "kind-projector" % "0.13.2" cross CrossVersion.full), + run / fork := true, + Test / fork := true, + javaAgents += "io.kamon" % "kanela-agent" % "1.0.14", buildInfoSettings, dockerSettings, releaseSettings, - parallelExecution in Test := false + Test / parallelExecution := false ) val schema = inputKey[Unit]("Generate the Avro schema file for the Schedule schema.") lazy val avro = (project in file("avro")) .settings(commonSettings) - .settings(schema := (run in Compile).toTask("").value) + .settings(compilerSettings) + .settings(libraryDependencies += Dependencies.avro4s) + .settings(schema := (Compile / run).toTask("").value) .dependsOn(scheduler % "compile->compile") .disablePlugins(ReleasePlugin) diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 6c0c4033..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,41 +0,0 @@ -version: '3.3' -services: - - kafka: - image: confluentinc/cp-kafka:3.3.0 - ports: - - "9092:9092" - depends_on: - - zookeeper - environment: - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: '1' - KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://kafka:9092' - - zookeeper: - image: confluentinc/cp-zookeeper:3.3.0 - environment: - ZOOKEEPER_CLIENT_PORT: '2181' - - schema-registry: - image: confluentinc/cp-schema-registry:3.3.0 - hostname: schema-registry - depends_on: - - kafka - - zookeeper - ports: - - "8082:8081" - environment: - SCHEMA_REGISTRY_HOST_NAME: schema-registry - SCHEMA_REGISTRY_KAFKASTORE_CONNECTION_URL: zookeeper:2181 - SCHEMA_REGISTRY_LISTENERS: http://0.0.0.0:8082 - - kms: - image: skyuk/kafka-message-scheduler - depends_on: - - kafka - environment: - KAFKA_BROKERS: kafka:9092 - SCHEDULE_TOPIC: schedules - ports: - - "9095:9095" diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 44bbb3a2..f531aac0 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,38 +1,40 @@ -version: '3.3' -services: - - zookeeper: - image: confluentinc/cp-zookeeper:3.3.0 - environment: - ZOOKEEPER_CLIENT_PORT: '2181' - - kafka: - image: confluentinc/cp-kafka:3.3.0 - ports: - - "9093:9093" - depends_on: - - zookeeper - environment: - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: '1' - KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,OUTSIDE://localhost:9093 - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,OUTSIDE:PLAINTEXT - - kms: - image: skyuk/kafka-message-scheduler:latest - depends_on: - - kafka - environment: - KAFKA_BROKERS: kafka:9092 - SCHEDULE_TOPICS: schedules - ports: - - "9095:9095" - - prometheus: - image: prom/prometheus:v2.25.2 - ports: - - "9090:9090" - volumes: - - ./prometheus.yml:/etc/prometheus/prometheus.yml - command: - - '--config.file=/etc/prometheus/prometheus.yml' +version: '3.8' + +services: + + kms: + image: skyuk/kafka-message-scheduler:latest + depends_on: + - kafka + - zookeeper + environment: + KAFKA_BROKERS: kafka:9092 + SCHEDULE_TOPICS: schedules + ports: + - "9095:9095" + + zookeeper: + image: confluentinc/cp-zookeeper:7.0.1 + environment: + ZOOKEEPER_CLIENT_PORT: '2181' + + kafka: + image: confluentinc/cp-kafka:7.0.1 + ports: + - "9093:9093" + depends_on: + - zookeeper + environment: + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: '1' + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,OUTSIDE://localhost:9093 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,OUTSIDE:PLAINTEXT + + prometheus: + image: prom/prometheus:v2.33.4 + ports: + - "9090:9090" + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + command: + - '--config.file=/etc/prometheus/prometheus.yml' diff --git a/project/Aliases.scala b/project/Aliases.scala index 318880b9..160ce5a8 100644 --- a/project/Aliases.scala +++ b/project/Aliases.scala @@ -2,10 +2,12 @@ import sbt._ object Aliases { - lazy val defineCommandAliases = { - addCommandAlias("ciBuild", ";clean; test; schema") ++ - addCommandAlias("ciRelease", ";clean; schema; project scheduler; release with-defaults") ++ - addCommandAlias("checkFmt", ";scalafmt::test; test:scalafmt::test; sbt:scalafmt::test") ++ - addCommandAlias("runFmt", ";scalafmt; test:scalafmt; sbt:scalafmt") - } + lazy val defineCommandAliases = + addCommandAlias("ciBuild", "checkFmt; checkFix; test; schema") ++ + addCommandAlias("ciRelease", "clean; schema; project scheduler; release with-defaults") ++ + addCommandAlias("checkFix", "scalafixAll --check OrganizeImports; scalafixAll --check") ++ + addCommandAlias("runFix", "scalafixAll OrganizeImports; scalafixAll") ++ + addCommandAlias("checkFmt", "scalafmtCheckAll; scalafmtSbtCheck") ++ + addCommandAlias("runFmt", "scalafmtAll; scalafmtSbt") + } diff --git a/project/Dependencies.scala b/project/Dependencies.scala new file mode 100644 index 00000000..5791afb1 --- /dev/null +++ b/project/Dependencies.scala @@ -0,0 +1,96 @@ +import sbt._ + +object Dependencies { + + object Akka { + private val version = "2.6.19" + val actor = "com.typesafe.akka" %% "akka-actor" % version + val stream = "com.typesafe.akka" %% "akka-stream" % version + val streamKafka = "com.typesafe.akka" %% "akka-stream-kafka" % "3.0.0" + val slf4j = "com.typesafe.akka" %% "akka-slf4j" % version + val testKit = "com.typesafe.akka" %% "akka-testkit" % version % Test + val streamTestKit = "com.typesafe.akka" %% "akka-stream-testkit" % version % Test + val base = Seq(actor, stream, streamKafka, slf4j) + val test = Seq(testKit, streamTestKit) + } + + object Cats { + private val version = "2.7.0" + val core = "org.typelevel" %% "cats-core" % version + val testKit = "org.typelevel" %% "cats-testkit" % version % Test + val scalatest = "com.ironcorelabs" %% "cats-scalatest" % "3.1.1" % Test + val base = Seq(core) + val test = Seq(testKit, scalatest) + } + + object Kafka { + private val version = "3.1.0" + val kafkaClients = "org.apache.kafka" % "kafka-clients" % version + val kafka = "org.apache.kafka" %% "kafka" % version % Test + val base = Seq(kafkaClients) + val test = Seq(kafka) + } + + object Kamon { + private val version = "2.5.1" + val core = "io.kamon" %% "kamon-core" % version + val akka = "io.kamon" %% "kamon-akka" % version + val prometheus = "io.kamon" %% "kamon-prometheus" % version + val all = Seq(core, akka, prometheus) + } + + object PureConfig { + private val version = "0.17.1" + val pureconfig = "com.github.pureconfig" %% "pureconfig" % version + val cats = "com.github.pureconfig" %% "pureconfig-cats" % version + val all = Seq(pureconfig, cats) + } + + object Refined { + private val version = "0.9.28" + val refined = "eu.timepit" %% "refined" % version + val pureconfig = "eu.timepit" %% "refined-pureconfig" % version + val scalaCheck = "eu.timepit" %% "refined-scalacheck" % version % Test + val base = Seq(refined, pureconfig) + val test = Seq(scalaCheck) + } + + val avro4s = "com.sksamuel.avro4s" %% "avro4s-core" % "4.0.12" + val kafkaTopicLoader = "uk.sky" %% "kafka-topic-loader" % "1.5.6" + val monix = "io.monix" %% "monix-execution" % "3.4.0" + val scalaLogging = "com.typesafe.scala-logging" %% "scala-logging" % "3.9.4" + + val janino = "org.codehaus.janino" % "janino" % "3.1.6" % Runtime + val logbackClassic = "ch.qos.logback" % "logback-classic" % "1.2.11" % Runtime + val logbackEncoder = "net.logstash.logback" % "logstash-logback-encoder" % "7.0.1" % Runtime + + val embeddedKafka = "io.github.embeddedkafka" %% "embedded-kafka" % "3.1.0" % Test + val mockito = "org.mockito" % "mockito-core" % "4.4.0" % Test + val randomDataGenerator = "com.danielasfregola" %% "random-data-generator" % "2.9" % Test + val scalaCheck = "org.scalacheck" %% "scalacheck" % "1.15.4" % Test + val scalaCheckDatetime = "com.47deg" %% "scalacheck-toolbox-datetime" % "0.6.0" % Test + val scalaTest = "org.scalatest" %% "scalatest" % "3.2.11" % Test + val scalaTestPlusMockito = "org.scalatestplus" %% "mockito-3-12" % "3.2.10.0" % Test + + val core: Seq[ModuleID] = Akka.base ++ Cats.base ++ Kafka.base ++ Kamon.all ++ PureConfig.all ++ Refined.base ++ Seq( + avro4s, + kafkaTopicLoader, + monix, + scalaLogging + ) + val runtime: Seq[ModuleID] = Seq( + janino, + logbackClassic, + logbackEncoder + ) + val test: Seq[ModuleID] = Akka.test ++ Cats.test ++ Kafka.test ++ Refined.test ++ Seq( + embeddedKafka, + mockito, + randomDataGenerator, + scalaCheck, + scalaCheckDatetime, + scalaTest, + scalaTestPlusMockito + ) + val all: Seq[sbt.ModuleID] = core ++ runtime ++ test +} diff --git a/project/Release.scala b/project/Release.scala index 691676fd..bf0a3d3f 100644 --- a/project/Release.scala +++ b/project/Release.scala @@ -9,15 +9,15 @@ import sbtrelease._ object Release { // Useful tasks to show what versions would be used if a release was performed. private val showReleaseVersion = taskKey[String]("the future version once releaseNextVersion has been applied to it") - private val showNextVersion = taskKey[String]("the future version once releaseNextVersion has been applied to it") + private val showNextVersion = taskKey[String]("the future version once releaseNextVersion has been applied to it") lazy val releaseSettings = Seq( - releaseUseGlobalVersion := false, - releaseVersionBump := sbtrelease.Version.Bump.Minor, - releaseTagName := s"${name.value}-${version.value}", - releaseTagComment := s"Releasing ${version.value} of module: ${name.value}", - releasePublishArtifactsAction:= (publish in Universal).value, - releaseProcess := Seq[ReleaseStep]( + releaseUseGlobalVersion := false, + releaseVersionBump := sbtrelease.Version.Bump.Minor, + releaseTagName := s"${name.value}-${version.value}", + releaseTagComment := s"Releasing ${version.value} of module: ${name.value}", + releasePublishArtifactsAction := (Universal / publish).value, + releaseProcess := Seq[ReleaseStep]( runClean, checkSnapshotDependencies, releaseStepCommand(ExtraReleaseCommands.initialVcsChecksCommand), @@ -26,12 +26,12 @@ object Release { runTest, commitReleaseVersion, tagRelease, - ReleaseStep(releaseStepTask(publish in docker)), + ReleaseStep(releaseStepTask(docker / publish)), setNextVersion, commitNextVersion, pushChanges ), - showReleaseVersion := { val rV = releaseVersion.value.apply(version.value); println(rV); rV }, - showNextVersion := { val nV = releaseNextVersion.value.apply(version.value); println(nV); nV } + showReleaseVersion := { val rV = releaseVersion.value.apply(version.value); println(rV); rV }, + showNextVersion := { val nV = releaseNextVersion.value.apply(version.value); println(nV); nV } ) } diff --git a/project/build.properties b/project/build.properties index 92debe03..4ff6415f 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.3.1 \ No newline at end of file +sbt.version=1.6.2 \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 9418e4c8..4e7c8e7f 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,7 +1,9 @@ -addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0") -addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.9") -addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.6") -addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.10") -addSbtPlugin("com.tapad" % "sbt-docker-compose" % "1.0.34") -addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15") -addSbtPlugin("io.kamon" % "sbt-kanela-runner" % "2.0.9") +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") +addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.13") +addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.6") +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.8.1") +addSbtPlugin("com.tapad" % "sbt-docker-compose" % "1.0.35") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.34") +addSbtPlugin("io.kamon" % "sbt-kanela-runner" % "2.0.12") +addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.3.0") diff --git a/scheduler/lib/kamon-jmx-collector_2.12-1.0.0-RC1.jar b/scheduler/lib/kamon-jmx-collector_2.12-1.0.0-RC1.jar deleted file mode 100644 index 894ff31aad01b409bb27dbab3ae1fad4d43bd487..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72070 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≻qZ0v#tx_=!(K2 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 literal 0 HcmV?d00001 diff --git a/scheduler/src/main/resources/application.conf b/scheduler/src/main/resources/application.conf index b644a6f5..58dc98a7 100644 --- a/scheduler/src/main/resources/application.conf +++ b/scheduler/src/main/resources/application.conf @@ -52,6 +52,9 @@ akka { kamon { environment.service = "kafka-message-scheduler" + + host-metrics.enabled = no + instrumentation.akka.filters.actors.track { includes = ["kafka-message-scheduler/user/scheduling-actor", "kafka-message-scheduler/user/publisher-actor"] excludes = ["kafka-message-scheduler/system/**"] diff --git a/scheduler/src/main/scala/com/sky/kms/AkkaComponents.scala b/scheduler/src/main/scala/com/sky/kms/AkkaComponents.scala index 7c8d83a0..b6edaeef 100644 --- a/scheduler/src/main/scala/com/sky/kms/AkkaComponents.scala +++ b/scheduler/src/main/scala/com/sky/kms/AkkaComponents.scala @@ -1,17 +1,10 @@ package com.sky.kms import akka.actor.ActorSystem -import akka.stream.Supervision.Stop -import akka.stream.{ActorMaterializer, ActorMaterializerSettings} import com.typesafe.scalalogging.LazyLogging trait AkkaComponents extends LazyLogging { - implicit lazy val system = ActorSystem("kafka-message-scheduler") - - implicit lazy val materializer = ActorMaterializer(ActorMaterializerSettings(system).withSupervisionStrategy(t => { - logger.error("Exception caught by stream supervisor", t) - Stop - })) + implicit lazy val system: ActorSystem = ActorSystem("kafka-message-scheduler") } diff --git a/scheduler/src/main/scala/com/sky/kms/Main.scala b/scheduler/src/main/scala/com/sky/kms/Main.scala index dfe91e21..1f31fa12 100644 --- a/scheduler/src/main/scala/com/sky/kms/Main.scala +++ b/scheduler/src/main/scala/com/sky/kms/Main.scala @@ -1,23 +1,25 @@ package com.sky.kms +import cats.implicits.toShow import com.sky.BuildInfo import com.sky.kms.config.AppConfig import com.typesafe.scalalogging.LazyLogging import eu.timepit.refined.pureconfig._ +import pureconfig.ConfigSource.default.loadOrThrow import pureconfig.generic.auto._ -import pureconfig.loadConfigOrThrow import pureconfig.module.cats._ object Main extends App with LazyLogging with AkkaComponents { logger.info(s"Kafka Message Scheduler ${BuildInfo.name} ${BuildInfo.version} starting up...") - val conf = loadConfigOrThrow[AppConfig] + val conf: AppConfig = loadOrThrow[AppConfig] - SchedulerApp.metricsInit + logger.info(s"Loaded Config ${conf.show}") - val app = SchedulerApp.configure apply conf + SchedulerApp.metricsInit - val runningApp = SchedulerApp.run apply app + val app = SchedulerApp.configure.apply(conf) + val runningApp = SchedulerApp.run.apply(app) logger.info("Kafka Message Scheduler initialisation complete.") } diff --git a/scheduler/src/main/scala/com/sky/kms/SchedulerApp.scala b/scheduler/src/main/scala/com/sky/kms/SchedulerApp.scala index f22e0a1f..de65e2f1 100644 --- a/scheduler/src/main/scala/com/sky/kms/SchedulerApp.scala +++ b/scheduler/src/main/scala/com/sky/kms/SchedulerApp.scala @@ -2,7 +2,6 @@ package com.sky.kms import akka.actor.{ActorRef, ActorSystem} import akka.kafka.scaladsl.Consumer.Control -import akka.stream.ActorMaterializer import com.sky.kms.actors._ import com.sky.kms.config.Configured import com.sky.kms.streams.{ScheduleReader, ScheduledMessagePublisher} @@ -11,9 +10,11 @@ import kamon.jmx.collector.KamonJmxMetricCollector import scala.concurrent.Future -case class SchedulerApp(reader: ScheduleReader[Future[Control]], - publisher: ScheduledMessagePublisher, - publisherActor: ActorRef) +case class SchedulerApp( + reader: ScheduleReader[Future[Control]], + publisher: ScheduledMessagePublisher, + publisherActor: ActorRef +) object SchedulerApp { @@ -30,13 +31,13 @@ object SchedulerApp { } yield SchedulerApp(scheduleReader, publisher, publisherActor) } - def run(implicit system: ActorSystem, mat: ActorMaterializer): Start[Running] = + def run(implicit system: ActorSystem): Start[Running] = for { publisher <- ScheduledMessagePublisher.run _ <- PublisherActor.init(publisher.materializedSource) runningReader <- ScheduleReader.run - running = Running(runningReader, publisher) - _ = ShutdownTasks.forScheduler(running) + running = Running(runningReader, publisher) + _ = ShutdownTasks.forScheduler(running) } yield running def metricsInit(implicit system: ActorSystem): Unit = { diff --git a/scheduler/src/main/scala/com/sky/kms/actors/PublisherActor.scala b/scheduler/src/main/scala/com/sky/kms/actors/PublisherActor.scala index 2e267e94..dc23171a 100644 --- a/scheduler/src/main/scala/com/sky/kms/actors/PublisherActor.scala +++ b/scheduler/src/main/scala/com/sky/kms/actors/PublisherActor.scala @@ -17,29 +17,26 @@ class PublisherActor extends Actor with ActorLogging { override def receive: Receive = waitForQueue - private def waitForQueue: Receive = { - case Init(queue) => - queue.watchCompletion().failed.foreach(t => self ! DownstreamFailure(t)) - context become (receiveWithQueue(queue) orElse stopOnError) + private def waitForQueue: Receive = { case Init(queue) => + queue.watchCompletion().failed.foreach(t => self ! DownstreamFailure(t)) + context become (receiveWithQueue(queue) orElse stopOnError) } - private def receiveWithQueue(queue: ScheduleQueue): Receive = { - case Trigger(scheduleId, schedule) => - queue.offer((scheduleId, messageFrom(schedule))) onComplete { - case Success(QueueOfferResult.Enqueued) => - log.debug(ScheduleQueueOfferResult(scheduleId, QueueOfferResult.Enqueued).show) - case Success(res) => - log.warning(ScheduleQueueOfferResult(scheduleId, res).show) - case Failure(t) => - log.error(t, s"Failed to enqueue $scheduleId") - self ! DownstreamFailure(t) - } + private def receiveWithQueue(queue: ScheduleQueue): Receive = { case Trigger(scheduleId, schedule) => + queue.offer((scheduleId, messageFrom(schedule))) onComplete { + case Success(QueueOfferResult.Enqueued) => + log.debug(ScheduleQueueOfferResult(scheduleId, QueueOfferResult.Enqueued).show) + case Success(res) => + log.warning(ScheduleQueueOfferResult(scheduleId, res).show) + case Failure(t) => + log.error(t, s"Failed to enqueue $scheduleId") + self ! DownstreamFailure(t) + } } - private def stopOnError: Receive = { - case DownstreamFailure(t) => - log.error(t, "Publisher stream has died") - context stop self + private def stopOnError: Receive = { case DownstreamFailure(t) => + log.error(t, "Publisher stream has died") + context stop self } private def messageFrom(schedule: ScheduleEvent) = @@ -57,7 +54,7 @@ object PublisherActor { case class DownstreamFailure(t: Throwable) def create(implicit system: ActorSystem): ActorRef = - system.actorOf(Props[PublisherActor], "publisher-actor") + system.actorOf(Props[PublisherActor](), "publisher-actor") def init(queue: ScheduleQueue): Start[Unit] = Start(_.publisherActor ! Init(queue)) diff --git a/scheduler/src/main/scala/com/sky/kms/actors/SchedulingActor.scala b/scheduler/src/main/scala/com/sky/kms/actors/SchedulingActor.scala index 254a455d..c9e69fbe 100644 --- a/scheduler/src/main/scala/com/sky/kms/actors/SchedulingActor.scala +++ b/scheduler/src/main/scala/com/sky/kms/actors/SchedulingActor.scala @@ -26,20 +26,18 @@ class SchedulingActor(publisher: ActorRef, monixScheduler: MonixScheduler, monit schedules -= scheduleId } - val finishInitialisation: Receive = { - case Initialised => - log.debug("State initialised - scheduling stored schedules") - val scheduled = schedules.map { - case (scheduleId, schedule) => - monitoring.scheduleReceived() - scheduleId -> scheduleOnce(scheduleId, schedule) - } - log.info("Reloaded state has been scheduled") - context become receiveWithSchedules(scheduled) + val finishInitialisation: Receive = { case Initialised => + log.debug("State initialised - scheduling stored schedules") + val scheduled = schedules.map { case (scheduleId, schedule) => + monitoring.scheduleReceived() + scheduleId -> scheduleOnce(scheduleId, schedule) + } + log.info("Reloaded state has been scheduled") + context become receiveWithSchedules(scheduled) } streamStartedOrFailed orElse { - (handleSchedulingMessage orElse finishInitialisation) andThen (_ => sender ! Ack) + (handleSchedulingMessage orElse finishInitialisation) andThen (_ => sender() ! Ack) } } @@ -50,7 +48,8 @@ class SchedulingActor(publisher: ActorRef, monixScheduler: MonixScheduler, monit scheduled.get(scheduleId).foreach(_.cancel()) val cancellable = scheduleOnce(scheduleId, schedule) log.info( - s"Scheduled $scheduleId from ${schedule.inputTopic} to ${schedule.outputTopic} in ${schedule.delay.toMillis} millis") + s"Scheduled $scheduleId from ${schedule.inputTopic} to ${schedule.outputTopic} in ${schedule.delay.toMillis} millis" + ) monitoring.scheduleReceived() scheduled += (scheduleId -> cancellable) @@ -65,7 +64,7 @@ class SchedulingActor(publisher: ActorRef, monixScheduler: MonixScheduler, monit } streamStartedOrFailed orElse { - handleSchedulingMessage andThen (_ => sender ! Ack) + handleSchedulingMessage andThen (_ => sender() ! Ack) } } @@ -78,8 +77,8 @@ class SchedulingActor(publisher: ActorRef, monixScheduler: MonixScheduler, monit case UpstreamFailure(t) => log.error(t, "Reader stream has died") context stop self - case StreamStarted => - sender ! Ack + case StreamStarted => + sender() ! Ack } } @@ -100,6 +99,8 @@ object SchedulingActor { case class UpstreamFailure(t: Throwable) def create(publisherActor: ActorRef)(implicit system: ActorSystem): ActorRef = - system.actorOf(Props(new SchedulingActor(publisherActor, MonixScheduler(system.dispatcher), new KamonMonitoring())), - "scheduling-actor") + system.actorOf( + Props(new SchedulingActor(publisherActor, MonixScheduler(system.dispatcher), new KamonMonitoring())), + "scheduling-actor" + ) } diff --git a/scheduler/src/main/scala/com/sky/kms/actors/TerminatorActor.scala b/scheduler/src/main/scala/com/sky/kms/actors/TerminatorActor.scala index 9ecfdb5f..8d93b790 100644 --- a/scheduler/src/main/scala/com/sky/kms/actors/TerminatorActor.scala +++ b/scheduler/src/main/scala/com/sky/kms/actors/TerminatorActor.scala @@ -10,14 +10,13 @@ import scala.concurrent.Future class TerminatorActor(terminate: Eval[Future[Done]], actorsToWatch: ActorRef*) extends Actor with ActorLogging { override def preStart(): Unit = - actorsToWatch foreach (context watch) - - override def receive: Receive = { - case Terminated(ref) => - log.error(s"$ref stopped. Shutting down") - actorsToWatch.filterNot(_ == ref).foreach(context stop) - terminate.value - context stop self + actorsToWatch foreach (context.watch) + + override def receive: Receive = { case Terminated(ref) => + log.error(s"$ref stopped. Shutting down") + actorsToWatch.filterNot(_ == ref).foreach(context.stop) + terminate.value + context.stop(self) } } @@ -27,5 +26,6 @@ object TerminatorActor { def create(actors: ActorRef*)(implicit system: ActorSystem): ActorRef = system.actorOf( - Props(new TerminatorActor(Eval.later(CoordinatedShutdown(system).run(StreamActorsTerminated)), actors: _*))) + Props(new TerminatorActor(Eval.later(CoordinatedShutdown(system).run(StreamActorsTerminated)), actors: _*)) + ) } diff --git a/scheduler/src/main/scala/com/sky/kms/avro/package.scala b/scheduler/src/main/scala/com/sky/kms/avro/package.scala index ef58974a..dacc3d0b 100644 --- a/scheduler/src/main/scala/com/sky/kms/avro/package.scala +++ b/scheduler/src/main/scala/com/sky/kms/avro/package.scala @@ -7,16 +7,19 @@ import org.apache.avro.Schema package object avro { - implicit object DateTimeSchemaFor extends SchemaFor[OffsetDateTime] { - override val schema: Schema = Schema.create(Schema.Type.LONG) - } + implicit val dateTimeSchemaFor = SchemaFor[OffsetDateTime](Schema.create(Schema.Type.LONG)) implicit object DateTimeEncoder extends Encoder[OffsetDateTime] { - override def encode(value: OffsetDateTime, schema: Schema): java.lang.Long = value.toInstant.toEpochMilli + override def encode(value: OffsetDateTime): java.lang.Long = value.toInstant.toEpochMilli + + override def schemaFor: SchemaFor[OffsetDateTime] = SchemaFor[OffsetDateTime] } implicit object DateTimeDecoder extends Decoder[OffsetDateTime] { - override def decode(value: Any, schema: Schema): OffsetDateTime = + override def decode(value: Any): OffsetDateTime = Instant.ofEpochMilli(value.toString.toLong).atZone(ZoneOffset.UTC).toOffsetDateTime + + override def schemaFor: SchemaFor[OffsetDateTime] = SchemaFor[OffsetDateTime] } + } diff --git a/scheduler/src/main/scala/com/sky/kms/config/config.scala b/scheduler/src/main/scala/com/sky/kms/config/config.scala index 78aa9a89..8044e1c5 100644 --- a/scheduler/src/main/scala/com/sky/kms/config/config.scala +++ b/scheduler/src/main/scala/com/sky/kms/config/config.scala @@ -1,5 +1,6 @@ package com.sky.kms.config +import cats.Show import cats.data.{NonEmptyList, Reader} import com.sky.kms.kafka.Topic @@ -7,6 +8,10 @@ import scala.concurrent.duration.FiniteDuration case class AppConfig(scheduler: SchedulerConfig) +object AppConfig { + implicit val show: Show[AppConfig] = Show.show(_.toString) +} + case class SchedulerConfig(reader: ReaderConfig, publisher: PublisherConfig) object SchedulerConfig { diff --git a/scheduler/src/main/scala/com/sky/kms/domain/ApplicationError.scala b/scheduler/src/main/scala/com/sky/kms/domain/ApplicationError.scala index eabef705..92687f5d 100644 --- a/scheduler/src/main/scala/com/sky/kms/domain/ApplicationError.scala +++ b/scheduler/src/main/scala/com/sky/kms/domain/ApplicationError.scala @@ -22,10 +22,10 @@ object ApplicationError extends LazyLogging { final case class InvalidTimeError(key: String, time: OffsetDateTime) extends ApplicationError implicit val showError: Show[ApplicationError] = show { - case error: InvalidSchemaError => s"Invalid schema used to produce message with key ${error.key}" + case error: InvalidSchemaError => s"Invalid schema used to produce message with key ${error.key}" case error: AvroMessageFormatError => s"Error when processing message with key ${error.key}. ${error.cause.getMessage}" - case error: InvalidTimeError => s"Time between now and ${error.time} is not within 292 years" + case error: InvalidTimeError => s"Time between now and ${error.time} is not within 292 years" } def extractError[T]: Flow[Either[ApplicationError, T], ApplicationError, NotUsed] = diff --git a/scheduler/src/main/scala/com/sky/kms/domain/PublishableMessage.scala b/scheduler/src/main/scala/com/sky/kms/domain/PublishableMessage.scala index c7f62525..3af4bfdf 100644 --- a/scheduler/src/main/scala/com/sky/kms/domain/PublishableMessage.scala +++ b/scheduler/src/main/scala/com/sky/kms/domain/PublishableMessage.scala @@ -4,12 +4,13 @@ sealed trait PublishableMessage extends Product with Serializable object PublishableMessage { - final case class ScheduledMessage(inputTopic: String, - outputTopic: String, - key: Array[Byte], - value: Option[Array[Byte]], - headers: Map[String, Array[Byte]]) - extends PublishableMessage + final case class ScheduledMessage( + inputTopic: String, + outputTopic: String, + key: Array[Byte], + value: Option[Array[Byte]], + headers: Map[String, Array[Byte]] + ) extends PublishableMessage final case class ScheduleDeletion(scheduleId: ScheduleId, scheduleTopic: String, headers: Map[String, Array[Byte]]) extends PublishableMessage diff --git a/scheduler/src/main/scala/com/sky/kms/domain/Schedule.scala b/scheduler/src/main/scala/com/sky/kms/domain/Schedule.scala index 91ffeb87..3261d14a 100644 --- a/scheduler/src/main/scala/com/sky/kms/domain/Schedule.scala +++ b/scheduler/src/main/scala/com/sky/kms/domain/Schedule.scala @@ -1,6 +1,7 @@ package com.sky.kms.domain import java.time.OffsetDateTime + import scala.concurrent.duration.FiniteDuration sealed trait Schedule extends Product with Serializable @@ -10,16 +11,18 @@ object Schedule { final case class ScheduleNoHeaders(time: OffsetDateTime, topic: String, key: Array[Byte], value: Option[Array[Byte]]) extends Schedule - final case class ScheduleWithHeaders(time: OffsetDateTime, - topic: String, - key: Array[Byte], - value: Option[Array[Byte]], - headers: Map[String, Array[Byte]]) - extends Schedule - - implicit class ScheduleOps(val s: Schedule) extends AnyVal { - def getTime = Schedule.getTime(s) - def toScheduleEvent(delay: FiniteDuration, inputTopic: String) = Schedule.toScheduleEvent(delay, inputTopic, s) + final case class ScheduleWithHeaders( + time: OffsetDateTime, + topic: String, + key: Array[Byte], + value: Option[Array[Byte]], + headers: Map[String, Array[Byte]] + ) extends Schedule + + implicit class ScheduleOps(private val s: Schedule) extends AnyVal { + def getTime: OffsetDateTime = Schedule.getTime(s) + def toScheduleEvent(delay: FiniteDuration, inputTopic: String): ScheduleEvent = + Schedule.toScheduleEvent(delay, inputTopic, s) } private def getTime(schedule: Schedule): OffsetDateTime = schedule match { @@ -29,7 +32,7 @@ object Schedule { private def toScheduleEvent(delay: FiniteDuration, inputTopic: String, schedule: Schedule): ScheduleEvent = schedule match { - case ScheduleNoHeaders(_, outputTopic, key, value) => + case ScheduleNoHeaders(_, outputTopic, key, value) => ScheduleEvent(delay, inputTopic, outputTopic, key, value, Map.empty) case ScheduleWithHeaders(_, outputTopic, key, value, headers) => ScheduleEvent(delay, inputTopic, outputTopic, key, value, headers) diff --git a/scheduler/src/main/scala/com/sky/kms/domain/ScheduleEvent.scala b/scheduler/src/main/scala/com/sky/kms/domain/ScheduleEvent.scala new file mode 100644 index 00000000..99d8ddc0 --- /dev/null +++ b/scheduler/src/main/scala/com/sky/kms/domain/ScheduleEvent.scala @@ -0,0 +1,12 @@ +package com.sky.kms.domain + +import scala.concurrent.duration.FiniteDuration + +case class ScheduleEvent( + delay: FiniteDuration, + inputTopic: String, + outputTopic: String, + key: Array[Byte], + value: Option[Array[Byte]], + headers: Map[String, Array[Byte]] +) diff --git a/scheduler/src/main/scala/com/sky/kms/domain/ScheduleQueueOfferResult.scala b/scheduler/src/main/scala/com/sky/kms/domain/ScheduleQueueOfferResult.scala index e7c695e6..62980812 100644 --- a/scheduler/src/main/scala/com/sky/kms/domain/ScheduleQueueOfferResult.scala +++ b/scheduler/src/main/scala/com/sky/kms/domain/ScheduleQueueOfferResult.scala @@ -10,13 +10,13 @@ case class ScheduleQueueOfferResult(scheduleId: ScheduleId, queueOfferResult: Qu object ScheduleQueueOfferResult { implicit val queueOfferResultShow: Show[ScheduleQueueOfferResult] = show { - case ScheduleQueueOfferResult(scheduleId, Enqueued) => + case ScheduleQueueOfferResult(scheduleId, Enqueued) => s"$scheduleId enqueued successfully" - case ScheduleQueueOfferResult(scheduleId, Dropped) => + case ScheduleQueueOfferResult(scheduleId, Dropped) => s"$scheduleId was dropped from queue, check the chosen overflow strategy" case ScheduleQueueOfferResult(scheduleId, QueueOfferResult.Failure(t)) => s"An error occurred when attempting to enqueue the $scheduleId: ${t.getMessage}" - case ScheduleQueueOfferResult(scheduleId, QueueClosed) => + case ScheduleQueueOfferResult(scheduleId, QueueClosed) => s"Unable to enqueue $scheduleId because the downstream source queue has been completed/closed" } diff --git a/scheduler/src/main/scala/com/sky/kms/domain/package.scala b/scheduler/src/main/scala/com/sky/kms/domain/package.scala index ed33e54e..37df08ba 100644 --- a/scheduler/src/main/scala/com/sky/kms/domain/package.scala +++ b/scheduler/src/main/scala/com/sky/kms/domain/package.scala @@ -5,21 +5,13 @@ import java.lang import org.apache.kafka.common.header.Header import org.apache.kafka.common.header.internals.RecordHeader -import scala.collection.JavaConverters._ -import scala.concurrent.duration.FiniteDuration +import scala.jdk.CollectionConverters._ package object domain { type ScheduleId = String - case class ScheduleEvent(delay: FiniteDuration, - inputTopic: String, - outputTopic: String, - key: Array[Byte], - value: Option[Array[Byte]], - headers: Map[String, Array[Byte]]) - - implicit class HeadersOps(val headers: Map[String, Array[Byte]]) extends AnyVal { + implicit class HeadersOps(private val headers: Map[String, Array[Byte]]) extends AnyVal { def asKafkaHeaders: lang.Iterable[Header] = headers.map { case (k, v) => new RecordHeader(k, v): Header }.asJava } diff --git a/scheduler/src/main/scala/com/sky/kms/monitoring/Monitoring.scala b/scheduler/src/main/scala/com/sky/kms/monitoring/Monitoring.scala index d48e8cda..8219b3ab 100644 --- a/scheduler/src/main/scala/com/sky/kms/monitoring/Monitoring.scala +++ b/scheduler/src/main/scala/com/sky/kms/monitoring/Monitoring.scala @@ -2,7 +2,7 @@ package com.sky.kms.monitoring trait Monitoring { - def scheduleReceived() + def scheduleReceived(): Unit - def scheduleDone() + def scheduleDone(): Unit } diff --git a/scheduler/src/main/scala/com/sky/kms/package.scala b/scheduler/src/main/scala/com/sky/kms/package.scala index 8b6f120d..179f7fcd 100644 --- a/scheduler/src/main/scala/com/sky/kms/package.scala +++ b/scheduler/src/main/scala/com/sky/kms/package.scala @@ -24,15 +24,17 @@ package object kms { scheduleEvent(cr, decoder[ScheduleWithHeaders]) .orElse(scheduleEvent(cr, decoder[ScheduleNoHeaders])) - private def scheduleEvent[A <: Schedule](cr: ConsumerRecord[String, Array[Byte]], - decode: Array[Byte] => Option[Try[A]]): ScheduleReader.In = + private def scheduleEvent[A <: Schedule]( + cr: ConsumerRecord[String, Array[Byte]], + decode: Array[Byte] => Option[Try[A]] + ): ScheduleReader.In = Option(cr.value).fold[ScheduleReader.In]((cr.key, None).asRight[ApplicationError]) { bytes => for { scheduleTry <- Either.fromOption(decode(bytes), InvalidSchemaError(cr.key)) avroSchedule <- scheduleTry.toEither.leftMap(AvroMessageFormatError(cr.key, _)) - delay <- Either - .catchNonFatal(MILLIS.between(OffsetDateTime.now, avroSchedule.getTime).millis) - .leftMap(_ => InvalidTimeError(cr.key, avroSchedule.getTime)) + delay <- Either + .catchNonFatal(MILLIS.between(OffsetDateTime.now, avroSchedule.getTime).millis) + .leftMap(_ => InvalidTimeError(cr.key, avroSchedule.getTime)) } yield cr.key -> avroSchedule.toScheduleEvent(delay, cr.topic).some } diff --git a/scheduler/src/main/scala/com/sky/kms/streams/ScheduleReader.scala b/scheduler/src/main/scala/com/sky/kms/streams/ScheduleReader.scala index e5bfc831..a66d62a6 100644 --- a/scheduler/src/main/scala/com/sky/kms/streams/ScheduleReader.scala +++ b/scheduler/src/main/scala/com/sky/kms/streams/ScheduleReader.scala @@ -4,7 +4,6 @@ import akka.Done import akka.actor.{ActorRef, ActorSystem, PoisonPill} import akka.kafka.scaladsl.Consumer.Control import akka.pattern.ask -import akka.stream._ import akka.stream.scaladsl._ import cats.Eval import com.sky.kafka.topicloader._ @@ -19,19 +18,20 @@ import org.apache.kafka.common.serialization.{ByteArrayDeserializer, Deserialize import scala.concurrent.Future -/** - * Provides stream from the schedule source to the scheduling actor. +/** Provides stream from the schedule source to the scheduling actor. */ -case class ScheduleReader[Mat](scheduleSource: Eval[Source[In, (Future[Done], Mat)]], - schedulingActor: ActorRef, - errorHandler: Sink[ApplicationError, Future[Done]], - timeouts: ReaderConfig.TimeoutConfig)(implicit system: ActorSystem) { +case class ScheduleReader[Mat]( + scheduleSource: Eval[Source[In, (Future[Done], Mat)]], + schedulingActor: ActorRef, + errorHandler: Sink[ApplicationError, Future[Done]], + timeouts: ReaderConfig.TimeoutConfig +)(implicit system: ActorSystem) { import system.dispatcher private def initSchedulingActorWhenReady(f: Future[Done]): Future[Any] = - f.flatMap(_ => (schedulingActor ? Initialised)(timeouts.initialisation)).recover { - case t => schedulingActor ! UpstreamFailure(t) + f.flatMap(_ => (schedulingActor ? Initialised)(timeouts.initialisation)).recover { case t => + schedulingActor ! UpstreamFailure(t) } def stream: RunnableGraph[Mat] = @@ -39,7 +39,7 @@ case class ScheduleReader[Mat](scheduleSource: Eval[Source[In, (Future[Done], Ma .map(ScheduleReader.toSchedulingMessage) .alsoTo(extractError.to(errorHandler)) .collect { case Right(msg) => msg } - .to(Sink.actorRefWithAck(schedulingActor, StreamStarted, Ack, PoisonPill, UpstreamFailure)) + .to(Sink.actorRefWithBackpressure(schedulingActor, StreamStarted, Ack, PoisonPill, UpstreamFailure)) } object ScheduleReader extends LazyLogging { @@ -50,9 +50,8 @@ object ScheduleReader extends LazyLogging { type LoadSchedule = SchedulingMessage => Future[Ack.type] def toSchedulingMessage(readResult: In): Either[ApplicationError, SchedulingMessage] = - readResult.map { - case (scheduleId, scheduleOpt) => - scheduleOpt.fold[SchedulingMessage](Cancel(scheduleId))(CreateOrUpdate(scheduleId, _)) + readResult.map { case (scheduleId, scheduleOpt) => + scheduleOpt.fold[SchedulingMessage](Cancel(scheduleId))(CreateOrUpdate(scheduleId, _)) } def configure(actorRef: ActorRef)(implicit system: ActorSystem): Configured[ScheduleReader[Future[Control]]] = @@ -72,7 +71,7 @@ object ScheduleReader extends LazyLogging { ) } - def run(implicit mat: ActorMaterializer): Start[Running[Future[Control]]] = + def run(implicit system: ActorSystem): Start[Running[Future[Control]]] = Start { app => Running(app.reader.stream.run()) } diff --git a/scheduler/src/main/scala/com/sky/kms/streams/ScheduledMessagePublisher.scala b/scheduler/src/main/scala/com/sky/kms/streams/ScheduledMessagePublisher.scala index 297a007f..6157111e 100644 --- a/scheduler/src/main/scala/com/sky/kms/streams/ScheduledMessagePublisher.scala +++ b/scheduler/src/main/scala/com/sky/kms/streams/ScheduledMessagePublisher.scala @@ -4,8 +4,8 @@ import akka.Done import akka.actor.ActorSystem import akka.kafka.ProducerSettings import akka.kafka.scaladsl.Producer +import akka.stream.OverflowStrategy import akka.stream.scaladsl._ -import akka.stream.{ActorMaterializer, OverflowStrategy} import cats.Eval import com.sky.kms.Start import com.sky.kms.actors.PublisherActor.ScheduleQueue @@ -19,10 +19,8 @@ import org.apache.kafka.common.serialization.ByteArraySerializer import scala.concurrent.Future -/** - * Provides a stream that consumes from the queue of triggered messages, - * writes the scheduled messages to the specified Kafka topics and then deletes the schedules - * from the scheduling Kafka topic to mark completion +/** Provides a stream that consumes from the queue of triggered messages, writes the scheduled messages to the specified + * Kafka topics and then deletes the schedules from the scheduling Kafka topic to mark completion */ case class ScheduledMessagePublisher(queueBufferSize: Int, publisherSink: Eval[Sink[SinkIn, SinkMat]]) extends LazyLogging { @@ -33,11 +31,11 @@ case class ScheduledMessagePublisher(queueBufferSize: Int, publisherSink: Eval[S .mapConcat(splitToMessageAndDeletion) .toMat(publisherSink.value)(Keep.both) - val splitToMessageAndDeletion: In => List[PublishableMessage] = { - case (scheduleId, scheduledMessage) => - logger.info( - s"Publishing scheduled message $scheduleId to ${scheduledMessage.outputTopic} and deleting it from ${scheduledMessage.inputTopic}") - List(scheduledMessage, ScheduleDeletion(scheduleId, scheduledMessage.inputTopic, scheduledMessage.headers)) + val splitToMessageAndDeletion: In => List[PublishableMessage] = { case (scheduleId, scheduledMessage) => + logger.info( + s"Publishing scheduled message $scheduleId to ${scheduledMessage.outputTopic} and deleting it from ${scheduledMessage.inputTopic}" + ) + List(scheduledMessage, ScheduleDeletion(scheduleId, scheduledMessage.inputTopic, scheduledMessage.headers)) } } @@ -57,7 +55,9 @@ object ScheduledMessagePublisher { Flow[PublishableMessage] .map(toProducerRecord) .toMat(Producer.plainSink(ProducerSettings(system, new ByteArraySerializer, new ByteArraySerializer)))( - Keep.right)) + Keep.right + ) + ) PublisherConfig.configure.map(c => ScheduledMessagePublisher(c.queueBufferSize, writeMsgToKafka)) } @@ -65,11 +65,11 @@ object ScheduledMessagePublisher { val toProducerRecord: PublishableMessage => ProducerRecord[Array[Byte], Array[Byte]] = { case ScheduledMessage(_, outputTopic, key, value, headers) => new ProducerRecord(outputTopic, null, key, value.orNull, headers.asKafkaHeaders) - case ScheduleDeletion(id, outputTopic, headers) => + case ScheduleDeletion(id, outputTopic, headers) => new ProducerRecord(outputTopic, null, id.getBytes, null, headers.asKafkaHeaders) } - def run(implicit mat: ActorMaterializer): Start[Running] = + def run(implicit system: ActorSystem): Start[Running] = Start { app => val (sourceMat, sinkMat) = app.publisher.stream.run() Running(sourceMat, sinkMat) diff --git a/scheduler/src/test/resources/logback-test.xml b/scheduler/src/test/resources/logback-test.xml index ba76427d..21050f06 100644 --- a/scheduler/src/test/resources/logback-test.xml +++ b/scheduler/src/test/resources/logback-test.xml @@ -10,4 +10,4 @@ - \ No newline at end of file + diff --git a/scheduler/src/test/scala/com/sky/kms/base/AkkaKafkaSpecBase.scala b/scheduler/src/test/scala/com/sky/kms/base/AkkaKafkaSpecBase.scala index b0242726..5d2feb30 100644 --- a/scheduler/src/test/scala/com/sky/kms/base/AkkaKafkaSpecBase.scala +++ b/scheduler/src/test/scala/com/sky/kms/base/AkkaKafkaSpecBase.scala @@ -1,7 +1,8 @@ package com.sky.kms.base +import akka.actor.ActorSystem import com.sky.kms.utils.TestActorSystem -trait AkkaKafkaSpecBase extends AkkaStreamSpecBase with KafkaIntSpecBase { - override implicit lazy val system = TestActorSystem(kafkaConfig.kafkaPort) +trait AkkaKafkaSpecBase extends AkkaSpecBase with KafkaIntSpecBase { + override implicit lazy val system: ActorSystem = TestActorSystem(kafkaConfig.kafkaPort) } diff --git a/scheduler/src/test/scala/com/sky/kms/base/AkkaSpecBase.scala b/scheduler/src/test/scala/com/sky/kms/base/AkkaSpecBase.scala index b87fa64e..fe237323 100644 --- a/scheduler/src/test/scala/com/sky/kms/base/AkkaSpecBase.scala +++ b/scheduler/src/test/scala/com/sky/kms/base/AkkaSpecBase.scala @@ -1,11 +1,12 @@ package com.sky.kms.base +import akka.actor.ActorSystem import akka.testkit.{TestKit, TestKitBase} import com.sky.kms.utils.TestActorSystem -abstract class AkkaSpecBase extends TestKitBase with SpecBase { +abstract class AkkaSpecBase extends SpecBase with TestKitBase { - override implicit lazy val system = TestActorSystem() + override implicit lazy val system: ActorSystem = TestActorSystem() override def afterAll(): Unit = { super.afterAll() diff --git a/scheduler/src/test/scala/com/sky/kms/base/AkkaStreamSpecBase.scala b/scheduler/src/test/scala/com/sky/kms/base/AkkaStreamSpecBase.scala deleted file mode 100644 index 7b4918ce..00000000 --- a/scheduler/src/test/scala/com/sky/kms/base/AkkaStreamSpecBase.scala +++ /dev/null @@ -1,13 +0,0 @@ -package com.sky.kms.base - -import akka.stream.ActorMaterializer - -abstract class AkkaStreamSpecBase extends AkkaSpecBase { - - implicit val materializer = ActorMaterializer() - - override def afterAll(): Unit = { - super.afterAll() - materializer.shutdown() - } -} diff --git a/scheduler/src/test/scala/com/sky/kms/base/KafkaIntSpecBase.scala b/scheduler/src/test/scala/com/sky/kms/base/KafkaIntSpecBase.scala index 1df9dc8a..1752af9a 100644 --- a/scheduler/src/test/scala/com/sky/kms/base/KafkaIntSpecBase.scala +++ b/scheduler/src/test/scala/com/sky/kms/base/KafkaIntSpecBase.scala @@ -3,23 +3,27 @@ package com.sky.kms.base import com.sky.kms.kafka.Topic import com.sky.kms.utils.RandomPort.randomPort import eu.timepit.refined.auto._ -import net.manub.embeddedkafka.Codecs.{nullDeserializer, stringDeserializer} -import net.manub.embeddedkafka.{EmbeddedKafka, EmbeddedKafkaConfig} +import io.github.embeddedkafka.Codecs.{nullDeserializer, stringDeserializer} +import io.github.embeddedkafka.{EmbeddedKafka, EmbeddedKafkaConfig} import org.apache.kafka.clients.consumer.{ConsumerRecord, KafkaConsumer} import org.apache.kafka.common.serialization.Deserializer -import org.scalatest.WordSpecLike +import org.scalatest.wordspec.AnyWordSpec -import collection.JavaConverters._ -import scala.concurrent.duration._ import scala.compat.java8.DurationConverters._ +import scala.concurrent.duration._ +import scala.jdk.CollectionConverters._ -trait KafkaIntSpecBase extends EmbeddedKafka with WordSpecLike { +trait KafkaIntSpecBase extends AnyWordSpec with EmbeddedKafka { - implicit lazy val kafkaConfig = EmbeddedKafkaConfig(kafkaPort = randomPort(), zooKeeperPort = randomPort()) + implicit lazy val kafkaConfig: EmbeddedKafkaConfig = + EmbeddedKafkaConfig( + kafkaPort = randomPort(), + zooKeeperPort = randomPort() + ) val scheduleTopic: Topic = "scheduleTopic" val extraScheduleTopic: Topic = "extraScheduleTopic" - def kafkaConsumerTimeout: FiniteDuration = 60 seconds + def kafkaConsumerTimeout: FiniteDuration = 60.seconds private def subscribeAndPoll[K, V](topic: String): KafkaConsumer[K, V] => Iterator[ConsumerRecord[K, V]] = { cr => cr.subscribe(List(topic).asJavaCollection) diff --git a/scheduler/src/test/scala/com/sky/kms/base/SchedulerIntSpecBase.scala b/scheduler/src/test/scala/com/sky/kms/base/SchedulerIntSpecBase.scala index 0864aa5e..589b324f 100644 --- a/scheduler/src/test/scala/com/sky/kms/base/SchedulerIntSpecBase.scala +++ b/scheduler/src/test/scala/com/sky/kms/base/SchedulerIntSpecBase.scala @@ -1,10 +1,13 @@ package com.sky.kms.base import cats.data.NonEmptyList +import com.sky.kms.config.SchedulerConfig import com.sky.kms.utils.TestConfig + import scala.concurrent.duration._ abstract class SchedulerIntSpecBase extends AkkaKafkaSpecBase { - implicit val conf = TestConfig(NonEmptyList.of(scheduleTopic, extraScheduleTopic)) - val tolerance = 1300 milliseconds + implicit val conf: SchedulerConfig = TestConfig(NonEmptyList.of(scheduleTopic, extraScheduleTopic)) + val tolerance: FiniteDuration = 1300.milliseconds + override implicit val patienceConfig: PatienceConfig = PatienceConfig(60.seconds, 1.second) } diff --git a/scheduler/src/test/scala/com/sky/kms/base/SpecBase.scala b/scheduler/src/test/scala/com/sky/kms/base/SpecBase.scala index 72b22864..b17b3212 100644 --- a/scheduler/src/test/scala/com/sky/kms/base/SpecBase.scala +++ b/scheduler/src/test/scala/com/sky/kms/base/SpecBase.scala @@ -1,7 +1,17 @@ package com.sky.kms.base +import cats.scalatest.EitherValues import com.danielasfregola.randomdatagenerator.RandomDataGenerator -import org.scalatest.concurrent.ScalaFutures -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.BeforeAndAfterAll +import org.scalatest.concurrent.{Eventually, ScalaFutures} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec -trait SpecBase extends WordSpecLike with Matchers with BeforeAndAfterAll with RandomDataGenerator with ScalaFutures +trait SpecBase + extends AnyWordSpec + with Matchers + with EitherValues + with BeforeAndAfterAll + with RandomDataGenerator + with ScalaFutures + with Eventually diff --git a/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerDeleteIntSpec.scala b/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerDeleteIntSpec.scala index 617f0973..1825ba5a 100644 --- a/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerDeleteIntSpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerDeleteIntSpec.scala @@ -1,10 +1,10 @@ package com.sky.kms.e2e import com.sky.kms.base.SchedulerIntSpecBase -import com.sky.kms.utils.TestDataUtils._ import com.sky.kms.domain._ +import com.sky.kms.utils.TestDataUtils._ import eu.timepit.refined.auto._ -import net.manub.embeddedkafka.Codecs.{stringSerializer, stringDeserializer, nullSerializer => arrayByteSerializer} +import io.github.embeddedkafka.Codecs.{nullSerializer => arrayByteSerializer, stringDeserializer, stringSerializer} class SchedulerDeleteIntSpec extends SchedulerIntSpecBase { diff --git a/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerIntSpec.scala b/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerIntSpec.scala index 001c05af..91caa995 100644 --- a/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerIntSpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerIntSpec.scala @@ -3,10 +3,10 @@ package com.sky.kms.e2e import java.time.OffsetDateTime import com.sky.kms.base.SchedulerIntSpecBase -import com.sky.kms.utils.TestDataUtils._ import com.sky.kms.domain._ +import com.sky.kms.utils.TestDataUtils._ import eu.timepit.refined.auto._ -import net.manub.embeddedkafka.Codecs._ +import io.github.embeddedkafka.Codecs._ class SchedulerIntSpec extends SchedulerIntSpecBase { @@ -28,44 +28,40 @@ class SchedulerIntSpec extends SchedulerIntSpecBase { private class TestContext { def createSchedules(numSchedules: Int, forTopics: List[String]): List[(ScheduleId, ScheduleEvent)] = random[(ScheduleId, ScheduleEvent)](numSchedules).toList - .zip(Stream.continually(forTopics.toStream).flatten.take(numSchedules).toList) - .map { - case ((id, schedule), topic) => - id -> schedule.copy(inputTopic = topic).secondsFromNow(4) + .zip(LazyList.continually(forTopics.to(LazyList)).flatten.take(numSchedules).toList) + .map { case ((id, schedule), topic) => + id -> schedule.copy(inputTopic = topic).secondsFromNow(4) } - def publish: List[(ScheduleId, ScheduleEvent)] => List[OffsetDateTime] = _.map { - case (id, scheduleEvent) => - val schedule = scheduleEvent.toSchedule - publishToKafka(scheduleEvent.inputTopic, id, schedule.toAvro) - schedule.time + def publish: List[(ScheduleId, ScheduleEvent)] => List[OffsetDateTime] = _.map { case (id, scheduleEvent) => + val schedule = scheduleEvent.toSchedule + publishToKafka(scheduleEvent.inputTopic, id, schedule.toAvro) + schedule.time } def assertMessagesWrittenFrom(time: OffsetDateTime, schedules: List[(ScheduleId, ScheduleEvent)]): Unit = - schedules.foreach { - case (_, schedule) => - val cr = consumeFirstFrom[Array[Byte]](schedule.outputTopic) + schedules.foreach { case (_, schedule) => + val cr = consumeFirstFrom[Array[Byte]](schedule.outputTopic) - cr.key should contain theSameElementsInOrderAs schedule.key + cr.key should contain theSameElementsInOrderAs schedule.key - schedule.value match { - case Some(value) => cr.value should contain theSameElementsAs value - case None => cr.value shouldBe null - } + schedule.value match { + case Some(value) => cr.value should contain theSameElementsAs value + case None => cr.value shouldBe null + } - cr.timestamp shouldBe time.toInstant.toEpochMilli +- tolerance.toMillis - cr.headers().toArray.map(h => h.key() -> h.value().toList) should contain theSameElementsAs - schedule.headers.map { - case (k, v) => (k, v.toList) - } + cr.timestamp shouldBe time.toInstant.toEpochMilli +- tolerance.toMillis + cr.headers().toArray.map(h => h.key() -> h.value().toList) should contain theSameElementsAs + schedule.headers.map { case (k, v) => + (k, v.toList) + } } def assertTombstoned(schedules: List[(ScheduleId, ScheduleEvent)]): Unit = - schedules.groupBy(_._2.inputTopic).foreach { - case (topic, schedulesByInputTopic) => - val tombstones = consumeSomeFrom[String](topic, schedulesByInputTopic.size * 2).filter(_.value == null) - tombstones.size shouldBe schedulesByInputTopic.size - tombstones.map(_.key) shouldBe schedulesByInputTopic.map(_._1).distinct + schedules.groupBy(_._2.inputTopic).foreach { case (topic, schedulesByInputTopic) => + val tombstones = consumeSomeFrom[String](topic, schedulesByInputTopic.size * 2).filter(_.value == null) + tombstones.size shouldBe schedulesByInputTopic.size + tombstones.map(_.key) shouldBe schedulesByInputTopic.map(_._1).distinct } } diff --git a/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerResiliencySpec.scala b/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerResiliencySpec.scala index 402487a0..516cbb81 100644 --- a/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerResiliencySpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerResiliencySpec.scala @@ -10,11 +10,12 @@ import akka.testkit.TestProbe import cats.data.NonEmptyList import cats.syntax.either._ import cats.syntax.option._ +import com.sky.kms.actors.SchedulingActor.UpstreamFailure import com.sky.kms.base.SpecBase import com.sky.kms.config._ import com.sky.kms.domain.{ApplicationError, ScheduleEvent} import com.sky.kms.kafka.Topic -import com.sky.kms.streams.{ScheduleReader, ScheduledMessagePublisher} +import com.sky.kms.streams.ScheduleReader import com.sky.kms.utils.TestDataUtils._ import com.sky.kms.utils.{StubControl, TestConfig} import com.sky.kms.{AkkaComponents, SchedulerApp} @@ -25,7 +26,7 @@ import scala.concurrent.{Await, Future} class SchedulerResiliencySpec extends SpecBase { - override implicit val patienceConfig = PatienceConfig(10 seconds, 500 millis) + override implicit val patienceConfig = PatienceConfig(10.seconds, 500.millis) "KMS" should { "terminate when the reader stream fails" in new TestContext with FailingSource with AkkaComponents { @@ -51,10 +52,10 @@ class SchedulerResiliencySpec extends SpecBase { } "terminate when the queue buffer becomes full" in new TestContext with IteratingSource with AkkaComponents { - val sameTimeSchedules = random[ScheduleEvent](n = 20).map(_.secondsFromNow(2)) - val probe = TestProbe() + val sameTimeSchedules = random[ScheduleEvent](n = 20).map(_.secondsFromNow(2)) + val probe = TestProbe() val sinkThatWillNotSignalDemand = Sink - .actorRefWithAck[ScheduledMessagePublisher.SinkIn](probe.ref, "", "", "") + .actorRefWithBackpressure(probe.ref, "", "", "", UpstreamFailure) .mapMaterializedValue(_ => Future.never) val app = @@ -76,7 +77,7 @@ class SchedulerResiliencySpec extends SpecBase { SchedulerApp.configure apply AppConfig(config) def hasActorSystemTerminated(implicit system: ActorSystem): Boolean = - Await.ready(system.whenTerminated, 10 seconds).isCompleted + Await.ready(system.whenTerminated, 10.seconds).isCompleted } private trait FailingSource { @@ -85,7 +86,8 @@ class SchedulerResiliencySpec extends SpecBase { val sourceThatWillFail: Source[ScheduleReader.In, (Future[Done], Future[Control])] = Source .fromIterator(() => - Iterator(("someId" -> none[ScheduleEvent]).asRight[ApplicationError]) ++ (throw new Exception("boom!"))) + Iterator(("someId" -> none[ScheduleEvent]).asRight[ApplicationError]) ++ (throw new Exception("boom!")) + ) .mapMaterializedValue(_ => Future.successful(Done) -> Future.successful(StubControl())) } @@ -95,7 +97,7 @@ class SchedulerResiliencySpec extends SpecBase { def sourceWith(schedules: Seq[ScheduleEvent]): Source[ScheduleReader.In, (Future[Done], Future[Control])] = { val scheduleIds = List.fill(schedules.size)(UUID.randomUUID().toString) - val elements = (scheduleIds, schedules.map(_.some)).zipped.toIterator.map(_.asRight[ApplicationError]).toList + val elements = scheduleIds.zip(schedules.map(_.some)).map(_.asRight[ApplicationError]) Source(elements).mapMaterializedValue(_ => Future.successful(Done) -> Future.successful(StubControl())) } diff --git a/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerSchemaEvolutionSpec.scala b/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerSchemaEvolutionSpec.scala index d59cecdd..05f04b51 100644 --- a/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerSchemaEvolutionSpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/e2e/SchedulerSchemaEvolutionSpec.scala @@ -1,16 +1,16 @@ package com.sky.kms.e2e -import com.danielasfregola.randomdatagenerator.RandomDataGenerator import cats.syntax.option._ +import com.danielasfregola.randomdatagenerator.RandomDataGenerator import com.sky.kms._ import com.sky.kms.base.SchedulerIntSpecBase import com.sky.kms.domain.ScheduleEvent import com.sky.kms.streams.ScheduleReader import com.sky.kms.utils.TestDataUtils._ -import net.manub.embeddedkafka.Codecs.{ - stringSerializer, +import io.github.embeddedkafka.Codecs.{ nullDeserializer => arrayByteDeserializer, - nullSerializer => arrayByteSerializer + nullSerializer => arrayByteSerializer, + stringSerializer } class SchedulerSchemaEvolutionSpec extends SchedulerIntSpecBase with RandomDataGenerator { @@ -47,7 +47,7 @@ class SchedulerSchemaEvolutionSpec extends SchedulerIntSpecBase with RandomDataG trait TestContext { val inputTopic = "cupcat" - val delay = 4 + val delay = 4L def publishAndGetDecoded(inputTopic: String, schedule: Array[Byte]) = { publishToKafka(inputTopic, inputTopic, schedule) @@ -62,10 +62,13 @@ class SchedulerSchemaEvolutionSpec extends SchedulerIntSpecBase with RandomDataG } implicit class HeaderOps(val schedule: ScheduleReader.In) { - def headers = - schedule.fold(_ => none[Map[String, Array[Byte]]], { - case (_, ose) => ose.fold(none[Map[String, Array[Byte]]])(_.headers.some) - }) + def headers = + schedule.fold( + _ => none[Map[String, Array[Byte]]], + { case (_, ose) => + ose.fold(none[Map[String, Array[Byte]]])(_.headers.some) + } + ) def headerKeys = headers.map(_.keys.toList) def headerValues = headers.map(_.values.toList.map(_.toList)) } diff --git a/scheduler/src/test/scala/com/sky/kms/e2e/package.scala b/scheduler/src/test/scala/com/sky/kms/e2e/package.scala index d2bb6020..39127528 100644 --- a/scheduler/src/test/scala/com/sky/kms/e2e/package.scala +++ b/scheduler/src/test/scala/com/sky/kms/e2e/package.scala @@ -1,25 +1,25 @@ package com.sky.kms -import akka.actor.{ActorSystem, CoordinatedShutdown} import akka.actor.CoordinatedShutdown.UnknownReason -import akka.stream.ActorMaterializer +import akka.actor.{ActorSystem, CoordinatedShutdown} import com.sky.kms.config.{AppConfig, SchedulerConfig} package object e2e { def withSchedulerApp[T]( - scenario: => T)(implicit conf: SchedulerConfig, system: ActorSystem, mat: ActorMaterializer): T = + scenario: => T + )(implicit conf: SchedulerConfig, system: ActorSystem): T = withRunningScheduler(SchedulerApp.configure apply AppConfig(conf))(_ => scenario) - def withRunningScheduler[T](schedulerApp: SchedulerApp)( - scenario: SchedulerApp.Running => T)(implicit system: ActorSystem, mat: ActorMaterializer): T = { + def withRunningScheduler[T]( + schedulerApp: SchedulerApp + )(scenario: SchedulerApp.Running => T)(implicit system: ActorSystem): T = { val runningApp = SchedulerApp.run apply schedulerApp - try { + try scenario(runningApp) - } finally { + finally CoordinatedShutdown(system).run(UnknownReason) - } } } diff --git a/scheduler/src/test/scala/com/sky/kms/integration/ScheduleReaderIntSpec.scala b/scheduler/src/test/scala/com/sky/kms/integration/ScheduleReaderIntSpec.scala index d2e32f73..3ac0ef06 100644 --- a/scheduler/src/test/scala/com/sky/kms/integration/ScheduleReaderIntSpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/integration/ScheduleReaderIntSpec.scala @@ -2,8 +2,8 @@ package com.sky.kms.integration import java.util.UUID +import akka.actor.ActorSystem import akka.testkit.{TestActor, TestProbe} -import cats.instances.tuple._ import cats.syntax.functor._ import com.sky.kms.actors.SchedulingActor._ import com.sky.kms.base.SchedulerIntSpecBase @@ -13,19 +13,32 @@ import com.sky.kms.streams.ScheduleReader import com.sky.kms.utils.TestActorSystem import com.sky.kms.utils.TestDataUtils._ import eu.timepit.refined.auto._ -import net.manub.embeddedkafka.Codecs.{stringSerializer, nullSerializer => arrayByteSerializer} -import org.scalatest.concurrent.Eventually +import io.github.embeddedkafka.Codecs.{nullSerializer => arrayByteSerializer, stringSerializer} import scala.concurrent.Await import scala.concurrent.duration._ -class ScheduleReaderIntSpec extends SchedulerIntSpecBase with Eventually { +class ScheduleReaderIntSpec extends SchedulerIntSpecBase { - override implicit lazy val system = TestActorSystem(kafkaConfig.kafkaPort, akkaExpectDuration = 20.seconds) + override implicit lazy val system: ActorSystem = + TestActorSystem(kafkaConfig.kafkaPort, akkaExpectDuration = 20.seconds) val numSchedules = 3 "stream" should { + "continue processing when Kafka becomes available" in withRunningScheduleReader { probe => + withRunningKafka { + probe.expectMsg(StreamStarted) + probe.expectMsg(Initialised) + scheduleShouldFlow(probe) + } + // Wait 5 seconds. Embedded Kafka causes issues if you restart too quickly on the same ports. + Thread.sleep(5000) + withRunningKafka { + scheduleShouldFlow(probe) + } + } + "not schedule messages that have been deleted but not compacted on startup" in withRunningKafka { val schedules @ firstSchedule :: _ = List.fill(numSchedules)(generateSchedule) writeSchedulesToKafka(schedules: _*) @@ -40,17 +53,6 @@ class ScheduleReaderIntSpec extends SchedulerIntSpecBase with Eventually { probe.expectMsg(Initialised) } } - - "continue processing when Kafka becomes available" in withRunningScheduleReader { probe => - withRunningKafka { - probe.expectMsg(StreamStarted) - probe.expectMsg(Initialised) - scheduleShouldFlow(probe) - } - withRunningKafka { - scheduleShouldFlow(probe) - } - } } private def generateSchedule: (ScheduleId, ScheduleEvent) = UUID.randomUUID().toString -> random[ScheduleEvent] @@ -63,7 +65,8 @@ class ScheduleReaderIntSpec extends SchedulerIntSpecBase with Eventually { case _ => sender ! Ack TestActor.KeepRunning - }) + } + ) p } @@ -73,17 +76,19 @@ class ScheduleReaderIntSpec extends SchedulerIntSpecBase with Eventually { .stream .run() - try { + try scenario(probe) - } finally { - Await.ready(controlF.flatMap(_.shutdown())(system.dispatcher), 5 seconds) - } + finally + Await.ready(controlF.flatMap(_.shutdown())(system.dispatcher), 5.seconds) } private def writeSchedulesToKafka(schedules: (ScheduleId, ScheduleEvent)*): Unit = - publishToKafka(scheduleTopic, schedules.map { - case (scheduleId, scheduleEvent) => (scheduleId, scheduleEvent.toSchedule.toAvro) - }) + publishToKafka( + scheduleTopic, + schedules.map { case (scheduleId, scheduleEvent) => + (scheduleId, scheduleEvent.toSchedule.toAvro) + } + ) private def scheduleShouldFlow(probe: TestProbe): SchedulingMessage = { writeSchedulesToKafka(generateSchedule) diff --git a/scheduler/src/test/scala/com/sky/kms/unit/AvroSpec.scala b/scheduler/src/test/scala/com/sky/kms/unit/AvroSpec.scala index fa09919b..1f9dc806 100644 --- a/scheduler/src/test/scala/com/sky/kms/unit/AvroSpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/unit/AvroSpec.scala @@ -3,8 +3,8 @@ package com.sky.kms.unit import java.time.OffsetDateTime import com.sksamuel.avro4s._ -import com.sky.kms.base.SpecBase import com.sky.kms.avro._ +import com.sky.kms.base.SpecBase import org.apache.avro.Schema import org.apache.avro.generic.GenericData diff --git a/scheduler/src/test/scala/com/sky/kms/unit/PublisherActorSpec.scala b/scheduler/src/test/scala/com/sky/kms/unit/PublisherActorSpec.scala index aaf04a06..f8ffeb75 100644 --- a/scheduler/src/test/scala/com/sky/kms/unit/PublisherActorSpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/unit/PublisherActorSpec.scala @@ -6,8 +6,8 @@ import akka.testkit.TestActorRef import com.sky.kms.actors.PublisherActor import com.sky.kms.actors.PublisherActor._ import com.sky.kms.base.AkkaSpecBase -import com.sky.kms.utils.TestDataUtils._ import com.sky.kms.domain.{ScheduleEvent, ScheduleId} +import com.sky.kms.utils.TestDataUtils._ import org.mockito.Mockito._ import org.scalatestplus.mockito.MockitoSugar diff --git a/scheduler/src/test/scala/com/sky/kms/unit/ScheduleReaderSpec.scala b/scheduler/src/test/scala/com/sky/kms/unit/ScheduleReaderSpec.scala index 9626f591..46c8130e 100644 --- a/scheduler/src/test/scala/com/sky/kms/unit/ScheduleReaderSpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/unit/ScheduleReaderSpec.scala @@ -8,18 +8,17 @@ import cats.Eval import cats.syntax.either._ import com.sky.kms.actors.SchedulingActor import com.sky.kms.actors.SchedulingActor._ -import com.sky.kms.base.AkkaStreamSpecBase +import com.sky.kms.base.{AkkaSpecBase, SpecBase} import com.sky.kms.config.ReaderConfig import com.sky.kms.domain._ import com.sky.kms.streams.ScheduleReader import com.sky.kms.streams.ScheduleReader.In import com.sky.kms.utils.TestDataUtils._ -import org.scalatest.concurrent.Eventually import scala.concurrent.duration._ import scala.concurrent.{Future, Promise} -class ScheduleReaderSpec extends AkkaStreamSpecBase with Eventually { +class ScheduleReaderSpec extends AkkaSpecBase with SpecBase { override implicit val patienceConfig = PatienceConfig(500.millis, 20.millis) @@ -97,25 +96,30 @@ class ScheduleReaderSpec extends AkkaStreamSpecBase with Eventually { case _ => sender ! Ack TestActor.KeepRunning - }) + } + ) p } def delayedSource = Source.tick(100.millis, 100.millis, msg).mapMaterializedValue(_ => NotUsed) - def runReader(in: Source[In, NotUsed], - errorHandler: Sink[ApplicationError, Future[Done]] = Sink.ignore, - sourceMatFuture: Future[Done] = Future.never): NotUsed = - ScheduleReader(Eval.now(in.mapMaterializedValue(nu => sourceMatFuture -> nu)), - probe.ref, - errorHandler, - ReaderConfig.TimeoutConfig(100.millis, 100.millis)).stream.run() + def runReader( + in: Source[In, NotUsed], + errorHandler: Sink[ApplicationError, Future[Done]] = Sink.ignore, + sourceMatFuture: Future[Done] = Future.never + ): NotUsed = + ScheduleReader( + Eval.now(in.mapMaterializedValue(nu => sourceMatFuture -> nu)), + probe.ref, + errorHandler, + ReaderConfig.TimeoutConfig(100.millis, 100.millis) + ).stream.run() } private trait ErrorHandler { this: TestContext => - val awaitingError = Promise[ApplicationError] + val awaitingError = Promise[ApplicationError]() val errorHandler = Sink.foreach[ApplicationError](awaitingError.trySuccess) } diff --git a/scheduler/src/test/scala/com/sky/kms/unit/ScheduledMessagePublisherSpec.scala b/scheduler/src/test/scala/com/sky/kms/unit/ScheduledMessagePublisherSpec.scala index 41d3516f..7268c380 100644 --- a/scheduler/src/test/scala/com/sky/kms/unit/ScheduledMessagePublisherSpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/unit/ScheduledMessagePublisherSpec.scala @@ -26,7 +26,7 @@ class ScheduledMessagePublisherSpec extends SpecBase { "convert schedule deletion to a delete record" in { val List(scheduleId, topic) = random[String](2).toList - val record = ScheduledMessagePublisher.toProducerRecord( + val record = ScheduledMessagePublisher.toProducerRecord( ScheduleDeletion(scheduleId, topic, Map.empty) ) @@ -46,7 +46,7 @@ class ScheduledMessagePublisherSpec extends SpecBase { "convert scheduled message with headers to producer record with headers" in { val schedule = random[ScheduleEvent] - val headers = ScheduledMessagePublisher + val headers = ScheduledMessagePublisher .toProducerRecord(schedule.toScheduledMessage) .headers() .toArray diff --git a/scheduler/src/test/scala/com/sky/kms/unit/SchedulerSpec.scala b/scheduler/src/test/scala/com/sky/kms/unit/SchedulerSpec.scala index c384219d..44cf0442 100644 --- a/scheduler/src/test/scala/com/sky/kms/unit/SchedulerSpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/unit/SchedulerSpec.scala @@ -44,7 +44,7 @@ class SchedulerSpec extends SpecBase { } "error if message does not adhere to our schema" in { - val cr = new ConsumerRecord[String, Array[Byte]]("scheduleTopic", 1, 1l, ScheduleId, Array.emptyByteArray) + val cr = new ConsumerRecord[String, Array[Byte]]("scheduleTopic", 1, 1L, ScheduleId, Array.emptyByteArray) scheduleConsumerRecordDecoder(cr) shouldBe Left(InvalidSchemaError(ScheduleId)) } @@ -54,13 +54,13 @@ class SchedulerSpec extends SpecBase { val schedule = TestSchedule.toSchedule.copy(time = tooDistantFuture) val cr = artificialConsumerRecord(ScheduleId, schedule.toAvro) - scheduleConsumerRecordDecoder(cr).left.get shouldBe a[InvalidTimeError] + scheduleConsumerRecordDecoder(cr).leftValue shouldBe a[InvalidTimeError] } } private def artificialConsumerRecord(scheduleId: ScheduleId, avroBytes: Array[Byte]) = - new ConsumerRecord[String, Array[Byte]]("scheduleTopic", 1, 1l, scheduleId, avroBytes) + new ConsumerRecord[String, Array[Byte]]("scheduleTopic", 1, 1L, scheduleId, avroBytes) private def equalHeaders(x: Map[String, Array[Byte]], y: Map[String, Array[Byte]]): Boolean = - x.mapValues(_.toList) === y.mapValues(_.toList) + x.view.mapValues(_.toList).toMap === y.view.mapValues(_.toList).toMap } diff --git a/scheduler/src/test/scala/com/sky/kms/unit/SchedulingActorSpec.scala b/scheduler/src/test/scala/com/sky/kms/unit/SchedulingActorSpec.scala index 694f408c..a504b315 100644 --- a/scheduler/src/test/scala/com/sky/kms/unit/SchedulingActorSpec.scala +++ b/scheduler/src/test/scala/com/sky/kms/unit/SchedulingActorSpec.scala @@ -8,9 +8,9 @@ import com.sky.kms.actors.PublisherActor.Trigger import com.sky.kms.actors.SchedulingActor import com.sky.kms.actors.SchedulingActor._ import com.sky.kms.base.AkkaSpecBase -import com.sky.kms.utils.TestDataUtils._ import com.sky.kms.domain._ import com.sky.kms.utils.SimpleCounterMonitoring +import com.sky.kms.utils.TestDataUtils._ import monix.execution.schedulers.TestScheduler import org.scalatest.concurrent.Eventually import org.scalatestplus.mockito.MockitoSugar @@ -19,7 +19,7 @@ import scala.concurrent.duration._ class SchedulingActorSpec extends AkkaSpecBase with ImplicitSender with MockitoSugar with Eventually { - val NoMsgTimeout = 2 seconds + val NoMsgTimeout = 2.seconds "A scheduling actor" must { diff --git a/scheduler/src/test/scala/com/sky/kms/utils/RandomPort.scala b/scheduler/src/test/scala/com/sky/kms/utils/RandomPort.scala index 44933385..84cb4e65 100644 --- a/scheduler/src/test/scala/com/sky/kms/utils/RandomPort.scala +++ b/scheduler/src/test/scala/com/sky/kms/utils/RandomPort.scala @@ -6,7 +6,7 @@ object RandomPort { def randomPort(): Int = { val socket = new ServerSocket(0) socket.setReuseAddress(true) - val port = socket.getLocalPort + val port = socket.getLocalPort socket.close() port } diff --git a/scheduler/src/test/scala/com/sky/kms/utils/TestActorSystem.scala b/scheduler/src/test/scala/com/sky/kms/utils/TestActorSystem.scala index 83f41e14..91db1406 100644 --- a/scheduler/src/test/scala/com/sky/kms/utils/TestActorSystem.scala +++ b/scheduler/src/test/scala/com/sky/kms/utils/TestActorSystem.scala @@ -16,6 +16,7 @@ object TestActorSystem { | test.single-expect-default = $akkaExpectDuration | coordinated-shutdown { | terminate-actor-system = $terminateActorSystem + | run-by-actor-system-terminate = off | run-by-jvm-shutdown-hook = off | } | @@ -33,9 +34,11 @@ object TestActorSystem { |} """.stripMargin - def apply(kafkaPort: Int = 9092, - terminateActorSystem: Boolean = false, - akkaExpectDuration: Duration = 3.seconds): ActorSystem = + def apply( + kafkaPort: Int = 9092, + terminateActorSystem: Boolean = false, + akkaExpectDuration: Duration = 3.seconds + ): ActorSystem = ActorSystem( name = s"test-actor-system-${UUID.randomUUID().toString}", config = ConfigFactory diff --git a/scheduler/src/test/scala/com/sky/kms/utils/TestConfig.scala b/scheduler/src/test/scala/com/sky/kms/utils/TestConfig.scala index cf7a423d..c8a96a2e 100644 --- a/scheduler/src/test/scala/com/sky/kms/utils/TestConfig.scala +++ b/scheduler/src/test/scala/com/sky/kms/utils/TestConfig.scala @@ -10,6 +10,6 @@ object TestConfig { def apply(topics: NonEmptyList[Topic]): SchedulerConfig = SchedulerConfig( ReaderConfig(topics, timeouts = ReaderConfig.TimeoutConfig(100.millis, 100.millis)), - PublisherConfig(queueBufferSize = 100), + PublisherConfig(queueBufferSize = 100) ) } diff --git a/scheduler/src/test/scala/com/sky/kms/utils/TestDataUtils.scala b/scheduler/src/test/scala/com/sky/kms/utils/TestDataUtils.scala index bc75123c..79631518 100644 --- a/scheduler/src/test/scala/com/sky/kms/utils/TestDataUtils.scala +++ b/scheduler/src/test/scala/com/sky/kms/utils/TestDataUtils.scala @@ -10,7 +10,7 @@ import akka.stream.scaladsl.{Sink, Source} import cats.Eval import com.fortysevendeg.scalacheck.datetime.GenDateTime.genDateTimeWithinRange import com.fortysevendeg.scalacheck.datetime.instances.jdk8._ -import com.sksamuel.avro4s.{AvroOutputStream, AvroSchema, Encoder, SchemaFor} +import com.sksamuel.avro4s.{AvroOutputStream, Encoder, SchemaFor} import com.sky.kms.SchedulerApp import com.sky.kms.avro._ import com.sky.kms.domain.PublishableMessage.ScheduledMessage @@ -18,7 +18,6 @@ import com.sky.kms.domain.Schedule.{ScheduleNoHeaders, ScheduleWithHeaders} import com.sky.kms.domain.{Schedule, ScheduleEvent} import com.sky.kms.streams.{ScheduleReader, ScheduledMessagePublisher} import org.scalacheck.{Arbitrary, Gen} -import org.zalando.grafter.syntax.rewriter._ import scala.concurrent.Future import scala.concurrent.duration._ @@ -42,7 +41,7 @@ object TestDataUtils { implicit val encoderForScheduleWithHeaders = Encoder[ScheduleWithHeaders] implicit val encoderForScheduleNoHeaders = Encoder[ScheduleNoHeaders] - implicit class ScheduleEventOps(val schedule: ScheduleEvent) extends AnyVal { + implicit class ScheduleEventOps(private val schedule: ScheduleEvent) extends AnyVal { def toSchedule: ScheduleWithHeaders = { val time = OffsetDateTime.now().toInstant.plusMillis(schedule.delay.toMillis).atOffset(ZoneOffset.UTC) ScheduleWithHeaders(time, schedule.outputTopic, schedule.key, schedule.value, schedule.headers) @@ -58,7 +57,7 @@ object TestDataUtils { def headerValues = schedule.headers.values } - implicit class ScheduleEventNoHeadersOps(val schedule: ScheduleEventNoHeaders) extends AnyVal { + implicit class ScheduleEventNoHeadersOps(private val schedule: ScheduleEventNoHeaders) extends AnyVal { def toScheduleWithoutHeaders: ScheduleNoHeaders = { val time = OffsetDateTime.now().toInstant.plusMillis(schedule.delay.toMillis).atOffset(ZoneOffset.UTC) ScheduleNoHeaders(time, schedule.outputTopic, schedule.key, schedule.value) @@ -71,35 +70,37 @@ object TestDataUtils { ScheduledMessage(schedule.inputTopic, schedule.outputTopic, schedule.key, schedule.value, Map.empty) } - implicit class ScheduleOps[T <: Schedule](val schedule: T) extends AnyVal { - def toAvro(implicit sf: SchemaFor[T], e: Encoder[T]): Array[Byte] = toAvroFrom(schedule) - def timeInMillis: Long = schedule.getTime.toInstant.toEpochMilli + implicit class ScheduleOps[T <: Schedule](private val schedule: T) extends AnyVal { + def toAvro(implicit e: Encoder[T]): Array[Byte] = toAvroFrom(schedule) + def timeInMillis: Long = schedule.getTime.toInstant.toEpochMilli } - private def toAvroFrom[T <: Schedule : Encoder : SchemaFor](t: T) = { + private def toAvroFrom[T <: Schedule : Encoder](t: T) = { val baos = new ByteArrayOutputStream() - val output = AvroOutputStream.binary[T].to(baos).build(AvroSchema[T]) + val output = AvroOutputStream.binary[T].to(baos).build() output.write(t) output.close() baos.toByteArray } - implicit class SchedulerAppOps(val schedulerApp: SchedulerApp) extends AnyVal { - def withReaderSource(src: Source[ScheduleReader.In, (Future[Done], Future[Control])])( - implicit as: ActorSystem): SchedulerApp = + implicit class SchedulerAppOps(private val schedulerApp: SchedulerApp) extends AnyVal { + def withReaderSource(src: Source[ScheduleReader.In, (Future[Done], Future[Control])])(implicit + as: ActorSystem + ): SchedulerApp = schedulerApp.copy(reader = schedulerApp.reader.copy(scheduleSource = Eval.later(src))) def withPublisherSink( - sink: Sink[ScheduledMessagePublisher.SinkIn, ScheduledMessagePublisher.SinkMat]): SchedulerApp = - schedulerApp.modifyWith[Any] { - case pub: ScheduledMessagePublisher => pub.replace(Eval.later(sink)) - } + newSink: Sink[ScheduledMessagePublisher.SinkIn, ScheduledMessagePublisher.SinkMat] + ): SchedulerApp = + schedulerApp.copy(publisher = schedulerApp.publisher.copy(publisherSink = Eval.later(newSink))) } - case class ScheduleEventNoHeaders(delay: FiniteDuration, - inputTopic: String, - outputTopic: String, - key: Array[Byte], - value: Option[Array[Byte]]) + case class ScheduleEventNoHeaders( + delay: FiniteDuration, + inputTopic: String, + outputTopic: String, + key: Array[Byte], + value: Option[Array[Byte]] + ) }