From c71696dc9c5f31e84c2f4bb283bdc72e2a13a0a1 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Mon, 1 Jul 2024 10:05:19 +1000 Subject: [PATCH] feat: verb request and response types can be any FTL type --- backend/controller/ingress/ingress.go | 9 ++++----- go-runtime/compile/schema_test.go | 6 ++++-- go-runtime/compile/testdata/one/one.go | 11 +++++++++++ go-runtime/schema/verb/analyzer.go | 6 ------ 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/backend/controller/ingress/ingress.go b/backend/controller/ingress/ingress.go index afa896058a..400282bd1c 100644 --- a/backend/controller/ingress/ingress.go +++ b/backend/controller/ingress/ingress.go @@ -60,13 +60,12 @@ func matchSegments(pattern, urlPath string, onMatch func(segment, value string)) } func ValidateCallBody(body []byte, verb *schema.Verb, sch *schema.Schema) error { - var requestMap map[string]any - err := json.Unmarshal(body, &requestMap) + var root any + err := json.Unmarshal(body, &root) if err != nil { - return fmt.Errorf("HTTP request body is not valid JSON: %w", err) + return fmt.Errorf("request body is not valid JSON: %w", err) } - - return validateValue(verb.Request, []string{verb.Request.String()}, requestMap, sch) + return validateValue(verb.Request, []string{verb.Request.String()}, root, sch) } func getBodyField(ref *schema.Ref, sch *schema.Schema) (*schema.Field, error) { diff --git a/go-runtime/compile/schema_test.go b/go-runtime/compile/schema_test.go index cf2728c3bc..15c9ddb968 100644 --- a/go-runtime/compile/schema_test.go +++ b/go-runtime/compile/schema_test.go @@ -164,6 +164,8 @@ func TestExtractModuleSchema(t *testing.T) { data WithoutDirectiveStruct { } + verb batchStringToTime([String]) [Time] + export verb http(builtin.HttpRequest) builtin.HttpResponse +ingress http GET /get @@ -173,6 +175,8 @@ func TestExtractModuleSchema(t *testing.T) { verb source(Unit) one.SourceResp + verb stringToTime(String) Time + verb verb(one.Req) one.Resp } ` @@ -558,7 +562,6 @@ func TestErrorReporting(t *testing.T) { `45:1-2: must have at most two parameters (context.Context, struct)`, `45:69-69: unsupported response type "ftl/failing.Response"`, `50:22-27: first parameter must be of type context.Context but is ftl/failing.Request`, - `50:37-43: second parameter must be a struct but is string`, `50:53-53: unsupported response type "ftl/failing.Response"`, `55:43-47: second parameter must not be ftl.Unit`, `55:59-59: unsupported response type "ftl/failing.Response"`, @@ -571,7 +574,6 @@ func TestErrorReporting(t *testing.T) { `74:35-35: unsupported request type "ftl/failing.Request"`, `74:48-48: must return an error but is ftl/failing.Response`, `79:41-41: unsupported request type "ftl/failing.Request"`, - `79:55-55: first result must be a struct but is string`, `79:63-63: must return an error but is string`, `79:63-63: second result must not be ftl.Unit`, // `86:1-2: duplicate declaration of "WrongResponse" at 79:6`, TODO: fix this diff --git a/go-runtime/compile/testdata/one/one.go b/go-runtime/compile/testdata/one/one.go index 70ff78e0d2..4b338506a8 100644 --- a/go-runtime/compile/testdata/one/one.go +++ b/go-runtime/compile/testdata/one/one.go @@ -2,6 +2,7 @@ package one import ( "context" + "errors" "time" "ftl/builtin" @@ -182,3 +183,13 @@ type NonFTLStruct struct { } func (NonFTLStruct) NonFTLInterface() {} + +//ftl:verb +func StringToTime(ctx context.Context, input string) (time.Time, error) { + return time.Time{}, errors.New("not implemented") +} + +//ftl:verb +func BatchStringToTime(ctx context.Context, input []string) ([]time.Time, error) { + return nil, errors.New("not implemented") +} diff --git a/go-runtime/schema/verb/analyzer.go b/go-runtime/schema/verb/analyzer.go index 4095b441b2..cdd69fd332 100644 --- a/go-runtime/schema/verb/analyzer.go +++ b/go-runtime/schema/verb/analyzer.go @@ -79,9 +79,6 @@ func checkSignature(pass *analysis.Pass, node *ast.FuncDecl, sig *types.Signatur } if params.Len() == 2 { - if !common.IsType[*types.Struct](params.At(1).Type()) { - common.TokenErrorf(pass, params.At(1).Pos(), params.At(1).Name(), "second parameter must be a struct but is %s", params.At(1).Type()) - } if params.At(1).Type().String() == common.FtlUnitTypePath { common.TokenErrorf(pass, params.At(1).Pos(), params.At(1).Name(), "second parameter must not be ftl.Unit") } @@ -98,9 +95,6 @@ func checkSignature(pass *analysis.Pass, node *ast.FuncDecl, sig *types.Signatur common.TokenErrorf(pass, results.At(results.Len()-1).Pos(), results.At(results.Len()-1).Name(), "must return an error but is %s", results.At(0).Type()) } if results.Len() == 2 { - if !common.IsType[*types.Struct](results.At(0).Type()) { - common.TokenErrorf(pass, results.At(0).Pos(), results.At(0).Name(), "first result must be a struct but is %s", results.At(0).Type()) - } if results.At(1).Type().String() == common.FtlUnitTypePath { common.TokenErrorf(pass, results.At(1).Pos(), results.At(1).Name(), "second result must not be ftl.Unit") }