diff --git a/FTL.code-workspace b/FTL.code-workspace index 9d52a2c9bd..e689ce6004 100644 --- a/FTL.code-workspace +++ b/FTL.code-workspace @@ -7,13 +7,15 @@ "path": "kotlin-runtime" }, { - "path": "examples/time" + "path": "examples/online-boutique" }, { - "path": "examples/echo" + "path": "examples/go", + "name": "examples/go" }, { - "path": "examples/online-boutique" + "path": "examples/kotlin", + "name": "examples/kotlin" }, { "path": "backend" @@ -42,4 +44,4 @@ "**/*.zip": true, } } -} \ No newline at end of file +} diff --git a/examples/echo/echo.go b/examples/go/echo/echo.go similarity index 96% rename from examples/echo/echo.go rename to examples/go/echo/echo.go index caa6419287..659f235254 100644 --- a/examples/echo/echo.go +++ b/examples/go/echo/echo.go @@ -24,7 +24,6 @@ type EchoResponse struct { // Echo returns a greeting with the current time. // //ftl:verb -//ftl:ingress GET /echo func Echo(ctx context.Context, req EchoRequest) (EchoResponse, error) { fmt.Println("Echo received a request!") tresp, err := sdk.Call(ctx, time.Time, time.TimeRequest{}) diff --git a/examples/echo/ftl.toml b/examples/go/echo/ftl.toml similarity index 100% rename from examples/echo/ftl.toml rename to examples/go/echo/ftl.toml diff --git a/examples/echo/go.mod b/examples/go/echo/go.mod similarity index 61% rename from examples/echo/go.mod rename to examples/go/echo/go.mod index 01739b33f8..1b74d2fbd3 100644 --- a/examples/echo/go.mod +++ b/examples/go/echo/go.mod @@ -1,23 +1,29 @@ module ftl/echo -go 1.21.5 +go 1.21.6 -require github.com/TBD54566975/ftl v0.95.0 +replace github.com/TBD54566975/ftl => ../../.. + +require github.com/TBD54566975/ftl v0.0.0-00010101000000-000000000000 require ( connectrpc.com/connect v1.14.0 // indirect connectrpc.com/grpcreflect v1.2.0 // indirect - connectrpc.com/otelconnect v0.6.0 // indirect + connectrpc.com/otelconnect v0.7.0 // indirect github.com/alecthomas/concurrency v0.0.2 // indirect github.com/alecthomas/participle/v2 v2.1.1 // indirect - github.com/alecthomas/types v0.9.0 // indirect + github.com/alecthomas/types v0.10.1 // indirect github.com/alessio/shellescape v1.4.2 // indirect github.com/danieljoos/wincred v1.2.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/google/uuid v1.5.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/iancoleman/strcase v0.2.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.2 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -25,15 +31,16 @@ require ( github.com/swaggest/jsonschema-go v0.3.64 // indirect github.com/swaggest/refl v1.3.0 // indirect github.com/zalando/go-keyring v0.2.3 // indirect - go.opentelemetry.io/otel v1.21.0 // indirect - go.opentelemetry.io/otel/metric v1.21.0 // indirect - go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.opentelemetry.io/otel v1.22.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect golang.design/x/reflect v0.0.0-20220504060917-02c43be63f3b // indirect + golang.org/x/crypto v0.18.0 // indirect golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.19.0 // indirect + golang.org/x/net v0.20.0 // indirect golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/protobuf v1.32.0 // indirect ) diff --git a/examples/echo/go.sum b/examples/go/echo/go.sum similarity index 72% rename from examples/echo/go.sum rename to examples/go/echo/go.sum index d6040a3de8..f307a4e48f 100644 --- a/examples/echo/go.sum +++ b/examples/go/echo/go.sum @@ -2,20 +2,18 @@ connectrpc.com/connect v1.14.0 h1:PDS+J7uoz5Oui2VEOMcfz6Qft7opQM9hPiKvtGC01pA= connectrpc.com/connect v1.14.0/go.mod h1:uoAq5bmhhn43TwhaKdGKN/bZcGtzPW1v+ngDTn5u+8s= connectrpc.com/grpcreflect v1.2.0 h1:Q6og1S7HinmtbEuBvARLNwYmTbhEGRpHDhqrPNlmK+U= connectrpc.com/grpcreflect v1.2.0/go.mod h1:nwSOKmE8nU5u/CidgHtPYk1PFI3U9ignz7iDMxOYkSY= -connectrpc.com/otelconnect v0.6.0 h1:VJAdQL9+sgdUw9+7+J+jq8pQo/h1S7tSFv2+vDcR7bU= -connectrpc.com/otelconnect v0.6.0/go.mod h1:jdcs0uiwXQVmSMgTJ2dAaWR5VbpNd7QKNkuoH7n86RA= -github.com/TBD54566975/ftl v0.95.0 h1:cAleh+j0owIlmPcCEmmhkaNj0gCWVyQ+pJINAispW/M= -github.com/TBD54566975/ftl v0.95.0/go.mod h1:8M4ZC4WrDAwxNSsj1CjrGiFHyfbyh/Xdobo2LjT4Kq4= -github.com/alecthomas/assert/v2 v2.4.1 h1:mwPZod/d35nlaCppr6sFP0rbCL05WH9fIo7lvsf47zo= -github.com/alecthomas/assert/v2 v2.4.1/go.mod h1:fw5suVxB+wfYJ3291t0hRTqtGzFYdSwstnRQdaQx2DM= +connectrpc.com/otelconnect v0.7.0 h1:ZH55ZZtcJOTKWWLy3qmL4Pam4RzRWBJFOqTPyAqCXkY= +connectrpc.com/otelconnect v0.7.0/go.mod h1:Bt2ivBymHZHqxvo4HkJ0EwHuUzQN6k2l0oH+mp/8nwc= +github.com/alecthomas/assert/v2 v2.5.0 h1:OJKYg53BQx06/bMRBSPDCO49CbCDNiUQXwdoNrt6x5w= +github.com/alecthomas/assert/v2 v2.5.0/go.mod h1:fw5suVxB+wfYJ3291t0hRTqtGzFYdSwstnRQdaQx2DM= github.com/alecthomas/concurrency v0.0.2 h1:Q3kGPtLbleMbH9lHX5OBFvJygfyFw29bXZKBg+IEVuo= github.com/alecthomas/concurrency v0.0.2/go.mod h1:GmuQb/iHX7mbNtPlC/WDzEFxDMB0HYFer2Qda9QTs7w= github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= github.com/alecthomas/repr v0.3.0 h1:NeYzUPfjjlqHY4KtzgKJiWd6sVq2eNUPTi34PiFGjY8= github.com/alecthomas/repr v0.3.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/alecthomas/types v0.9.0 h1:g/P/fNElr7Ot1tWxSEms/I2gnRemxAqGNeetW6HOlO4= -github.com/alecthomas/types v0.9.0/go.mod h1:t7PnU03TVweFpbPVKaeLtFykjJD8rqiBJ7gfkp6UvLQ= +github.com/alecthomas/types v0.10.1 h1:PuBMoHpFL2jaW3VgPDRhCk1oKoBCzfbsL5sAxEc3U3A= +github.com/alecthomas/types v0.10.1/go.mod h1:fIOGnLeeUJXe1AAVofQmMaEMWLxY9bK4QxTLGIo30PA= github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/bool64/dev v0.2.31 h1:OS57EqYaYe2M/2bw9uhDCIFiZZwywKFS/4qMLN6JUmQ= @@ -24,6 +22,7 @@ github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -37,14 +36,22 @@ github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.2 h1:iLlpgp4Cp/gC9Xuscl7lFL1PhhW+ZLtXZcrfCt4C3tA= +github.com/jackc/pgx/v5 v5.5.2/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= @@ -62,8 +69,11 @@ github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6Ng github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ= @@ -78,35 +88,39 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3Ifn github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0= -go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/sdk/metric v1.22.0 h1:ARrRetm1HCVxq0cbnaZQlfwODYJHo3gFL8Z3tSmHBcI= +go.opentelemetry.io/otel/sdk/metric v1.22.0/go.mod h1:KjQGeMIDlBNEOo6HvjhxIec1p/69/kULDcp4gr0oLQQ= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= golang.design/x/reflect v0.0.0-20220504060917-02c43be63f3b h1:lkOPTy76R9NZ6FeDQWkDj3NsLtD8Csc9AAFYEl3kiME= golang.design/x/reflect v0.0.0-20220504060917-02c43be63f3b/go.mod h1:QXG482h3unP32W/YwIPOc+09bvY447B7T+iLjC/JPcA= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= diff --git a/examples/time/ftl.toml b/examples/go/time/ftl.toml similarity index 100% rename from examples/time/ftl.toml rename to examples/go/time/ftl.toml diff --git a/examples/go/time/go.mod b/examples/go/time/go.mod new file mode 100644 index 0000000000..d740551ce1 --- /dev/null +++ b/examples/go/time/go.mod @@ -0,0 +1,5 @@ +module ftl/time + +go 1.21.6 + +replace github.com/TBD54566975/ftl => ../../.. diff --git a/examples/time/go.sum b/examples/go/time/go.sum similarity index 100% rename from examples/time/go.sum rename to examples/go/time/go.sum diff --git a/examples/go/time/time.go b/examples/go/time/time.go new file mode 100644 index 0000000000..ed9ff61ed0 --- /dev/null +++ b/examples/go/time/time.go @@ -0,0 +1,19 @@ +//ftl:module time +package time + +import ( + "context" + "time" +) + +type TimeRequest struct{} +type TimeResponse struct { + Time time.Time +} + +// Time returns the current time. +// +//ftl:verb +func Time(ctx context.Context, req TimeRequest) (TimeResponse, error) { + return TimeResponse{Time: time.Now()}, nil +} diff --git a/examples/kotlin/ftl-module-ad/src/main/kotlin/ftl/ad/Ad.kt b/examples/kotlin/ftl-module-ad/src/main/kotlin/ftl/ad/Ad.kt index 3faf1406ce..5d2e7f44b5 100644 --- a/examples/kotlin/ftl-module-ad/src/main/kotlin/ftl/ad/Ad.kt +++ b/examples/kotlin/ftl-module-ad/src/main/kotlin/ftl/ad/Ad.kt @@ -2,8 +2,11 @@ package ftl.ad import com.google.gson.Gson 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.Method import xyz.block.ftl.Verb import java.util.* @@ -16,12 +19,14 @@ class AdModule { private val database: Map = loadDatabase() @Verb - @Ingress(Method.GET, "/get") - fun get(context: Context, req: AdRequest): AdResponse { - return when { - req.contextKeys != null -> AdResponse(ads = contextualAds(req.contextKeys)) - else -> AdResponse(ads = randomAds()) + @Ingress(Method.GET, "/get", HTTP) + fun get(context: Context, req: HttpRequest): HttpResponse { + val ads: List = when { + req.body.contextKeys != null -> contextualAds(req.body.contextKeys) + else -> randomAds() } + + return HttpResponse(status = 200, headers = emptyMap(), body = AdResponse(ads = ads)) } private fun contextualAds(contextKeys: List): List { 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 dc5d453725..081436ed59 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 @@ -4,6 +4,7 @@ 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 @@ -15,7 +16,6 @@ data class EchoResponse(val message: String) class Echo { @Throws(InvalidInput::class) @Verb - @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}.") diff --git a/examples/online-boutique/services/ad/ad.go b/examples/online-boutique/services/ad/ad.go index 63024c7803..5cbbfd573f 100644 --- a/examples/online-boutique/services/ad/ad.go +++ b/examples/online-boutique/services/ad/ad.go @@ -6,6 +6,8 @@ import ( _ "embed" "math/rand" + "ftl/builtin" + "github.com/TBD54566975/ftl/examples/online-boutique/common" "golang.org/x/exp/maps" ) @@ -34,15 +36,17 @@ type AdResponse struct { //ftl:verb //ftl:ingress GET /ad -func Get(ctx context.Context, req AdRequest) (AdResponse, error) { - resp := AdResponse{} - if len(req.ContextKeys) > 0 { - resp.Ads = contextualAds(req.ContextKeys) - } - if len(resp.Ads) == 0 { - resp.Ads = randomAds() +func Get(ctx context.Context, req builtin.HttpRequest[AdRequest]) (builtin.HttpResponse[AdResponse], error) { + var ads []Ad + if len(req.Body.ContextKeys) > 0 { + ads = contextualAds(req.Body.ContextKeys) + } else { + ads = randomAds() } - return resp, nil + + return builtin.HttpResponse[AdResponse]{ + Body: AdResponse{Name: "ad", Ads: ads}, + }, nil } func contextualAds(contextKeys []string) (ads []Ad) { diff --git a/examples/online-boutique/services/cart/cart.go b/examples/online-boutique/services/cart/cart.go index f73cd9aada..610a712003 100644 --- a/examples/online-boutique/services/cart/cart.go +++ b/examples/online-boutique/services/cart/cart.go @@ -3,6 +3,7 @@ package cart import ( "context" + "ftl/builtin" ) var store = NewStore() @@ -26,9 +27,11 @@ type Cart struct { //ftl:verb //ftl:ingress POST /cart/add -func AddItem(ctx context.Context, req AddItemRequest) (AddItemResponse, error) { - store.Add(req.UserID, req.Item) - return AddItemResponse{}, nil +func AddItem(ctx context.Context, req builtin.HttpRequest[AddItemRequest]) (builtin.HttpResponse[AddItemResponse], error) { + store.Add(req.Body.UserID, req.Body.Item) + return builtin.HttpResponse[AddItemResponse]{ + Body: AddItemResponse{}, + }, nil } type GetCartRequest struct { @@ -37,8 +40,10 @@ type GetCartRequest struct { //ftl:verb //ftl:ingress GET /cart -func GetCart(ctx context.Context, req GetCartRequest) (Cart, error) { - return Cart{Items: store.Get(req.UserID), UserID: req.UserID}, nil +func GetCart(ctx context.Context, req builtin.HttpRequest[GetCartRequest]) (builtin.HttpResponse[Cart], error) { + return builtin.HttpResponse[Cart]{ + Body: Cart{Items: store.Get(req.Body.UserID), UserID: req.Body.UserID}, + }, nil } type EmptyCartRequest struct { @@ -49,7 +54,9 @@ type EmptyCartResponse struct{} //ftl:verb //ftl:ingress POST /cart/empty -func EmptyCart(ctx context.Context, req EmptyCartRequest) (EmptyCartResponse, error) { - store.Empty(req.UserID) - return EmptyCartResponse{}, nil +func EmptyCart(ctx context.Context, req builtin.HttpRequest[EmptyCartRequest]) (builtin.HttpResponse[EmptyCartResponse], error) { + store.Empty(req.Body.UserID) + return builtin.HttpResponse[EmptyCartResponse]{ + Body: EmptyCartResponse{}, + }, nil } diff --git a/examples/online-boutique/services/checkout/checkout.go b/examples/online-boutique/services/checkout/checkout.go index fd02049533..303a922f46 100644 --- a/examples/online-boutique/services/checkout/checkout.go +++ b/examples/online-boutique/services/checkout/checkout.go @@ -7,6 +7,7 @@ import ( "github.com/google/uuid" + "ftl/builtin" "ftl/cart" "ftl/currency" "ftl/payment" @@ -15,6 +16,7 @@ import ( "github.com/TBD54566975/ftl/backend/common/slices" "github.com/TBD54566975/ftl/examples/online-boutique/common/money" + ftl "github.com/TBD54566975/ftl/go-runtime/sdk" ) @@ -42,78 +44,88 @@ type Order struct { //ftl:verb //ftl:ingress POST /checkout/{userID} -func PlaceOrder(ctx context.Context, req PlaceOrderRequest) (Order, error) { - cartItems, err := ftl.Call(ctx, cart.GetCart, cart.GetCartRequest{UserID: req.UserID}) +func PlaceOrder(ctx context.Context, req builtin.HttpRequest[PlaceOrderRequest]) (builtin.HttpResponse[Order], error) { + cartItems, err := ftl.Call(ctx, cart.GetCart, builtin.HttpRequest[cart.GetCartRequest]{Body: cart.GetCartRequest{UserID: req.Body.UserID}}) if err != nil { - return Order{}, fmt.Errorf("failed to get cart: %w", err) + return builtin.HttpResponse[Order]{}, fmt.Errorf("failed to get cart: %w", err) } - orders := make([]OrderItem, len(cartItems.Items)) - for i, item := range cartItems.Items { - product, err := ftl.Call(ctx, productcatalog.Get, productcatalog.GetRequest{Id: item.ProductID}) + orders := make([]OrderItem, len(cartItems.Body.Items)) + for i, item := range cartItems.Body.Items { + products, err := ftl.Call(ctx, productcatalog.Get, builtin.HttpRequest[productcatalog.GetRequest]{Body: productcatalog.GetRequest{Id: item.ProductID}}) if err != nil { - return Order{}, fmt.Errorf("failed to get product #%q: %w", item.ProductID, err) + return builtin.HttpResponse[Order]{}, fmt.Errorf("failed to get product #%q: %w", item.ProductID, err) } - price, err := ftl.Call(ctx, currency.Convert, currency.ConvertRequest{ - From: product.PriceUSD, - ToCode: req.UserCurrency, + price, err := ftl.Call(ctx, currency.Convert, builtin.HttpRequest[currency.ConvertRequest]{ + Body: currency.ConvertRequest{ + From: products.Body.PriceUSD, + ToCode: req.Body.UserCurrency, + }, }) if err != nil { - return Order{}, fmt.Errorf("failed to convert price of %q to %s: %w", item.ProductID, req.UserCurrency, err) + return builtin.HttpResponse[Order]{}, fmt.Errorf("failed to convert price of %q to %s: %w", item.ProductID, req.Body.UserCurrency, err) } orders[i] = OrderItem{ Item: item, - Cost: price, + Cost: price.Body, } } - shippingUSD, err := ftl.Call(ctx, shipping.GetQuote, shipping.ShippingRequest{ - Address: req.Address, - Items: slices.Map(orders, func(i OrderItem) cart.Item { return i.Item }), + shippingUSD, err := ftl.Call(ctx, shipping.GetQuote, builtin.HttpRequest[shipping.ShippingRequest]{ + Body: shipping.ShippingRequest{ + Address: req.Body.Address, + Items: slices.Map(orders, func(i OrderItem) cart.Item { return i.Item }), + }, }) if err != nil { - return Order{}, fmt.Errorf("failed to get shipping quote: %w", err) + return builtin.HttpResponse[Order]{}, fmt.Errorf("failed to get shipping quote: %w", err) } - shippingPrice, err := ftl.Call(ctx, currency.Convert, currency.ConvertRequest{ - From: shippingUSD, - ToCode: req.UserCurrency, + shippingPrice, err := ftl.Call(ctx, currency.Convert, builtin.HttpRequest[currency.ConvertRequest]{ + Body: currency.ConvertRequest{ + From: shippingUSD.Body, + ToCode: req.Body.UserCurrency, + }, }) if err != nil { - return Order{}, fmt.Errorf("failed to convert shipping cost to currency: %w", err) + return builtin.HttpResponse[Order]{}, fmt.Errorf("failed to convert shipping cost to currency: %w", err) } - total := currency.Money{CurrencyCode: req.UserCurrency} - total = money.Must(money.Sum(total, shippingPrice)) + total := currency.Money{CurrencyCode: req.Body.UserCurrency} + total = money.Must(money.Sum(total, shippingPrice.Body)) for _, it := range orders { multPrice := money.MultiplySlow(it.Cost, uint32(it.Item.Quantity)) total = money.Must(money.Sum(total, multPrice)) } - txID, err := ftl.Call(ctx, payment.Charge, payment.ChargeRequest{ - Amount: total, - CreditCard: req.CreditCard, + txID, err := ftl.Call(ctx, payment.Charge, builtin.HttpRequest[payment.ChargeRequest]{ + Body: payment.ChargeRequest{ + Amount: total, + CreditCard: req.Body.CreditCard, + }, }) if err != nil { - return Order{}, fmt.Errorf("failed to charge card: %w", err) + return builtin.HttpResponse[Order]{}, fmt.Errorf("failed to charge card: %w", err) } - fmt.Printf("Charged card, ID %s", txID.TransactionID) + fmt.Printf("Charged card, ID %s", txID.Body.TransactionID) - shippingTrackingID, err := ftl.Call(ctx, shipping.ShipOrder, shipping.ShippingRequest{ - Address: req.Address, - Items: cartItems.Items, + shippingTrackingID, err := ftl.Call(ctx, shipping.ShipOrder, builtin.HttpRequest[shipping.ShippingRequest]{ + Body: shipping.ShippingRequest{ + Address: req.Body.Address, + Items: cartItems.Body.Items, + }, }) if err != nil { - return Order{}, fmt.Errorf("shipping error: %w", err) + return builtin.HttpResponse[Order]{}, fmt.Errorf("shipping error: %w", err) } - fmt.Printf("Shipped order, tracking ID %s", shippingTrackingID.Id) + fmt.Printf("Shipped order, tracking ID %s", shippingTrackingID.Body.Id) // Empty the cart, but don't worry about errors. - _, _ = ftl.Call(ctx, cart.EmptyCart, cart.EmptyCartRequest{UserID: req.UserID}) + _, _ = ftl.Call(ctx, cart.EmptyCart, builtin.HttpRequest[cart.EmptyCartRequest]{Body: cart.EmptyCartRequest{UserID: req.Body.UserID}}) order := Order{ ID: uuid.New().String(), - ShippingTrackingID: shippingTrackingID.Id, - ShippingCost: shippingPrice, - ShippingAddress: req.Address, + ShippingTrackingID: shippingTrackingID.Body.Id, + ShippingCost: shippingPrice.Body, + ShippingAddress: req.Body.Address, Items: orders, } // if err := s.emailService.Get().SendOrderConfirmation(ctx, req.Email, order); err != nil { @@ -122,5 +134,5 @@ func PlaceOrder(ctx context.Context, req PlaceOrderRequest) (Order, error) { // s.Logger(ctx).Info("order confirmation email sent", "email", req.Email) // } - return order, nil + return builtin.HttpResponse[Order]{Body: order}, nil } diff --git a/examples/online-boutique/services/currency/currency.go b/examples/online-boutique/services/currency/currency.go index 5e1530317e..af282020c9 100644 --- a/examples/online-boutique/services/currency/currency.go +++ b/examples/online-boutique/services/currency/currency.go @@ -7,6 +7,8 @@ import ( "fmt" "math" + "ftl/builtin" + "golang.org/x/exp/maps" "github.com/TBD54566975/ftl/examples/online-boutique/common" @@ -45,8 +47,10 @@ type GetSupportedCurrenciesResponse struct { //ftl:verb //ftl:ingress GET /currency/supported -func GetSupportedCurrencies(ctx context.Context, req GetSupportedCurrenciesRequest) (GetSupportedCurrenciesResponse, error) { - return GetSupportedCurrenciesResponse{CurrencyCodes: maps.Keys(database)}, nil +func GetSupportedCurrencies(ctx context.Context, req builtin.HttpRequest[GetSupportedCurrenciesRequest]) (builtin.HttpResponse[GetSupportedCurrenciesResponse], error) { + return builtin.HttpResponse[GetSupportedCurrenciesResponse]{ + Body: GetSupportedCurrenciesResponse{CurrencyCodes: maps.Keys(database)}, + }, nil } type ConvertRequest struct { @@ -56,20 +60,21 @@ type ConvertRequest struct { //ftl:verb //ftl:ingress POST /currency/convert -func Convert(ctx context.Context, req ConvertRequest) (Money, error) { - from := req.From +func Convert(ctx context.Context, req builtin.HttpRequest[ConvertRequest]) (builtin.HttpResponse[Money], error) { + from := req.Body.From fromRate, ok := database[from.CurrencyCode] if !ok { - return Money{}, fmt.Errorf("unknown origin currency %q", req.From.CurrencyCode) + return builtin.HttpResponse[Money]{}, fmt.Errorf("unknown origin currency %q", req.Body.From.CurrencyCode) } - toRate, ok := database[req.ToCode] + toRate, ok := database[req.Body.ToCode] if !ok { - return Money{}, fmt.Errorf("unknown destination currency %q", req.ToCode) + return builtin.HttpResponse[Money]{}, fmt.Errorf("unknown destination currency %q", req.Body.ToCode) } euros := carry(float64(from.Units)/fromRate, float64(from.Nanos)/fromRate) to := carry(float64(euros.Units)*toRate, float64(euros.Nanos)*toRate) - to.CurrencyCode = req.ToCode - return to, nil + to.CurrencyCode = req.Body.ToCode + + return builtin.HttpResponse[Money]{Body: to}, nil } // carry is a helper function that handles decimal/fractional carrying. diff --git a/examples/online-boutique/services/payment/payment.go b/examples/online-boutique/services/payment/payment.go index 588293dbcc..07367031d7 100644 --- a/examples/online-boutique/services/payment/payment.go +++ b/examples/online-boutique/services/payment/payment.go @@ -9,6 +9,7 @@ import ( "github.com/google/uuid" + "ftl/builtin" "ftl/currency" ) @@ -53,25 +54,25 @@ type ChargeResponse struct { //ftl:verb //ftl:ingress POST /payment/charge -func Charge(ctx context.Context, req ChargeRequest) (ChargeResponse, error) { - card := req.CreditCard +func Charge(ctx context.Context, req builtin.HttpRequest[ChargeRequest]) (builtin.HttpResponse[ChargeResponse], error) { + card := req.Body.CreditCard number := strings.ReplaceAll(card.Number, "-", "") var company string switch { case len(number) < 4: - return ChargeResponse{}, InvalidCreditCardErr{} + return builtin.HttpResponse[ChargeResponse]{Body: ChargeResponse{}}, InvalidCreditCardErr{} case number[0] == '4': company = "Visa" case number[0] == '5': company = "MasterCard" default: - return ChargeResponse{}, InvalidCreditCardErr{} + return builtin.HttpResponse[ChargeResponse]{Body: ChargeResponse{}}, InvalidCreditCardErr{} } if card.CVV < 100 || card.CVV > 9999 { - return ChargeResponse{}, InvalidCreditCardErr{} + return builtin.HttpResponse[ChargeResponse]{Body: ChargeResponse{}}, InvalidCreditCardErr{} } if time.Date(card.ExpirationYear, card.ExpirationMonth, 0, 0, 0, 0, 0, time.Local).Before(time.Now()) { - return ChargeResponse{}, ExpiredCreditCardErr{} + return builtin.HttpResponse[ChargeResponse]{Body: ChargeResponse{}}, InvalidCreditCardErr{} } // Card is valid: process the transaction. @@ -79,8 +80,10 @@ func Charge(ctx context.Context, req ChargeRequest) (ChargeResponse, error) { "Transaction processed", "company", company, "last_four", number[len(number)-4:], - "currency", req.Amount.CurrencyCode, - "amount", fmt.Sprintf("%d.%d", req.Amount.Units, req.Amount.Nanos), + "currency", req.Body.Amount.CurrencyCode, + "amount", fmt.Sprintf("%d.%d", req.Body.Amount.Units, req.Body.Amount.Nanos), ) - return ChargeResponse{TransactionID: uuid.New().String()}, nil + return builtin.HttpResponse[ChargeResponse]{ + Body: ChargeResponse{TransactionID: uuid.New().String()}, + }, nil } diff --git a/examples/online-boutique/services/productcatalog/productcatalog.go b/examples/online-boutique/services/productcatalog/productcatalog.go index dfc9af0d28..e0c8727d55 100644 --- a/examples/online-boutique/services/productcatalog/productcatalog.go +++ b/examples/online-boutique/services/productcatalog/productcatalog.go @@ -7,6 +7,7 @@ import ( "fmt" "strings" + "ftl/builtin" "ftl/currency" "github.com/TBD54566975/ftl/examples/online-boutique/common" @@ -38,8 +39,10 @@ type ListResponse struct { //ftl:verb //ftl:ingress GET /productcatalog -func List(ctx context.Context, req ListRequest) (ListResponse, error) { - return ListResponse{Products: database}, nil +func List(ctx context.Context, req builtin.HttpRequest[ListRequest]) (builtin.HttpResponse[ListResponse], error) { + return builtin.HttpResponse[ListResponse]{ + Body: ListResponse{Products: database}, + }, nil } type GetRequest struct { @@ -48,13 +51,13 @@ type GetRequest struct { //ftl:verb //ftl:ingress GET /productcatalog/{id} -func Get(ctx context.Context, req GetRequest) (Product, error) { +func Get(ctx context.Context, req builtin.HttpRequest[GetRequest]) (builtin.HttpResponse[Product], error) { for _, p := range database { - if p.ID == req.ID { - return p, nil + if p.ID == req.Body.ID { + return builtin.HttpResponse[Product]{Body: p}, nil } } - return Product{}, fmt.Errorf("product not found: %q", req.ID) + return builtin.HttpResponse[Product]{Body: Product{}}, fmt.Errorf("product not found: %q", req.Body.ID) } type SearchRequest struct { diff --git a/examples/online-boutique/services/recommendation/recommendation.go b/examples/online-boutique/services/recommendation/recommendation.go index ab3b1a5db5..fa5264a074 100644 --- a/examples/online-boutique/services/recommendation/recommendation.go +++ b/examples/online-boutique/services/recommendation/recommendation.go @@ -6,6 +6,7 @@ import ( "fmt" "math/rand" + "ftl/builtin" "ftl/productcatalog" ftl "github.com/TBD54566975/ftl/go-runtime/sdk" @@ -22,21 +23,20 @@ type ListResponse struct { //ftl:verb //ftl:ingress GET /recommendation -func List(ctx context.Context, req ListRequest) (ListResponse, error) { - - catalog, err := ftl.Call(ctx, productcatalog.List, productcatalog.ListRequest{}) +func List(ctx context.Context, req builtin.HttpRequest[ListRequest]) (builtin.HttpResponse[ListResponse], error) { + cresp, err := ftl.Call(ctx, productcatalog.List, builtin.HttpRequest[productcatalog.ListRequest]{}) if err != nil { - return ListResponse{}, fmt.Errorf("%s: %w", "failed to retrieve product catalog", err) + return builtin.HttpResponse[ListResponse]{Body: ListResponse{}}, fmt.Errorf("%s: %w", "failed to retrieve product catalog", err) } // Remove user-provided products from the catalog, to avoid recommending // them. - userIDs := make(map[string]struct{}, len(req.UserProductIDs)) - for _, id := range req.UserProductIDs { + userIDs := make(map[string]struct{}, len(req.Body.UserProductIDs)) + for _, id := range req.Body.UserProductIDs { userIDs[id] = struct{}{} } - filtered := make([]string, 0, len(catalog.Products)) - for _, product := range catalog.Products { + filtered := make([]string, 0, len(cresp.Body.Products)) + for _, product := range cresp.Body.Products { if _, ok := userIDs[product.Id]; ok { continue } @@ -53,6 +53,6 @@ func List(ctx context.Context, req ListRequest) (ListResponse, error) { break } } - return ListResponse{ProductIDs: ret}, nil + return builtin.HttpResponse[ListResponse]{Body: ListResponse{ProductIDs: ret}}, nil } diff --git a/examples/online-boutique/services/shipping/shipping.go b/examples/online-boutique/services/shipping/shipping.go index 5f7e178e64..455ac21f69 100644 --- a/examples/online-boutique/services/shipping/shipping.go +++ b/examples/online-boutique/services/shipping/shipping.go @@ -6,6 +6,7 @@ import ( "fmt" "math" + "ftl/builtin" "ftl/cart" "ftl/currency" ) @@ -25,8 +26,8 @@ type ShippingRequest struct { //ftl:verb //ftl:ingress POST /shipping/quote -func GetQuote(ctx context.Context, req ShippingRequest) (currency.Money, error) { - return moneyFromUSD(8.99), nil +func GetQuote(ctx context.Context, req builtin.HttpRequest[ShippingRequest]) (builtin.HttpResponse[currency.Money], error) { + return builtin.HttpResponse[currency.Money]{Body: moneyFromUSD(8.99)}, nil } type ShipOrderResponse struct { @@ -35,9 +36,9 @@ type ShipOrderResponse struct { //ftl:verb //ftl:ingress POST /shipping/ship -func ShipOrder(ctx context.Context, req ShippingRequest) (ShipOrderResponse, error) { - baseAddress := fmt.Sprintf("%s, %s, %s", req.Address.StreetAddress, req.Address.City, req.Address.State) - return ShipOrderResponse{ID: createTrackingID(baseAddress)}, nil +func ShipOrder(ctx context.Context, req builtin.HttpRequest[ShippingRequest]) (builtin.HttpResponse[ShipOrderResponse], error) { + baseAddress := fmt.Sprintf("%s, %s, %s", req.Body.Address.StreetAddress, req.Body.Address.City, req.Body.Address.State) + return builtin.HttpResponse[ShipOrderResponse]{Body: ShipOrderResponse{ID: createTrackingID(baseAddress)}}, nil } func moneyFromUSD(value float64) currency.Money { diff --git a/examples/time/go.mod b/examples/time/go.mod deleted file mode 100644 index 80d2bfe3ac..0000000000 --- a/examples/time/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module ftl/time - -go 1.21.6 - -replace github.com/TBD54566975/ftl => ../.. diff --git a/examples/time/time.go b/examples/time/time.go deleted file mode 100644 index edc36254d7..0000000000 --- a/examples/time/time.go +++ /dev/null @@ -1,26 +0,0 @@ -//ftl:module time -package time - -import ( - "context" - "time" - - "ftl/builtin" -) - -type TimeRequest struct{} -type TimeResponse struct { - Time time.Time `json:"time"` -} - -// Time returns the current time. -// -//ftl:verb -//ftl:ingress GET /time -func Time(ctx context.Context, req builtin.HttpRequest[TimeRequest]) (builtin.HttpResponse[TimeResponse], error) { - return builtin.HttpResponse[TimeResponse]{ - Status: 200, - Headers: map[string][]string{"Get": {"Header from FTL"}}, - Body: TimeResponse{Time: time.Now()}, - }, nil -} diff --git a/go-runtime/compile/build.go b/go-runtime/compile/build.go index 2cb79c6e2a..170b178053 100644 --- a/go-runtime/compile/build.go +++ b/go-runtime/compile/build.go @@ -145,6 +145,16 @@ var scaffoldFuncs = scaffolder.FuncMap{ } imports[path.Join("ftl", n.Module)] = "ftl" + n.Module + for _, tp := range n.TypeParameters { + tpRef, err := schema.ParseDataRef(tp.String()) + if err != nil { + return err + } + if tpRef.Module != "" && tpRef.Module != m.Name { + imports[path.Join("ftl", tpRef.Module)] = "ftl" + tpRef.Module + } + } + case *schema.Time: imports["time"] = "stdtime"