github.com/google/go-github/v60@v60.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/v60/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  }