Skip to content

Commit

Permalink
Rework GraphQL mutations (#3046)
Browse files Browse the repository at this point in the history
This contains the following breaking changes:
- use mutation name as given in Github GraphQL documentation
- remove capitalized mutation name from mutation request
- rename `variables` argument to `mutation_input`
- rename `output` argument to `output_schema`
- return response data according to output schema (drop outer
data.mutationName path)
  • Loading branch information
EnricoMi authored Oct 28, 2024
1 parent 72fa627 commit 2722225
Show file tree
Hide file tree
Showing 15 changed files with 66 additions and 68 deletions.
12 changes: 12 additions & 0 deletions doc/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ Change log
Stable versions
~~~~~~~~~~~~~~~

Version 2.5.0 (XXXXXXX)
-----------------------

Breaking Changes
^^^^^^^^^^^^^^^^

* Parameters of method ``github.Requester.Requester.graphql_named_mutation`` have been renamed:

* Parameter ``variables`` renamed to ``mutation_input``
* Parameter ``output`` renamed to ``output_schema``
* Default value of parameter ``output`` has been removed

Version 2.4.0 (August 26, 2024)
-------------------------------

Expand Down
16 changes: 8 additions & 8 deletions github/IssueComment.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,11 @@ def minimize(self, reason: str = "OUTDATED") -> bool:
"classifier": reason,
}
_, data = self._requester.graphql_named_mutation(
mutation_name="minimize_comment",
variables={"input": NotSet.remove_unset_items(variables)},
output="minimizedComment { isMinimized }",
mutation_name="minimizeComment",
mutation_input=NotSet.remove_unset_items(variables),
output_schema="minimizedComment { isMinimized }",
)
return data["data"]["minimizeComment"]["minimizedComment"]["isMinimized"] is True
return data["minimizedComment"]["isMinimized"] is True

