github.com/google/go-github/v64@v64.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/v64/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 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 //{"rate_limit", &github.RateLimits{}}, 53 {"users/octocat", &github.User{}}, 54 {"user", &github.User{}}, 55 {"users/willnorris/keys", &[]github.Key{}}, 56 {"orgs/google-test", &github.Organization{}}, 57 {"repos/google/go-github", &github.Repository{}}, 58 {"repos/google/go-github/issues/1", &github.Issue{}}, 59 {"/gists/9257657", &github.Gist{}}, 60 } { 61 err := testType(tt.url, tt.typ) 62 if err != nil { 63 fmt.Printf("error: %v\n", err) 64 } 65 } 66 } 67 68 // testType fetches the JSON resource at urlStr and compares its keys to the 69 // struct fields of typ. 70 func testType(urlStr string, typ interface{}) error { 71 slice := reflect.Indirect(reflect.ValueOf(typ)).Kind() == reflect.Slice 72 73 req, err := client.NewRequest("GET", urlStr, nil) 74 if err != nil { 75 return err 76 } 77 78 // start with a json.RawMessage so we can decode multiple ways below 79 raw := new(json.RawMessage) 80 _, err = client.Do(context.Background(), req, raw) 81 if err != nil { 82 return err 83 } 84 85 // unmarshal directly to a map 86 var m1 map[string]interface{} 87 if slice { 88 var s []map[string]interface{} 89 err = json.Unmarshal(*raw, &s) 90 if err != nil { 91 return err 92 } 93 m1 = s[0] 94 } else { 95 err = json.Unmarshal(*raw, &m1) 96 if err != nil { 97 return err 98 } 99 } 100 101 // unmarshal to typ first, then re-marshal and unmarshal to a map 102 err = json.Unmarshal(*raw, typ) 103 if err != nil { 104 return err 105 } 106 107 var byt []byte 108 if slice { 109 // use first item in slice 110 v := reflect.Indirect(reflect.ValueOf(typ)) 111 byt, err = json.Marshal(v.Index(0).Interface()) 112 if err != nil { 113 return err 114 } 115 } else { 116 byt, err = json.Marshal(typ) 117 if err != nil { 118 return err 119 } 120 } 121 122 var m2 map[string]interface{} 123 err = json.Unmarshal(byt, &m2) 124 if err != nil { 125 return err 126 } 127 128 // now compare the two maps 129 for k, v := range m1 { 130 if *skipURLs && strings.HasSuffix(k, "_url") { 131 continue 132 } 133 if _, ok := m2[k]; !ok { 134 fmt.Printf("%v missing field for key: %v (example value: %v)\n", reflect.TypeOf(typ), k, v) 135 } 136 } 137 138 return nil 139 }