github.com/google/go-github/v68@v68.0.0/test/fields/fields.go (about) 1 // Copyright 2014 The go-github AUTHORS. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 // This tool tests for the JSON mappings in the go-github data types. It will 7 // identify fields that are returned by the live GitHub API, but that are not 8 // currently mapped into a struct field of the relevant go-github type. This 9 // helps to ensure that all relevant data returned by the API is being made 10 // accessible, particularly new fields that are periodically (and sometimes 11 // quietly) added to the API over time. 12 // 13 // These tests simply aid in identifying which fields aren't being mapped; it 14 // is not necessarily true that every one of them should always be mapped. 15 // Some fields may be undocumented for a reason, either because they aren't 16 // actually used yet or should not be relied upon. 17 package main 18 19 import ( 20 "context" 21 "encoding/json" 22 "flag" 23 "fmt" 24 "os" 25 "reflect" 26 "strings" 27 28 "github.com/google/go-github/v68/github" 29 ) 30 31 var ( 32 client *github.Client 33 34 skipURLs = flag.Bool("skip_urls", false, "skip url fields") 35 ) 36 37 func main() { 38 flag.Parse() 39 40 token := os.Getenv("GITHUB_AUTH_TOKEN") 41 if token == "" { 42 fmt.Print("!!! No OAuth token. Some tests won't run. !!!\n\n") 43 client = github.NewClient(nil) 44 } else { 45 client = github.NewClient(nil).WithAuthToken(token) 46 } 47 48 for _, tt := range []struct { 49 url string 50 typ interface{} 51 }{ 52 {"users/octocat", &github.User{}}, 53 {"user", &github.User{}}, 54 {"users/willnorris/keys", &[]github.Key{}}, 55 {"orgs/google-test", &github.Organization{}}, 56 {"repos/google/go-github", &github.Repository{}}, 57 {"repos/google/go-github/issues/1", &github.Issue{}}, 58 {"/gists/9257657", &github.Gist{}}, 59 } { 60 err := testType(tt.url, tt.typ) 61 if err != nil { 62 fmt.Printf("error: %v\n", err) 63 } 64 } 65 } 66 67 // testType fetches the JSON resource at urlStr and compares its keys to the 68 // struct fields of typ. 69 func testType(urlStr string, typ interface{}) error { 70 slice := reflect.Indirect(reflect.ValueOf(typ)).Kind() == reflect.Slice 71 72 req, err := client.NewRequest("GET", urlStr, nil) 73 if err != nil { 74 return err 75 } 76 77 // start with a json.RawMessage so we can decode multiple ways below 78 raw := new(json.RawMessage) 79 _, err = client.Do(context.Background(), req, raw) 80 if err != nil { 81 return err 82 } 83 84 // unmarshal directly to a map 85 var m1 map[string]interface{} 86 if slice { 87 var s []map[string]interface{} 88 err = json.Unmarshal(*raw, &s) 89 if err != nil { 90 return err 91 } 92 m1 = s[0] 93 } else { 94 err = json.Unmarshal(*raw, &m1) 95 if err != nil { 96 return err 97 } 98 } 99 100 // unmarshal to typ first, then re-marshal and unmarshal to a map 101 err = json.Unmarshal(*raw, typ) 102 if err != nil { 103 return err 104 } 105 106 var byt []byte 107 if slice { 108 // use first item in slice 109 v := reflect.Indirect(reflect.ValueOf(typ)) 110 byt, err = json.Marshal(v.Index(0).Interface()) 111 if err != nil { 112 return err 113 } 114 } else { 115 byt, err = json.Marshal(typ) 116 if err != nil { 117 return err 118 } 119 } 120 121 var m2 map[string]interface{} 122 err = json.Unmarshal(byt, &m2) 123 if err != nil { 124 return err 125 } 126 127 // now compare the two maps 128 for k, v := range m1 { 129 if *skipURLs && strings.HasSuffix(k, "_url") { 130 continue 131 } 132 if _, ok := m2[k]; !ok { 133 fmt.Printf("%v missing field for key: %v (example value: %v)\n", reflect.TypeOf(typ), k, v) 134 } 135 } 136 137 return nil 138 }