Skip to content

Commit

Permalink
chore: use HttpIngress instead if Ingress in kotlin
Browse files Browse the repository at this point in the history
fixes #874
  • Loading branch information
worstell committed Feb 7, 2024
1 parent 2142e19 commit 1a91d4f
Show file tree
Hide file tree
Showing 11 changed files with 51 additions and 76 deletions.
5 changes: 2 additions & 3 deletions examples/kotlin/ftl-module-ad/src/main/kotlin/ftl/ad/Ad.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import com.google.gson.reflect.TypeToken
import ftl.builtin.HttpRequest
import ftl.builtin.HttpResponse
import xyz.block.ftl.Context
import xyz.block.ftl.Ingress
import xyz.block.ftl.Ingress.Type.HTTP
import xyz.block.ftl.HttpIngress
import xyz.block.ftl.Method
import xyz.block.ftl.Verb
import java.util.*
Expand All @@ -19,7 +18,7 @@ class AdModule {
private val database: Map<String, Ad> = loadDatabase()

@Verb
@Ingress(Method.GET, "/get", HTTP)
@HttpIngress(Method.GET, "/get")
fun get(context: Context, req: HttpRequest<AdRequest>): HttpResponse<AdResponse> {
val ads: List<Ad> = when {
req.body.contextKeys != null -> contextualAds(req.body.contextKeys)
Expand Down
6 changes: 3 additions & 3 deletions examples/kotlin/ftl-module-api/src/main/kotlin/ftl/api/Api.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ class Api {
private val headers = mapOf("Content-Type" to arrayListOf("application/json"))

@Verb
@Ingress(Method.GET, "/api/status")
@HttpIngress(Method.GET, "/api/status")
fun status(context: Context, req: HttpRequest<GetStatusRequest>): HttpResponse<GetStatusResponse> {
return HttpResponse<GetStatusResponse>(status = 200, headers = mapOf(), body = GetStatusResponse("OK"))
}

@Verb
@Ingress(Method.GET, "/api/todos/{id}")
@HttpIngress(Method.GET, "/api/todos/{id}")
fun getTodo(context: Context, req: HttpRequest<GetTodoRequest>): HttpResponse<GetTodoResponse> {
val todoId = req.pathParameters["id"]?.toIntOrNull()
val todo = todos[todoId]
Expand All @@ -81,7 +81,7 @@ class Api {
}

@Verb
@Ingress(Method.POST, "/api/todos")
@HttpIngress(Method.POST, "/api/todos")
fun addTodo(context: Context, req: HttpRequest<CreateTodoRequest>): HttpResponse<CreateTodoResponse> {
val todoReq = req.body
val id = idCounter.incrementAndGet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package ftl.echo
import ftl.time.TimeModuleClient
import ftl.time.TimeRequest
import xyz.block.ftl.Context
import xyz.block.ftl.Ingress
import xyz.block.ftl.Ingress.Type.HTTP
import xyz.block.ftl.Method
import xyz.block.ftl.Verb

Expand Down
21 changes: 10 additions & 11 deletions integration/testdata/kotlin/httpingress/Echo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import kotlin.String
import kotlin.Unit
import xyz.block.ftl.Alias
import xyz.block.ftl.Context
import xyz.block.ftl.Ingress
import xyz.block.ftl.Ingress.Type.HTTP
import xyz.block.ftl.HttpIngress
import xyz.block.ftl.Method
import xyz.block.ftl.Verb

Expand Down Expand Up @@ -50,8 +49,8 @@ typealias HtmlRequest = Unit

class Echo {
@Verb
@Ingress(
Method.GET, "/users/{userID}/posts/{postID}", HTTP)
@HttpIngress(
Method.GET, "/users/{userID}/posts/{postID}")
fun `get`(context: Context, req: HttpRequest<GetRequest>): HttpResponse<GetResponse> {
return HttpResponse<GetResponse>(
status = 200,
Expand All @@ -64,7 +63,7 @@ class Echo {
}

@Verb
@Ingress(Method.POST, "/users", HTTP)
@HttpIngress(Method.POST, "/users")
fun post(context: Context, req: HttpRequest<PostRequest>): HttpResponse<PostResponse> {
return HttpResponse<PostResponse>(
status = 201,
Expand All @@ -74,7 +73,7 @@ class Echo {
}

@Verb
@Ingress(Method.PUT, "/users/{userId}", HTTP)
@HttpIngress(Method.PUT, "/users/{userId}")
fun put(context: Context, req: HttpRequest<PutRequest>): HttpResponse<PutResponse> {
return HttpResponse<PutResponse>(
status = 200,
Expand All @@ -84,7 +83,7 @@ class Echo {
}

@Verb
@Ingress(Method.DELETE, "/users/{userId}", HTTP)
@HttpIngress(Method.DELETE, "/users/{userId}")
fun delete(context: Context, req: HttpRequest<DeleteRequest>): HttpResponse<DeleteResponse> {
return HttpResponse<DeleteResponse>(
status = 200,
Expand All @@ -94,7 +93,7 @@ class Echo {
}

@Verb
@Ingress(Method.GET, "/html", HTTP)
@HttpIngress(Method.GET, "/html")
fun html(context: Context, req: HttpRequest<HtmlRequest>): HttpResponse<String> {
return HttpResponse<String>(
status = 200,
Expand All @@ -104,7 +103,7 @@ class Echo {
}

@Verb
@Ingress(Method.POST, "/bytes", HTTP)
@HttpIngress(Method.POST, "/bytes")
fun bytes(context: Context, req: HttpRequest<ByteArray>): HttpResponse<ByteArray> {
return HttpResponse<ByteArray>(
status = 200,
Expand All @@ -114,7 +113,7 @@ class Echo {
}

@Verb
@Ingress(Method.GET, "/empty", HTTP)
@HttpIngress(Method.GET, "/empty")
fun empty(context: Context, req: HttpRequest<Unit>): HttpResponse<Unit> {
return HttpResponse<Unit>(
status = 200,
Expand All @@ -124,7 +123,7 @@ class Echo {
}

@Verb
@Ingress(Method.GET, "/string", HTTP)
@HttpIngress(Method.GET, "/string")
fun string(context: Context, req: HttpRequest<String>): HttpResponse<String> {
return HttpResponse<String>(
status = 200,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import xyz.block.ftl.Context
import xyz.block.ftl.Ignore
import xyz.block.ftl.Ingress
import xyz.block.ftl.HttpIngress
import xyz.block.ftl.v1.schema.*
import java.io.File
import java.nio.file.Path
Expand Down Expand Up @@ -108,10 +108,9 @@ class ModuleGenerator() {
verb.metadata.forEach { metadata ->
metadata.ingress?.let {
verbFunBuilder.addAnnotation(
AnnotationSpec.builder(Ingress::class)
AnnotationSpec.builder(HttpIngress::class)
.addMember("%T", ClassName("xyz.block.ftl.Method", it.method.replaceBefore(".", "")))
.addMember("%S", ingressPathString(it.path))
.addMember("%T", ClassName("xyz.block.ftl.Ingress.Type", it.type.uppercase().replaceBefore(".", "")))
.build()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,8 @@ package ftl.test
import kotlin.Unit
import xyz.block.ftl.Context
import xyz.block.ftl.HttpIngress
import xyz.block.ftl.Ignore
import xyz.block.ftl.Ingress
import xyz.block.ftl.Ingress.Type.HTTP
import xyz.block.ftl.Method.GET
import xyz.block.ftl.Verb
Expand All @@ -223,10 +222,9 @@ public class TestModule() {
* TestIngressVerb comments
*/
@Verb
@Ingress(
@HttpIngress(
GET,
"/test",
HTTP,
)
public fun TestIngressVerb(context: Context, req: TestRequest): TestResponse = throw
NotImplementedError("Verb stubs should not be called directly, instead use context.call(TestModule::TestIngressVerb, ...)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ enum class Method {
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class Ingress(val method: Method, val path: String, val type: Type = Type.HTTP) {
enum class Type {
HTTP
}
}
annotation class HttpIngress(val method: Method, val path: String)

/**
* A field marked with Alias will be renamed to the specified name on ingress from external inputs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ class SchemaExtractor(

private fun extractIngress(requestType: Type, responseType: Type): MetadataIngress? {
return verb.annotationEntries.firstOrNull {
bindingContext.get(BindingContext.ANNOTATION, it)?.fqName?.asString() == Ingress::class.qualifiedName
bindingContext.get(BindingContext.ANNOTATION, it)?.fqName?.asString() == HttpIngress::class.qualifiedName
}?.let { annotationEntry ->
val sourcePos = annotationEntry.getLineAndColumn()
require(requestType.dataRef != null) {
Expand All @@ -253,52 +253,38 @@ class SchemaExtractor(
require(responseType.dataRef != null) {
"$sourcePos ingress ${verb.name} response must be a data class"
}
require(requestType.dataRef.compare("builtin", "HttpRequest")) {
"$sourcePos @HttpIngress-annotated ${verb.name} request must be ftl.builtin.HttpRequest"
}
require(responseType.dataRef.compare("builtin", "HttpResponse")) {
"$sourcePos @HttpIngress-annotated ${verb.name} response must be ftl.builtin.HttpResponse"
}
require(annotationEntry.valueArguments.size >= 2) {
"$sourcePos ${verb.name} @Ingress annotation requires at least 2 arguments"
"$sourcePos ${verb.name} @HttpIngress annotation requires at least 2 arguments"
}

var typeArg = Ingress.Type.HTTP.name
var pathArg: List<IngressPathComponent>? = null
var methodArg: String? = null
annotationEntry.valueArguments.map { arg ->
val args = annotationEntry.valueArguments.partition { arg ->
// Method arg is named "method" or is of type xyz.block.ftl.Method (in the case where args are
// positional rather than named).
if (arg.getArgumentName()?.asName?.asString() == "method"
|| arg.getArgumentExpression()?.getType(bindingContext)?.fqNameOrNull()
?.asString() == Method::class.qualifiedName
) {
methodArg = arg.getArgumentExpression()?.text?.substringAfter(".")
// Type arg is named "type" or is of type xyz.block.ftl.Ingress.Type.
} else if (arg.getArgumentName()?.asName?.asString() == "type"
|| arg.getArgumentExpression()?.getType(bindingContext)?.fqNameOrNull()
?.asString() == Ingress.Type::class.qualifiedName
) {
typeArg = requireNotNull(arg.getArgumentExpression()?.text?.substringAfter(".")) {
"$sourcePos Could not extract type from ${verb.name} @Ingress annotation"
}
// Path arg is named "path" or is of type kotlin.String.
} else if (arg.getArgumentName()?.asName?.asString() == "path"
arg.getArgumentName()?.asName?.asString() == "method"
|| arg.getArgumentExpression()?.getType(bindingContext)?.fqNameOrNull()
?.asString() == String::class.qualifiedName
) {
pathArg = arg.getArgumentExpression()?.text?.let { extractPathComponents(it.trim('\"')) }
}
?.asString() == Method::class.qualifiedName
}

// If it's HTTP ingress, validate the signature.
if (typeArg == Ingress.Type.HTTP.name) {
require(requestType.dataRef.compare("builtin", "HttpRequest")) {
"$sourcePos HTTP @Ingress-annotated ${verb.name} request must be ftl.builtin.HttpRequest"
}
require(responseType.dataRef.compare("builtin", "HttpResponse")) {
"$sourcePos HTTP @Ingress-annotated ${verb.name} response must be ftl.builtin.HttpResponse"
}
val methodArg = requireNotNull(args.first.single().getArgumentExpression()?.text?.substringAfter(".")) {
"Could not extract method from ${verb.name} @HttpIngress annotation"
}
val pathArg = requireNotNull(args.second.single().getArgumentExpression()?.text?.let {
extractPathComponents(it.trim('\"'))
}) {
"Could not extract path from ${verb.name} @HttpIngress annotation"
}


MetadataIngress(
path = requireNotNull(pathArg) { "Could not extract path from ${verb.name} @Ingress annotation" },
method = requireNotNull(methodArg) { "Could not extract method from ${verb.name} @Ingress annotation" },
type = typeArg.lowercase(),
type = "http",
path = pathArg,
method = methodArg,
pos = sourcePos.toPosition(verb.containingKtFile.name),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class RenamedVerb {

class ExampleVerb {
@Verb
@Ingress(Method.GET, "/test")
@HttpIngress(Method.GET, "/test")
fun verb(context: Context, req: VerbRequest): VerbResponse {
return VerbResponse("test")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal class ExtractSchemaRuleTest(private val env: KotlinCoreEnvironment) {
import ftl.time.TimeResponse
import xyz.block.ftl.Alias
import xyz.block.ftl.Context
import xyz.block.ftl.Ingress
import xyz.block.ftl.HttpIngress
import xyz.block.ftl.Method
import xyz.block.ftl.Verb
Expand All @@ -61,7 +61,7 @@ internal class ExtractSchemaRuleTest(private val env: KotlinCoreEnvironment) {
*/
@Throws(InvalidInput::class)
@Verb
@Ingress(Method.GET, "/echo")
@HttpIngress(Method.GET, "/echo")
fun echo(context: Context, req: HttpRequest<EchoRequest<String>>): HttpResponse<EchoResponse> {
callTime(context)
Expand Down Expand Up @@ -256,7 +256,7 @@ internal class ExtractSchemaRuleTest(private val env: KotlinCoreEnvironment) {
import ftl.time.TimeRequest
import ftl.time.TimeResponse
import xyz.block.ftl.Context
import xyz.block.ftl.Ingress
import xyz.block.ftl.HttpIngress
import xyz.block.ftl.Method
import xyz.block.ftl.Verb
Expand All @@ -279,7 +279,7 @@ internal class ExtractSchemaRuleTest(private val env: KotlinCoreEnvironment) {
*/
@Throws(InvalidInput::class)
@Verb
@Ingress(Method.GET, "/echo")
@HttpIngress(Method.GET, "/echo")
fun echo(context: Context, req: EchoRequest): EchoResponse {
callTime(context)
return EchoResponse(messages = listOf(EchoMessage(message = "Hello!")))
Expand All @@ -301,7 +301,7 @@ internal class ExtractSchemaRuleTest(private val env: KotlinCoreEnvironment) {
package ftl.echo
import xyz.block.ftl.Context
import xyz.block.ftl.Ingress
import xyz.block.ftl.HttpIngress
import xyz.block.ftl.Method
import xyz.block.ftl.Verb
Expand All @@ -320,13 +320,13 @@ internal class ExtractSchemaRuleTest(private val env: KotlinCoreEnvironment) {
*/
@Throws(InvalidInput::class)
@Verb
@Ingress(Method.GET, "/echo")
@HttpIngress(Method.GET, "/echo")
fun echo(context: Context, req: EchoRequest): EchoResponse {
return EchoResponse(messages = listOf(EchoMessage(message = "Hello!")))
}
}
"""
assertThrows<java.lang.IllegalArgumentException>(message = "HTTP @Ingress-annotated echo request must be ftl.builtin.HttpRequest") {
assertThrows<java.lang.IllegalArgumentException>(message = "@HttpIngress-annotated echo request must be ftl.builtin.HttpRequest") {
ExtractSchemaRule(Config.empty).compileAndLintWithContext(env, code)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class TimeModuleClient() {
* Time returns the current time.
*/
@Verb
@Ingress(
@HttpIngress(
GET,
"/time",
)
Expand Down

0 comments on commit 1a91d4f

Please sign in to comment.