Skip to content

Commit 48e428b

Browse files
authored
Create separate threads on GitHub Discussions (#5)
* create new discussions * revert after testing is done * pass PRs as string to the template * trying to fix: TypeError: sequence item 0: expected str instance, PullRequest found * converting PRs to str * debug deletion * correct variable name in deletion query * test title * revert testing tweaks * requested changes and mypy fixes * fix linter * fix: unit tests * testing: to not unnecessary mention * testing: change repo name * testing: final tweaks * fix: syntax * revert: testing tweaks
1 parent 9765c6a commit 48e428b

File tree

5 files changed

+303
-405
lines changed

5 files changed

+303
-405
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Hi {{ username }},
22

3-
It looks like you're assigned to this PR, but haven't taken any action for at least 2 days:
3+
The following PRs are currently blocked on your review:
44
{{ pr_list }}
55

6-
Please review and unassign yourself from the pending PRs as soon as possible.
6+
Please review and unassign yourself from the pending PRs as soon as possible, then mark this discussion thread as 'Done'.
77

88
To avoid these messages in the future, please bookmark [this link](https://github.com/pulls/assigned) and check it daily. Thanks!

src/github_services.py

Lines changed: 101 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import datetime
2222
import logging
2323

24-
from typing import Any, Callable, DefaultDict, Dict, List, Optional, Tuple, Union
24+
from typing import Any, Callable, DefaultDict, Dict, List, Optional, Union
2525
from dateutil import parser
2626
import requests
2727
from src import github_domain
@@ -203,18 +203,51 @@ def get_pull_request_dict_with_timestamp(
203203
assignee['created_at'] = parser.parse(event['created_at'])
204204
return pr_dict
205205

206-
207206
@check_token
208-
def _get_discussion_data(
207+
def _get_repository_id(
209208
org_name: str,
210209
repo_name: str,
211-
discussion_category: str,
212-
discussion_title: str,
213-
) -> Tuple[str, int]:
214-
"""Fetch discussion data from api and return corresponding discussion id and
215-
discussion number.
210+
) -> str:
211+
"""Fetch repository id from given org and repo and return the id."""
212+
213+
query = """
214+
query ($org_name: String!, $repository: String!) {
215+
repository(owner: $org_name, name: $repository) {
216+
id
217+
}
218+
}
216219
"""
217220

221+
variables = {
222+
'org_name': org_name,
223+
'repository': repo_name
224+
}
225+
226+
response = requests.post(
227+
GITHUB_GRAPHQL_URL,
228+
json={'query': query, 'variables': variables},
229+
headers=_get_request_headers(),
230+
timeout=TIMEOUT_SECS
231+
)
232+
data = response.json()
233+
234+
repository_id: str = (
235+
data['data']['repository']['id'])
236+
237+
if repository_id is None:
238+
raise builtins.BaseException(
239+
f'{org_name}/{repo_name} doesn\'t exist.')
240+
241+
return repository_id
242+
243+
@check_token
244+
def _get_category_id(
245+
org_name: str,
246+
repo_name: str,
247+
discussion_category: str
248+
) -> str:
249+
"""Fetch discussion category id from given category name and return the id."""
250+
218251
# The following query is written in GraphQL and is being used to fetch the category
219252
# ids and titles from the GitHub discussions. To learn more, check this out
220253
# https://docs.github.com/en/graphql.
@@ -244,7 +277,7 @@ def _get_discussion_data(
244277
)
245278
data = response.json()
246279

247-
category_id = None
280+
category_id: Optional[str] = None
248281
discussion_categories = (
249282
data['data']['repository']['discussionCategories']['nodes'])
250283

@@ -257,6 +290,21 @@ def _get_discussion_data(
257290
raise builtins.BaseException(
258291
f'{discussion_category} category is missing in GitHub Discussion.')
259292

293+
assert category_id is not None
294+
return category_id
295+
296+
@check_token
297+
def _get_discussion_ids(
298+
org_name: str,
299+
repo_name: str,
300+
discussion_category: str,
301+
) -> List[str]:
302+
"""Fetch discussion data from api and return corresponding discussion id and
303+
discussion number.
304+
"""
305+
306+
category_id = _get_category_id(org_name, repo_name, discussion_category)
307+
260308
# The following query is written in GraphQL and is being used to fetch discussions
261309
# from a particular GitHub discussion category. This helps to find out the discussion
262310
# where we want to comment. To learn more, check this out
@@ -289,62 +337,34 @@ def _get_discussion_data(
289337
timeout=TIMEOUT_SECS
290338
)
291339
data = response.json()
292-
discussion_id = None
293340

294341
discussions = data['data']['repository']['discussions']['nodes']
342+
discussion_ids = [
343+
discussion['id'] for discussion in discussions if discussion['id'] is not None
344+
]
295345

296-
for discussion in discussions:
297-
if discussion['title'] == discussion_title:
298-
discussion_id = discussion['id']
299-
discussion_number = discussion['number']
300-
break
301346

302-
if discussion_id is None:
303-
raise builtins.BaseException(
304-
f'Discussion with title {discussion_title} not found, please create a '
305-
'discussion with that title.')
306-
307-
return discussion_id, discussion_number
308-
309-
310-
def _get_past_time(days: int=60) -> str:
311-
"""Returns the subtraction of current time and the arg passed in days."""
312-
return (
313-
datetime.datetime.now(
314-
datetime.timezone.utc) - datetime.timedelta(days=days)).strftime(
315-
'%Y-%m-%dT%H:%M:%SZ')
347+
if not discussion_ids:
348+
logging.info('No existing discussions found')
316349

350+
return discussion_ids
317351

318-
def _get_old_comment_ids(
319-
org_name: str,
320-
repo_name: str,
321-
discussion_number: int
322-
) -> List[str]:
323-
"""Return the old comment ids."""
352+
def _delete_discussion(discussion_id: str) -> None:
353+
"""Delete the GitHub Discussion comment related to the comment id."""
324354

325-
# The following query is written in GraphQL and is being used to fetch the oldest 50
326-
# comments in a existing GitHub discussions. Assuming that, this workflow will run
327-
# twice every week, we will may not have more than 50 comments to delete. To learn
328-
# more, check this out https://docs.github.com/en/graphql.
329355
query = """
330-
query ($org_name: String!, $repository: String!, $discussion_number: Int!) {
331-
repository(owner: $org_name, name: $repository) {
332-
discussion(number: $discussion_number) {
333-
comments(first: 50) {
334-
nodes {
335-
id
336-
createdAt
337-
}
338-
}
356+
mutation deleteDiscussion($discussion_id: ID!) {
357+
deleteDiscussion(input: {id: $discussion_id}) {
358+
clientMutationId,
359+
discussion {
360+
title
339361
}
340362
}
341363
}
342364
"""
343365

344366
variables = {
345-
'org_name': org_name,
346-
'repository': repo_name,
347-
'discussion_number': discussion_number
367+
'discussion_id': discussion_id
348368
}
349369

350370
response = requests.post(
@@ -353,76 +373,50 @@ def _get_old_comment_ids(
353373
headers=_get_request_headers(),
354374
timeout=TIMEOUT_SECS
355375
)
356-
357376
response.raise_for_status()
358-
data = response.json()
359-
360-
comment_ids: List[str] = []
361-
362-
discussion_comments = (
363-
data['data']['repository']['discussion']['comments']['nodes']
364-
)
365-
366-
# Delete comments posted before this time.
367-
delete_comments_before_in_days = _get_past_time(DELETE_COMMENTS_BEFORE_IN_DAYS)
368-
369-
for comment in discussion_comments:
370-
if comment['createdAt'] < delete_comments_before_in_days:
371-
comment_ids.append(comment['id'])
372-
else:
373-
break
374-
375-
return comment_ids
376377

377378

378-
def _delete_comment(comment_id: str) -> None:
379-
"""Delete the GitHub Discussion comment related to the comment id."""
380-
381-
query = """
382-
mutation deleteComment($comment_id: ID!) {
383-
deleteDiscussionComment(input: {id: $comment_id}) {
384-
clientMutationId
385-
comment {
386-
bodyText
387-
}
388-
}
389-
}
390-
"""
391-
392-
variables = {
393-
'comment_id': comment_id
394-
}
379+
@check_token
380+
def delete_discussions(
381+
org_name: str,
382+
repo_name: str,
383+
discussion_category: str,
384+
) -> None:
385+
"""Delete all existing discussions in the given discussion category."""
395386

396-
response = requests.post(
397-
GITHUB_GRAPHQL_URL,
398-
json={'query': query, 'variables': variables},
399-
headers=_get_request_headers(),
400-
timeout=TIMEOUT_SECS
401-
)
402-
response.raise_for_status()
387+
discussion_ids = _get_discussion_ids(
388+
org_name, repo_name, discussion_category)
403389

390+
for discussion_id in discussion_ids:
391+
_delete_discussion(discussion_id)
404392

405-
def _post_comment(discussion_id: str, message: str) -> None:
406-
"""Post the given message in an existing discussion."""
393+
@check_token
394+
def create_discussion(
395+
org_name: str,
396+
repo_name: str,
397+
discussion_category: str,
398+
discussion_title: str,
399+
discussion_body: str
400+
) -> None:
401+
"""Create a new discussion with the given title and body in the given discussion category."""
407402

408-
# The following code is written in GraphQL and is being used to perform a mutation
409-
# operation. More specifically, we are using it to comment in GitHub discussion to
410-
# let reviewers know about some of their pending tasks. To learn more, check this out:
411-
# https://docs.github.com/en/graphql.
403+
category_id = _get_category_id(org_name, repo_name, discussion_category)
404+
repo_id = _get_repository_id(org_name, repo_name)
412405
query = """
413-
mutation post_comment($discussion_id: ID!, $comment: String!) {
414-
addDiscussionComment(input: {discussionId: $discussion_id, body: $comment}) {
415-
clientMutationId
416-
comment {
406+
mutation createDiscussion($repo_id: ID!, $category_id: ID!, $title: String!, $body: String!) {
407+
createDiscussion(input: {repositoryId: $repo_id, categoryId: $category_id, title: $title, body: $body}) {
408+
discussion {
417409
id
418410
}
419411
}
420412
}
421413
"""
422414

423415
variables = {
424-
'discussion_id': discussion_id,
425-
'comment': message
416+
'repo_id': repo_id,
417+
'category_id': category_id,
418+
'title': discussion_title,
419+
'body': discussion_body
426420
}
427421

428422
response = requests.post(
@@ -431,38 +425,5 @@ def _post_comment(discussion_id: str, message: str) -> None:
431425
headers=_get_request_headers(),
432426
timeout=TIMEOUT_SECS
433427
)
434-
response.raise_for_status()
435-
436428

437-
@check_token
438-
def delete_discussion_comments(
439-
org_name: str,
440-
repo_name: str,
441-
discussion_category: str,
442-
discussion_title: str
443-
) -> None:
444-
"""Delete old comments from GitHub Discussion."""
445-
446-
_, discussion_number = _get_discussion_data(
447-
org_name, repo_name, discussion_category, discussion_title)
448-
449-
comment_ids = _get_old_comment_ids(org_name, repo_name, discussion_number)
450-
451-
for comment_id in comment_ids:
452-
_delete_comment(comment_id)
453-
454-
455-
@check_token
456-
def add_discussion_comments(
457-
org_name: str,
458-
repo_name: str,
459-
discussion_category: str,
460-
discussion_title: str,
461-
message: str
462-
) -> None:
463-
"""Add comments in an existing GitHub discussion."""
464-
465-
discussion_id, _ = _get_discussion_data(
466-
org_name, repo_name, discussion_category, discussion_title)
467-
468-
_post_comment(discussion_id, message)
429+
response.raise_for_status()

0 commit comments

Comments
 (0)