githubkit icon indicating copy to clipboard operation
githubkit copied to clipboard

Differences between regular GH and GHES API graphql endpoints

Open fau-st opened this issue 1 year ago • 4 comments

Hello,

I'm successfully using this library to interact with a GitHub Enterprise Server through the REST API. As you may know, for GHES, the REST API endpoint is located at https://[hostname]/api/v3/ instead of https://api.github.com/. So far so good as I can use the base_url parameter when building a GitHub object.

However, the troubles appear when I try to make a request using the graphql endpoint. Indeed, GHES uses this endpoint for graphql : https://[hostname]/api/graphql/ (the v3/ suffix is no longer present) and I can no longer use base_url because it's different whether I use the REST or graphql API.

Sample code :

from githubkit import (
    AppAuthStrategy,
    GitHub,
)

INSTALLATION_ID: int = ...
auth_strategy: AppAuthStrategy = ...

gh = GitHub(
    auth_strategy.as_installation(INSTALLATION_ID),
    base_url='https://ghes-hostname/api/v3',
)

gh.graphql('query { viewer { login } }')
# >>> githubkit.exception.RequestFailed: Response(404 Not Found, data_model=typing.Any)

Here, githubkit successfully retrieves a token through the REST API to use for the graphql query but then this query is made against https://ghes-hostname/api/v3/graphql and a 404 response is returned.

I'd like to know your opinion on this as supporting GHES might not be your priority. Let me know if you need more information.

fau-st avatar Sep 19 '24 10:09 fau-st

According to octokit, a workaround for GHES is needed. https://github.com/octokit/graphql.js/blob/dae781b027c19bcd458577cd9ac6ca888b2fdfeb/src/graphql.ts#L67-L72

yanyongyu avatar Sep 19 '24 13:09 yanyongyu

Based on your findings, I wrote a quick workaround based on HTTPS's event hooks.

async def _fix_ghes_graphql_endpoint(request: httpx.Request) -> None:
    GHES_GRAPHQL_SUFFIX_TOFIX = b"/api/v3/graphql"
    if request.url.raw_path.endswith(GHES_GRAPHQL_SUFFIX_TOFIX):
        request.url = request.url.copy_with(
            raw_path=request.url.raw_path[: -len(GHES_GRAPHQL_SUFFIX_TOFIX)] + b"/api/graphql"
        )

It can then be used during Client's creation and transparently fix URLs:

return httpx.Client(
    **self._get_client_defaults(),
    transport=transport,
    event_hooks={'request': [_fix_ghes_graphql_endpoint]}
)

What's your opinion on this? I'm not totally satisfied because hooks make it harder to follow the flow of the request but here it seems a good fit.

FYI: I'll be away for the next two weeks or so, I can try to create a PR when I'm back.

fau-st avatar Sep 20 '24 14:09 fau-st

Maybe a direct patch to endpoint url in graphql request function looks clearer. i will create a pr when i am free.

yanyongyu avatar Sep 20 '24 14:09 yanyongyu

Hi @fau-st , i have just create a PR to fix this, but i do not have a GHES account to test this. Could you please test or review the code to verify the graphql endpoint? you can install the githubkit from the fix/ghes-graphql branch.

yanyongyu avatar Sep 21 '24 07:09 yanyongyu

I just added some tests and docs about the base_url option in readme.

yanyongyu avatar Sep 28 '24 06:09 yanyongyu

@yanyongyu Thank you very much, I tested your changes and everything seems to work great.

fau-st avatar Oct 07 '24 07:10 fau-st