Skip to content

Commit 3cd38a4

Browse files
Merge pull request #13 from bmeg/feature/bugfixes
bugfix schema translator
2 parents eecb848 + b95685b commit 3cd38a4

File tree

4 files changed

+68
-49
lines changed

4 files changed

+68
-49
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,11 @@ specify custom namespace for edge generation with "namespace" key in extra args,
113113
```
114114
jsonschemagraph gen-dir ../iceberg/schemas/graph DATA OUT --extraArgs '{"auth_resource_path": "/programs/ohsu/projects/test", "namespace": "CALIPERIDP.org"}'
115115
```
116+
117+
### Example commands for generating graphql schema from jsonschema
118+
119+
```
120+
jsonschemagraph gen-graphql --graphName CALIPER --jsonSchema graph-fhir.json --configPath config.yaml --writeIntermediateFile
121+
```
122+
123+
This translator does not support codeable references

graphql/convertSchema.go

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ package graphql
33
import (
44
"encoding/json"
55
"fmt"
6-
"log"
76
"os"
87
"path/filepath"
8+
"reflect"
99
"slices"
1010
"strings"
1111
"unicode"
@@ -56,10 +56,14 @@ func LowerFirstLetter(s string) string {
5656

5757
func generateQueryList(classes []string) {
5858
for i, v := range classes {
59-
classes[i] = LowerFirstLetter(classes[i]) + "(offset: Int first: Int filter: JSON sort: JSON accessibility: Accessibility = all format: Format = json): [" + v + "Type]"
59+
classes[i] = LowerFirstLetter(classes[i]) + "(offset: Int first: Int filter: JSON sort: [SortInput] accessibility: Accessibility = all): [" + v + "Type!]!"
6060
}
6161
}
6262

63+
func isSlice(v interface{}) bool {
64+
return reflect.TypeOf(v).Kind() == reflect.Slice
65+
}
66+
6367
func ParseIntoGraphqlSchema(relpath string, graphName string, vertexSubset []string, writeFile bool) ([]*gripql.Graph, error) {
6468
out, err := graph.Load(relpath)
6569
if err != nil {
@@ -85,31 +89,22 @@ func ParseIntoGraphqlSchema(relpath string, graphName string, vertexSubset []str
8589
continue
8690
}
8791

88-
vertVal := ParseSchema(sch)
89-
switch vertVal.(type) {
90-
case string:
91-
vertexData[key] = vertVal.(string)
92-
case int:
93-
vertexData[key] = vertVal.(int)
94-
case bool:
95-
vertexData[key] = vertVal.(bool)
96-
case float64:
97-
vertexData[key] = vertVal.(float64)
98-
case []any:
99-
if vertVal.([]any)[0].(string) == "Resource" {
100-
vertVal.([]any)[0] = "ResourceUnion"
101-
}
102-
vertexData[key] = vertVal.([]any)
103-
case nil:
104-
default:
105-
log.Printf("ERR State for type: ", vertVal)
106-
continue
92+
value := ParseSchema(sch)
93+
94+
// Fields with edges that aren't defined in our internal schema are not present in the graphql schema either
95+
if value == nil {
96+
fmt.Printf("WARNING: key %s on type %s may not be supported\n", key, class.Title)
97+
} else if isSlice(value) && value.([]any)[0] == "Resource" {
98+
value.([]any)[0] = "ResourceUnion"
99+
vertexData[key] = value
100+
} else {
101+
vertexData[key] = value
107102
}
108103
}
109104

110105
if ext, ok := class.Extensions[compile.GraphExtensionTag]; ok {
111-
enumData := map[string][]string{}
112-
enumSeen := map[string]bool{}
106+
unionData := map[string][]string{}
107+
unionSeen := map[string]bool{}
113108
for _, target := range ext.(compile.GraphExtension).Targets {
114109
parts := strings.Split(target.Rel, "_")
115110
RegexMatch := target.TargetHints.RegexMatch[0][:len(target.TargetHints.RegexMatch[0])-2]
@@ -121,24 +116,39 @@ func ParseIntoGraphqlSchema(relpath string, graphName string, vertexSubset []str
121116
}
122117
vertexData[parts[0]] = RegexMatch
123118
continue
119+
} else if len(parts) == 2 {
120+
base, targetType := parts[0], parts[len(parts)-1]
121+
if targetType != RegexMatch {
122+
if value, ok := vertexData[parts[0]]; ok && value != nil {
123+
strValue := ""
124+
if slice, isSlice := value.([]any); isSlice && len(slice) > 0 {
125+
strValue = slice[0].(string)
126+
} else if s, isString := value.(string); isString {
127+
strValue = s
128+
}
129+
if len(strValue) >= 5 && strings.HasSuffix(strValue, "Union") {
130+
vertexData[parts[0]] = strValue[:len(strValue)-5]
131+
}
132+
} else {
133+
fmt.Printf("Key not found or value is nil: %s\n %s", parts[0], vertexData)
134+
}
135+
continue
136+
}
137+
unionTitle := fmt.Sprintf("%s%s", class.Title, cases.Title(language.Und, cases.NoLower).String(base)) + "Union"
138+
if _, seen := unionSeen[targetType+unionTitle]; !seen {
139+
vertexData[base] = unionTitle
140+
unionSeen[targetType+unionTitle] = true
141+
unionData[unionTitle] = append(unionData[unionTitle], targetType)
142+
}
124143
}
125-
base, targetType := parts[0], parts[len(parts)-1]
126-
// In places where there are in-node traversals before hitting an edge, need to
127-
// continue with execution to avoid creating a redundant enum.
128-
if targetType != RegexMatch {
129-
continue
130-
}
131-
unionTitle := fmt.Sprintf("%s%s", class.Title, cases.Title(language.Und, cases.NoLower).String(base)) + "Union"
132-
if _, seen := enumSeen[targetType+unionTitle]; !seen {
133-
vertexData[base] = unionTitle
134-
enumSeen[targetType+unionTitle] = true
135-
enumData[unionTitle] = append(enumData[unionTitle], targetType)
136-
}
144+
/* else { base, targetType := parts[0], parts[len(parts)-1]
145+
fmt.Println("BASE: ", base, "TARGET TYPE: ", targetType) */
146+
137147
}
138-
if enumData != nil {
139-
for k, v := range enumData {
140-
enum := map[string]any{"data": map[string]any{k: v}, "label": "Vertex", "gid": "Union"}
141-
graphSchema["vertices"] = append(graphSchema["vertices"].([]map[string]any), enum)
148+
if unionData != nil {
149+
for k, v := range unionData {
150+
union := map[string]any{"data": map[string]any{k: v}, "label": "Vertex", "gid": "Union"}
151+
graphSchema["vertices"] = append(graphSchema["vertices"].([]map[string]any), union)
142152
}
143153
}
144154
}

graphql/griptographql.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ func GripGraphqltoGraphql(graph *gripql.Graph) string {
2323
// Write gen3 style boiler plate to mirror thier args
2424
schemaBuilder.WriteString("scalar JSON\n")
2525
schemaBuilder.WriteString("enum Accessibility {\n all\n accessible\n unaccessible\n}\n")
26-
schemaBuilder.WriteString("enum Format {\n json\n tsv\n csv\n}\n")
26+
schemaBuilder.WriteString("input SortInput {\n field: String!\n descending: Boolean\n}\n")
2727

2828
for _, v := range graph.Vertices {
29+
//fmt.Println("V: ", v)
30+
//fmt.Printf("BREAK: \n\n")
2931
if v.Gid != "Query" {
3032
executedFirstBlock := false
3133
for name, values := range v.Data.AsMap() {
@@ -51,11 +53,12 @@ func GripGraphqltoGraphql(graph *gripql.Graph) string {
5153
for field, fieldType := range v.Data.AsMap() {
5254
strFieldType, ok := fieldType.(string)
5355
if ok && (strings.HasSuffix(strFieldType, "Type") || strings.HasSuffix(strFieldType, "Union")) {
54-
schemaBuilder.WriteString(fmt.Sprintf(" %s(offset: Int first: Int filter: JSON sort: JSON accessibility: Accessibility = all format: Format = json): [%s]\n", field, strFieldType))
56+
schemaBuilder.WriteString(fmt.Sprintf(" %s(offset: Int first: Int): %s!\n", field, strFieldType))
5557
} else {
5658
schemaBuilder.WriteString(fmt.Sprintf(" %s: %s\n", field, fieldType))
5759
}
5860
}
61+
schemaBuilder.WriteString(" auth_resource_path: String\n")
5962
schemaBuilder.WriteString("}\n")
6063
}
6164
} else {

graphql/parse.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,20 @@ package graphql
33
import (
44
"fmt"
55
"slices"
6-
"strings"
76

87
"github.com/bmeg/jsonschema/v5"
98
)
109

1110
func jsontographlprimitiveType(returnType any) any {
12-
switch returnType.(type) {
13-
case string:
11+
switch returnType {
12+
case "string":
1413
return "String"
15-
case int:
14+
case "integer":
1615
return "Int"
17-
case bool:
16+
case "boolean":
1817
return "Boolean"
19-
case float64:
18+
case "number":
2019
return "Float"
21-
case []any:
22-
return []any{[]any{strings.Title(returnType.([]any)[0].(string))}}
2320
default:
2421
fmt.Println("ERR State for jsontographlprimitiveType: ", returnType)
2522
return ""
@@ -37,6 +34,7 @@ func ParseSchema(schema *jsonschema.Schema) any {
3734
if schema.Items2020 != nil {
3835
if schema.Items2020.Ref != nil &&
3936
schema.Items2020.Ref.Title != "" {
37+
// Don't include keys that contain references which types can't be discerned from reading the schema
4038
if slices.Contains([]string{"Reference", "FHIRPrimitiveExtension"}, schema.Items2020.Ref.Title) {
4139
return nil
4240
}

0 commit comments

Comments
 (0)