def unminimize(self) -> bool:
"""
Expand All @@ -219,11 +219,11 @@ def unminimize(self) -> bool:
"subjectId": self.node_id,
}
_, data = self._requester.graphql_named_mutation(
mutation_name="unminimize_comment",
variables={"input": NotSet.remove_unset_items(variables)},
output="unminimizedComment { isMinimized }",
mutation_name="unminimizeComment",
mutation_input=NotSet.remove_unset_items(variables),
output_schema="unminimizedComment { isMinimized }",
)
return data["data"]["unminimizeComment"]["unminimizedComment"]["isMinimized"] is False
return data["unminimizedComment"]["isMinimized"] is False

def _useAttributes(self, attributes: dict[str, Any]) -> None:
if "body" in attributes: # pragma no branch
Expand Down
12 changes: 6 additions & 6 deletions github/PullRequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,9 @@ def enable_automerge(

# Make the request
_, data = self._requester.graphql_named_mutation(
mutation_name="enable_pull_request_auto_merge",
variables={"input": NotSet.remove_unset_items(variables)},
output="actor { avatarUrl login resourcePath url } clientMutationId",
mutation_name="enablePullRequestAutoMerge",
mutation_input=NotSet.remove_unset_items(variables),
output_schema="actor { avatarUrl login resourcePath url } clientMutationId",
)
return data

Expand All @@ -841,9 +841,9 @@ def disable_automerge(

# Make the request
_, data = self._requester.graphql_named_mutation(
mutation_name="disable_pull_request_auto_merge",
variables={"input": NotSet.remove_unset_items(variables)},
output="actor { avatarUrl login resourcePath url } clientMutationId",
mutation_name="disablePullRequestAutoMerge",
mutation_input=NotSet.remove_unset_items(variables),
output_schema="actor { avatarUrl login resourcePath url } clientMutationId",
)
return data

Expand Down
22 changes: 10 additions & 12 deletions github/Requester.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,23 +632,21 @@ def graphql_query(self, query: str, variables: Dict[str, Any]) -> Tuple[Dict[str
return response_headers, data

def graphql_named_mutation(
self, mutation_name: str, variables: Dict[str, Any], output: Optional[str] = None
self, mutation_name: str, mutation_input: Dict[str, Any], output_schema: str
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
"""
Create a mutation in the format:
mutation MutationName($input: MutationNameInput!) {
mutationName(input: $input) {
<output>
}
mutation Mutation($input: MutationNameInput!) {
mutationName(input: $input) { <output_schema> }
}
and call the self.graphql_query method
"""
title = "".join([x.capitalize() for x in mutation_name.split("_")])
mutation_name = title[:1].lower() + title[1:]
output = output or ""
query = f"mutation {title}($input: {title}Input!) {{ {mutation_name}(input: $input) {{ {output} }} }}"
and call the self.graphql_query method.
return self.graphql_query(query, variables)
Returns the response data according to given output schema.
"""
mutation_input_name = mutation_name[:1].upper() + mutation_name[1:] + "Input!"
query = f"mutation Mutation($input: {mutation_input_name}) {{ {mutation_name}(input: $input) {{ {output_schema} }} }}"
headers, data = self.graphql_query(query, {"input": mutation_input})
return headers, data.get("data", {}).get(mutation_name, {})

def __check(
self,
Expand Down
18 changes: 7 additions & 11 deletions tests/GraphQl.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,13 @@ def setUp(self):

def expected(self, base_url: str = "https://github.com") -> Dict[Any, Any]:
return {
"data": {
"disablePullRequestAutoMerge": {
"actor": {
"avatarUrl": "/~https://avatars.githubusercontent.com/u/14806300?u=786f9f8ef8782d45381b01580f7f7783cf9c7e37&v=4",
"login": "heitorpolidoro",
"resourcePath": "/heitorpolidoro",
"url": f"{base_url}/heitorpolidoro",
},
"clientMutationId": None,
}
}
"actor": {
"avatarUrl": "/~https://avatars.githubusercontent.com/u/14806300?u=786f9f8ef8782d45381b01580f7f7783cf9c7e37&v=4",
"login": "heitorpolidoro",
"resourcePath": "/heitorpolidoro",
"url": f"{base_url}/heitorpolidoro",
},
"clientMutationId": None,
}

def testRequesterGraphQlPrefix(self):
Expand Down
36 changes: 14 additions & 22 deletions tests/PullRequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,17 +522,13 @@ def testEnableAutomerge(self):
expected_head_oid="0283d46537193f1fed7d46859f15c5304b9836f9",
)
assert response == {
"data": {
"enablePullRequestAutoMerge": {
"actor": {
"avatarUrl": "/~https://avatars.githubusercontent.com/u/14806300?u=786f9f8ef8782d45381b01580f7f7783cf9c7e37&v=4",
"login": "heitorpolidoro",
"resourcePath": "/heitorpolidoro",
"url": "/~https://github.com/heitorpolidoro",
},
"clientMutationId": None,
}
}
"actor": {
"avatarUrl": "/~https://avatars.githubusercontent.com/u/14806300?u=786f9f8ef8782d45381b01580f7f7783cf9c7e37&v=4",
"login": "heitorpolidoro",
"resourcePath": "/heitorpolidoro",
"url": "/~https://github.com/heitorpolidoro",
},
"clientMutationId": None,
}

def testEnableAutomergeDefaultValues(self):
Expand Down Expand Up @@ -566,15 +562,11 @@ def testEnableAutomergeError(self):
def testDisableAutomerge(self):
response = self.pull.disable_automerge()
assert response == {
"data": {
"disablePullRequestAutoMerge": {
"actor": {
"avatarUrl": "/~https://avatars.githubusercontent.com/u/14806300?u=786f9f8ef8782d45381b01580f7f7783cf9c7e37&v=4",
"login": "heitorpolidoro",
"resourcePath": "/heitorpolidoro",
"url": "/~https://github.com/heitorpolidoro",
},
"clientMutationId": None,
}
}
"actor": {
"avatarUrl": "/~https://avatars.githubusercontent.com/u/14806300?u=786f9f8ef8782d45381b01580f7f7783cf9c7e37&v=4",
"login": "heitorpolidoro",
"resourcePath": "/heitorpolidoro",
"url": "/~https://github.com/heitorpolidoro",
},
"clientMutationId": None,
}
2 changes: 1 addition & 1 deletion tests/ReplayData/GraphQl.testDefaultUrl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ api.github.com
None
/graphql
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'}
{"query": "mutation DisablePullRequestAutoMerge($input: DisablePullRequestAutoMergeInput!) { disablePullRequestAutoMerge(input: $input) { actor { avatarUrl login resourcePath url } clientMutationId } }", "variables": {"input": {"pullRequestId": "MDExOlB1bGxSZXF1ZXN0MTQzNjIxNQ=="}}}
{"query": "mutation Mutation($input: DisablePullRequestAutoMergeInput!) { disablePullRequestAutoMerge(input: $input) { actor { avatarUrl login resourcePath url } clientMutationId } }", "variables": {"input": {"pullRequestId": "MDExOlB1bGxSZXF1ZXN0MTQzNjIxNQ=="}}}
200
[('Server', 'GitHub.com'), ('Date', 'Wed, 07 Jun 2023 08:27:45 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Transfer-Encoding', 'chunked'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With'), ('ETag', 'W/"1573389419d98a2b3d9a6b1fc058a68f1fc3d31108ccfdab8ca4121153626211"'), ('Last-Modified', 'Wed, 07 Jun 2023 04:02:03 GMT'), ('X-OAuth-Scopes', 'public_repo'), ('X-Accepted-OAuth-Scopes', 'repo'), ('X-GitHub-Media-Type', 'github.v3; format=json'), ('x-github-api-version-selected', '2022-11-28'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4959'), ('X-RateLimit-Reset', '1686126614'), ('X-RateLimit-Used', '41'), ('X-RateLimit-Resource', 'core'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Content-Encoding', 'gzip'), ('X-GitHub-Request-Id', '8A96:10854:895AE2:8AB25B:64803F81')]
{"data": {"disablePullRequestAutoMerge": {"actor": {"avatarUrl": "/~https://avatars.githubusercontent.com/u/14806300?u=786f9f8ef8782d45381b01580f7f7783cf9c7e37&v=4", "login": "heitorpolidoro", "resourcePath": "/heitorpolidoro", "url": "/~https://github.com/heitorpolidoro"}, "clientMutationId": null}}}
2 changes: 1 addition & 1 deletion tests/ReplayData/GraphQl.testOtherPort.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ my.enterprise.com
8080
/api/graphql
{'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'}
{"query": "mutation DisablePullRequestAutoMerge($input: DisablePullRequestAutoMergeInput!) { disablePullRequestAutoMerge(input: $input) { actor { avatarUrl login resourcePath url } clientMutationId } }", "variables": {"input": {"pullRequestId": "MDExOlB1bGxSZXF1ZXN0MTQzNjIxNQ=="}}}
{"query": "mutation Mutation($input: DisablePullRequestAutoMergeInput!) { disablePullRequestAutoMerge(input: $input) { actor { avatarUrl login resourcePath url } clientMutationId } }", "variables": {"input": {"pullRequestId": "MDExOlB1bGxSZXF1ZXN0MTQzNjIxNQ=="}}}
200
[('Server', 'enterprise.com'), ('Date', 'Wed, 07 Jun 2023 08:27:45 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Transfer-Encoding', 'chunked'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With'), ('ETag', 'W/"1573389419d98a2b3d9a6b1fc058a68f1fc3d31108ccfdab8ca4121153626211"'), ('Last-Modified', 'Wed, 07 Jun 2023 04:02:03 GMT'), ('X-OAuth-Scopes', 'public_repo'), ('X-Accepted-OAuth-Scopes', 'repo'), ('X-GitHub-Media-Type', 'github.v3; format=json'), ('x-github-api-version-selected', '2022-11-28'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4959'), ('X-RateLimit-Reset', '1686126614'), ('X-RateLimit-Used', '41'), ('X-RateLimit-Resource', 'core'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Content-Encoding', 'gzip'), ('X-GitHub-Request-Id', '8A96:10854:895AE2:8AB25B:64803F81')]
{"data": {"disablePullRequestAutoMerge": {"actor": {"avatarUrl": "/~https://avatars.githubusercontent.com/u/14806300?u=786f9f8ef8782d45381b01580f7f7783cf9c7e37&v=4", "login": "heitorpolidoro", "resourcePath": "/heitorpolidoro", "url": "https://my.enterprise.com:8080/api/v3/heitorpolidoro"}, "clientMutationId": null}}}
2 changes: 1 addition & 1 deletion tests/ReplayData/GraphQl.testOtherUrl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ my.enterprise.com
None
/api/graphql
{'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'}
{"query": "mutation DisablePullRequestAutoMerge($input: DisablePullRequestAutoMergeInput!) { disablePullRequestAutoMerge(input: $input) { actor { avatarUrl login resourcePath url } clientMutationId } }", "variables": {"input": {"pullRequestId": "MDExOlB1bGxSZXF1ZXN0MTQzNjIxNQ=="}}}
{"query": "mutation Mutation($input: DisablePullRequestAutoMergeInput!) { disablePullRequestAutoMerge(input: $input) { actor { avatarUrl login resourcePath url } clientMutationId } }", "variables": {"input": {"pullRequestId": "MDExOlB1bGxSZXF1ZXN0MTQzNjIxNQ=="}}}
200
[('Server', 'enterprise.com'), ('Date', 'Wed, 07 Jun 2023 08:27:45 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Transfer-Encoding', 'chunked'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With'), ('ETag', 'W/"1573389419d98a2b3d9a6b1fc058a68f1fc3d31108ccfdab8ca4121153626211"'), ('Last-Modified', 'Wed, 07 Jun 2023 04:02:03 GMT'), ('X-OAuth-Scopes', 'public_repo'), ('X-Accepted-OAuth-Scopes', 'repo'), ('X-GitHub-Media-Type', 'github.v3; format=json'), ('x-github-api-version-selected', '2022-11-28'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4959'), ('X-RateLimit-Reset', '1686126614'), ('X-RateLimit-Used', '41'), ('X-RateLimit-Resource', 'core'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Content-Encoding', 'gzip'), ('X-GitHub-Request-Id', '8A96:10854:895AE2:8AB25B:64803F81')]
{"data": {"disablePullRequestAutoMerge": {"actor": {"avatarUrl": "/~https://avatars.githubusercontent.com/u/14806300?u=786f9f8ef8782d45381b01580f7f7783cf9c7e37&v=4", "login": "heitorpolidoro", "resourcePath": "/heitorpolidoro", "url": "https://my.enterprise.com/api/v3/heitorpolidoro"}, "clientMutationId": null}}}
2 changes: 1 addition & 1 deletion tests/ReplayData/IssueComment.testMinimize.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ api.github.com
None
/graphql
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'}
{"query": "mutation MinimizeComment($input: MinimizeCommentInput!) { minimizeComment(input: $input) { minimizedComment { isMinimized } } }", "variables": {"input": {"subjectId": "IC_kwDOGpsAJ86Gecc_", "classifier": "OUTDATED"}}}
{"query": "mutation Mutation($input: MinimizeCommentInput!) { minimizeComment(input: $input) { minimizedComment { isMinimized } } }", "variables": {"input": {"subjectId": "IC_kwDOGpsAJ86Gecc_", "classifier": "OUTDATED"}}}
200
[('Server', 'GitHub.com'), ('Date', 'Mon, 29 Jul 2024 14:43:44 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Transfer-Encoding', 'chunked'), ('github-authentication-token-expiration', '2024-08-28 16:30:05 +0200'), ('X-GitHub-Media-Type', 'github.v4; format=json'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4991'), ('X-RateLimit-Reset', '1722267171'), ('X-RateLimit-Used', '9'), ('X-RateLimit-Resource', 'graphql'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Vary', 'Accept-Encoding, Accept, X-Requested-With'), ('Content-Encoding', 'gzip'), ('X-GitHub-Request-Id', 'C08C:1A4F2F:3A882EB:3B486EE:66A7AAA0')]
{"data":{"minimizeComment":{"minimizedComment":{"isMinimized":true}}}}
2 changes: 1 addition & 1 deletion tests/ReplayData/IssueComment.testUnminimize.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ api.github.com
None
/graphql
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json'}
{"query": "mutation UnminimizeComment($input: UnminimizeCommentInput!) { unminimizeComment(input: $input) { unminimizedComment { isMinimized } } }", "variables": {"input": {"subjectId": "IC_kwDOGpsAJ86Gecc_"}}}
{"query": "mutation Mutation($input: UnminimizeCommentInput!) { unminimizeComment(input: $input) { unminimizedComment { isMinimized } } }", "variables": {"input": {"subjectId": "IC_kwDOGpsAJ86Gecc_"}}}
200
[('Server', 'GitHub.com'), ('Date', 'Mon, 29 Jul 2024 14:43:55 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Transfer-Encoding', 'chunked'), ('github-authentication-token-expiration', '2024-08-28 16:30:05 +0200'), ('X-GitHub-Media-Type', 'github.v4; format=json'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4990'), ('X-RateLimit-Reset', '1722267171'), ('X-RateLimit-Used', '10'), ('X-RateLimit-Resource', 'graphql'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Vary', 'Accept-Encoding, Accept, X-Requested-With'), ('Content-Encoding', 'gzip'), ('X-GitHub-Request-Id', 'C064:1A4F2F:3A8AFE1:3B4B457:66A7AAAB')]
{"data":{"unminimizeComment":{"unminimizedComment":{"isMinimized":false}}}}
Loading

0 comments on commit 2722225

Please sign in to comment.