Example usage

To use ghgql in a project:

I’ve designed ghgql to work on query files rather than pure query strings. This is fully intentional to not get into the habit of putting query strings all around in the code. Instead when the queries live in files we can validate them agains Github’s GraphQL schema.

Long story short, instead of using ghgql.GithubGraphQL.query() you should use ghgql.GithubGraphQL.query_from_file()!

Running our first query

Suppose we have the following query to fetch the last three issues kwk/ghgql repository:

query = """
query ($owner: String!, $repo: String!) {
  repository(owner: $owner, name: $repo) {
    issues(last: 3) {
      edges {
        node {
          title
          state
          author {
            login
          }
        }
      }
      totalCount
    }
  }
}
"""

This is how we can use it with ghgql:

import os
import ghgql

with ghgql.GithubGraphQL(token=os.getenv("GITHUB_TOKEN")) as ghapi:
    result = ghapi.query(query=query, variables={"owner": "kwk", "repo": "ghgql"})

Easy inspection of results

    from pprint import pprint
    pprint(result)
{'data': {'repository': {'issues': {'edges': [{'node': {'author': {'login': 'tru'},
                                                        'state': 'CLOSED',
                                                        'title': 'Token not '
                                                                 'set to the '
                                                                 'session if '
                                                                 'not used as '
                                                                 'a '
                                                                 'contextmanager'}},
                                              {'node': {'author': {'login': 'tru'},
                                                        'state': 'CLOSED',
                                                        'title': 'Let '
                                                                 'result.get() '
                                                                 'return a '
                                                                 'Result or '
                                                                 'provide some '
                                                                 'other way to '
                                                                 'use the '
                                                                 'get() '
                                                                 'function on '
                                                                 'sub dicts.'}},
                                              {'node': {'author': {'login': 'kwk'},
                                                        'state': 'CLOSED',
                                                        'title': 'no local '
                                                                 'precedence '
                                                                 'for '
                                                                 'raise_on_error'}}],
                                    'totalCount': 5}}}}

Notice that result is a dictionary with a top-level "data" key. This indicates that there are no errors. if there were errors, we would see a top-level errors element.

Convenient access

The ghgql library advocates the use of the fnc library to query nested results. Let’s import fnc really quick. For your convenience we’ve made fnc a dependency of ghgql.

    import fnc

Let’s say we want to query for the states of all issues.

    issues = fnc.get("data.repository.issues.edges", result)
    states = fnc.map("node.state", issues)
    pprint(list(states))
['CLOSED', 'CLOSED', 'CLOSED']

Handle errors

God forbid, but there might be errors when you’re writing a GraphQL query. Let’s query github with a completely invalid query and inspect the results:

query="Yes, I'm invalid!"
with ghgql.GithubGraphQL(token=os.getenv("GITHUB_TOKEN")) as ghapi:
    result = ghapi.query(query=query, variables={"searchQuery": "llvm/llvm-project"})
    pprint(result)
{'errors': [{'locations': [{'column': 1, 'line': 1}],
             'message': 'Parse error on "Yes" (IDENTIFIER) at [1, 1]'}]}

Notice that ther no longer is a data key on the top-level of the result dictionary. It is your responsibility to query for the errors and then handle it as you like.

    if fnc.has("errors", result):
        print("ERROR: {}".format(RuntimeError(fnc.get("errors[0]", result))))
ERROR: {'message': 'Parse error on "Yes" (IDENTIFIER) at [1, 1]', 'locations': [{'line': 1, 'column': 1}]}

I you prefer getting an exception, you can tell ghgql to throw one in case of an error. There are two options:

  1. When constructing the GithubGraphQL object so that all failing queries throw and exception.

  2. Per query

# One for all
query="Yes, I'm invalid!"
with ghgql.GithubGraphQL(token=os.getenv("GITHUB_TOKEN"), raise_on_error=True) as ghapi:
    try:
        result = ghapi.query(query=query, variables={"searchQuery": "llvm/llvm-project"})
    except RuntimeError as ex:
        print(f"Caught exception: {str(ex)}")
Caught exception: Parse error on "Yes" (IDENTIFIER) at [1, 1]
# Per query
query="Yes, I'm invalid!"
with ghgql.GithubGraphQL(token=os.getenv("GITHUB_TOKEN")) as ghapi:
    try:
        result = ghapi.query(query=query, variables={"searchQuery": "llvm/llvm-project"}, raise_on_error=True)
    except RuntimeError as ex:
        print(f"Caught exception: {str(ex)}")
Caught exception: Parse error on "Yes" (IDENTIFIER) at [1, 1]

Conclusion

ghgql provides ways to query the Github GraphQL and allows for easy inspection of the resulting objects with the help of fnc.

TODO(kwk): In the future we can show how mutations work with ghgql.