Skip to content

Commit

Permalink
fix: extract ingress path to components (#726)
Browse files Browse the repository at this point in the history
  • Loading branch information
wesbillman authored Dec 12, 2023
1 parent f07100d commit 8141b2b
Show file tree
Hide file tree
Showing 24 changed files with 784 additions and 196 deletions.
6 changes: 5 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ deploy-time:

# Deploy the Kotlin echo module
deploy-echo-kotlin:
cd examples/echo-kotlin && mvn compile && ftl deploy target
cd examples/echo-kotlin && mvn compile && ftl deploy target

regen-schema:
bit protos/xyz/block/ftl/v1/schema/schema.proto
bit protos/xyz/block/ftl/v1/schema/schema.pb.go
15 changes: 14 additions & 1 deletion backend/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -1148,11 +1148,24 @@ func extractIngressRoutingEntries(req *ftlv1.CreateDeploymentRequest) []dal.Ingr
ingressRoutes = append(ingressRoutes, dal.IngressRoutingEntry{
Verb: verb.Verb.Name,
Method: ingress.Ingress.Method,
Path: ingress.Ingress.Path,
Path: ingressPathString(ingress.Ingress.Path),
})
}
}
}
}
return ingressRoutes
}

func ingressPathString(path []*schemapb.IngressPathComponent) string {
pathString := make([]string, len(path))
for i, p := range path {
switch p.Value.(type) {
case *schemapb.IngressPathComponent_IngressPathLiteral:
pathString[i] = p.GetIngressPathLiteral().Text
case *schemapb.IngressPathComponent_IngressPathParameter:
pathString[i] = fmt.Sprintf("{%s}", p.GetIngressPathParameter().Name)
}
}
return "/" + strings.Join(pathString, "/")
}
34 changes: 31 additions & 3 deletions backend/schema/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,39 @@ func (*MetadataCalls) schemaMetadata() {}
var _ Metadata = (*MetadataIngress)(nil)

func (m *MetadataIngress) String() string {
return fmt.Sprintf("ingress %s %s", strings.ToUpper(m.Method), m.Path)
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)
}
}
return fmt.Sprintf("ingress %s /%s", strings.ToUpper(m.Method), strings.Join(path, "/"))
}

func (m *MetadataIngress) schemaChildren() []Node { return nil }
func (*MetadataIngress) schemaMetadata() {}
func (m *MetadataIngress) schemaChildren() []Node {
out := make([]Node, 0, len(m.Path))
for _, ref := range m.Path {
out = append(out, ref)
}
return out
}

func (*MetadataIngress) schemaMetadata() {}

var _ IngressPathComponent = (*IngressPathLiteral)(nil)

func (l *IngressPathLiteral) String() string { return l.Text }
func (*IngressPathLiteral) schemaChildren() []Node { return nil }
func (*IngressPathLiteral) schemaIngressPathComponent() {}

var _ IngressPathComponent = (*IngressPathParameter)(nil)

func (l *IngressPathParameter) String() string { return l.Name }
func (*IngressPathParameter) schemaChildren() []Node { return nil }
func (*IngressPathParameter) schemaIngressPathComponent() {}

var _ Node = (*Module)(nil)

Expand Down
2 changes: 1 addition & 1 deletion backend/schema/jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func nodeToJSSchema(node Node, dataRefs map[DataRef]bool) *jsonschema.Schema {
{TypeObject: &jsonschema.Schema{Type: &jsonschema.Type{SimpleTypes: &null}}},
}}

case Decl, *Field, Metadata, *MetadataCalls, *MetadataIngress, *Module, *Schema, Type, *Verb, *VerbRef:
case Decl, *Field, Metadata, *MetadataCalls, *MetadataIngress, IngressPathComponent, *IngressPathLiteral, *IngressPathParameter, *Module, *Schema, Type, *Verb, *VerbRef:
panic(fmt.Sprintf("unsupported node type %T", node))

