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