-
Notifications
You must be signed in to change notification settings - Fork 427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/serverless zio #2975
Feature/serverless zio #2975
Changes from 3 commits
7f51d28
a6c03b5
71bae56
c919398
f0b57c0
dec2e0b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -219,8 +219,11 @@ lazy val rawAllAggregates = core.projectRefs ++ | |||||
nettyServerZio.projectRefs ++ | ||||||
zio1HttpServer.projectRefs ++ | ||||||
zioHttpServer.projectRefs ++ | ||||||
awsLambda.projectRefs ++ | ||||||
awsLambdaTests.projectRefs ++ | ||||||
awsLambdaCore.projectRefs ++ | ||||||
awsLambdaCatsEffect.projectRefs ++ | ||||||
awsLambdaCatsEffectTests.projectRefs ++ | ||||||
awsLambdaZio.projectRefs ++ | ||||||
awsLambdaZioTests.projectRefs ++ | ||||||
awsSam.projectRefs ++ | ||||||
awsTerraform.projectRefs ++ | ||||||
awsExamples.projectRefs ++ | ||||||
|
@@ -1442,7 +1445,88 @@ lazy val zioHttpServer: ProjectMatrix = (projectMatrix in file("server/zio-http- | |||||
|
||||||
// serverless | ||||||
|
||||||
lazy val awsLambda: ProjectMatrix = (projectMatrix in file("serverless/aws/lambda")) | ||||||
lazy val awsLambdaCore: ProjectMatrix = (projectMatrix in file("serverless/aws/lambda-core")) | ||||||
.settings(commonJvmSettings) | ||||||
.settings( | ||||||
name := "tapir-aws-lambda-core", | ||||||
libraryDependencies ++= loggerDependencies | ||||||
) | ||||||
.jvmPlatform(scalaVersions = scala2And3Versions) | ||||||
.jsPlatform(scalaVersions = scala2Versions) | ||||||
.dependsOn(serverCore, circeJson, tests % "test") | ||||||
|
||||||
lazy val awsLambdaZio: ProjectMatrix = (projectMatrix in file("serverless/aws/lambda-zio")) | ||||||
.settings(commonJvmSettings) | ||||||
.settings( | ||||||
name := "tapir-aws-lambda-zio", | ||||||
libraryDependencies ++= loggerDependencies, | ||||||
libraryDependencies ++= Seq( | ||||||
"com.amazonaws" % "aws-lambda-java-runtime-interface-client" % Versions.awsLambdaInterface | ||||||
) | ||||||
) | ||||||
.jvmPlatform(scalaVersions = scala2And3Versions) | ||||||
.dependsOn(serverCore, awsLambdaCore, zio, zioHttpServer, circeJson, tests % "test") | ||||||
|
||||||
// integration tests for lambda interpreter | ||||||
// it's a separate project since it needs a fat jar with lambda code which cannot be build from tests sources | ||||||
// runs sam local cmd line tool to start AWS Api Gateway with lambda proxy | ||||||
lazy val awsLambdaZioTests: ProjectMatrix = (projectMatrix in file("serverless/aws/lambda-zio-tests")) | ||||||
.settings(commonJvmSettings) | ||||||
.settings( | ||||||
name := "tapir-aws-lambda-zio-tests", | ||||||
assembly / assemblyJarName := "tapir-aws-lambda-zio-tests.jar", | ||||||
assembly / test := {}, // no tests before building jar | ||||||
assembly / assemblyMergeStrategy := { | ||||||
case PathList("META-INF", "io.netty.versions.properties") => MergeStrategy.first | ||||||
case PathList(ps @ _*) if ps.last contains "FlowAdapters" => MergeStrategy.first | ||||||
case PathList(ps @ _*) if ps.last == "module-info.class" => MergeStrategy.first | ||||||
case _ @("scala/annotation/nowarn.class" | "scala/annotation/nowarn$.class") => MergeStrategy.first | ||||||
case PathList("deriving.conf") => MergeStrategy.concat | ||||||
case x => (assembly / assemblyMergeStrategy).value(x) | ||||||
}, | ||||||
Test / test := { | ||||||
if (scalaVersion.value == scala2_13) { // only one test can run concurrently, as it starts a local sam instance | ||||||
(Test / test) | ||||||
.dependsOn( | ||||||
Def.sequential( | ||||||
(Compile / runMain).toTask(" sttp.tapir.serverless.aws.ziolambda.tests.LambdaSamTemplate"), | ||||||
assembly | ||||||
) | ||||||
) | ||||||
.value | ||||||
} | ||||||
}, | ||||||
Test / testOptions ++= { | ||||||
val log = sLog.value | ||||||
// process uses template.yaml which is generated by `LambdaSamTemplate` called above | ||||||
lazy val sam = Process("sam local start-api -p 3002 -t aws-lambda-zio-template.yaml --warm-containers EAGER").run() | ||||||
Seq( | ||||||
Tests.Setup(() => { | ||||||
val samReady = PollingUtils.poll(60.seconds, 1.second) { | ||||||
sam.isAlive() && PollingUtils.urlConnectionAvailable(new URL(s"http://127.0.0.1:3002/health")) | ||||||
} | ||||||
if (!samReady) { | ||||||
sam.destroy() | ||||||
val exit = sam.exitValue() | ||||||
log.error(s"failed to start sam local within 60 seconds (exit code: $exit") | ||||||
} | ||||||
}), | ||||||
Tests.Cleanup(() => { | ||||||
sam.destroy() | ||||||
val exit = sam.exitValue() | ||||||
log.info(s"stopped sam local (exit code: $exit") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||||||
}) | ||||||
) | ||||||
}, | ||||||
Test / parallelExecution := false | ||||||
) | ||||||
.jvmPlatform(scalaVersions = scala2Versions) | ||||||
.dependsOn(core, zio, circeJson, awsLambdaZio, awsSam, sttpStubServer, serverTests) | ||||||
.settings( | ||||||
libraryDependencies ++= Seq("dev.zio" %% "zio-interop-cats" % Versions.zioInteropCats) | ||||||
) | ||||||
|
||||||
lazy val awsLambdaCatsEffect: ProjectMatrix = (projectMatrix in file("serverless/aws/lambda-cats-effect")) | ||||||
.settings(commonJvmSettings) | ||||||
.settings( | ||||||
name := "tapir-aws-lambda", | ||||||
|
@@ -1454,16 +1538,16 @@ lazy val awsLambda: ProjectMatrix = (projectMatrix in file("serverless/aws/lambd | |||||
) | ||||||
.jvmPlatform(scalaVersions = scala2And3Versions) | ||||||
.jsPlatform(scalaVersions = scala2Versions) | ||||||
.dependsOn(serverCore, cats, catsEffect, circeJson, tests % "test") | ||||||
.dependsOn(serverCore, awsLambdaCore, cats, catsEffect, circeJson, tests % "test") | ||||||
|
||||||
// integration tests for lambda interpreter | ||||||
// it's a separate project since it needs a fat jar with lambda code which cannot be build from tests sources | ||||||
// runs sam local cmd line tool to start AWS Api Gateway with lambda proxy | ||||||
lazy val awsLambdaTests: ProjectMatrix = (projectMatrix in file("serverless/aws/lambda-tests")) | ||||||
lazy val awsLambdaCatsEffectTests: ProjectMatrix = (projectMatrix in file("serverless/aws/lambda-cats-effect-tests")) | ||||||
.settings(commonJvmSettings) | ||||||
.settings( | ||||||
name := "tapir-aws-lambda-tests", | ||||||
assembly / assemblyJarName := "tapir-aws-lambda-tests.jar", | ||||||
name := "tapir-aws-lambda-cats-effect-tests", | ||||||
assembly / assemblyJarName := "tapir-aws-lambda-cats-effect-tests.jar", | ||||||
assembly / test := {}, // no tests before building jar | ||||||
assembly / assemblyMergeStrategy := { | ||||||
case PathList("META-INF", "io.netty.versions.properties") => MergeStrategy.first | ||||||
|
@@ -1487,11 +1571,11 @@ lazy val awsLambdaTests: ProjectMatrix = (projectMatrix in file("serverless/aws/ | |||||
Test / testOptions ++= { | ||||||
val log = sLog.value | ||||||
// process uses template.yaml which is generated by `LambdaSamTemplate` called above | ||||||
lazy val sam = Process("sam local start-api --warm-containers EAGER").run() | ||||||
lazy val sam = Process("sam local start-api -p 3001 -t aws-lambda-cats-effect-template.yaml --warm-containers EAGER").run() | ||||||
Seq( | ||||||
Tests.Setup(() => { | ||||||
val samReady = PollingUtils.poll(60.seconds, 1.second) { | ||||||
sam.isAlive() && PollingUtils.urlConnectionAvailable(new URL(s"http://127.0.0.1:3000/health")) | ||||||
sam.isAlive() && PollingUtils.urlConnectionAvailable(new URL(s"http://127.0.0.1:3001/health")) | ||||||
} | ||||||
if (!samReady) { | ||||||
sam.destroy() | ||||||
|
@@ -1509,7 +1593,7 @@ lazy val awsLambdaTests: ProjectMatrix = (projectMatrix in file("serverless/aws/ | |||||
Test / parallelExecution := false | ||||||
) | ||||||
.jvmPlatform(scalaVersions = scala2Versions) | ||||||
.dependsOn(core, cats, circeJson, awsLambda, awsSam, sttpStubServer, serverTests) | ||||||
.dependsOn(core, awsLambdaCatsEffect, cats, circeJson, awsSam, sttpStubServer, serverTests) | ||||||
|
||||||
// integration tests for aws cdk interpreter | ||||||
// it's a separate project since it needs a fat jar with lambda code which cannot be build from tests sources | ||||||
|
@@ -1609,7 +1693,7 @@ lazy val awsCdk: ProjectMatrix = (projectMatrix in file("serverless/aws/cdk")) | |||||
) | ||||||
) | ||||||
.jvmPlatform(scalaVersions = scala2And3Versions) | ||||||
.dependsOn(core, tests % Test, awsLambda) | ||||||
.dependsOn(core, tests % Test, awsLambdaCore, awsLambdaCatsEffect) | ||||||
|
||||||
lazy val awsTerraform: ProjectMatrix = (projectMatrix in file("serverless/aws/terraform")) | ||||||
.settings(commonJvmSettings) | ||||||
|
@@ -1654,7 +1738,7 @@ lazy val awsExamples: ProjectMatrix = (projectMatrix in file("serverless/aws/exa | |||||
scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.CommonJSModule) } | ||||||
) | ||||||
) | ||||||
.dependsOn(awsLambda) | ||||||
.dependsOn(awsLambdaCore, awsLambdaCatsEffect) | ||||||
|
||||||
lazy val awsExamples2_12 = awsExamples.jvm(scala2_12).dependsOn(awsSam.jvm(scala2_12), awsTerraform.jvm(scala2_12), awsCdk.jvm(scala2_12)) | ||||||
lazy val awsExamples2_13 = awsExamples.jvm(scala2_13).dependsOn(awsSam.jvm(scala2_13), awsTerraform.jvm(scala2_13), awsCdk.jvm(scala2_13)) | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ import sttp.tapir.server.interceptor.RequestResult | |
import sttp.tapir.server.interceptor.reject.RejectInterceptor | ||
import sttp.tapir.server.interpreter.{BodyListener, FilterServerEndpoints, ServerInterpreter} | ||
|
||
private[lambda] abstract class AwsServerInterpreter[F[_]: MonadError] { | ||
private[aws] abstract class AwsServerInterpreter[F[_]: MonadError] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: This change isn't needed, right? Or maybe I'm missing something? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is required, because |
||
|
||
def awsServerOptions: AwsServerOptions[F] | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package sttp.tapir.serverless.aws.ziolambda.tests | ||
|
||
import sttp.tapir.serverless.aws.sam.{AwsSamInterpreter, AwsSamOptions, CodeSource} | ||
|
||
import java.nio.charset.StandardCharsets.UTF_8 | ||
import java.nio.file.{Files, Paths} | ||
|
||
object LambdaSamTemplate extends App { | ||
|
||
val jarPath = Paths.get("serverless/aws/lambda-zio-tests/target/jvm-2.13/tapir-aws-lambda-zio-tests.jar").toAbsolutePath.toString | ||
|
||
val samOptions: AwsSamOptions = AwsSamOptions( | ||
"Tests", | ||
source = CodeSource( | ||
"java11", | ||
jarPath, | ||
"sttp.tapir.serverless.aws.ziolambda.tests.ZioLambdaHandlerImpl::handleRequest" | ||
), | ||
memorySize = 1024 | ||
) | ||
val yaml = AwsSamInterpreter(samOptions).toSamTemplate(allEndpoints.map(_.endpoint).toList).toYaml | ||
Files.write(Paths.get("aws-lambda-zio-template.yaml"), yaml.getBytes(UTF_8)) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package sttp.tapir.serverless.aws.ziolambda.tests | ||
|
||
import com.amazonaws.services.lambda.runtime.{Context, RequestStreamHandler} | ||
import io.circe.generic.auto._ | ||
import sttp.tapir.serverless.aws.lambda.AwsRequest | ||
import sttp.tapir.serverless.aws.ziolambda.ZioLambdaHandler | ||
import sttp.tapir.ztapir.RIOMonadError | ||
import zio.{Runtime, Unsafe} | ||
|
||
import java.io.{InputStream, OutputStream} | ||
|
||
class ZioLambdaHandlerImpl extends RequestStreamHandler { | ||
private implicit val m = new RIOMonadError[Any] | ||
private val handler = ZioLambdaHandler.default[Any](allEndpoints.toList) | ||
|
||
override def handleRequest(input: InputStream, output: OutputStream, context: Context): Unit = { | ||
val runtime = Runtime.default | ||
Unsafe.unsafe { implicit unsafe => | ||
runtime.unsafe.run(handler.process[AwsRequest](input, output)).getOrThrowFiberFailure() | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package sttp.tapir.serverless.aws.ziolambda | ||
|
||
import sttp.tapir.{endpoint, stringToPath} | ||
import sttp.tapir.tests.Basic._ | ||
import sttp.tapir.tests.Mapping.in_4query_out_4header_extended | ||
import sttp.tapir.tests.TestUtil.inputStreamToByteArray | ||
import sttp.tapir.ztapir.ZTapir | ||
import zio.ZIO | ||
|
||
import java.io.{ByteArrayInputStream, InputStream} | ||
|
||
package object tests extends ZTapir { | ||
|
||
type ZioEndpoint = ZServerEndpoint[Any, Any] | ||
|
||
// this endpoint is used to wait until sam local starts up before running actual tests | ||
val health_endpoint: ZioEndpoint = | ||
endpoint.get.in("health").zServerLogic(_ => ZIO.unit) | ||
|
||
val in_path_path_out_string_endpoint: ZioEndpoint = | ||
in_path_path_out_string.zServerLogic { case (fruit: String, amount: Int) => | ||
ZIO.attempt(s"$fruit $amount").mapError(_ => ()) | ||
} | ||
|
||
val in_string_out_string_endpoint: ZioEndpoint = | ||
in_string_out_string.in("string").zServerLogic(v => ZIO.attempt(v).mapError(_ => ())) | ||
|
||
val in_json_out_json_endpoint: ZioEndpoint = | ||
in_json_out_json.in("json").zServerLogic(v => ZIO.attempt(v).mapError(_ => ())) | ||
|
||
val in_headers_out_headers_endpoint: ZioEndpoint = | ||
in_headers_out_headers.zServerLogic(v => ZIO.attempt(v).mapError(_ => ())) | ||
|
||
val in_input_stream_out_input_stream_endpoint: ZioEndpoint = | ||
in_input_stream_out_input_stream.in("is").zServerLogic { is => | ||
ZIO.attempt(new ByteArrayInputStream(inputStreamToByteArray(is)): InputStream).orDie | ||
} | ||
|
||
val in_4query_out_4header_extended_endpoint: ZioEndpoint = | ||
in_4query_out_4header_extended.in("echo" / "query").zServerLogic(v => ZIO.attempt(v).mapError(_ => ())) | ||
|
||
val allEndpoints: Set[ZioEndpoint] = Set( | ||
health_endpoint, | ||
in_path_path_out_string_endpoint, | ||
in_string_out_string_endpoint, | ||
in_json_out_json_endpoint, | ||
in_headers_out_headers_endpoint, | ||
in_input_stream_out_input_stream_endpoint, | ||
in_4query_out_4header_extended_endpoint | ||
) | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done