Skip to content

Commit

Permalink
Merge pull request #446 from armanbilge/topic/remove-core
Browse files Browse the repository at this point in the history
Remove core module
  • Loading branch information
armanbilge authored Jan 11, 2024
2 parents 8449ef0 + 254401a commit eb1de8f
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 177 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ jobs:

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: mkdir -p lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target core/.js/target lambda-http4s/.js/target core/.jvm/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target lambda-cloudformation-custom-resource/.jvm/target project/target
run: mkdir -p lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target lambda-cloudformation-custom-resource/.jvm/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: tar cf targets.tar lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target core/.js/target lambda-http4s/.js/target core/.jvm/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target lambda-cloudformation-custom-resource/.jvm/target project/target
run: tar cf targets.tar lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target lambda-cloudformation-custom-resource/.jvm/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand Down
17 changes: 2 additions & 15 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ lazy val commonSettings = Seq(
lazy val root =
tlCrossRootProject
.aggregate(
core,
lambda,
lambdaHttp4s,
lambdaCloudFormationCustomResource,
Expand All @@ -83,22 +82,12 @@ lazy val rootSbtScalafix = project
.aggregate(scalafix.componentProjectReferences: _*)
.enablePlugins(NoPublishPlugin)

lazy val core = crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.in(file("core"))
.settings(
name := "feral-core",
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-effect" % catsEffectVersion
)
)
.settings(commonSettings)

lazy val lambda = crossProject(JSPlatform, JVMPlatform)
.in(file("lambda"))
.settings(
name := "feral-lambda",
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-effect" % catsEffectVersion,
"org.tpolecat" %%% "natchez-core" % natchezVersion,
"io.circe" %%% "circe-scodec" % circeVersion,
"io.circe" %%% "circe-jawn" % circeVersion,
Expand All @@ -125,7 +114,6 @@ lazy val lambda = crossProject(JSPlatform, JVMPlatform)
"co.fs2" %%% "fs2-io" % fs2Version
)
)
.dependsOn(core)

lazy val sbtLambda = project
.in(file("sbt-lambda"))
Expand All @@ -140,7 +128,7 @@ lazy val sbtLambda = project
scriptedLaunchOpts := {
scriptedLaunchOpts.value ++ Seq("-Xmx1024M", "-Dplugin.version=" + version.value)
},
scripted := scripted.dependsOn(core.js / publishLocal, lambda.js / publishLocal).evaluated,
scripted := scripted.dependsOn(lambda.js / publishLocal).evaluated,
Test / test := scripted.toTask("").value
)

Expand Down Expand Up @@ -207,7 +195,6 @@ lazy val unidocs = project
inProjects(sbtLambda)
else
inProjects(
core.jvm,
lambda.jvm,
lambdaHttp4s.jvm,
lambdaCloudFormationCustomResource.jvm
Expand Down
41 changes: 0 additions & 41 deletions core/src/main/scala/feral/IOSetup.scala

This file was deleted.

21 changes: 16 additions & 5 deletions lambda/js/src/main/scala/feral/lambda/IOLambdaPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,39 @@
package feral.lambda

import cats.effect.IO
import cats.effect.std.Dispatcher
import cats.syntax.all._
import io.circe.scalajs._

import scala.scalajs.js
import scala.scalajs.js.JSConverters._

private[lambda] trait IOLambdaPlatform[Event, Result] {
private[lambda] abstract class IOLambdaPlatform[Event, Result] {
this: IOLambda[Event, Result] =>

final def main(args: Array[String]): Unit =
js.Dynamic.global.exports.updateDynamic(handlerName)(handlerFn)

protected def handlerName: String = getClass.getSimpleName.init

private lazy val handlerFn
private[lambda] lazy val handlerFn
: js.Function2[js.Any, facade.Context, js.Promise[js.UndefOr[js.Any]]] = {
val dispatcherHandle = {
Dispatcher
.parallel[IO](await = false)
.product(handler)
.allocated
.map(_._1) // drop unused finalizer
.unsafeToPromise()(runtime)
}

(event: js.Any, context: facade.Context) =>
setupMemo.toJSPromise(scala.concurrent.ExecutionContext.parasitic).`then`[js.Any] {
case (dispatcher, lambda) =>
dispatcherHandle.`then`[js.Any] {
case (dispatcher, handle) =>
val io =
for {
event <- IO.fromEither(decodeJs[Event](event))
result <- lambda(event, Context.fromJS(context))
result <- handle(Invocation.pure(event, Context.fromJS(context)))
} yield result.map(_.asJsAny).orUndefined

dispatcher.unsafeToPromise(io)
Expand Down
66 changes: 30 additions & 36 deletions lambda/js/src/main/scala/feral/lambda/facade/Context.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,48 +18,42 @@ package feral.lambda.facade

import scala.scalajs.js

@js.native
private[lambda] sealed trait Context extends js.Object {
def callbackWaitsForEmptyEventLoop: Boolean = js.native
def functionName: String = js.native
def functionVersion: String = js.native
def invokedFunctionArn: String = js.native
def memoryLimitInMB: String = js.native
def awsRequestId: String = js.native
def logGroupName: String = js.native
def logStreamName: String = js.native
def identity: js.UndefOr[CognitoIdentity] = js.native
def clientContext: js.UndefOr[ClientContext] = js.native
def getRemainingTimeInMillis(): Double = js.native
private[lambda] trait Context extends js.Object {
def functionName: String
def functionVersion: String
def invokedFunctionArn: String
def memoryLimitInMB: String
def awsRequestId: String
def logGroupName: String
def logStreamName: String
def identity: js.UndefOr[CognitoIdentity]
def clientContext: js.UndefOr[ClientContext]
def getRemainingTimeInMillis(): Double
}

@js.native
private[lambda] sealed trait CognitoIdentity extends js.Object {
def cognitoIdentityId: String = js.native
def cognitoIdentityPoolId: String = js.native
private[lambda] trait CognitoIdentity extends js.Object {
def cognitoIdentityId: String
def cognitoIdentityPoolId: String
}

@js.native
private[lambda] sealed trait ClientContext extends js.Object {
def client: ClientContextClient = js.native
def custom: js.UndefOr[js.Any] = js.native
def env: ClientContextEnv = js.native
private[lambda] trait ClientContext extends js.Object {
def client: ClientContextClient
def custom: js.UndefOr[js.Any]
def env: ClientContextEnv
}

@js.native
private[lambda] sealed trait ClientContextClient extends js.Object {
def installationId: String = js.native
def appTitle: String = js.native
def appVersionName: String = js.native
def appVersionCode: String = js.native
def appPackageName: String = js.native
private[lambda] trait ClientContextClient extends js.Object {
def installationId: String
def appTitle: String
def appVersionName: String
def appVersionCode: String
def appPackageName: String
}

@js.native
private[lambda] sealed trait ClientContextEnv extends js.Object {
def platformVersion: String = js.native
def platform: String = js.native
def make: String = js.native
def model: String = js.native
def locale: String = js.native
private[lambda] trait ClientContextEnv extends js.Object {
def platformVersion: String
def platform: String
def make: String
def model: String
def locale: String
}
85 changes: 85 additions & 0 deletions lambda/js/src/test/scala/feral/lambda/IOLambdaJsSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2021 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package feral.lambda

import cats.effect.IO
import cats.effect.kernel.Resource
import cats.syntax.all._
import io.circe.Json
import io.circe.literal._
import io.circe.scalajs._
import munit.CatsEffectSuite

import java.util.concurrent.atomic.AtomicInteger
import scala.scalajs.js

class IOLambdaJsSuite extends CatsEffectSuite {

test("initializes handler once") {

val allocationCounter = new AtomicInteger
val invokeCounter = new AtomicInteger
val lambda = new IOLambda[String, String] {
def handler = Resource
.eval(IO(allocationCounter.getAndIncrement()))
.as(_.event.map(Some(_)) <* IO(invokeCounter.getAndIncrement()))
}

val chars = 'A' to 'Z'
chars.toList.traverse { c =>
IO.fromPromise(IO(lambda.handlerFn(c.toString, DummyContext)))
.assertEquals(c.toString.asInstanceOf[js.UndefOr[js.Any]])
} *> IO {
assertEquals(allocationCounter.get(), 1)
assertEquals(invokeCounter.get(), chars.length)
}
}

test("reads input and writes output") {

val input = json"""{ "foo": "bar" }"""
val output = json"""{ "woozle": "heffalump" }"""

val lambda = new IOLambda[Json, Json] {
def handler = Resource.pure(_ => IO(Some(output)))
}

IO.fromPromise(
IO(
lambda.handlerFn(
input.asJsAny,
DummyContext
)
)
).map(decodeJs[Json](_))
.assertEquals(Right(output))
}

object DummyContext extends facade.Context {
def functionName = ""
def functionVersion = ""
def invokedFunctionArn = ""
def memoryLimitInMB = "0"
def awsRequestId = ""
def logGroupName = ""
def logStreamName = ""
def identity = js.undefined
def clientContext = js.undefined
def getRemainingTimeInMillis(): Double = 0
}

}
17 changes: 12 additions & 5 deletions lambda/jvm/src/main/scala/feral/lambda/IOLambdaPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package feral.lambda

import cats.effect.IO
import cats.effect.std.Dispatcher
import cats.syntax.all._
import com.amazonaws.services.lambda.{runtime => lambdaRuntime}
import io.circe.Printer
import io.circe.jawn
Expand All @@ -26,24 +28,29 @@ import java.io.InputStream
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.nio.channels.Channels
import scala.concurrent.Await
import scala.concurrent.duration._

private[lambda] abstract class IOLambdaPlatform[Event, Result]
extends lambdaRuntime.RequestStreamHandler { this: IOLambda[Event, Result] =>

private[this] val (dispatcher, handle) = {
Dispatcher
.parallel[IO](await = false)
.product(handler)
.allocated
.map(_._1) // drop unused finalizer
.unsafeRunSync()(runtime)
}

final def handleRequest(
input: InputStream,
output: OutputStream,
runtimeContext: lambdaRuntime.Context): Unit = {
val (dispatcher, lambda) =
Await.result(setupMemo, runtimeContext.getRemainingTimeInMillis().millis)

val event = jawn.decodeChannel[Event](Channels.newChannel(input)).fold(throw _, identity(_))
val context = Context.fromJava[IO](runtimeContext)
dispatcher
.unsafeRunTimed(
lambda(event, context),
handle(Invocation.pure(event, context)),
runtimeContext.getRemainingTimeInMillis().millis
)
.foreach { result =>
Expand Down
Loading

0 comments on commit eb1de8f

Please sign in to comment.