arcade-github 0.0.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,554 @@
1
+ import json
2
+ from typing import Annotated, Optional
3
+
4
+ import httpx
5
+
6
+ from arcade.core.errors import RetryableToolError
7
+ from arcade.core.schema import ToolContext
8
+ from arcade.sdk import tool
9
+ from arcade.sdk.auth import GitHub
10
+ from arcade_github.tools.models import (
11
+ DiffSide,
12
+ PRSortProperty,
13
+ PRState,
14
+ ReviewCommentSortProperty,
15
+ ReviewCommentSubjectType,
16
+ SortDirection,
17
+ )
18
+ from arcade_github.tools.utils import (
19
+ get_github_diff_headers,
20
+ get_github_json_headers,
21
+ get_url,
22
+ handle_github_response,
23
+ remove_none_values,
24
+ )
25
+
26
+
27
+ # Implements https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests
28
+ # Example `arcade chat` usage: "get all open PRs that <USER> has that are in the <OWNER>/<REPO> repo"
29
+ # TODO: Validate owner/repo combination is valid for the authenticated user. If not, return RetryableToolError with available repos.
30
+ # TODO: list repo's branches and validate base is in the list (or default to main). If not, return RetryableToolError with available branches.
31
+ @tool(requires_auth=GitHub())
32
+ async def list_pull_requests(
33
+ context: ToolContext,
34
+ owner: Annotated[str, "The account owner of the repository. The name is not case sensitive."],
35
+ repo: Annotated[
36
+ str,
37
+ "The name of the repository without the .git extension. The name is not case sensitive.",
38
+ ],
39
+ state: Annotated[Optional[PRState], "The state of the pull requests to return."] = PRState.OPEN,
40
+ head: Annotated[
41
+ Optional[str],
42
+ "Filter pulls by head user or head organization and branch name in the format of user:ref-name or organization:ref-name.",
43
+ ] = None,
44
+ base: Annotated[Optional[str], "Filter pulls by base branch name."] = "main",
45
+ sort: Annotated[
46
+ Optional[PRSortProperty], "The property to sort the results by."
47
+ ] = PRSortProperty.CREATED,
48
+ direction: Annotated[Optional[SortDirection], "The direction of the sort."] = None,
49
+ per_page: Annotated[Optional[int], "The number of results per page (max 100)."] = 30,
50
+ page: Annotated[Optional[int], "The page number of the results to fetch."] = 1,
51
+ include_extra_data: Annotated[
52
+ bool,
53
+ "If true, return all the data available about the pull requests. This is a large payload and may impact performance - use with caution.",
54
+ ] = False,
55
+ ) -> Annotated[str, "JSON string containing a list of pull requests with their details"]:
56
+ """
57
+ List pull requests in a GitHub repository.
58
+
59
+ Example:
60
+ ```
61
+ list_pull_requests(owner="octocat", repo="Hello-World", state=PRState.OPEN, sort=PRSort.UPDATED)
62
+ ```
63
+ """
64
+ url = get_url("repo_pulls", owner=owner, repo=repo)
65
+ params = {
66
+ "base": base,
67
+ "state": state.value,
68
+ "sort": sort.value,
69
+ "per_page": min(max(1, per_page), 100), # clamp per_page to 1-100
70
+ "page": page,
71
+ "head": head,
72
+ "direction": direction, # Note: Github defaults to desc when sort is 'created' or not specified, otherwise defaults to asc
73
+ }
74
+ params = remove_none_values(params)
75
+ headers = get_github_json_headers(context.authorization.token)
76
+
77
+ async with httpx.AsyncClient() as client:
78
+ response = await client.get(url, headers=headers, params=params)
79
+
80
+ handle_github_response(response, url)
81
+
82
+ pull_requests = response.json()
83
+ results = []
84
+ for pr in pull_requests:
85
+ if include_extra_data:
86
+ results.append(pr)
87
+ continue
88
+ results.append({
89
+ "number": pr.get("number"),
90
+ "title": pr.get("title"),
91
+ "body": pr.get("body"),
92
+ "state": pr.get("state"),
93
+ "html_url": pr.get("html_url"),
94
+ "diff_url": pr.get("diff_url"),
95
+ "created_at": pr.get("created_at"),
96
+ "updated_at": pr.get("updated_at"),
97
+ "user": pr.get("user", {}).get("login"),
98
+ "base": pr.get("base", {}).get("ref"),
99
+ "head": pr.get("head", {}).get("ref"),
100
+ })
101
+ return json.dumps({"pull_requests": results})
102
+
103
+
104
+ # Implements https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request
105
+ # Example `arcade chat` usage: "get the PR #72 in the <OWNER>/<REPO> repo. Include diff content in your response."
106
+ @tool(requires_auth=GitHub())
107
+ async def get_pull_request(
108
+ context: ToolContext,
109
+ owner: Annotated[str, "The account owner of the repository. The name is not case sensitive."],
110
+ repo: Annotated[
111
+ str,
112
+ "The name of the repository without the .git extension. The name is not case sensitive.",
113
+ ],
114
+ pull_number: Annotated[int, "The number that identifies the pull request."],
115
+ include_diff_content: Annotated[
116
+ Optional[bool],
117
+ "If true, return the diff content of the pull request.",
118
+ ] = False,
119
+ include_extra_data: Annotated[
120
+ Optional[bool],
121
+ "If true, return all the data available about the pull requests. This is a large payload and may impact performance - use with caution.",
122
+ ] = False,
123
+ ) -> Annotated[
124
+ str,
125
+ "JSON string containing details of the specified pull request, optionally including diff content",
126
+ ]:
127
+ """
128
+ Get details of a pull request in a GitHub repository.
129
+
130
+ Example:
131
+ ```
132
+ get_pull_request(owner="octocat", repo="Hello-World", pull_number=1347)
133
+ ```
134
+ """
135
+ url = get_url("repo_pull", owner=owner, repo=repo, pull_number=pull_number)
136
+ headers = get_github_json_headers(context.authorization.token)
137
+ diff_headers = get_github_diff_headers(context.authorization.token)
138
+
139
+ async with httpx.AsyncClient() as client:
140
+ response = await client.get(url, headers=headers)
141
+ if include_diff_content:
142
+ diff_response = await client.get(url, headers=diff_headers)
143
+
144
+ handle_github_response(response, url)
145
+
146
+ if include_diff_content:
147
+ handle_github_response(diff_response, url)
148
+
149
+ pr_data = response.json()
150
+
151
+ if include_extra_data:
152
+ result = pr_data
153
+ if include_diff_content:
154
+ result["diff_content"] = diff_response.content.decode("utf-8")
155
+ return json.dumps(result)
156
+
157
+ important_info = {
158
+ "number": pr_data.get("number"),
159
+ "title": pr_data.get("title"),
160
+ "body": pr_data.get("body"),
161
+ "state": pr_data.get("state"),
162
+ "html_url": pr_data.get("html_url"),
163
+ "diff_url": pr_data.get("diff_url"),
164
+ "created_at": pr_data.get("created_at"),
165
+ "updated_at": pr_data.get("updated_at"),
166
+ "user": pr_data.get("user", {}).get("login"),
167
+ "base": pr_data.get("base", {}).get("ref"),
168
+ "head": pr_data.get("head", {}).get("ref"),
169
+ }
170
+
171
+ if include_diff_content:
172
+ important_info["diff_content"] = diff_response.content.decode("utf-8")
173
+
174
+ return json.dumps(important_info)
175
+
176
+
177
+ # Implements https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#update-a-pull-request
178
+ # Example `arcade chat` usage: "update PR #72 in the <OWNER>/<REPO> repo by changing the title to 'New Title' and setting the body to 'This PR description was added via arcade chat!'."
179
+ # TODO: Enable this tool to append to the PR contents instead of only replacing content.
180
+ @tool(requires_auth=GitHub())
181
+ async def update_pull_request(
182
+ context: ToolContext,
183
+ owner: Annotated[str, "The account owner of the repository. The name is not case sensitive."],
184
+ repo: Annotated[
185
+ str,
186
+ "The name of the repository without the .git extension. The name is not case sensitive.",
187
+ ],
188
+ pull_number: Annotated[int, "The number that identifies the pull request."],
189
+ title: Annotated[Optional[str], "The title of the pull request."] = None,
190
+ body: Annotated[Optional[str], "The contents of the pull request."] = None,
191
+ state: Annotated[
192
+ Optional[PRState], "State of this Pull Request. Either open or closed."
193
+ ] = None,
194
+ base: Annotated[
195
+ Optional[str], "The name of the branch you want your changes pulled into."
196
+ ] = None,
197
+ maintainer_can_modify: Annotated[
198
+ Optional[bool], "Indicates whether maintainers can modify the pull request."
199
+ ] = None,
200
+ ) -> Annotated[str, "JSON string containing updated information about the pull request"]:
201
+ """
202
+ Update a pull request in a GitHub repository.
203
+
204
+ Example:
205
+ ```
206
+ update_pull_request(owner="octocat", repo="Hello-World", pull_number=1347, title="new title", body="updated body")
207
+ ```
208
+ """
209
+ url = get_url("repo_pull", owner=owner, repo=repo, pull_number=pull_number)
210
+
211
+ data = {
212
+ "title": title,
213
+ "body": body,
214
+ "state": state.value if state else None,
215
+ "base": base,
216
+ "maintainer_can_modify": maintainer_can_modify,
217
+ }
218
+ data = remove_none_values(data)
219
+
220
+ headers = get_github_json_headers(context.authorization.token)
221
+
222
+ async with httpx.AsyncClient() as client:
223
+ response = await client.patch(url, headers=headers, json=data)
224
+
225
+ handle_github_response(response, url)
226
+
227
+ pr_data = response.json()
228
+ important_info = {
229
+ "url": pr_data.get("url"),
230
+ "id": pr_data.get("id"),
231
+ "html_url": pr_data.get("html_url"),
232
+ "number": pr_data.get("number"),
233
+ "state": pr_data.get("state"),
234
+ "title": pr_data.get("title"),
235
+ "user": pr_data.get("user", {}).get("login"),
236
+ "body": pr_data.get("body"),
237
+ "created_at": pr_data.get("created_at"),
238
+ "updated_at": pr_data.get("updated_at"),
239
+ }
240
+ return json.dumps(important_info)
241
+
242
+
243
+ # Implements https://docs.github.com/en/rest/pulls/commits?apiVersion=2022-11-28#list-commits-on-a-pull-request
244
+ # Example `arcade chat` usage: "list all of the commits for the PR 72 in the <OWNER>/<REPO> repo"
245
+ @tool(requires_auth=GitHub())
246
+ async def list_pull_request_commits(
247
+ context: ToolContext,
248
+ owner: Annotated[str, "The account owner of the repository. The name is not case sensitive."],
249
+ repo: Annotated[
250
+ str,
251
+ "The name of the repository without the .git extension. The name is not case sensitive.",
252
+ ],
253
+ pull_number: Annotated[int, "The number that identifies the pull request."],
254
+ per_page: Annotated[Optional[int], "The number of results per page (max 100)."] = 30,
255
+ page: Annotated[Optional[int], "The page number of the results to fetch."] = 1,
256
+ include_extra_data: Annotated[
257
+ bool,
258
+ "If true, return all the data available about the pull requests. This is a large payload and may impact performance - use with caution.",
259
+ ] = False,
260
+ ) -> Annotated[str, "JSON string containing a list of commits for the specified pull request"]:
261
+ """
262
+ List commits (from oldest to newest) on a pull request in a GitHub repository.
263
+
264
+ Example:
265
+ ```
266
+ list_pull_request_commits(owner="octocat", repo="Hello-World", pull_number=1347)
267
+ ```
268
+ """
269
+ url = get_url("repo_pull_commits", owner=owner, repo=repo, pull_number=pull_number)
270
+
271
+ params = {
272
+ "per_page": max(1, min(100, per_page)), # clamp per_page to 1-100
273
+ "page": page,
274
+ }
275
+
276
+ headers = get_github_json_headers(context.authorization.token)
277
+
278
+ async with httpx.AsyncClient() as client:
279
+ response = await client.get(url, headers=headers, params=params)
280
+
281
+ handle_github_response(response, url)
282
+
283
+ commits = response.json()
284
+ if include_extra_data:
285
+ return json.dumps({"commits": commits})
286
+
287
+ filtered_commits = []
288
+ for commit in commits:
289
+ filtered_commit = {
290
+ "sha": commit.get("sha"),
291
+ "html_url": commit.get("html_url"),
292
+ "diff_url": commit.get("html_url") + ".diff" if commit.get("html_url") else None,
293
+ "commit": {
294
+ "message": commit.get("commit", {}).get("message"),
295
+ "author": commit.get("commit", {}).get("author", {}).get("name"),
296
+ "committer": commit.get("commit", {}).get("committer", {}).get("name"),
297
+ "date": commit.get("commit", {}).get("committer", {}).get("date"),
298
+ },
299
+ "author": commit.get("author", {}).get("login"),
300
+ "committer": commit.get("committer", {}).get("login"),
301
+ }
302
+ filtered_commits.append(filtered_commit)
303
+
304
+ return json.dumps({"commits": filtered_commits})
305
+
306
+
307
+ # Implements https://docs.github.com/en/rest/pulls/comments?apiVersion=2022-11-28#create-a-reply-for-a-review-comment
308
+ # Example `arcade chat` usage: "create a reply to the review comment 1778019974 in arcadeai/arcade-ai for the PR 72 that says 'Thanks for the suggestion.'"
309
+ # Note: This tool requires the ID of the review comment to reply to. To obtain this ID, you should first call the `list_review_comments_on_pull_request` function.
310
+ # The returned JSON will contain the `id` field for each comment, which can be used as the `comment_id` parameter in this function.
311
+ @tool(requires_auth=GitHub())
312
+ async def create_reply_for_review_comment(
313
+ context: ToolContext,
314
+ owner: Annotated[str, "The account owner of the repository. The name is not case sensitive."],
315
+ repo: Annotated[
316
+ str,
317
+ "The name of the repository without the .git extension. The name is not case sensitive.",
318
+ ],
319
+ pull_number: Annotated[int, "The number that identifies the pull request."],
320
+ comment_id: Annotated[int, "The unique identifier of the comment."],
321
+ body: Annotated[str, "The text of the review comment."],
322
+ ) -> Annotated[str, "JSON string containing details of the created reply comment"]:
323
+ """
324
+ Create a reply to a review comment for a pull request.
325
+
326
+ Example:
327
+ ```
328
+ create_reply_for_review_comment(owner="octocat", repo="Hello-World", pull_number=1347, comment_id=42, body="Looks good to me!")
329
+ ```
330
+ """
331
+ url = get_url(
332
+ "repo_pull_comment_replies",
333
+ owner=owner,
334
+ repo=repo,
335
+ pull_number=pull_number,
336
+ comment_id=comment_id,
337
+ )
338
+
339
+ headers = get_github_json_headers(context.authorization.token)
340
+
341
+ data = {"body": body}
342
+
343
+ async with httpx.AsyncClient() as client:
344
+ response = await client.post(url, headers=headers, json=data)
345
+
346
+ handle_github_response(response, url)
347
+
348
+ return json.dumps(response.json())
349
+
350
+
351
+ # Implements https://docs.github.com/en/rest/pulls/comments?apiVersion=2022-11-28#list-review-comments-on-a-pull-request
352
+ # Example `arcade chat` usage: "list all of the review comments for PR 72 in <OWNER>/<REPO>"
353
+ @tool(requires_auth=GitHub())
354
+ async def list_review_comments_on_pull_request(
355
+ context: ToolContext,
356
+ owner: Annotated[str, "The account owner of the repository. The name is not case sensitive."],
357
+ repo: Annotated[
358
+ str,
359
+ "The name of the repository without the .git extension. The name is not case sensitive.",
360
+ ],
361
+ pull_number: Annotated[int, "The number that identifies the pull request."],
362
+ sort: Annotated[
363
+ Optional[ReviewCommentSortProperty],
364
+ "The property to sort the results by. Can be one of: created, updated.",
365
+ ] = ReviewCommentSortProperty.CREATED,
366
+ direction: Annotated[
367
+ Optional[SortDirection], "The direction to sort results. Can be one of: asc, desc."
368
+ ] = SortDirection.DESC,
369
+ since: Annotated[
370
+ Optional[str],
371
+ "Only show results that were last updated after the given time. This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ.",
372
+ ] = None,
373
+ per_page: Annotated[Optional[int], "The number of results per page (max 100)."] = 30,
374
+ page: Annotated[Optional[int], "The page number of the results to fetch."] = 1,
375
+ include_extra_data: Annotated[
376
+ bool,
377
+ "If true, return all the data available about the pull requests. This is a large payload and may impact performance - use with caution.",
378
+ ] = False,
379
+ ) -> Annotated[
380
+ str, "JSON string containing a list of review comments for the specified pull request"
381
+ ]:
382
+ """
383
+ List review comments on a pull request in a GitHub repository.
384
+
385
+ Example:
386
+ ```
387
+ list_review_comments_on_pull_request(owner="octocat", repo="Hello-World", pull_number=1347)
388
+ ```
389
+ """
390
+ url = get_url("repo_pull_comments", owner=owner, repo=repo, pull_number=pull_number)
391
+
392
+ params = {
393
+ "sort": sort,
394
+ "direction": direction,
395
+ "per_page": max(1, min(100, per_page)), # clamp per_page to 1-100
396
+ "page": page,
397
+ "since": since,
398
+ }
399
+ params = remove_none_values(params)
400
+
401
+ headers = get_github_json_headers(context.authorization.token)
402
+
403
+ async with httpx.AsyncClient() as client:
404
+ response = await client.get(url, headers=headers, params=params)
405
+
406
+ handle_github_response(response, url)
407
+
408
+ review_comments = response.json()
409
+ if include_extra_data:
410
+ return json.dumps(review_comments)
411
+
412
+ filtered_comments = []
413
+ for comment in review_comments:
414
+ filtered_comment = {
415
+ "id": comment.get("id"),
416
+ "url": comment.get("url"),
417
+ "diff_hunk": comment.get("diff_hunk"),
418
+ "path": comment.get("path"),
419
+ "position": comment.get("position"),
420
+ "original_position": comment.get("original_position"),
421
+ "commit_id": comment.get("commit_id"),
422
+ "original_commit_id": comment.get("original_commit_id"),
423
+ "in_reply_to_id": comment.get("in_reply_to_id"),
424
+ "user": comment.get("user", {}).get("login"),
425
+ "body": comment.get("body"),
426
+ "created_at": comment.get("created_at"),
427
+ "updated_at": comment.get("updated_at"),
428
+ "html_url": comment.get("html_url"),
429
+ "line": comment.get("line"),
430
+ "side": comment.get("side"),
431
+ "pull_request_url": comment.get("pull_request_url"),
432
+ }
433
+ filtered_comments.append(filtered_comment)
434
+ return json.dumps({"review_comments": filtered_comments})
435
+
436
+
437
+ # Implements https://docs.github.com/en/rest/pulls/comments?apiVersion=2022-11-28#create-a-review-comment-for-a-pull-request
438
+ # Example `arcade chat` usage: "create a review comment for PR 72 in <OWNER>/<REPO> that says 'Great stuff! This looks good to merge. Add the comment to README.md file.'"
439
+ # TODO: Verify that path parameter exists in the PR's files that have changed (Or should we allow for any file in the repo?). If not, then throw RetryableToolError with all valid file paths.
440
+ @tool(requires_auth=GitHub())
441
+ async def create_review_comment(
442
+ context: ToolContext,
443
+ owner: Annotated[str, "The account owner of the repository. The name is not case sensitive."],
444
+ repo: Annotated[
445
+ str,
446
+ "The name of the repository without the .git extension. The name is not case sensitive.",
447
+ ],
448
+ pull_number: Annotated[int, "The number that identifies the pull request."],
449
+ body: Annotated[str, "The text of the review comment."],
450
+ path: Annotated[str, "The relative path to the file that necessitates a comment."],
451
+ commit_id: Annotated[
452
+ Optional[str],
453
+ "The SHA of the commit needing a comment. If not provided, the latest commit SHA of the PR's base branch will be used.",
454
+ ] = None,
455
+ start_line: Annotated[
456
+ Optional[int],
457
+ "The start line of the range of lines in the pull request diff that the comment applies to. Required unless 'subject_type' is 'file'.",
458
+ ] = None,
459
+ end_line: Annotated[
460
+ Optional[int],
461
+ "The end line of the range of lines in the pull request diff that the comment applies to. Required unless 'subject_type' is 'file'.",
462
+ ] = None,
463
+ side: Annotated[
464
+ Optional[DiffSide],
465
+ "The side of the diff that the pull request's changes appear on. Use LEFT for deletions that appear in red. Use RIGHT for additions that appear in green or unchanged lines that appear in white and are shown for context",
466
+ ] = DiffSide.RIGHT,
467
+ start_side: Annotated[
468
+ Optional[str], "The starting side of the diff that the comment applies to."
469
+ ] = None,
470
+ subject_type: Annotated[
471
+ Optional[ReviewCommentSubjectType],
472
+ "The type of subject that the comment applies to. Can be one of: file, hunk, or line.",
473
+ ] = ReviewCommentSubjectType.FILE,
474
+ include_extra_data: Annotated[
475
+ bool,
476
+ "If true, return all the data available about the review comment. This is a large payload and may impact performance - use with caution.",
477
+ ] = False,
478
+ ) -> Annotated[str, "JSON string containing details of the created review comment"]:
479
+ """
480
+ Create a review comment for a pull request in a GitHub repository.
481
+
482
+ If the subject_type is not 'file', then the start_line and end_line parameters are required.
483
+ If the subject_type is 'file', then the start_line and end_line parameters are ignored.
484
+ If the commit_id is not provided, the latest commit SHA of the PR's base branch will be used.
485
+
486
+ Example:
487
+ ```
488
+ create_review_comment(owner="octocat", repo="Hello-World", pull_number=1347, body="Great stuff!", commit_id="6dcb09b5b57875f334f61aebed695e2e4193db5e", path="file1.txt", line=2, side="RIGHT")
489
+ ```
490
+ """
491
+ # If the subject_type is 'file', then the line_range parameter is ignored
492
+ if subject_type == ReviewCommentSubjectType.FILE:
493
+ start_line, end_line = None, None
494
+
495
+ if (start_line is None or end_line is None) and subject_type != ReviewCommentSubjectType.FILE:
496
+ raise RetryableToolError(
497
+ "'start_line' and 'end_line' parameters are required when 'subject_type' parameter is not 'file'. Either provide both a start_line and end_line or set subject_type to 'file'."
498
+ )
499
+
500
+ # Ensure the line range goes from lowest to highest
501
+ if start_line is not None and end_line is not None:
502
+ start_line, end_line = (min(start_line, end_line), max(start_line, end_line))
503
+
504
+ # Get the latest commit SHA of the PR's base branch and use that for the commit_id
505
+ if not commit_id:
506
+ commits_json = await list_pull_request_commits(context, owner, repo, pull_number)
507
+ commits_data = json.loads(commits_json)
508
+ commits = commits_data.get("commits", [])
509
+ latest_commit = commits[-1] if commits else {}
510
+ commit_id = latest_commit.get("sha")
511
+
512
+ if not commit_id:
513
+ raise RetryableToolError(
514
+ f"Failed to get the latest commit SHA of PR {pull_number} in repo {repo} owned by {owner}. Does the PR exist?"
515
+ )
516
+
517
+ url = get_url("repo_pull_comments", owner=owner, repo=repo, pull_number=pull_number)
518
+ data = {
519
+ "body": body,
520
+ "commit_id": commit_id,
521
+ "path": path,
522
+ "side": side,
523
+ "line": end_line if end_line else None,
524
+ "start_line": start_line
525
+ if start_line and start_line != end_line
526
+ else None, # Only send start_line when using multi-line comments
527
+ "start_side": start_side,
528
+ }
529
+ data = remove_none_values(data)
530
+ headers = get_github_json_headers(context.authorization.token)
531
+
532
+ async with httpx.AsyncClient() as client:
533
+ response = await client.post(url, headers=headers, json=data)
534
+
535
+ handle_github_response(response, url)
536
+
537
+ comment_data = response.json()
538
+ if include_extra_data:
539
+ return json.dumps(comment_data)
540
+
541
+ important_info = {
542
+ "id": comment_data.get("id"),
543
+ "url": comment_data.get("url"),
544
+ "body": comment_data.get("body"),
545
+ "path": comment_data.get("path"),
546
+ "line": comment_data.get("line"),
547
+ "side": comment_data.get("side"),
548
+ "commit_id": comment_data.get("commit_id"),
549
+ "user": comment_data.get("user", {}).get("login"),
550
+ "created_at": comment_data.get("created_at"),
551
+ "updated_at": comment_data.get("updated_at"),
552
+ "html_url": comment_data.get("html_url"),
553
+ }
554
+ return json.dumps(important_info)