Skip to content

Commit

Permalink
feat: add export to verb, data, and enum (#1366)
Browse files Browse the repository at this point in the history
Fixes #1354
  • Loading branch information
wesbillman authored May 1, 2024
1 parent 223151a commit 29a7150
Show file tree
Hide file tree
Showing 25 changed files with 483 additions and 278 deletions.
18 changes: 16 additions & 2 deletions backend/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -657,14 +657,20 @@ func (s *Service) callWithRequest(ctx context.Context, req *connect.Request[ftlv
if req.Msg.Body == nil {
return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("body is required"))
}
verbRef := schema.RefFromProto(req.Msg.Verb)

sch, err := s.getActiveSchema(ctx)
if err != nil {
return nil, err
}

err = ingress.ValidateCallBody(req.Msg.Body, verbRef, sch)
verbRef := schema.RefFromProto(req.Msg.Verb)
verb := &schema.Verb{}
err = sch.ResolveRefToType(verbRef, verb)
if err != nil {
return nil, err
}

err = ingress.ValidateCallBody(req.Msg.Body, verb, sch)
if err != nil {
return nil, err
}
Expand All @@ -684,6 +690,14 @@ func (s *Service) callWithRequest(ctx context.Context, req *connect.Request[ftlv
return nil, err
}

if !verb.IsExported() {
for _, caller := range callers {
if caller.Module != module {
return nil, connect.NewError(connect.CodePermissionDenied, fmt.Errorf("verb %q is not exported", verbRef))
}
}
}

var requestKey model.RequestKey
isNewRequestKey := false
if k, ok := key.Get(); ok {
Expand Down
8 changes: 4 additions & 4 deletions backend/controller/ingress/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ func TestIngress(t *testing.T) {
foo String
}
verb getAlias(HttpRequest<test.AliasRequest>) HttpResponse<Empty, Empty>
export verb getAlias(HttpRequest<test.AliasRequest>) HttpResponse<Empty, Empty>
+ingress http GET /getAlias
verb getPath(HttpRequest<test.PathParameterRequest>) HttpResponse<Empty, Empty>
export verb getPath(HttpRequest<test.PathParameterRequest>) HttpResponse<Empty, Empty>
+ingress http GET /getPath/{username}
verb postMissingTypes(HttpRequest<test.MissingTypes>) HttpResponse<Empty, Empty>
export verb postMissingTypes(HttpRequest<test.MissingTypes>) HttpResponse<Empty, Empty>
+ingress http POST /postMissingTypes
verb postJsonPayload(HttpRequest<test.JsonPayload>) HttpResponse<Empty, Empty>
export verb postJsonPayload(HttpRequest<test.JsonPayload>) HttpResponse<Empty, Empty>
+ingress http POST /postJsonPayload
}
`)
Expand Down
10 changes: 2 additions & 8 deletions backend/controller/ingress/ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,9 @@ func matchSegments(pattern, urlPath string, onMatch func(segment, value string))
return true
}

func ValidateCallBody(body []byte, ref *schema.Ref, sch *schema.Schema) error {
verb := &schema.Verb{}
err := sch.ResolveRefToType(ref, verb)
if err != nil {
return err
}

func ValidateCallBody(body []byte, verb *schema.Verb, sch *schema.Schema) error {
var requestMap map[string]any
err = json.Unmarshal(body, &requestMap)
err := json.Unmarshal(body, &requestMap)
if err != nil {
return fmt.Errorf("HTTP request body is not valid JSON: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion backend/controller/ingress/ingress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func TestValidation(t *testing.T) {
schema: `module test { data Nested { intValue Int } data Test { mapValue {String: test.Nested} } }`,
request: obj{"mapValue": obj{"key1": obj{"intValue": 10.0}, "key2": obj{"intValue": 20.0}}}},
{name: "OtherModuleRef",
schema: `module other { data Other { intValue Int } } module test { data Test { otherRef other.Other } }`,
schema: `module other { export data Other { intValue Int } } module test { data Test { otherRef other.Other } }`,
request: obj{"otherRef": obj{"intValue": 10.0}}},
{name: "AllowedMissingFieldTypes",
schema: `
Expand Down
8 changes: 4 additions & 4 deletions backend/controller/ingress/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,16 @@ func TestBuildRequestBody(t *testing.T) {
foo String
}
verb getAlias(HttpRequest<test.AliasRequest>) HttpResponse<Empty, Empty>
export verb getAlias(HttpRequest<test.AliasRequest>) HttpResponse<Empty, Empty>
+ingress http GET /getAlias
verb getPath(HttpRequest<test.PathParameterRequest>) HttpResponse<Empty, Empty>
export verb getPath(HttpRequest<test.PathParameterRequest>) HttpResponse<Empty, Empty>
+ingress http GET /getPath/{username}
verb postMissingTypes(HttpRequest<test.MissingTypes>) HttpResponse<Empty, Empty>
export verb postMissingTypes(HttpRequest<test.MissingTypes>) HttpResponse<Empty, Empty>
+ingress http POST /postMissingTypes
verb postJsonPayload(HttpRequest<test.JsonPayload>) HttpResponse<Empty, Empty>
export verb postJsonPayload(HttpRequest<test.JsonPayload>) HttpResponse<Empty, Empty>
+ingress http POST /postJsonPayload
}
`)
Expand Down
239 changes: 134 additions & 105 deletions backend/protos/xyz/block/ftl/v1/schema/schema.pb.go

Large diffs are not rendered by default.

27 changes: 15 additions & 12 deletions backend/protos/xyz/block/ftl/v1/schema/schema.proto
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ message Config {
message Data {
optional Position pos = 1;
repeated string comments = 2;
string name = 3;
repeated Field fields = 4;
repeated Metadata metadata = 5;
repeated TypeParameter typeParameters = 6;
bool export = 3;
string name = 4;
repeated TypeParameter typeParameters = 5;
repeated Field fields = 6;
repeated Metadata metadata = 7;
}

message Database {
Expand All @@ -61,9 +62,10 @@ message Decl {
message Enum {
optional Position pos = 1;
repeated string comments = 2;
string name = 3;
optional Type type = 4;
repeated EnumVariant variants = 5;
bool export = 3;
string name = 4;
optional Type type = 5;
repeated EnumVariant variants = 6;
}

message EnumVariant {
Expand Down Expand Up @@ -260,9 +262,10 @@ message Verb {
optional VerbRuntime runtime = 31634;

optional Position pos = 1;
string name = 2;
repeated string comments = 3;
Type request = 4;
Type response = 5;
repeated Metadata metadata = 6;
repeated string comments = 2;
bool export = 3;
string name = 4;
Type request = 5;
Type response = 6;
repeated Metadata metadata = 7;
}
6 changes: 3 additions & 3 deletions backend/schema/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const BuiltinsSource = `
// Built-in types for FTL.
builtin module builtin {
// HTTP request structure used for HTTP ingress verbs.
data HttpRequest<Body> {
export data HttpRequest<Body> {
method String
path String
pathParameters {String: String}
Expand All @@ -17,15 +17,15 @@ builtin module builtin {
}
// HTTP response structure used for HTTP ingress verbs.
data HttpResponse<Body, Error> {
export data HttpResponse<Body, Error> {
status Int
headers {String: [String]}
// Either "body" or "error" must be present, not both.
body Body?
error Error?
}
data Empty {}
export data Empty {}
}
`

Expand Down
1 change: 1 addition & 0 deletions backend/schema/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var _ Decl = (*Config)(nil)
var _ Symbol = (*Config)(nil)

func (s *Config) GetName() string { return s.Name }
func (s *Config) IsExported() bool { return false }
func (s *Config) Position() Position { return s.Pos }
func (s *Config) String() string { return fmt.Sprintf("config %s %s", s.Name, s.Type) }

Expand Down
25 changes: 17 additions & 8 deletions backend/schema/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ type Data struct {
Pos Position `parser:"" protobuf:"1,optional"`

Comments []string `parser:"@Comment*" protobuf:"2"`
Name string `parser:"'data' @Ident" protobuf:"3"`
TypeParameters []*TypeParameter `parser:"( '<' @@ (',' @@)* '>' )?" protobuf:"6"`
Fields []*Field `parser:"'{' @@* '}'" protobuf:"4"`
Metadata []Metadata `parser:"@@*" protobuf:"5"`
Export bool `parser:"@'export'?" protobuf:"3"`
Name string `parser:"'data' @Ident" protobuf:"4"`
TypeParameters []*TypeParameter `parser:"( '<' @@ (',' @@)* '>' )?" protobuf:"5"`
Fields []*Field `parser:"'{' @@* '}'" protobuf:"6"`
Metadata []Metadata `parser:"@@*" protobuf:"7"`
}

var _ Decl = (*Data)(nil)
Expand Down Expand Up @@ -148,7 +149,8 @@ func (d *Data) schemaChildren() []Node {
return children
}

func (d *Data) GetName() string { return d.Name }
func (d *Data) GetName() string { return d.Name }
func (d *Data) IsExported() bool { return d.Export }

func (d *Data) String() string {
w := &strings.Builder{}
Expand All @@ -164,6 +166,9 @@ func (d *Data) String() string {
}
typeParameters += ">"
}
if d.Export {
fmt.Fprint(w, "export ")
}
fmt.Fprintf(w, "data %s%s {\n", d.Name, typeParameters)
for _, f := range d.Fields {
fmt.Fprintln(w, indent(f.String()))
Expand All @@ -175,18 +180,22 @@ func (d *Data) String() string {

func (d *Data) ToProto() proto.Message {
return &schemapb.Data{
Pos: posToProto(d.Pos),
TypeParameters: nodeListToProto[*schemapb.TypeParameter](d.TypeParameters),
Pos: posToProto(d.Pos),

Name: d.Name,
Export: d.Export,
TypeParameters: nodeListToProto[*schemapb.TypeParameter](d.TypeParameters),
Fields: nodeListToProto[*schemapb.Field](d.Fields),
Comments: d.Comments,
}
}

func DataFromProto(s *schemapb.Data) *Data {
return &Data{
Pos: posFromProto(s.Pos),
Pos: posFromProto(s.Pos),

Name: s.Name,
Export: s.Export,
TypeParameters: typeParametersToSchema(s.TypeParameters),
Fields: fieldListToSchema(s.Fields),
Comments: s.Comments,
Expand Down
3 changes: 2 additions & 1 deletion backend/schema/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ func (d *Database) ToProto() proto.Message {
}
}

func (d *Database) GetName() string { return d.Name }
func (d *Database) GetName() string { return d.Name }
func (d *Database) IsExported() bool { return false }

func DatabaseFromProto(s *schemapb.Database) *Database {
return &Database{
Expand Down
15 changes: 11 additions & 4 deletions backend/schema/enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ type Enum struct {
Pos Position `parser:"" protobuf:"1,optional"`

Comments []string `parser:"@Comment*" protobuf:"2"`
Name string `parser:"'enum' @Ident" protobuf:"3"`
Type Type `parser:"(':' @@)?" protobuf:"4,optional"`
Variants []*EnumVariant `parser:"'{' @@* '}'" protobuf:"5"`
Export bool `parser:"@'export'?" protobuf:"3"`
Name string `parser:"'enum' @Ident" protobuf:"4"`
Type Type `parser:"(':' @@)?" protobuf:"5,optional"`
Variants []*EnumVariant `parser:"'{' @@* '}'" protobuf:"6"`
}

var _ Decl = (*Enum)(nil)
Expand All @@ -26,6 +27,9 @@ func (e *Enum) Position() Position { return e.Pos }
func (e *Enum) String() string {
w := &strings.Builder{}
fmt.Fprint(w, encodeComments(e.Comments))
if e.Export {
fmt.Fprint(w, "export ")
}
fmt.Fprintf(w, "enum %s", e.Name)
if e.Type != nil {
fmt.Fprintf(w, ": %s", e.Type)
Expand Down Expand Up @@ -54,6 +58,7 @@ func (e *Enum) ToProto() proto.Message {
Pos: posToProto(e.Pos),
Comments: e.Comments,
Name: e.Name,
Export: e.Export,
Variants: nodeListToProto[*schemapb.EnumVariant](e.Variants),
}
if e.Type != nil {
Expand All @@ -62,12 +67,14 @@ func (e *Enum) ToProto() proto.Message {
return se
}

func (e *Enum) GetName() string { return e.Name }
func (e *Enum) GetName() string { return e.Name }
func (e *Enum) IsExported() bool { return e.Export }

func EnumFromProto(s *schemapb.Enum) *Enum {
e := &Enum{
Pos: posFromProto(s.Pos),
Name: s.Name,
Export: s.Export,
Comments: s.Comments,
Variants: enumVariantListToSchema(s.Variants),
}
Expand Down
3 changes: 2 additions & 1 deletion backend/schema/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ func (m *Module) ToProto() proto.Message {
}
}

func (m *Module) GetName() string { return m.Name }
func (m *Module) GetName() string { return m.Name }
func (m *Module) IsExported() bool { return false }

// ModuleFromProtoFile loads a module from the given proto-encoded file.
func ModuleFromProtoFile(filename string) (*Module, error) {
Expand Down
2 changes: 2 additions & 0 deletions backend/schema/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var (
participle.Lexer(Lexer),
participle.Elide("Whitespace"),
participle.Unquote(),
participle.UseLookahead(2),
participle.Map(func(token lexer.Token) (lexer.Token, error) {
token.Value = strings.TrimSpace(strings.TrimPrefix(token.Value, "//"))
return token, nil
Expand Down Expand Up @@ -142,6 +143,7 @@ type Named interface {
type Decl interface {
Symbol
GetName() string
IsExported() bool
schemaDecl()
}

Expand Down
Loading

0 comments on commit 29a7150

Please sign in to comment.