github.com/google/go-github/v49@v49.1.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/v49/github" 29 "golang.org/x/oauth2" 30 ) 31 32 var ( 33 client *github.Client 34 35 // auth indicates whether tests are being run with an OAuth token. 36 // Tests can use this flag to skip certain tests when run without auth. 37 auth bool 38 39 skipURLs = flag.Bool("skip_urls", false, "skip url fields") 40 ) 41 42 func main() { 43 flag.Parse() 44 45 token := os.Getenv("GITHUB_AUTH_TOKEN") 46 if token == "" { 47 print("!!! No OAuth token. Some tests won't run. !!!\n\n") 48 client = github.NewClient(nil) 49 } else { 50 tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( 51 &oauth2.Token{AccessToken: token}, 52 )) 53 client = github.NewClient(tc) 54 auth = true 55 } 56 57 for _, tt := range []struct { 58 url string 59 typ interface{} 60 }{ 61 //{"rate_limit", &github.RateLimits{}}, 62 {"users/octocat", &github.User{}}, 63 {"user", &github.User{}}, 64 {"users/willnorris/keys", &[]github.Key{}}, 65 {"orgs/google-test", &github.Organization{}}, 66 {"repos/google/go-github", &github.Repository{}}, 67 {"repos/google/go-github/issues/1", &github.Issue{}}, 68 {"/gists/9257657", &github.Gist{}}, 69 } { 70 err := testType(tt.url, tt.typ) 71 if err != nil { 72 fmt.Printf("error: %v\n", err) 73 } 74 } 75 } 76 77 // testType fetches the JSON resource at urlStr and compares its keys to the 78 // struct fields of typ. 79 func testType(urlStr string, typ interface{}) error { 80 slice := reflect.Indirect(reflect.ValueOf(typ)).Kind() == reflect.Slice 81 82 req, err := client.NewRequest("GET", urlStr, nil) 83 if err != nil { 84 return err 85 } 86 87 // start with a json.RawMessage so we can decode multiple ways below 88 raw := new(json.RawMessage) 89 _, err = client.Do(context.Background(), req, raw) 90 if err != nil { 91 return err 92 } 93 94 // unmarshal directly to a map 95 var m1 map[string]interface{} 96 if slice { 97 var s []map[string]interface{} 98 err = json.Unmarshal(*raw, &s) 99 if err != nil { 100 return err 101 } 102 m1 = s[0] 103 } else { 104 err = json.Unmarshal(*raw, &m1) 105 if err != nil { 106 return err 107 } 108 } 109 110 // unmarshal to typ first, then re-marshal and unmarshal to a map 111 err = json.Unmarshal(*raw, typ) 112 if err != nil { 113 return err 114 } 115 116 var byt []byte 117 if slice { 118 // use first item in slice 119 v := reflect.Indirect(reflect.ValueOf(typ)) 120 byt, err = json.Marshal(v.Index(0).Interface()) 121 if err != nil { 122 return err 123 } 124 } else { 125 byt, err = json.Marshal(typ) 126 if err != nil { 127 return err 128 } 129 } 130 131 var m2 map[string]interface{} 132 err = json.Unmarshal(byt, &m2) 133 if err != nil { 134 return err 135 } 136 137 // now compare the two maps 138 for k, v := range m1 { 139 if *skipURLs && strings.HasSuffix(k, "_url") { 140 continue 141 } 142 if _, ok := m2[k]; !ok { 143 fmt.Printf("%v missing field for key: %v (example value: %v)\n", reflect.TypeOf(typ), k, v) 144 } 145 } 146 147 return nil 148 }