From 8f126edfb35548b83d2f4c4d72c92409f69856c1 Mon Sep 17 00:00:00 2001 From: worstell Date: Fri, 26 Jan 2024 17:29:44 -0500 Subject: [PATCH] fix: transform aliases transitively (#839) --- backend/controller/ingress/ingress.go | 28 +++++++++++++++++-- .../src/main/kotlin/ftl/echo/Echo.kt | 7 ++--- integration/integration_test.go | 2 +- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/backend/controller/ingress/ingress.go b/backend/controller/ingress/ingress.go index 8454900ecb..ea8704c54a 100644 --- a/backend/controller/ingress/ingress.go +++ b/backend/controller/ingress/ingress.go @@ -122,6 +122,11 @@ func ValidateAndExtractRequestBody(route *dal.IngressRoute, r *http.Request, sch requestMap["headers"] = r.Header requestMap["body"] = bodyMap + requestMap, err = transformAliasedFields(request, sch, requestMap) + if err != nil { + return nil, err + } + err = validateRequestMap(request, []string{request.String()}, requestMap, sch) if err != nil { return nil, err @@ -197,7 +202,6 @@ func buildRequest(route *dal.IngressRoute, r *http.Request, dataRef *schema.Data requestMap[key] = value } } - transformAliasedFields(dataRef, sch, requestMap) return requestMap, nil } @@ -452,12 +456,32 @@ func hasInvalidQueryChars(s string) bool { return strings.ContainsAny(s, "{}[]|\\^`") } -func transformAliasedFields(dataRef *schema.DataRef, sch *schema.Schema, request map[string]any) { +func transformAliasedFields(dataRef *schema.DataRef, sch *schema.Schema, request map[string]any) (map[string]any, error) { data := sch.ResolveDataRef(dataRef) + if len(dataRef.TypeParameters) > 0 { + var err error + data, err = data.Monomorphise(dataRef.TypeParameters...) + if err != nil { + return nil, err + } + } + for _, field := range data.Fields { if _, ok := request[field.Name]; !ok && field.Alias != "" { request[field.Name] = request[field.Alias] delete(request, field.Alias) } + + if d, ok := field.Type.(*schema.DataRef); ok { + if _, found := request[field.Name]; found { + rMap, err := transformAliasedFields(d, sch, request[field.Name].(map[string]any)) + if err != nil { + return nil, err + } + request[field.Name] = rMap + } + } } + + return request, nil } diff --git a/examples/kotlin/ftl-module-echo/src/main/kotlin/ftl/echo/Echo.kt b/examples/kotlin/ftl-module-echo/src/main/kotlin/ftl/echo/Echo.kt index 96716f9cf0..dc5d453725 100644 --- a/examples/kotlin/ftl-module-echo/src/main/kotlin/ftl/echo/Echo.kt +++ b/examples/kotlin/ftl-module-echo/src/main/kotlin/ftl/echo/Echo.kt @@ -6,19 +6,18 @@ import xyz.block.ftl.Context import xyz.block.ftl.Ingress import xyz.block.ftl.Method import xyz.block.ftl.Verb -import xyz.block.ftl.Alias class InvalidInput(val field: String) : Exception() -data class EchoRequest(val name: String?, @Alias("m") val metadata: String) +data class EchoRequest(val name: String?) data class EchoResponse(val message: String) class Echo { @Throws(InvalidInput::class) @Verb - @Ingress(Method.POST, "/echo") + @Ingress(Method.GET, "/echo") fun echo(context: Context, req: EchoRequest): EchoResponse { val response = context.call(TimeModuleClient::time, TimeRequest) - return EchoResponse(message = "Hello, ${req.name ?: "anonymous"}! The time is ${response.time}. Metadata: ${req.metadata}") + return EchoResponse(message = "Hello, ${req.name ?: "anonymous"}! The time is ${response.time}.") } } diff --git a/integration/integration_test.go b/integration/integration_test.go index 9c3bc2ccea..65f6ed2da2 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -61,7 +61,7 @@ func TestIntegration(t *testing.T) { deploymentExists("echo"), }}, {name: "CallEchoKotlin", assertions: assertions{ - call("echo", "echo", obj{"name": "Alice", "metadata": "hi"}, func(t testing.TB, resp obj) { + call("echo", "echo", obj{"name": "Alice"}, func(t testing.TB, resp obj) { message, ok := resp["message"].(string) assert.True(t, ok, "message is not a string") assert.True(t, regexp.MustCompile(`^Hello, Alice!`).MatchString(message), "%q does not match %q", message, `^Hello, Alice!`)