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