Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bugfix schema translator #13

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,11 @@ specify custom namespace for edge generation with "namespace" key in extra args,
```
jsonschemagraph gen-dir ../iceberg/schemas/graph DATA OUT --extraArgs '{"auth_resource_path": "/programs/ohsu/projects/test", "namespace": "CALIPERIDP.org"}'
```

### Example commands for generating graphql schema from jsonschema

```
jsonschemagraph gen-graphql --graphName CALIPER --jsonSchema graph-fhir.json --configPath config.yaml --writeIntermediateFile
```

This translator does not support codeable references
88 changes: 49 additions & 39 deletions graphql/convertSchema.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package graphql
import (
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"reflect"
"slices"
"strings"
"unicode"
Expand Down Expand Up @@ -56,10 +56,14 @@ func LowerFirstLetter(s string) string {

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

func isSlice(v interface{}) bool {
return reflect.TypeOf(v).Kind() == reflect.Slice
}

func ParseIntoGraphqlSchema(relpath string, graphName string, vertexSubset []string, writeFile bool) ([]*gripql.Graph, error) {
out, err := graph.Load(relpath)
if err != nil {
Expand All @@ -85,31 +89,22 @@ func ParseIntoGraphqlSchema(relpath string, graphName string, vertexSubset []str
continue
}

vertVal := ParseSchema(sch)
switch vertVal.(type) {
case string:
vertexData[key] = vertVal.(string)
case int:
vertexData[key] = vertVal.(int)
case bool:
vertexData[key] = vertVal.(bool)
case float64:
vertexData[key] = vertVal.(float64)
case []any:
if vertVal.([]any)[0].(string) == "Resource" {
vertVal.([]any)[0] = "ResourceUnion"
}
vertexData[key] = vertVal.([]any)
case nil:
default:
log.Printf("ERR State for type: ", vertVal)
continue
value := ParseSchema(sch)

// Fields with edges that aren't defined in our internal schema are not present in the graphql schema either
if value == nil {
fmt.Printf("WARNING: key %s on type %s may not be supported\n", key, class.Title)
} else if isSlice(value) && value.([]any)[0] == "Resource" {
value.([]any)[0] = "ResourceUnion"
vertexData[key] = value
} else {
vertexData[key] = value
}
}

if ext, ok := class.Extensions[compile.GraphExtensionTag]; ok {
enumData := map[string][]string{}
enumSeen := map[string]bool{}
unionData := map[string][]string{}
unionSeen := map[string]bool{}
for _, target := range ext.(compile.GraphExtension).Targets {
parts := strings.Split(target.Rel, "_")
RegexMatch := target.TargetHints.RegexMatch[0][:len(target.TargetHints.RegexMatch[0])-2]
Expand All @@ -121,24 +116,39 @@ func ParseIntoGraphqlSchema(relpath string, graphName string, vertexSubset []str
}
vertexData[parts[0]] = RegexMatch
continue
} else if len(parts) == 2 {
base, targetType := parts[0], parts[len(parts)-1]
if targetType != RegexMatch {
if value, ok := vertexData[parts[0]]; ok && value != nil {
strValue := ""
if slice, isSlice := value.([]any); isSlice && len(slice) > 0 {
strValue = slice[0].(string)
} else if s, isString := value.(string); isString {
strValue = s
}
if len(strValue) >= 5 && strings.HasSuffix(strValue, "Union") {
vertexData[parts[0]] = strValue[:len(strValue)-5]
}
} else {
fmt.Printf("Key not found or value is nil: %s\n %s", parts[0], vertexData)
}
continue
}
unionTitle := fmt.Sprintf("%s%s", class.Title, cases.Title(language.Und, cases.NoLower).String(base)) + "Union"
if _, seen := unionSeen[targetType+unionTitle]; !seen {
vertexData[base] = unionTitle
unionSeen[targetType+unionTitle] = true
unionData[unionTitle] = append(unionData[unionTitle], targetType)
}
}
base, targetType := parts[0], parts[len(parts)-1]
// In places where there are in-node traversals before hitting an edge, need to
// continue with execution to avoid creating a redundant enum.
if targetType != RegexMatch {
continue
}
unionTitle := fmt.Sprintf("%s%s", class.Title, cases.Title(language.Und, cases.NoLower).String(base)) + "Union"
if _, seen := enumSeen[targetType+unionTitle]; !seen {
vertexData[base] = unionTitle
enumSeen[targetType+unionTitle] = true
enumData[unionTitle] = append(enumData[unionTitle], targetType)
}
/* else { base, targetType := parts[0], parts[len(parts)-1]
fmt.Println("BASE: ", base, "TARGET TYPE: ", targetType) */

}
if enumData != nil {
for k, v := range enumData {
enum := map[string]any{"data": map[string]any{k: v}, "label": "Vertex", "gid": "Union"}
graphSchema["vertices"] = append(graphSchema["vertices"].([]map[string]any), enum)
if unionData != nil {
for k, v := range unionData {
union := map[string]any{"data": map[string]any{k: v}, "label": "Vertex", "gid": "Union"}
graphSchema["vertices"] = append(graphSchema["vertices"].([]map[string]any), union)
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion graphql/griptographql.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ func GripGraphqltoGraphql(graph *gripql.Graph) string {
schemaBuilder.WriteString("enum Format {\n json\n tsv\n csv\n}\n")

for _, v := range graph.Vertices {
//fmt.Println("V: ", v)
//fmt.Printf("BREAK: \n\n")
if v.Gid != "Query" {
executedFirstBlock := false
for name, values := range v.Data.AsMap() {
Expand All @@ -51,11 +53,12 @@ func GripGraphqltoGraphql(graph *gripql.Graph) string {
for field, fieldType := range v.Data.AsMap() {
strFieldType, ok := fieldType.(string)
if ok && (strings.HasSuffix(strFieldType, "Type") || strings.HasSuffix(strFieldType, "Union")) {
schemaBuilder.WriteString(fmt.Sprintf(" %s(offset: Int first: Int filter: JSON sort: JSON accessibility: Accessibility = all format: Format = json): [%s]\n", field, strFieldType))
schemaBuilder.WriteString(fmt.Sprintf(" %s(offset: Int first: Int filter: JSON sort: JSON accessibility: Accessibility = all format: Format = json): %s!\n", field, strFieldType))
} else {
schemaBuilder.WriteString(fmt.Sprintf(" %s: %s\n", field, fieldType))
}
}
schemaBuilder.WriteString(" auth_resource_path: String\n")
schemaBuilder.WriteString("}\n")
}
} else {
Expand Down
14 changes: 6 additions & 8 deletions graphql/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,20 @@ package graphql
import (
"fmt"
"slices"
"strings"

"github.com/bmeg/jsonschema/v5"
)

func jsontographlprimitiveType(returnType any) any {
switch returnType.(type) {
case string:
switch returnType {
case "string":
return "String"
case int:
case "integer":
return "Int"
case bool:
case "boolean":
return "Boolean"
case float64:
case "number":
return "Float"
case []any:
return []any{[]any{strings.Title(returnType.([]any)[0].(string))}}
default:
fmt.Println("ERR State for jsontographlprimitiveType: ", returnType)
return ""
Expand All @@ -37,6 +34,7 @@ func ParseSchema(schema *jsonschema.Schema) any {
if schema.Items2020 != nil {
if schema.Items2020.Ref != nil &&
schema.Items2020.Ref.Title != "" {
// Don't include keys that contain references which types can't be discerned from reading the schema
if slices.Contains([]string{"Reference", "FHIRPrimitiveExtension"}, schema.Items2020.Ref.Title) {
return nil
}
Expand Down