Github GraphQL API: How can I find out which fields are searchable? - rest

When I run the query:
{
"query": "{user(login: \"furknyavuz\") {repositories(first: 50, isPrivate: false) {nodes {name url}}}}"
}
I getting the following error:
{
"data": null,
"errors": [
{
"message": "Field 'repositories' doesn't accept argument 'isPrivate'",
"locations": [
{
"line": 1,
"column": 51
}
]
}
]
}
I can see isPivate is field of Repository object but I'm unable to search with it.
I'm not expecting to search with all fields of the object, but critical question is, how can I see which fields are searchable or indexable?

isPrivate is a field of Repository object but repositories inside User object is of type RepositoryConnection and repositories connection item has the following argument/type :
affiliations [RepositoryAffiliation]
after String
before String
first Int
isFork Boolean
isLocked Boolean
last Int
orderBy RepositoryOrder
privacy RepositoryPrivacy
RepositoryPrivacy is an enum with two values : PUBLIC and PRIVATE.
the following request will return private repo :
{
user(login: "furknyavuz") {
repositories(first: 50, privacy:PRIVATE) {
nodes {
name
url
}
}
}
}
Note that in the explorer, if you type CTRL+space you will have the schema listing with types :
Also, CTRL+space again after ":" will gives you the enum values :

Autocomplete:
Navigate to Github's GraphQL API Explorer. This is a GraphiQL interface that lets you write your queries and run in them in real time. One of the neat features of GraphiQL is that includes an auto-complete feature. When you're typing the arguments for a field, just press Alt+Space or Shift+Space and a list of possible arguments will pop up. This works for fields too.
The docs:
You can also view the documentation for the schema by hitting the Docs link in the upper right corner of the interface. This will bring up a list of all possible fields, including what arguments they take. There's also a schema reference page here.
GraphQL:
Lastly, you can actually just ask the GraphQL endpoint yourself. For example, running this query will list all types for the schema and the arguments used by each one:
{
__schema {
types {
name
inputFields {
name
description
type {
name
}
defaultValue
}
}
}
}

Related

Distinguish private/internal github repositories in graphql query

I'm trying to fetch all private repositories from our organization. In the following graphql query it returns both internal and private repositories.
For privacy it is only possible to filter on PRIVATE and PUBLIC.
It seems there is only one field called isPrivate, nothing like isInternal as far I can see.
query organizationRepositories($owner: String!) {
organization(login:$owner) {
repositories(first: 100, privacy: PRIVATE) {
totalCount
nodes {
owner {
login
}
name
id
url
isPrivate
}
}
}
}
All results using above query show isPrivate: true, I know for a fact some of these are internal and some are private.
Is there a way to distinguish between private and internal repositories? Either by looping over the results based on some fields combo which indicates private vs internal or by querying in a different way.
You can use a search query:
query {
search(query: "org:<my-org> is:internal", type: REPOSITORY, first: 100) {
repositoryCount
nodes {
... on Repository {
name
}
}
}
}
For more info, see https://docs.github.com/en/github/searching-for-information-on-github/searching-for-repositories#search-by-repository-visibility

Why am I able to bypass pagination when I call the same field twice (with different queries) in GitHub's GraphQL API

I noticed something I don't understand while trying to get the number of open issues per repository for a user.
When I use the following query I am asked to perform pagination (as expected) -
query {
user(login:"armsp"){
repositories{
nodes{
name
issues(states: OPEN){
totalCount
}
}
}
}
}
The error message after running the above -
{
"data": {
"user": null
},
"errors": [
{
"type": "MISSING_PAGINATION_BOUNDARIES",
"path": [
"user",
"repositories"
],
"locations": [
{
"line": 54,
"column": 5
}
],
"message": "You must provide a `first` or `last` value to properly paginate the `repositories` connection."
}
]
}
However when I do the following I actually get all the results which doesn't make any sense to me -
query {
user(login:"armsp"){
repositories{
totalCount
}
repositories{
nodes{
name
issues(states: OPEN){
totalCount
}
}
}
}
}
Shouldn't I be asked for pagination in the second query too ?
TLDR; This appears to be a bug. There's no way to bypass the limit applied when fetching a list of resources.
Limiting responses like this is a common feature of public APIs -- if the response could include thousands or millions of results, it'll tie up a lot of server resources to fulfill it all at once. Allowing users to make those sort of queries is both costly and a potential security risk.
Github's intent appears to be to always limit the amount of results when fetching a list of resources. This isn't well documented on the GraphQL side, but matches the behavior of their REST API:
Requests that return multiple items will be paginated to 30 items by default. You can specify further pages with the ?page parameter. For some resources, you can also set a custom page size up to 100 with the ?per_page parameter.
For connections, it looks like the check for the first or last parameter is only ran whenever the nodes field is present in the selection set. This makes sense, since this is ultimately the field we want to limit -- requesting other fields like totalDiskUsage or totalDiskUsage, even without a limit argument, is harmless with the regard to above concerns.
Things get funky when you consider how GraphQL handles selection sets with selections that have the same name. Without getting into the nitty gritty details, GraphQL will let you request the same field multiple times. If the field in question has a selection set, it will effectively merge the selection sets into a single one. So
query {
user(login:"armsp") {
repositories {
totalCount
}
repositories {
totalDiskUsage
}
}
}
becomes and is equivalent to
query {
user(login:"armsp") {
repositories {
totalCount
totalDiskUsage
}
}
}
Side note: The above does not hold true if you explicitly give one of the fields an alias since then the two fields have different response names.
All that to say, technically this query:
query {
user(login:"armsp"){
repositories{
totalCount
}
repositories{
nodes{
name
issues(states: OPEN){
totalCount
}
}
}
}
}
should also blow up with the same MISSING_PAGINATION_BOUNDARIES error. The fact that it doesn't means the selection set merging is somehow borking the check that's in place. This is clearly a bug. However, even while this appears to "work", it still doesn't get around whatever limits Github has applies at the storage layer -- you will always get at most 100 results even when exploiting the above bug.

