githubv4 icon indicating copy to clipboard operation
githubv4 copied to clipboard

Decode struct within a slice

Open nitsanfiverr opened this issue 6 years ago • 8 comments

Hi, I was just using the package to query tags list on my repo. the query looks like this:

{
  repository(owner: "rails", name: "rails") {
    name
    tags: refs(refPrefix: "refs/tags/", first: 2, orderBy: {field: TAG_COMMIT_DATE, direction: DESC}) {
      edges {
        tag: node {
          name
        }
      }
    }
  }
}

The response looks like this:

{
  "data": {
    "repository": {
      "name": "rails",
      "tags": {
        "edges": [
          {
            "tag": {
              "name": "v5.2.0.rc2"
            }
          },
          {
            "tag": {
              "name": "v5.1.5"
            }
          }
        ]
      }
    }
  }
}

it's working fine on graphQL explorer, but fails to decode here in the package. from the small Debugging I did it seems like there is no attempt to decode objects within the slice and I'm getting slice doesn't exist in any of 1 places to unmarshal Any idea or fix ? Thanks !

nitsanfiverr avatar Mar 22 '18 11:03 nitsanfiverr

Thanks for reporting.

Can you share the Go code you wrote for that query to help me investigate this?

dmitshur avatar Mar 22 '18 19:03 dmitshur

For anyone else facing this issue, double check that all of your struct fields are matching the GraphQL response and that you aren't forgetting any slices

alexdor avatar May 19 '18 10:05 alexdor

Heey, I'm facing a similar issue (Full disclosure: Total newbie to Golang and generally programming, really). It tried some debugging in your graphql and githubv4, and it seems that the request done in GraphiQL and your libraries differ somehow. The response doesn't get populated with the releases, but the releases are there in Github and I get them when I run the request in GraphQL API Explorer (dumped with debugginging and parsed into browser).

My code is a programming excercise for myself:


import (
	"context"
	"fmt"
	"os"
	"strconv"

	"github.com/olekukonko/tablewriter"

	"github.com/shurcooL/githubv4"
	"golang.org/x/oauth2"
)

// Define the structure of the GraphQL request
type node struct {
	Name githubv4.String
}

type release struct {
	Nodes []node
}

type repository struct {
	Name       string
	IsArchived bool
	Releases   struct {
		release
	} `graphql:"releases(last:1, orderBy: {field: CREATED_AT, direction: ASC})"`
}

var q struct {
	Organization struct {
		Name         string
		Repositories struct {
			TotalCount int
			PageInfo   struct {
				EndCursor   githubv4.String
				HasNextPage bool
			}
			Nodes []repository
		} `graphql:"repositories(first: 100, after: $pageCursor)"`
	} `graphql:"organization(login: \"Hashicorp\")"`
}

func main() {
	src := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")},
	)
	httpClient := oauth2.NewClient(context.Background(), src)
	client := githubv4.NewClient(httpClient)

	var allRepos []repository

	variables := map[string]interface{}{
		"pageCursor": (*githubv4.String)(nil), // Null after argument to get first page.
	}
	for { // we'll have to loop through the request as the response will be paginated, at 100 nodes per page.
		err := client.Query(context.Background(), &q, variables)

		if err != nil {
			// Handle error.
			fmt.Println(err)
		}
		allRepos = append(allRepos, q.Organization.Repositories.Nodes...)

		if !q.Organization.Repositories.PageInfo.HasNextPage {
			break
		}
		// this cursor points to the last node returned in every request, so well use that to get the start of the next request
		variables["pageCursor"] = githubv4.NewString(q.Organization.Repositories.PageInfo.EndCursor)
	}

	// Create and render the table
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Repositories", "Last release", "Archived"})
	for _, node := range allRepos {
		if len(node.Releases.Nodes) > 0 { // most repos doesn't have releases (nodes in this case), so release.Name will be undefined = solve with if-else
			for _, release := range node.Releases.Nodes {
				table.Append([]string{node.Name, string(release.Name), strconv.FormatBool(node.IsArchived)})
			}
		} else {
			table.Append([]string{node.Name, "No release identified. ", strconv.FormatBool(node.IsArchived)})
		}

	}
	table.Render()

	// Summary
	fmt.Println("Amount of repos in", q.Organization.Name, "is", q.Organization.Repositories.TotalCount)

}

UPDATE: Actually this might be a bug on the GitHub side. curl or net/http will get the same result (missing release-nodes-names) but GitHubs own API...Exploder will get the expected one. Sooo... bug there?

maxramqvist avatar Jun 27 '18 09:06 maxramqvist

I'm also seeing a similar issue.

The following works in the GitHub GraphQL explorer:

{
  repository(owner: "kubernetes", name: "kubernetes") {
    issues(last: 5, states: OPEN) {
      edges {
        node {
          title
          url
        }
      }
    }
  }
}

But I get the error when making the query in Go.

var Query struct {
	Repository struct {
		Issues struct {
			Edges struct {
				Node struct {
					Title string
					Url string
				}
			}
		} `graphql:"issues(last: 5)"`
	} `graphql:"repository(owner: \"kubernetes\", name: \"kubernetes\")"`
}
err := client.Query(context.Background(), &Query, nil)

vllry avatar Feb 24 '19 18:02 vllry

@vllry Edges should be a slice of structs rather than a single strict.

dmitshur avatar Feb 24 '19 19:02 dmitshur

Thank you @dmitshur, sorry for the misunderstanding!

vllry avatar Feb 24 '19 19:02 vllry

I meet same problem too, I have checked value in *out.Data and it is correct, but got error after jsonutil.UnmarshalGraphQL

here is my code :

	client := graphql.NewClient("http://192.1.1.115:8080/v1/graphql",http.DefaultClient)

	var query struct {
		erp_token []struct {
			token graphql.String `json:"token"`
			last_used graphql.String `json:"last_used"`
		} `json:"erp_token"`
	}

	err := client.Query(context.Background(), &query, nil)
	if err != nil {
                // I got `struct field for "erp_token" doesn't exist in any of 1 places to unmarshal` here.
		fmt.Println(err)
	}

and this is what I got in *out.Data

    "erp_token": [
      {
        "token": "103ad542-ea30-46d8-bfae-b8f3f4c60c58",
        "last_used": "2019-11-21T17:12:55"
      }
    ]

did i do anything wrong ?

tooilxui avatar Mar 06 '20 09:03 tooilxui

@tooilxui I'm seeing two issues in your query variable. You're using unexported fields, and you're using json: struct field tags. You should use exported fields and graphql: struct field tags instead:

var query struct {
	ERPToken []struct {
		Token    graphql.String `graphql:"token"`
		LastUsed graphql.String `graphql:"last_used"`
	} `graphql:"erp_token"`
}

See https://github.com/shurcooL/githubv4#arguments-and-variables.

dmitshur avatar Mar 07 '20 18:03 dmitshur