Skip to content

Instantly share code, notes, and snippets.

@FingerLiu
Created January 26, 2019 14:54
Show Gist options
  • Save FingerLiu/ecf7cbb600abe832b1cad162109444d7 to your computer and use it in GitHub Desktop.
Save FingerLiu/ecf7cbb600abe832b1cad162109444d7 to your computer and use it in GitHub Desktop.
inspect json and generate graphql schema
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