default:
Expand Down
9 changes: 8 additions & 1 deletion backend/schema/normalise.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,18 @@ func Normalise[T Node](n T) T {

case *MetadataIngress:
c.Pos = zero
c.Path = normaliseSlice(c.Path)

case *Optional:
c.Type = Normalise(c.Type)

case Decl, Metadata, Type: // Can never occur in reality, but here to satisfy the sum-type check.
case *IngressPathLiteral:
c.Pos = zero

case *IngressPathParameter:
c.Pos = zero

case Decl, Metadata, IngressPathComponent, Type: // Can never occur in reality, but here to satisfy the sum-type check.
panic("??")
}
return ni.(T) //nolint:forcetypeassert
Expand Down
22 changes: 21 additions & 1 deletion backend/schema/protobuf_dec.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func metadataToSchema(s *schemapb.Metadata) Metadata {
return &MetadataIngress{
Pos: PosFromProto(s.Ingress.Pos),
Method: s.Ingress.Method,
Path: s.Ingress.Path,
Path: ingressPathComponentListToSchema(s.Ingress.Path),
}

default:
Expand All @@ -212,3 +212,23 @@ func verbRefListToSchema(s []*schemapb.VerbRef) []*VerbRef {
}
return out
}

func ingressPathComponentListToSchema(s []*schemapb.IngressPathComponent) []IngressPathComponent {
var out []IngressPathComponent
for _, n := range s {
switch n := n.Value.(type) {
case *schemapb.IngressPathComponent_IngressPathLiteral:
out = append(out, &IngressPathLiteral{
Pos: PosFromProto(n.IngressPathLiteral.Pos),
Text: n.IngressPathLiteral.Text,
})
case *schemapb.IngressPathComponent_IngressPathParameter:
out = append(out, &IngressPathParameter{
Pos: PosFromProto(n.IngressPathParameter.Pos),
Name: n.IngressPathParameter.Name,
})
}
}

return out
}
26 changes: 25 additions & 1 deletion backend/schema/protobuf_enc.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ func metadataListToProto(nodes []Metadata) []*schemapb.Metadata {
return out
}

func ingressListToProto(nodes []IngressPathComponent) []*schemapb.IngressPathComponent {
var out []*schemapb.IngressPathComponent
for _, n := range nodes {
switch n := n.(type) {
case *IngressPathLiteral:
out = append(out, &schemapb.IngressPathComponent{Value: &schemapb.IngressPathComponent_IngressPathLiteral{IngressPathLiteral: n.ToProto().(*schemapb.IngressPathLiteral)}})
case *IngressPathParameter:
out = append(out, &schemapb.IngressPathComponent{Value: &schemapb.IngressPathComponent_IngressPathParameter{IngressPathParameter: n.ToProto().(*schemapb.IngressPathParameter)}})

default:
panic(fmt.Sprintf("unhandled ingress path component type %T", n))
}
}
return out
}

func (p Position) ToProto() proto.Message {
return &schemapb.Position{
Line: int64(p.Line),
Expand Down Expand Up @@ -131,10 +147,18 @@ func (m *MetadataIngress) ToProto() proto.Message {
return &schemapb.MetadataIngress{
Pos: m.Pos.ToProto().(*schemapb.Position),
Method: m.Method,
Path: m.Path,
Path: ingressListToProto(m.Path),
}
}

func (l *IngressPathLiteral) ToProto() proto.Message {
return &schemapb.IngressPathLiteral{Text: l.Text}
}

func (l *IngressPathParameter) ToProto() proto.Message {
return &schemapb.IngressPathParameter{Name: l.Name}
}

func (i *Int) ToProto() proto.Message {
return &schemapb.Int{}
}
Expand Down
38 changes: 24 additions & 14 deletions backend/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ var (
nonOptionalTypeUnion = []Type{&Int{}, &Float{}, &String{}, &Bool{}, &Time{}, &Array{}, &Map{} /*&VerbRef{},*/, &DataRef{}}
typeUnion = append(nonOptionalTypeUnion, &Optional{})
metadataUnion = []Metadata{&MetadataCalls{}, &MetadataIngress{}}
ingressUnion = []IngressPathComponent{&IngressPathLiteral{}, &IngressPathParameter{}}

// Used by protobuf generation.
unions = map[reflect.Type][]reflect.Type{
reflect.TypeOf((*Type)(nil)).Elem(): reflectUnion(typeUnion...),
reflect.TypeOf((*Metadata)(nil)).Elem(): reflectUnion(metadataUnion...),
reflect.TypeOf((*Decl)(nil)).Elem(): reflectUnion(declUnion...),
reflect.TypeOf((*Type)(nil)).Elem(): reflectUnion(typeUnion...),
reflect.TypeOf((*Metadata)(nil)).Elem(): reflectUnion(metadataUnion...),
reflect.TypeOf((*IngressPathComponent)(nil)).Elem(): reflectUnion(ingressUnion...),
reflect.TypeOf((*Decl)(nil)).Elem(): reflectUnion(declUnion...),
}
)

Expand Down Expand Up @@ -181,18 +183,25 @@ type MetadataCalls struct {
type MetadataIngress struct {
Pos Position `parser:"" protobuf:"1,optional"`

Method string `parser:"'ingress' @('GET' | 'POST')" protobuf:"2"`
Path string `parser:"@('/' @('{' | '}' | Ident)+)+" protobuf:"3"`
Method string `parser:"'ingress' @('GET' | 'POST')" protobuf:"2"`
Path []IngressPathComponent `parser:"('/' @@)+" protobuf:"3"`
}

func (m *MetadataIngress) Parameters() []string {
var params []string
for _, part := range strings.Split(m.Path, "/") {
if len(part) > 0 && part[0] == '{' && part[len(part)-1] == '}' {
params = append(params, part[1:len(part)-1])
}
}
return params
type IngressPathComponent interface {
Node
schemaIngressPathComponent()
}

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

Text string `parser:"@Ident" protobuf:"2"`
}

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

Name string `parser:"'{' @Ident '}'" protobuf:"2"`
}

type Module struct {
Expand Down Expand Up @@ -326,7 +335,7 @@ var (
{Name: "Comment", Pattern: `//.*`},
{Name: "String", Pattern: `"(?:\\.|[^"])*"`},
{Name: "Number", Pattern: `[0-9]+(?:\.[0-9]+)?`},
{Name: "Punct", Pattern: `[-:[\]{}<>()*+?.,\\^$|#]`},
{Name: "Punct", Pattern: `[/-:[\]{}<>()*+?.,\\^$|#]`},
})

commonParserOptions = []participle.Option{
Expand All @@ -338,6 +347,7 @@ var (
return token, nil
}, "Comment"),
participle.Union(metadataUnion...),
participle.Union(ingressUnion...),
participle.Union(declUnion...),
}

Expand Down
21 changes: 19 additions & 2 deletions backend/schema/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,22 @@ var schema = Normalise(&Schema{
&Verb{Name: "destroy",
Request: &DataRef{Module: "todo", Name: "DestroyRequest"},
Response: &DataRef{Module: "todo", Name: "DestroyResponse"},
Metadata: []Metadata{
&MetadataIngress{
Method: "GET",
Path: []IngressPathComponent{
&IngressPathLiteral{Text: "todo"},
&IngressPathLiteral{Text: "destroy"},
&IngressPathParameter{Name: "id"},
},
},
},
},
},
},
},
})
},
)

func TestIndent(t *testing.T) {
assert.Equal(t, " a\n b\n c", indent("a\nb\nc"))
Expand Down Expand Up @@ -84,7 +95,8 @@ module todo {
calls todo.destroy
verb destroy(todo.DestroyRequest) todo.DestroyResponse
verb destroy(todo.DestroyRequest) todo.DestroyResponse
ingress GET /todo/destroy/{id}
}
`
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(schema.String()))
Expand Down Expand Up @@ -134,6 +146,10 @@ Schema
Verb
DataRef
DataRef
MetadataIngress
IngressPathLiteral
IngressPathLiteral
IngressPathParameter
`
actual := &strings.Builder{}
i := 0
Expand Down Expand Up @@ -261,6 +277,7 @@ module todo {
verb create(todo.CreateRequest) todo.CreateResponse
calls todo.destroy
verb destroy(todo.DestroyRequest) todo.DestroyResponse
ingress GET /todo/destroy/{id}
}
`
actual, err := ParseModuleString("", input)
Expand Down
3 changes: 2 additions & 1 deletion backend/schema/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ func ValidateModule(module *Module) error {

case *Array, *Bool, *DataRef, *Field, *Float, *Int,
*Time, *Map, *Module, *Schema, *String, *VerbRef,
*MetadataCalls, *MetadataIngress, *Optional:
*MetadataCalls, *MetadataIngress, IngressPathComponent,
*IngressPathLiteral, *IngressPathParameter, *Optional:
case Type, Metadata, Decl: // Union sql.
}
return next()
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/protos/google/protobuf/timestamp_pb.ts

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

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

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

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

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

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

Loading

0 comments on commit 8141b2b

Please sign in to comment.