Skip to content

Commit

Permalink
feat: add support for a "Bytes" type (#743)
Browse files Browse the repository at this point in the history
In Go this is []byte, in Kotlin it's ByteArray.

This is a prerequisite for supporting raw HTTP request/response ingress
routes.

PS. Also did some linter fixes forced on me.
  • Loading branch information
alecthomas authored Jan 8, 2024
1 parent da5c653 commit 76d835c
Show file tree
Hide file tree
Showing 22 changed files with 627 additions and 432 deletions.
3 changes: 1 addition & 2 deletions backend/common/bind/bind_allocator.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package bind

import (
"fmt"
"net"
"net/url"
"strconv"
Expand Down Expand Up @@ -44,7 +43,7 @@ func (b *BindAllocator) Next() *url.URL {
_ = l.Close()

newURL := *b.baseURL
newURL.Host = net.JoinHostPort(b.baseURL.Hostname(), fmt.Sprintf("%d", b.port.Load()))
newURL.Host = net.JoinHostPort(b.baseURL.Hostname(), strconv.Itoa(int(b.port.Load())))
return &newURL
}
}
2 changes: 1 addition & 1 deletion backend/common/plugin/spawn.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const pluginRetryDelay = time.Millisecond * 100

// PingableClient is a gRPC client that can be pinged.
type PingableClient interface {
Ping(context.Context, *connect.Request[ftlv1.PingRequest]) (*connect.Response[ftlv1.PingResponse], error)
Ping(ctx context.Context, req *connect.Request[ftlv1.PingRequest]) (*connect.Response[ftlv1.PingResponse], error)
}

type pluginOptions struct {
Expand Down
2 changes: 1 addition & 1 deletion backend/common/rpc/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ var (
)

type Pingable interface {
Ping(context.Context, *connect.Request[ftlv1.PingRequest]) (*connect.Response[ftlv1.PingResponse], error)
Ping(ctx context.Context, req *connect.Request[ftlv1.PingRequest]) (*connect.Response[ftlv1.PingResponse], error)
}

// GetHTTPClient returns a HTTP client usable for the given URL.
Expand Down
6 changes: 6 additions & 0 deletions backend/schema/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ func (*String) schemaChildren() []Node { return nil }
func (*String) schemaType() {}
func (*String) String() string { return "String" }

var _ Type = (*Bytes)(nil)

func (*Bytes) schemaChildren() []Node { return nil }
func (*Bytes) schemaType() {}
func (*Bytes) String() string { return "Bytes" }

var _ Type = (*Bool)(nil)

func (*Bool) schemaChildren() []Node { return nil }
Expand Down
10 changes: 10 additions & 0 deletions backend/schema/jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ func nodeToJSSchema(node Node, dataRefs map[DataRef]bool) *jsonschema.Schema {
st := jsonschema.String
return &jsonschema.Schema{Type: &jsonschema.Type{SimpleTypes: &st}}

case *Bytes:
st := jsonschema.String
encoding := "base64"
mediaType := "application/octet-stream"
return &jsonschema.Schema{
Type: &jsonschema.Type{SimpleTypes: &st},
ContentEncoding: &encoding,
ContentMediaType: &mediaType,
}

case *Bool:
st := jsonschema.Boolean
return &jsonschema.Schema{Type: &jsonschema.Type{SimpleTypes: &st}}
Expand Down
4 changes: 4 additions & 0 deletions backend/schema/normalise.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func Normalise[T Node](n T) T {
c.Str = false
c.Pos = zero

case *Bytes:
c.Bytes = false
c.Pos = zero

case *Verb:
c.Pos = zero
c.Request = Normalise(c.Request)
Expand Down
2 changes: 2 additions & 0 deletions backend/schema/protobuf_dec.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ func typeToSchema(s *schemapb.Type) Type {
return &Float{Pos: PosFromProto(s.Float.Pos)}
case *schemapb.Type_String_:
return &String{Pos: PosFromProto(s.String_.Pos)}
case *schemapb.Type_Bytes:
return &Bytes{Pos: PosFromProto(s.Bytes.Pos)}
case *schemapb.Type_Time:
return &Time{Pos: PosFromProto(s.Time.Pos)}
case *schemapb.Type_Bool:
Expand Down
7 changes: 7 additions & 0 deletions backend/schema/protobuf_enc.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ func (s *String) ToProto() proto.Message {
return &schemapb.String{}
}

func (s *Bytes) ToProto() proto.Message {
return &schemapb.Bytes{}
}

func (b *Bool) ToProto() proto.Message {
return &schemapb.Bool{}
}
Expand Down Expand Up @@ -211,6 +215,9 @@ func typeToProto(t Type) *schemapb.Type {
case *String:
return &schemapb.Type{Value: &schemapb.Type_String_{String_: t.ToProto().(*schemapb.String)}}

case *Bytes:
return &schemapb.Type{Value: &schemapb.Type_Bytes{Bytes: t.ToProto().(*schemapb.Bytes)}}

case *Time:
return &schemapb.Type{Value: &schemapb.Type_Time{Time: t.ToProto().(*schemapb.Time)}}

Expand Down
8 changes: 7 additions & 1 deletion backend/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

var (
declUnion = []Decl{&Data{}, &Verb{}}
nonOptionalTypeUnion = []Type{&Int{}, &Float{}, &String{}, &Bool{}, &Time{}, &Array{}, &Map{} /*&VerbRef{},*/, &DataRef{}}
nonOptionalTypeUnion = []Type{&Int{}, &Float{}, &String{}, &Bytes{}, &Bool{}, &Time{}, &Array{}, &Map{} /*&VerbRef{},*/, &DataRef{}}
typeUnion = append(nonOptionalTypeUnion, &Optional{})
metadataUnion = []Metadata{&MetadataCalls{}, &MetadataIngress{}}
ingressUnion = []IngressPathComponent{&IngressPathLiteral{}, &IngressPathParameter{}}
Expand Down Expand Up @@ -87,6 +87,12 @@ type String struct {
Str bool `parser:"@'String'" protobuf:"-"`
}

type Bytes struct {
Pos Position `parser:"" protobuf:"1,optional"`

Bytes bool `parser:"@'Bytes'" protobuf:"-"`
}

type Bool struct {
Pos Position `parser:"" protobuf:"1,optional"`

Expand Down
6 changes: 5 additions & 1 deletion backend/schema/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ import (

var (
// Identifiers that can't be used as data or verb names.
//
// We don't need Array/Map/VerbRef/DataRef here because there are no
// keywords associated with these types.
reservedIdentNames = map[string]bool{
"Int": true, "Float": true, "String": true, "Bool": true, "Time": true,
"Bytes": true,
}
)

Expand Down Expand Up @@ -128,7 +132,7 @@ func ValidateModule(module *Module) error {
}

case *Array, *Bool, *DataRef, *Field, *Float, *Int,
*Time, *Map, *Module, *Schema, *String, *VerbRef,
*Time, *Map, *Module, *Schema, *String, *Bytes, *VerbRef,
*MetadataCalls, *MetadataIngress, IngressPathComponent,
*IngressPathLiteral, *IngressPathParameter, *Optional:
case Type, Metadata, Decl: // Union sql.
Expand Down
3 changes: 2 additions & 1 deletion cmd/ftl/cmd_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net/url"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -102,7 +103,7 @@ func (s *serveCmd) setupDB(ctx context.Context) (string, error) {
}

recreate := s.Recreate
port := fmt.Sprintf("%d", s.DBPort)
port := strconv.Itoa(s.DBPort)

if len(output) == 0 {
logger.Infof("Creating docker container '%s' for postgres db", ftlContainerName)
Expand Down
68 changes: 56 additions & 12 deletions frontend/src/protos/xyz/block/ftl/v1/schema/schema_pb.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions go-runtime/compile/generate/external_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ func genType(module *schema.Module, t schema.Type) string {
case *schema.Time:
return "time.Time"

case *schema.Bytes:
return "[]byte"

case *schema.Int, *schema.Bool, *schema.String:
return strings.ToLower(t.String())

Expand Down
6 changes: 5 additions & 1 deletion go-runtime/compile/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,11 @@ func parseMap(pctx *parseContext, node ast.Node, tnode *types.Map) (*schema.Map,
}, nil
}

func parseSlice(pctx *parseContext, node ast.Node, tnode *types.Slice) (*schema.Array, error) {
func parseSlice(pctx *parseContext, node ast.Node, tnode *types.Slice) (schema.Type, error) {
// If it's a []byte, treat it as a Bytes type.
if basic, ok := tnode.Elem().Underlying().(*types.Basic); ok && basic.Kind() == types.Byte {
return &schema.Bytes{Pos: goPosToSchemaPos(node.Pos())}, nil
}
value, err := parseType(pctx, node, tnode.Elem())
if err != nil {
return nil, err
Expand Down
1 change: 1 addition & 0 deletions go-runtime/compile/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func TestExtractModuleSchema(t *testing.T) {
optional Nested?
time Time
user two.User
bytes Bytes
}
data Resp {
Expand Down
1 change: 1 addition & 0 deletions go-runtime/compile/testdata/one/one.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Req struct {
Optional sdk.Option[Nested]
Time time.Time
User two.User
Bytes []byte
}
type Resp struct{}

Expand Down
4 changes: 4 additions & 0 deletions kotlin-runtime/ftl-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<!-- Download the Wire compiler. -->
<plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class ModuleGenerator() {
type.int != null -> ClassName("kotlin", "Long")
type.float != null -> ClassName("kotlin", "Float")
type.string != null -> ClassName("kotlin", "String")
type.bytes != null -> ClassName("kotlin", "ByteArray")
type.bool != null -> ClassName("kotlin", "Boolean")
type.time != null -> ClassName("java.time", "OffsetDateTime")
type.array != null -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class TestModule()
Field(name = "int", type = Type(int = Int())),
Field(name = "float", type = Type(float = Float())),
Field(name = "string", type = Type(string = String())),
Field(name = "bytes", type = Type(bytes = Bytes())),
Field(name = "bool", type = Type(bool = Bool())),
Field(name = "time", type = Type(time = Time())),
Field(name = "optional", type = Type(optional = Optional(type = Type(string = String())))),
Expand Down Expand Up @@ -84,6 +85,7 @@ package ftl.test
import java.time.OffsetDateTime
import kotlin.Boolean
import kotlin.ByteArray
import kotlin.Float
import kotlin.Long
import kotlin.String
Expand All @@ -106,6 +108,7 @@ public data class TestResponse(
public val int: Long,
public val float: Float,
public val string: String,
public val bytes: ByteArray,
public val bool: Boolean,
public val time: OffsetDateTime,
public val optional: String? = null,
Expand Down Expand Up @@ -144,9 +147,11 @@ public class TestModule()
request = DataRef(name = "TestRequest"),
response = DataRef(name = "TestResponse"),
metadata = listOf(
Metadata(ingress = MetadataIngress(
path = listOf(IngressPathComponent(ingressPathLiteral = IngressPathLiteral(text = "test"))),
method = "GET")
Metadata(
ingress = MetadataIngress(
path = listOf(IngressPathComponent(ingressPathLiteral = IngressPathLiteral(text = "test"))),
method = "GET"
)
),
)
)
Expand Down
Loading

0 comments on commit 76d835c

Please sign in to comment.