diff --git a/common/schema/metadataingress.go b/common/schema/metadataingress.go index c8ec235786..8e78889d56 100644 --- a/common/schema/metadataingress.go +++ b/common/schema/metadataingress.go @@ -3,6 +3,8 @@ package schema import ( "fmt" "strings" + + "github.com/block/ftl/common/slices" ) //protobuf:2 @@ -11,7 +13,7 @@ type MetadataIngress struct { Type string `parser:"'+' 'ingress' @('http')?" protobuf:"2"` Method string `parser:"@('GET' | 'POST' | 'PUT' | 'DELETE')" protobuf:"3"` - Path []IngressPathComponent `parser:"('/' @@)+" protobuf:"4"` + Path []IngressPathComponent `parser:"'/' (@@ ('/' @@)*)?" protobuf:"4"` } var _ Metadata = (*MetadataIngress)(nil) @@ -25,15 +27,7 @@ func (m *MetadataIngress) String() string { // // For example, /foo/{bar} func (m *MetadataIngress) PathString() string { - path := make([]string, len(m.Path)) - for i, p := range m.Path { - switch v := p.(type) { - case *IngressPathLiteral: - path[i] = v.Text - case *IngressPathParameter: - path[i] = fmt.Sprintf("{%s}", v.Name) - } - } + path := slices.Map(m.Path, func(c IngressPathComponent) string { return c.String() }) return "/" + strings.Join(path, "/") } @@ -76,6 +70,6 @@ type IngressPathParameter struct { var _ IngressPathComponent = (*IngressPathParameter)(nil) func (l *IngressPathParameter) Position() Position { return l.Pos } -func (l *IngressPathParameter) String() string { return l.Name } +func (l *IngressPathParameter) String() string { return "{" + l.Name + "}" } func (*IngressPathParameter) schemaChildren() []Node { return nil } func (*IngressPathParameter) schemaIngressPathComponent() {} diff --git a/common/schema/schema_test.go b/common/schema/schema_test.go index 4fe9c9f81f..e80fea805b 100644 --- a/common/schema/schema_test.go +++ b/common/schema/schema_test.go @@ -314,6 +314,46 @@ func TestParsing(t *testing.T) { errors []string expected *Schema }{ + {name: "IngressRoot", + input: ` + realm test { + module test { + export verb index(builtin.HttpRequest) builtin.HttpResponse + +ingress http GET / + } + } + `, + expected: &Schema{ + Realms: []*Realm{{ + Name: "test", + Modules: []*Module{{ + Name: "test", + Decls: []Decl{ + &Verb{ + Visibility: VisibilityScopeModule, + Name: "index", + Request: &Ref{ + Module: "builtin", + Name: "HttpRequest", + TypeParameters: []Type{&Unit{}, &Unit{}, &Unit{}}, + }, + Response: &Ref{ + Module: "builtin", + Name: "HttpResponse", + TypeParameters: []Type{&Unit{}, &String{}}, + }, + Metadata: []Metadata{ + &MetadataIngress{ + Type: "http", + Method: "GET", + }, + }, + }, + }, + }}, + }}, + }, + }, {name: "Empty Realm", input: `realm foo { }`, expected: &Schema{Realms: []*Realm{{Name: "foo"}}}, @@ -843,7 +883,7 @@ realm foo { } else { assert.NoError(t, err) actual = Normalise(actual) - assert.NotZero(t, test.expected, "test.expected is nil") + assert.NotZero(t, test.expected, "test.expected is nil but should contain a schema") test.expected.Realms[0].Modules = append([]*Module{Builtins()}, test.expected.Realms[0].Modules...) assert.Equal(t, Normalise(test.expected), Normalise(actual), assert.OmitEmpty(), assert.Exclude[Position]()) }