Created
January 26, 2019 14:54
-
-
Save FingerLiu/ecf7cbb600abe832b1cad162109444d7 to your computer and use it in GitHub Desktop.
inspect json and generate graphql schema
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
/* | |
parse json, extract type in json and save to graphql.schema | |
*/ | |
import ( | |
"io/ioutil" | |
"encoding/json" | |
"reflect" | |
"fmt" | |
"unicode" | |
"path/filepath" | |
"text/template" | |
"os" | |
"log" | |
) | |
func unmarshal(filename string) (interface{}, error) { | |
var jsonRaw []byte | |
var result interface{} | |
var err error | |
jsonRaw, err = ioutil.ReadFile(filename) | |
if err != nil { | |
return nil, err | |
} | |
err = json.Unmarshal(jsonRaw, &result) | |
if err != nil { | |
return nil, err | |
} | |
return result, nil | |
} | |
type Node struct { | |
Name string | |
ValueType reflect.Kind | |
Children *[] Node | |
} | |
func (n *Node) RealType() string { | |
var fieldMapping = map[reflect.Kind]string { | |
reflect.Int: "Int", | |
reflect.Float32: "Float", | |
reflect.Float64: "Float64", | |
reflect.String: "String", | |
reflect.Bool: "Boolean", | |
reflect.Map: "Map", | |
reflect.Slice: "[]", | |
reflect.Interface: "String //TODO check this field", | |
} | |
realType := fieldMapping[n.ValueType] | |
if realType == "Map" { | |
realType = UppercaseFirst(n.Name) | |
} | |
return realType | |
} | |
type GqlType struct { | |
Name string | |
Children *[] Node | |
} | |
func UppercaseFirst(s string) string { | |
if s == "" { | |
return "" | |
} | |
r := []rune(s) | |
r[0] = unicode.ToUpper(r[0]) | |
return string(r) | |
} | |
func getRootType(s string) string { | |
ext := filepath.Ext(s) | |
return UppercaseFirst(s[0:len(s) - len(ext)]) | |
} | |
func ensureAndAppend(ptr *[]Node, node Node) *[]Node { | |
if ptr == nil { | |
arr := make([]Node, 0) | |
ptr = &arr | |
} | |
*ptr = append(*ptr, node) | |
return ptr | |
} | |
func Parse(obj interface{}, gqlTypesPtr *[]GqlType, gqlType GqlType, node Node) { | |
for key, value := range obj.(map[string]interface{}) { | |
var valueType reflect.Kind | |
if value == nil { | |
valueType = reflect.Interface | |
} else { | |
valueType = reflect.TypeOf(value).Kind() | |
} | |
child := Node{Name:key, ValueType:valueType} | |
if value != nil && valueType == reflect.Map{ | |
childGqlType := GqlType{Name:UppercaseFirst(key)} | |
Parse(value, gqlTypesPtr, childGqlType, child) | |
} else if valueType == reflect.Slice { | |
// TODO | |
panic("unsupported value type: list is not supported yet.") | |
} | |
// add node for parent type | |
gqlType.Children = ensureAndAppend(gqlType.Children, child) | |
// add node for parent node | |
node.Children = ensureAndAppend(node.Children, child) | |
} | |
*gqlTypesPtr = append(*gqlTypesPtr, gqlType) | |
} | |
func GenerateSchema(gqlTypes []GqlType, tmpl string) error { | |
output := "draft.graphql" | |
f, err := os.Create(output) | |
if err != nil { | |
log.Println("create file: ", err) | |
return err | |
} | |
defer f.Close() | |
tname := filepath.Base(tmpl) | |
t := template.Must(template.New(tname).Funcs(template.FuncMap{ | |
"Deref": func(children *[]Node) []Node { return *children}, | |
}).ParseFiles(tmpl)) | |
m := map[string]interface{}{"gqlTypes": gqlTypes} | |
err = t.ExecuteTemplate(f, tname, m) | |
if err != nil { | |
fmt.Println(err) | |
return err | |
} | |
fmt.Println() | |
return nil | |
} | |
func main() { | |
tmpl := "inspect/templates/schema.gotpl" | |
/* | |
the schema is something like this | |
// Code generated by github.com/99designs/gqlgen | |
// This is only draft of schema, please double check fields with TODO | |
// before run `go run scripts/gqlgen.go` | |
{{ range $gqlType := .gqlTypes }} | |
type {{ $gqlType.Name }} { | |
{{- range $field := (Deref $gqlType.Children) }} | |
{{- if eq $field.RealType "[]" }} | |
{{ $field.Name }}: [String] | |
{{- else }} | |
{{ $field.Name }}: {{ $field.RealType }} | |
{{- end }} | |
{{- end }} | |
} | |
{{ end }} | |
*/ | |
filename := "platform.json" | |
/* | |
{ | |
"platform": { | |
"id": 61, | |
"created": "2018-06-26T18:19:13.701579+08:00", | |
"updated": "2018-11-23T11:19:59.734070+08:00", | |
"ptype": 3, | |
"direction": 0, | |
"account": "2", | |
"account_ext": "", | |
"display_name": "得到公司测试店铺", | |
"key1": "77c3f00f1f1d4002a8", | |
"site": "", | |
"api_url": "http://shzfopenapi.test-bj-0-k8s.luojilab.com/", | |
"auth_valid": true, | |
"is_active": true, | |
"interval": "00:05:00", | |
"extra": { | |
"page_size": "100", | |
"showcase_id": "dev2_2", | |
"recheck_offset": "1800", | |
"cache_key_order": "10003_orders", | |
"parent_platform": "4", | |
"cache_key_refund": "2_refunds", | |
"routing_key_order": "2.*.*", | |
"routing_key_refund": "2.*" | |
}, | |
"last_success": null, | |
"company": 5, | |
"admin": 9 | |
} | |
} | |
*/ | |
rootTypeName := getRootType(filename) | |
rootObj, err := unmarshal(filename) | |
if err != nil { | |
panic(err) | |
} | |
t := reflect.TypeOf(rootObj) | |
mapType := t | |
root := Node{Name:"root", ValueType:t.Kind()} | |
rootType := GqlType{Name: rootTypeName+"Result"} | |
gqlTypes := make([]GqlType, 0) | |
gqlTypesPtr := &gqlTypes | |
if t == mapType { | |
Parse(rootObj, gqlTypesPtr, rootType, root) | |
GenerateSchema(gqlTypes, tmpl) | |
} else { | |
// TODO | |
panic("unsupported json format: root object is not map.") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment