Skip to content

Commit

Permalink
Add json sample objects to verb form based off jsonschema from the ba…
Browse files Browse the repository at this point in the history
…ckend (#335)

<img width="1127" alt="Screenshot 2023-08-29 at 1 27 19 PM"
src="https://github.com/TBD54566975/ftl/assets/51647/d3fb488b-19ad-4f98-97d9-0a32efe89dba">
<img width="534" alt="Screenshot 2023-08-29 at 1 29 46 PM"
src="https://github.com/TBD54566975/ftl/assets/51647/2e7f4321-32fd-48ea-a840-18e0f2d35ebb">
  • Loading branch information
wesbillman authored Aug 29, 2023
1 parent 87df56a commit e264cbc
Show file tree
Hide file tree
Showing 10 changed files with 399 additions and 240 deletions.
25 changes: 23 additions & 2 deletions backend/controller/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package controller

import (
"context"
"encoding/json"
"time"

"github.com/alecthomas/errors"
Expand Down Expand Up @@ -42,6 +43,12 @@ func (c *ConsoleService) GetModules(ctx context.Context, req *connect.Request[pb
return nil, errors.WithStack(err)
}

sch := &schema.Schema{
Modules: slices.Map(deployments, func(d dal.Deployment) *schema.Module {
return d.Schema
}),
}

var modules []*pbconsole.Module
for _, deployment := range deployments {
var verbs []*pbconsole.Verb
Expand All @@ -52,9 +59,23 @@ func (c *ConsoleService) GetModules(ctx context.Context, req *connect.Request[pb
case *schema.Verb:
//nolint:forcetypeassert
v := decl.ToProto().(*pschema.Verb)
verbSchema := schema.VerbToSchema(v)
dataRef := schema.DataRef{
Module: deployment.Module,
Name: verbSchema.Request.Name,
}
jsonRequestSchema, err := schema.DataToJSONSchema(sch, dataRef)
if err != nil {
return nil, errors.WithStack(err)
}
jsonData, err := json.MarshalIndent(jsonRequestSchema, "", " ")
if err != nil {
return nil, errors.WithStack(err)
}
verbs = append(verbs, &pbconsole.Verb{
Verb: v,
Schema: schema.VerbToSchema(v).String(),
Verb: v,
Schema: verbSchema.String(),
JsonRequestSchema: string(jsonData),
})
case *schema.Data:
//nolint:forcetypeassert
Expand Down
24 changes: 15 additions & 9 deletions backend/schema/jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func DataToJSONSchema(schema *Schema, dataRef DataRef) (*js.Schema, error) {

// Encode root, and collect all data types reachable from the root.
dataRefs := map[DataRef]bool{}
root := nodeToJSSchema(rootData, dataRefs)
root := nodeToJSSchema(rootData, dataRef, dataRefs)
if len(dataRefs) == 0 {
return root, nil
}
Expand All @@ -41,12 +41,12 @@ func DataToJSONSchema(schema *Schema, dataRef DataRef) (*js.Schema, error) {
if !ok {
return nil, errors.Errorf("unknown data type %s", dataRef)
}
root.Definitions[dataRef.String()] = js.SchemaOrBool{TypeObject: nodeToJSSchema(data, dataRefs)}
root.Definitions[dataRef.String()] = js.SchemaOrBool{TypeObject: nodeToJSSchema(data, dataRef, dataRefs)}
}
return root, nil
}

func nodeToJSSchema(node Node, dataRefs map[DataRef]bool) *js.Schema {
func nodeToJSSchema(node Node, rootRef DataRef, dataRefs map[DataRef]bool) *js.Schema {
switch node := node.(type) {
case *Data:
st := js.Object
Expand All @@ -57,7 +57,7 @@ func nodeToJSSchema(node Node, dataRefs map[DataRef]bool) *js.Schema {
AdditionalProperties: jsBool(false),
}
for _, field := range node.Fields {
jsField := nodeToJSSchema(field.Type, dataRefs)
jsField := nodeToJSSchema(field.Type, rootRef, dataRefs)
jsField.Description = jsComments(field.Comments)
schema.Properties[field.Name] = js.SchemaOrBool{TypeObject: jsField}
}
Expand Down Expand Up @@ -88,21 +88,27 @@ func nodeToJSSchema(node Node, dataRefs map[DataRef]bool) *js.Schema {
st := js.Array
return &js.Schema{
Type: &js.Type{SimpleTypes: &st},
Items: &js.Items{SchemaOrBool: &js.SchemaOrBool{TypeObject: nodeToJSSchema(node.Element, dataRefs)}},
Items: &js.Items{SchemaOrBool: &js.SchemaOrBool{TypeObject: nodeToJSSchema(node.Element, rootRef, dataRefs)}},
}

case *Map:
st := js.Object
// JSON schema generic map of key type to value type
return &js.Schema{
Type: &js.Type{SimpleTypes: &st},
AdditionalProperties: &js.SchemaOrBool{TypeObject: nodeToJSSchema(node.Value, dataRefs)},
PropertyNames: &js.SchemaOrBool{TypeObject: nodeToJSSchema(node.Key, dataRefs)},
AdditionalProperties: &js.SchemaOrBool{TypeObject: nodeToJSSchema(node.Value, rootRef, dataRefs)},
PropertyNames: &js.SchemaOrBool{TypeObject: nodeToJSSchema(node.Key, rootRef, dataRefs)},
}

case *DataRef:
ref := fmt.Sprintf("#/definitions/%s", node.String())
dataRefs[*node] = true
dataRef := *node
if dataRef.Module == "" {
// handle root data types
dataRef.Module = rootRef.Module
}

ref := fmt.Sprintf("#/definitions/%s", dataRef.String())
dataRefs[dataRef] = true
return &js.Schema{Ref: &ref}

case Decl, *Field, Metadata, *MetadataCalls, *MetadataIngress, *Module, *Schema, Type, *Verb, *VerbRef:
Expand Down
50 changes: 36 additions & 14 deletions backend/schema/jsonschema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,27 @@ import (
func TestDataToJSONSchema(t *testing.T) {
schema, err := DataToJSONSchema(&Schema{
Modules: []*Module{
{Name: "foo", Decls: []Decl{&Data{
Name: "Foo",
Comments: []string{"Data comment"},
Fields: []*Field{
{Name: "string", Type: &String{}, Comments: []string{"Field comment"}},
{Name: "int", Type: &Int{}},
{Name: "float", Type: &Float{}},
{Name: "bool", Type: &Bool{}},
{Name: "time", Type: &Time{}},
{Name: "array", Type: &Array{Element: &String{}}},
{Name: "arrayOfArray", Type: &Array{Element: &Array{Element: &String{}}}},
{Name: "map", Type: &Map{Key: &String{}, Value: &Int{}}},
{Name: "ref", Type: &DataRef{Module: "bar", Name: "Bar"}},
}}}},
{Name: "foo", Decls: []Decl{
&Data{
Name: "Foo",
Comments: []string{"Data comment"},
Fields: []*Field{
{Name: "string", Type: &String{}, Comments: []string{"Field comment"}},
{Name: "int", Type: &Int{}},
{Name: "float", Type: &Float{}},
{Name: "bool", Type: &Bool{}},
{Name: "time", Type: &Time{}},
{Name: "array", Type: &Array{Element: &String{}}},
{Name: "arrayOfRefs", Type: &Array{Element: &DataRef{Name: "Item"}}},
{Name: "arrayOfArray", Type: &Array{Element: &Array{Element: &String{}}}},
{Name: "map", Type: &Map{Key: &String{}, Value: &Int{}}},
{Name: "ref", Type: &DataRef{Module: "bar", Name: "Bar"}},
},
},
&Data{
Name: "Item", Fields: []*Field{{Name: "name", Type: &String{}}},
},
}},
{Name: "bar", Decls: []Decl{
&Data{Name: "Bar", Fields: []*Field{{Name: "bar", Type: &String{}}}},
}},
Expand All @@ -44,6 +51,15 @@ func TestDataToJSONSchema(t *testing.T) {
}
},
"type": "object"
},
"foo.Item": {
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
}
},
"type": "object"
}
},
"properties": {
Expand All @@ -62,6 +78,12 @@ func TestDataToJSONSchema(t *testing.T) {
},
"type": "array"
},
"arrayOfRefs": {
"items": {
"$ref": "#/definitions/foo.Item"
},
"type": "array"
},
"bool": {
"type": "boolean"
},
Expand Down
110 changes: 107 additions & 3 deletions console/client/package-lock.json

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

4 changes: 3 additions & 1 deletion console/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
"@bufbuild/protobuf": "1.3.0",
"@headlessui/react": "1.7.16",
"@heroicons/react": "2.0.18",
"@monaco-editor/react": "^4.5.2",
"@tailwindcss/forms": "^0.5.5",
"json-schema-faker": "0.5.0-rcv.46",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-router-dom": "6.15.0",
Expand All @@ -63,6 +65,7 @@
"buffer": "^6.0.3",
"chokidar": "3.5.3",
"eslint": "8.47.0",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-compat": "4.1.4",
"eslint-plugin-react": "7.33.2",
"fast-glob": "3.3.1",
Expand All @@ -74,7 +77,6 @@
"parcel": "2.9.3",
"playwright": "1.37.0",
"postcss": "8.4.28",
"eslint-config-prettier": "9.0.0",
"prettier": "3.0.2",
"process": "^0.11.10",
"start-server-and-test": "2.0.0",
Expand Down
Loading

0 comments on commit e264cbc

Please sign in to comment.