Compound queries

I have a RESTful service that accepts a custom query, like this:
/entities/User?actions=
{
"$link": {
"entityType": "Manager",
"entity": {
"name": "John Smith"
},
"linkName": "managers",
"backLinkName": "account",
"$set": {
"propertyName": "aclWrite",
"propertyValue": {
"$ref": {
"propertyName": "entityId"
}
}
}
}
}
Which simply means:
Create a new Entity of type User
Create a new Entity of type Manager with the field name, linking the User to be created to this Manager through link name "managers"
Then back-linking the Manager entity to be created to the User with a link name "account" and setting the Manager entity write ACL (Access Control List) to the ID of the User to be created.
I created this query structure because I can't find any suitable Query language that can seem to support such action/procedure.
The question here is are there any Query language that can support such compound action/procedure or can GraphQL handle such?
As a specification, GraphQL doesn't care what fields your schema defines, what arguments those fields take or what your field resolvers do with those arguments. So it's perfectly feasible to design a schema that would let the client compose an equivalent mutation:
mutation {
link(
entityType: "Manager"
entity: {
name: "John Smith"
}
linkName: "managers"
backLinkName: "account"
set: {
propertyName: "aclWrite"
propertyValue: {
ref: {
propertyName: "entityId"
}
}
}
) {
# some fields here to return in the response
}
}
GraphQL does not support references to other nodes inside the same query, so you would still probably want a single mutation whose input mirrored your existing API. That said, using GraphQL for this may still be preferable because of request validation, which is all the more important with complex requests like this. Having an IDE like GraphiQL or GraphQL Playground that lets you write your queries using autocomplete is a big plus too.

must have a selection of subfields. Did you mean \"createEvent { ... }\"?", [graphql] [duplicate]

Hi I am trying to learn GraphQL language. I have below snippet of code.
// Welcome to Launchpad!
// Log in to edit and save pads, run queries in GraphiQL on the right.
// Click "Download" above to get a zip with a standalone Node.js server.
// See docs and examples at https://github.com/apollographql/awesome-launchpad
// graphql-tools combines a schema string with resolvers.
import { makeExecutableSchema } from 'graphql-tools';
// Construct a schema, using GraphQL schema language
const typeDefs = `
type User {
name: String!
age: Int!
}
type Query {
me: User
}
`;
const user = { name: 'Williams', age: 26};
// Provide resolver functions for your schema fields
const resolvers = {
Query: {
me: (root, args, context) => {
return user;
},
},
};
// Required: Export the GraphQL.js schema object as "schema"
export const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
// Optional: Export a function to get context from the request. It accepts two
// parameters - headers (lowercased http headers) and secrets (secrets defined
// in secrets section). It must return an object (or a promise resolving to it).
export function context(headers, secrets) {
return {
headers,
secrets,
};
};
// Optional: Export a root value to be passed during execution
// export const rootValue = {};
// Optional: Export a root function, that returns root to be passed
// during execution, accepting headers and secrets. It can return a
// promise. rootFunction takes precedence over rootValue.
// export function rootFunction(headers, secrets) {
// return {
// headers,
// secrets,
// };
// };
Request:
{
me
}
Response:
{
"errors": [
{
"message": "Field \"me\" of type \"User\" must have a selection of subfields. Did you mean \"me { ... }\"?",
"locations": [
{
"line": 4,
"column": 3
}
]
}
]
}
Does anyone know what I am doing wrong ? How to fix it ?
From the docs:
A GraphQL object type has a name and fields, but at some point those
fields have to resolve to some concrete data. That's where the scalar
types come in: they represent the leaves of the query.
GraphQL requires that you construct your queries in a way that only returns concrete data. Each field has to ultimately resolve to one or more scalars (or enums). That means you cannot just request a field that resolves to a type without also indicating which fields of that type you want to get back.
That's what the error message you received is telling you -- you requested a User type, but you didn't tell GraphQL at least one field to get back from that type.
To fix it, just change your request to include name like this:
{
me {
name
}
}
... or age. Or both. You cannot, however, request a specific type and expect GraphQL to provide all the fields for it -- you will always have to provide a selection (one or more) of fields for that type.

How to get info about specific user using github graphQL?

How can I get info about specific user or specific repo using github GraphQL? I've try this query:
{
search (query: "torvalds", type: USER, first: 1){
edges {
node {
}
}
}
}
but autocomplete for node show only __typename which return string "User".
DEMO
search returns a SearchResultItem, which is an interface. In order to access fields on it, you need to use a fragment on a concrete type like so:
{
search (query: "torvalds", type: USER, first: 1){
edges {
node {
... on User {
login
}
}
}
}
}
I made a short video tour of GitHub's GraphQL API which you might find useful: https://youtu.be/6xO87LlijoQ
EDIT: If you're just looking for a user or org and know the exact name, #stubailo's answer is actually better. You'll still need to use a fragment for most fields, but you'll get just one result of type RepositoryOwner.
The best way to get information about a specific user is to use the repositoryOwner query, like so:
{
repositoryOwner(login: "stubailo") {
login
... on User {
bio
}
}
}
Search is good too, but if you know the name of a user or organization, it's more direct to use the query above.