diff --git a/README.md b/README.md index 00152ec..cd648a2 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ And since a HTTP/JSON API is exposed, you don't necessarily have to deal with th **NOTE**: while extremely useful during development, **kara** is not intended for production use. ## Usage -- Add **kara** as a plugin to the sbt project adding a line containing `addSbtPlugin("com.ea.kara" % "kara" % "0.2.1")` in `project/plugins.sbt`. +- Add **kara** as a plugin to the sbt project adding a line containing `addSbtPlugin("com.ea.kara" % "kara" % "0.2.2")` in `project/plugins.sbt`. - In your project settings in `build.sbt`: - configure `karaServices := Seq("fully_qualified_service_1", "fully_qualified_service_2, ...)` to indicate the Thrift services Kara should generate Finagle services and Swagger UI for. Services should be listed in `.` format. - enable the the plugin with `.enablePlugins(Kara)` on the project that lists the Thrift sources and on which `ScroogeSBT` is enabled. @@ -31,7 +31,7 @@ On compilation (`sbt compile`), a Finagle HTTP service named `Http #### project/plugins.sbt ```scala -addSbtPlugin("com.ea.kara" % "kara" % "0.2.1") +addSbtPlugin("com.ea.kara" % "kara" % "0.2.2") ``` #### build.sbt diff --git a/build.sbt b/build.sbt index 457cebf..b9230b0 100644 --- a/build.sbt +++ b/build.sbt @@ -10,8 +10,8 @@ val circeVersion = "0.14.1" ThisBuild / credentials += Credentials(Path.userHome / ".ivy2" / ".credentials") ThisBuild / envVars ++= Map("CI_PROJECT_DIR" -> sys.env.getOrElse("CI_PROJECT_DIR", ".")) ThisBuild / scalaVersion := "2.12.15" -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % "always" -ThisBuild / versionScheme := Some("early-semver") +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % VersionScheme.Always +ThisBuild / versionScheme := Some(VersionScheme.EarlySemVer) lazy val sbtOps = sys.env .get("SBT_OPTS") diff --git a/project/plugins.sbt b/project/plugins.sbt index 79c16c0..44aa171 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,8 +3,8 @@ */ libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") -addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.0") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.3") -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.11") +addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.0") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.3") +addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.11") diff --git a/src/main/scala/com/ea/kara/Context.scala b/src/main/scala/com/ea/kara/Context.scala index 87eb97c..a086ed9 100644 --- a/src/main/scala/com/ea/kara/Context.scala +++ b/src/main/scala/com/ea/kara/Context.scala @@ -65,33 +65,29 @@ case class CodegenContext( val docToSvc = thriftFiles .flatMap(file => file.document.services.map(service => (file.document, service))) - val pkgToSvc = servicePackageAndName.flatMap { - case (pkg, names) => names.map((pkg, _)) - } - val servicesByDocument: Map[Document, Seq[String]] = pkgToSvc - .map { - case (pkg, name) => - docToSvc - .find { - case (doc, docSvc) => - doc.javaNamespace == pkg && - docSvc.sid.name == name - } - .getOrElse { - throw new RuntimeException( - s"No service '$name' found in package '$pkg'." + - "Valid alternatives are: " + - docToSvc - .filter { case (doc, _) => doc.javaNamespace == pkg } - .map { case (_, svc) => svc.sid.name } - .mkString(", ") - ) - } + val servicesByDocument: Map[Document, Seq[String]] = servicePackageAndName + .toList + .flatMap { case (pkg, svcNames) => svcNames.map((pkg, _)) } + .map { case (pkg, svcName) => + docToSvc + .find { + case (doc, docSvc) => + doc.javaNamespace == pkg && + docSvc.sid.name == svcName + } + .getOrElse { + throw new RuntimeException( + s"No service '$svcName' found in package '$pkg'." + + "Valid alternatives are: " + + docToSvc + .filter { case (doc, _) => doc.javaNamespace == pkg } + .map { case (_, svc) => svc.sid.name } + .mkString(", ") + ) + } } .groupBy(_._1) - .map { - case (k, v) => (k, v.map(_._2.sid.name).toSeq) - } + .map { case (k, v) => (k, v.map(_._2.sid.name)) } servicesByDocument } diff --git a/src/main/scala/com/ea/kara/Generator.scala b/src/main/scala/com/ea/kara/Generator.scala index 73d295c..6afa620 100644 --- a/src/main/scala/com/ea/kara/Generator.scala +++ b/src/main/scala/com/ea/kara/Generator.scala @@ -81,7 +81,7 @@ class Generator( thriftFile.document.services.filter(service => serviceNames.contains(service.sid.name)) logger.debug( - s"Generating services [${services.map(_.sid.name)}] for Thrift file ${thriftFile.name}..." + s"Generating services [${services.map(_.sid.name).mkString(", ")}] for Thrift file ${thriftFile.name}..." ) services.foreach { service => diff --git a/src/main/scala/com/ea/kara/Kara.scala b/src/main/scala/com/ea/kara/Kara.scala index 027ee5e..a392129 100644 --- a/src/main/scala/com/ea/kara/Kara.scala +++ b/src/main/scala/com/ea/kara/Kara.scala @@ -5,8 +5,8 @@ package com.ea.kara import com.twitter.scrooge.ScroogeSBT -import sbt._ -import sbt.Keys._ +import sbt.* +import sbt.Keys.* object Kara extends AutoPlugin { @@ -44,16 +44,16 @@ object Kara extends AutoPlugin { ) } - import autoImport._ - import ScroogeSBT.autoImport._ + import autoImport.* + import ScroogeSBT.autoImport.* val circeVersion: String = "0.14.1" - override lazy val globalSettings: Seq[Setting[_]] = Seq( + override lazy val globalSettings: Seq[Setting[?]] = Seq( Compile / karaHeaders := Seq.empty ) - override lazy val projectSettings: Seq[Setting[_]] = Seq( + override lazy val projectSettings: Seq[Setting[?]] = Seq( libraryDependencies ++= Seq("finagle-http", "scrooge-serializer", "finagle-thrift") .map("com.twitter" %% _) @@ -82,7 +82,7 @@ object Kara extends AutoPlugin { if (oldSources.nonEmpty) { val karaDir = (generator.sourcePath ** "kara").filter(_.isDirectory).get.headOption logger.info( - s"Generated Kara sources in $karaDir are out of date, deleting them and re-generating" + s"Generated Kara sources in $karaDir are out of date, deleting them and re-generating..." ) karaDir.foreach(IO.delete) } diff --git a/src/sbt-test/kara/declaration_order/project/build.sbt b/src/sbt-test/kara/declaration_order/project/build.sbt index fe4cfa9..b32e419 100644 --- a/src/sbt-test/kara/declaration_order/project/build.sbt +++ b/src/sbt-test/kara/declaration_order/project/build.sbt @@ -1 +1,5 @@ -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % "always" \ No newline at end of file +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % VersionScheme.Always \ No newline at end of file diff --git a/src/sbt-test/kara/dirty/project/build.sbt b/src/sbt-test/kara/dirty/project/build.sbt index fe4cfa9..b32e419 100644 --- a/src/sbt-test/kara/dirty/project/build.sbt +++ b/src/sbt-test/kara/dirty/project/build.sbt @@ -1 +1,5 @@ -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % "always" \ No newline at end of file +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % VersionScheme.Always \ No newline at end of file diff --git a/src/sbt-test/kara/e2e/project/build.sbt b/src/sbt-test/kara/e2e/project/build.sbt index fe4cfa9..b32e419 100644 --- a/src/sbt-test/kara/e2e/project/build.sbt +++ b/src/sbt-test/kara/e2e/project/build.sbt @@ -1 +1,5 @@ -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % "always" \ No newline at end of file +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % VersionScheme.Always \ No newline at end of file diff --git a/src/sbt-test/kara/e2e/test b/src/sbt-test/kara/e2e/test index ad9a259..474c3ef 100644 --- a/src/sbt-test/kara/e2e/test +++ b/src/sbt-test/kara/e2e/test @@ -1,6 +1,6 @@ # Copyright (C) 2022 Electronic Arts Inc. All rights reserved. -# Make sure the generated sources compile +# Make sure all request/response patterns are supported > run mirror > run oneArgUnwrapped > run knownException diff --git a/src/sbt-test/kara/imports/project/build.sbt b/src/sbt-test/kara/imports/project/build.sbt index fe4cfa9..b32e419 100644 --- a/src/sbt-test/kara/imports/project/build.sbt +++ b/src/sbt-test/kara/imports/project/build.sbt @@ -1 +1,5 @@ -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % "always" \ No newline at end of file +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % VersionScheme.Always \ No newline at end of file diff --git a/src/sbt-test/kara/invalid_service/project/build.sbt b/src/sbt-test/kara/invalid_service/project/build.sbt index fe4cfa9..b32e419 100644 --- a/src/sbt-test/kara/invalid_service/project/build.sbt +++ b/src/sbt-test/kara/invalid_service/project/build.sbt @@ -1 +1,5 @@ -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % "always" \ No newline at end of file +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % VersionScheme.Always \ No newline at end of file diff --git a/src/sbt-test/kara/multi_service/build.sbt b/src/sbt-test/kara/multi_service/build.sbt new file mode 100644 index 0000000..334f3ee --- /dev/null +++ b/src/sbt-test/kara/multi_service/build.sbt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +import sbt._ + +val twitterVersion = "20.10.0" +val circeVersion = "0.14.1" + +lazy val root = (project in file(".")) + .settings( + scalaVersion := "2.12.15", + libraryDependencies ++= Seq( + "com.twitter" %% "twitter-server" % twitterVersion, + "com.twitter" %% "scrooge-core" % twitterVersion, + "com.twitter" %% "finagle-http" % twitterVersion, + "io.circe" %% "circe-generic" % circeVersion, + "io.circe" %% "circe-parser" % circeVersion + ), + karaServices := Seq( + "com.local.ServiceOne", + "com.local.ServiceTwo", + "com.local.ServiceThree", + "com.local.ServiceFour", + ) + ) + .enablePlugins(Kara) diff --git a/src/sbt-test/kara/multi_service/project/build.sbt b/src/sbt-test/kara/multi_service/project/build.sbt new file mode 100644 index 0000000..b32e419 --- /dev/null +++ b/src/sbt-test/kara/multi_service/project/build.sbt @@ -0,0 +1,5 @@ +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % VersionScheme.Always \ No newline at end of file diff --git a/src/sbt-test/kara/multi_service/project/plugins.sbt b/src/sbt-test/kara/multi_service/project/plugins.sbt new file mode 100644 index 0000000..1e5dc62 --- /dev/null +++ b/src/sbt-test/kara/multi_service/project/plugins.sbt @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +sys.props.get("plugin.version") match { + case Some(x) => addSbtPlugin("com.ea.kara" % "kara" % x) + case _ => sys.error("""|The system property 'plugin.version' is not defined. + |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) +} diff --git a/src/sbt-test/kara/multi_service/src/main/scala/MultiServiceTest.scala b/src/sbt-test/kara/multi_service/src/main/scala/MultiServiceTest.scala new file mode 100644 index 0000000..b703719 --- /dev/null +++ b/src/sbt-test/kara/multi_service/src/main/scala/MultiServiceTest.scala @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +import java.net.InetSocketAddress + +import com.local._ +import com.twitter.finagle.http +import com.twitter.finagle.Http +import com.twitter.finagle.Service +import com.twitter.util.{Await, Future} +import io.circe._ +import io.circe.generic.auto._ +import com.ea.kara.generated.svc_one._ +import com.ea.kara.generated.svc_two._ +import com.ea.kara.generated.svcs_three_four._ + +object MultiServiceTest extends App { + + case class GenericError(message: String) + + lazy val svcOne: Service[http.Request, http.Response] = new HttpServiceOne(new ServiceOne) + lazy val svcTwo: Service[http.Request, http.Response] = new HttpServiceTwo(new ServiceTwo) + lazy val svcThree: Service[http.Request, http.Response] = new HttpServiceThree(new ServiceThree) + lazy val svcFour: Service[http.Request, http.Response] = new HttpServiceFour(new ServiceFour) + + override def main(args: Array[String]): Unit = { + assert(args.length == 1, "Should pass the name of the test to run.") + + val testName = args.head + + val request = finagleRequest("ping") + + testName match { + case "pingServiceOne" => serve(svcOne, request) + case "pingServiceTwo" => serve(svcTwo, request) + case "pingServiceThree" => serve(svcThree, request) + case "pingServiceFour" => serve(svcFour, request) + } + } + + def serve(svc: Service[http.Request, http.Response], request: http.Request): Unit = { + val response = Await.result(svc(request)) + assertSuccessfulResponse(response) + } + + def finagleRequest(rpcName: String): http.Request = { + val request = http.Request(method = com.twitter.finagle.http.Method.Post, uri = s"/$rpcName") + request.setContentTypeJson() + request + } + + def assertSuccessfulResponse(response: http.Response): Unit = { + assertJsonResponse(response) + assertResponseStatus(response, http.Status.Ok) + } + + def assertResponseStatus(response: http.Response, expectedStatus: http.Status): Unit = + assert( + response.status == expectedStatus, + s""" + |Status code should be '$expectedStatus', + |instead was found ${response.status}. + """.stripMargin + ) + + def assertJsonResponse(response: http.Response): Unit = { + val applicationJsonContentType = "application/json;charset=utf-8" + assert( + response.contentType == Some(applicationJsonContentType), + s""" + |Content type should be '$applicationJsonContentType', + |instead was found '${response.contentType}'. + """.stripMargin + ) + } +} + +class ServiceOne extends ServiceOne.MethodPerEndpoint { + override def ping(): Future[Unit] = Future.Unit +} + +class ServiceTwo extends ServiceTwo.MethodPerEndpoint { + override def ping(): Future[Unit] = Future.Unit +} + +class ServiceThree extends ServiceThree.MethodPerEndpoint { + override def ping(): Future[Unit] = Future.Unit +} + +class ServiceFour extends ServiceFour.MethodPerEndpoint { + override def ping(): Future[Unit] = Future.Unit +} diff --git a/src/sbt-test/kara/multi_service/src/main/thrift/svc_one.thrift b/src/sbt-test/kara/multi_service/src/main/thrift/svc_one.thrift new file mode 100644 index 0000000..bd788a9 --- /dev/null +++ b/src/sbt-test/kara/multi_service/src/main/thrift/svc_one.thrift @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +namespace java com.local + +service ServiceOne { + void ping() +} diff --git a/src/sbt-test/kara/multi_service/src/main/thrift/svc_two.thrift b/src/sbt-test/kara/multi_service/src/main/thrift/svc_two.thrift new file mode 100644 index 0000000..4c6ab5a --- /dev/null +++ b/src/sbt-test/kara/multi_service/src/main/thrift/svc_two.thrift @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +namespace java com.local + +service ServiceTwo { + void ping() +} diff --git a/src/sbt-test/kara/multi_service/src/main/thrift/svcs_three_four.thrift b/src/sbt-test/kara/multi_service/src/main/thrift/svcs_three_four.thrift new file mode 100644 index 0000000..7ab1f9a --- /dev/null +++ b/src/sbt-test/kara/multi_service/src/main/thrift/svcs_three_four.thrift @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +namespace java com.local + +service ServiceThree { + void ping() +} + +service ServiceFour { + void ping() +} diff --git a/src/sbt-test/kara/multi_service/test b/src/sbt-test/kara/multi_service/test new file mode 100644 index 0000000..61561ff --- /dev/null +++ b/src/sbt-test/kara/multi_service/test @@ -0,0 +1,8 @@ +# Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + +# Make sure both services can be hit +> run pingServiceOne +> run pingServiceTwo +> run pingServiceThree +> run pingServiceFour + diff --git a/src/sbt-test/kara/no_package/project/build.sbt b/src/sbt-test/kara/no_package/project/build.sbt index fe4cfa9..b32e419 100644 --- a/src/sbt-test/kara/no_package/project/build.sbt +++ b/src/sbt-test/kara/no_package/project/build.sbt @@ -1 +1,5 @@ -ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % "always" \ No newline at end of file +/* + * Copyright (C) 2022 Electronic Arts Inc. All rights reserved. + */ + +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-parser-combinators" % VersionScheme.Always \ No newline at end of file diff --git a/version.sbt b/version.sbt index ec127dc..449ca0e 100644 --- a/version.sbt +++ b/version.sbt @@ -7,4 +7,4 @@ def buildSuffix: String = { if (isSnapshot) "-SNAPSHOT" else "" } -ThisBuild / version := "0.2.1" + buildSuffix +ThisBuild / version := "0.2.2" + buildSuffix