airbyte-source-github 1.5.7__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.
Files changed (88) hide show
  1. airbyte_source_github-1.5.7.dist-info/METADATA +144 -0
  2. airbyte_source_github-1.5.7.dist-info/RECORD +88 -0
  3. airbyte_source_github-1.5.7.dist-info/WHEEL +5 -0
  4. airbyte_source_github-1.5.7.dist-info/entry_points.txt +2 -0
  5. airbyte_source_github-1.5.7.dist-info/top_level.txt +3 -0
  6. integration_tests/__init__.py +0 -0
  7. integration_tests/abnormal_state.json +237 -0
  8. integration_tests/acceptance.py +16 -0
  9. integration_tests/configured_catalog.json +435 -0
  10. integration_tests/configured_catalog_full_refresh_test.json +415 -0
  11. integration_tests/invalid_config.json +5 -0
  12. integration_tests/sample_config.json +5 -0
  13. integration_tests/sample_state.json +137 -0
  14. source_github/__init__.py +27 -0
  15. source_github/config_migrations.py +106 -0
  16. source_github/constants.py +9 -0
  17. source_github/github_schema.py +41034 -0
  18. source_github/graphql.py +327 -0
  19. source_github/run.py +17 -0
  20. source_github/schemas/assignees.json +63 -0
  21. source_github/schemas/branches.json +48 -0
  22. source_github/schemas/collaborators.json +80 -0
  23. source_github/schemas/comments.json +104 -0
  24. source_github/schemas/commit_comment_reactions.json +4 -0
  25. source_github/schemas/commit_comments.json +53 -0
  26. source_github/schemas/commits.json +126 -0
  27. source_github/schemas/contributor_activity.json +109 -0
  28. source_github/schemas/deployments.json +77 -0
  29. source_github/schemas/events.json +63 -0
  30. source_github/schemas/issue_comment_reactions.json +4 -0
  31. source_github/schemas/issue_events.json +335 -0
  32. source_github/schemas/issue_labels.json +30 -0
  33. source_github/schemas/issue_milestones.json +61 -0
  34. source_github/schemas/issue_reactions.json +28 -0
  35. source_github/schemas/issue_timeline_events.json +1056 -0
  36. source_github/schemas/issues.json +281 -0
  37. source_github/schemas/organizations.json +197 -0
  38. source_github/schemas/project_cards.json +50 -0
  39. source_github/schemas/project_columns.json +38 -0
  40. source_github/schemas/projects.json +50 -0
  41. source_github/schemas/projects_v2.json +80 -0
  42. source_github/schemas/pull_request_comment_reactions.json +28 -0
  43. source_github/schemas/pull_request_commits.json +122 -0
  44. source_github/schemas/pull_request_stats.json +84 -0
  45. source_github/schemas/pull_requests.json +363 -0
  46. source_github/schemas/releases.json +126 -0
  47. source_github/schemas/repositories.json +313 -0
  48. source_github/schemas/review_comments.json +118 -0
  49. source_github/schemas/reviews.json +69 -0
  50. source_github/schemas/shared/events/comment.json +188 -0
  51. source_github/schemas/shared/events/commented.json +118 -0
  52. source_github/schemas/shared/events/committed.json +56 -0
  53. source_github/schemas/shared/events/cross_referenced.json +784 -0
  54. source_github/schemas/shared/events/reviewed.json +139 -0
  55. source_github/schemas/shared/reaction.json +27 -0
  56. source_github/schemas/shared/reactions.json +35 -0
  57. source_github/schemas/shared/user.json +59 -0
  58. source_github/schemas/shared/user_graphql.json +26 -0
  59. source_github/schemas/stargazers.json +19 -0
  60. source_github/schemas/tags.json +32 -0
  61. source_github/schemas/team_members.json +66 -0
  62. source_github/schemas/team_memberships.json +24 -0
  63. source_github/schemas/teams.json +50 -0
  64. source_github/schemas/users.json +63 -0
  65. source_github/schemas/workflow_jobs.json +109 -0
  66. source_github/schemas/workflow_runs.json +449 -0
  67. source_github/schemas/workflows.json +41 -0
  68. source_github/source.py +339 -0
  69. source_github/spec.json +179 -0
  70. source_github/streams.py +1678 -0
  71. source_github/utils.py +152 -0
  72. unit_tests/__init__.py +3 -0
  73. unit_tests/conftest.py +29 -0
  74. unit_tests/projects_v2_pull_requests_query.json +3 -0
  75. unit_tests/pull_request_stats_query.json +3 -0
  76. unit_tests/responses/contributor_activity_response.json +33 -0
  77. unit_tests/responses/graphql_reviews_responses.json +405 -0
  78. unit_tests/responses/issue_timeline_events.json +166 -0
  79. unit_tests/responses/issue_timeline_events_response.json +170 -0
  80. unit_tests/responses/projects_v2_response.json +45 -0
  81. unit_tests/responses/pull_request_comment_reactions.json +744 -0
  82. unit_tests/responses/pull_request_stats_response.json +317 -0
  83. unit_tests/test_migrations/test_config.json +8 -0
  84. unit_tests/test_migrations/test_new_config.json +8 -0
  85. unit_tests/test_multiple_token_authenticator.py +160 -0
  86. unit_tests/test_source.py +326 -0
  87. unit_tests/test_stream.py +1471 -0
  88. unit_tests/utils.py +78 -0
@@ -0,0 +1,327 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+ import heapq
6
+ import itertools
7
+ from typing import Optional
8
+
9
+ import sgqlc.operation
10
+ from sgqlc.operation import Selector
11
+
12
+ from . import github_schema
13
+
14
+ _schema = github_schema
15
+ _schema_root = _schema.github_schema
16
+
17
+
18
+ def select_user_fields(user):
19
+ user.__fields__(
20
+ id="node_id",
21
+ database_id="id",
22
+ login=True,
23
+ avatar_url="avatar_url",
24
+ url="html_url",
25
+ is_site_admin="site_admin",
26
+ )
27
+
28
+
29
+ def get_query_pull_requests(owner, name, first, after, direction):
30
+ kwargs = {"first": first, "order_by": {"field": "UPDATED_AT", "direction": direction}}
31
+ if after:
32
+ kwargs["after"] = after
33
+
34
+ op = sgqlc.operation.Operation(_schema_root.query_type)
35
+ repository = op.repository(owner=owner, name=name)
36
+ repository.name()
37
+ repository.owner.login()
38
+ pull_requests = repository.pull_requests(**kwargs)
39
+ pull_requests.nodes.__fields__(
40
+ id="node_id",
41
+ database_id="id",
42
+ number=True,
43
+ updated_at="updated_at",
44
+ changed_files="changed_files",
45
+ deletions=True,
46
+ additions=True,
47
+ merged=True,
48
+ mergeable=True,
49
+ can_be_rebased="can_be_rebased",
50
+ maintainer_can_modify="maintainer_can_modify",
51
+ merge_state_status="merge_state_status",
52
+ )
53
+ pull_requests.nodes.comments.__fields__(total_count=True)
54
+ pull_requests.nodes.commits.__fields__(total_count=True)
55
+ reviews = pull_requests.nodes.reviews(first=100, __alias__="review_comments")
56
+ reviews.total_count()
57
+ reviews.nodes.comments.__fields__(total_count=True)
58
+ user = pull_requests.nodes.merged_by(__alias__="merged_by").__as__(_schema_root.User)
59
+ select_user_fields(user)
60
+ pull_requests.page_info.__fields__(has_next_page=True, end_cursor=True)
61
+ return str(op)
62
+
63
+
64
+ def get_query_projectsV2(owner, name, first, after, direction):
65
+ kwargs = {"first": first, "order_by": {"field": "UPDATED_AT", "direction": direction}}
66
+ if after:
67
+ kwargs["after"] = after
68
+
69
+ op = sgqlc.operation.Operation(_schema_root.query_type)
70
+ repository = op.repository(owner=owner, name=name)
71
+ repository.name()
72
+ repository.owner.login()
73
+ projects_v2 = repository.projects_v2(**kwargs)
74
+ projects_v2.nodes.__fields__(
75
+ closed=True,
76
+ created_at="created_at",
77
+ closed_at="closed_at",
78
+ updated_at="updated_at",
79
+ creator="creator",
80
+ id="node_id",
81
+ database_id="id",
82
+ number=True,
83
+ public=True,
84
+ readme="readme",
85
+ short_description="short_description",
86
+ template=True,
87
+ title="title",
88
+ url="url",
89
+ viewer_can_close=True,
90
+ viewer_can_reopen=True,
91
+ viewer_can_update=True,
92
+ )
93
+ projects_v2.nodes.owner.__fields__(id="id")
94
+ projects_v2.page_info.__fields__(has_next_page=True, end_cursor=True)
95
+ return str(op)
96
+
97
+
98
+ def get_query_reviews(owner, name, first, after, number=None):
99
+ op = sgqlc.operation.Operation(_schema_root.query_type)
100
+ repository = op.repository(owner=owner, name=name)
101
+ repository.name()
102
+ repository.owner.login()
103
+ if number:
104
+ pull_request = repository.pull_request(number=number)
105
+ else:
106
+ kwargs = {"first": first, "order_by": {"field": "UPDATED_AT", "direction": "ASC"}}
107
+ if after:
108
+ kwargs["after"] = after
109
+ pull_requests = repository.pull_requests(**kwargs)
110
+ pull_requests.page_info.__fields__(has_next_page=True, end_cursor=True)
111
+ pull_request = pull_requests.nodes
112
+
113
+ pull_request.__fields__(number=True, url=True)
114
+ kwargs = {"first": first}
115
+ if number and after:
116
+ kwargs["after"] = after
117
+ reviews = pull_request.reviews(**kwargs)
118
+ reviews.page_info.__fields__(has_next_page=True, end_cursor=True)
119
+ reviews.nodes.__fields__(
120
+ id="node_id",
121
+ database_id="id",
122
+ body=True,
123
+ state=True,
124
+ url="html_url",
125
+ author_association="author_association",
126
+ submitted_at="submitted_at",
127
+ created_at="created_at",
128
+ updated_at="updated_at",
129
+ )
130
+ reviews.nodes.commit.oid()
131
+ user = reviews.nodes.author(__alias__="user").__as__(_schema_root.User)
132
+ select_user_fields(user)
133
+ return str(op)
134
+
135
+
136
+ def get_query_issue_reactions(owner, name, first, after, number=None):
137
+ op = sgqlc.operation.Operation(_schema_root.query_type)
138
+ repository = op.repository(owner=owner, name=name)
139
+ repository.name()
140
+ repository.owner.login()
141
+ if number:
142
+ issue = repository.issue(number=number)
143
+ else:
144
+ kwargs = {"first": first}
145
+ if after:
146
+ kwargs["after"] = after
147
+ issues = repository.issues(**kwargs)
148
+ issues.page_info.__fields__(has_next_page=True, end_cursor=True)
149
+ issue = issues.nodes
150
+
151
+ issue.__fields__(number=True)
152
+ kwargs = {"first": first}
153
+ if number and after:
154
+ kwargs["after"] = after
155
+ reactions = issue.reactions(**kwargs)
156
+ reactions.page_info.__fields__(has_next_page=True, end_cursor=True)
157
+ reactions.nodes.__fields__(
158
+ id="node_id",
159
+ database_id="id",
160
+ content=True,
161
+ created_at="created_at",
162
+ )
163
+ select_user_fields(reactions.nodes.user())
164
+ return str(op)
165
+
166
+
167
+ class QueryReactions:
168
+
169
+ # AVERAGE_REVIEWS - optimal number of reviews to fetch inside every pull request.
170
+ # If we try to fetch too many (up to 100) we will spend too many scores of query cost.
171
+ # https://docs.github.com/en/graphql/overview/resource-limitations#calculating-a-rate-limit-score-before-running-the-call
172
+ # If we query too low we would need to make additional sub-queries to fetch the rest of the reviews inside specific pull request.
173
+ AVERAGE_REVIEWS = 5
174
+ AVERAGE_COMMENTS = 2
175
+ AVERAGE_REACTIONS = 2
176
+
177
+ def get_query_root_repository(self, owner: str, name: str, first: int, after: Optional[str] = None):
178
+ """
179
+ Get GraphQL query which allows fetching reactions starting from the repository:
180
+ query {
181
+ repository {
182
+ pull_requests(first: page_size) {
183
+ reviews(first: AVERAGE_REVIEWS) {
184
+ comments(first: AVERAGE_COMMENTS) {
185
+ reactions(first: AVERAGE_REACTIONS) {
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
192
+ """
193
+ op = self._get_operation()
194
+ repository = op.repository(owner=owner, name=name)
195
+ repository.name()
196
+ repository.owner.login()
197
+
198
+ kwargs = {"first": first}
199
+ if after:
200
+ kwargs["after"] = after
201
+ pull_requests = repository.pull_requests(**kwargs)
202
+ pull_requests.page_info.__fields__(has_next_page=True, end_cursor=True)
203
+ pull_requests.total_count()
204
+ pull_requests.nodes.id(__alias__="node_id")
205
+
206
+ reviews = self._select_reviews(pull_requests.nodes, first=self.AVERAGE_REVIEWS)
207
+ comments = self._select_comments(reviews.nodes, first=self.AVERAGE_COMMENTS)
208
+ self._select_reactions(comments.nodes, first=self.AVERAGE_REACTIONS)
209
+ return str(op)
210
+
211
+ def get_query_root_pull_request(self, node_id: str, first: int, after: str):
212
+ """
213
+ Get GraphQL query which allows fetching reactions starting from the pull_request:
214
+ query {
215
+ pull_request {
216
+ reviews(first: AVERAGE_REVIEWS) {
217
+ comments(first: AVERAGE_COMMENTS) {
218
+ reactions(first: AVERAGE_REACTIONS) {
219
+ }
220
+ }
221
+ }
222
+ }
223
+ }
224
+ """
225
+ op = self._get_operation()
226
+ pull_request = op.node(id=node_id).__as__(_schema_root.PullRequest)
227
+ pull_request.id(__alias__="node_id")
228
+ pull_request.repository.name()
229
+ pull_request.repository.owner.login()
230
+
231
+ reviews = self._select_reviews(pull_request, first, after)
232
+ comments = self._select_comments(reviews.nodes, first=self.AVERAGE_COMMENTS)
233
+ self._select_reactions(comments.nodes, first=self.AVERAGE_REACTIONS)
234
+ return str(op)
235
+
236
+ def get_query_root_review(self, node_id: str, first: int, after: str):
237
+ """
238
+ Get GraphQL query which allows fetching reactions starting from the review:
239
+ query {
240
+ review {
241
+ comments(first: AVERAGE_COMMENTS) {
242
+ reactions(first: AVERAGE_REACTIONS) {
243
+ }
244
+ }
245
+ }
246
+ }
247
+ """
248
+ op = self._get_operation()
249
+ review = op.node(id=node_id).__as__(_schema_root.PullRequestReview)
250
+ review.id(__alias__="node_id")
251
+ review.repository.name()
252
+ review.repository.owner.login()
253
+
254
+ comments = self._select_comments(review, first, after)
255
+ self._select_reactions(comments.nodes, first=self.AVERAGE_REACTIONS)
256
+ return str(op)
257
+
258
+ def get_query_root_comment(self, node_id: str, first: int, after: str):
259
+ """
260
+ Get GraphQL query which allows fetching reactions starting from the comment:
261
+ query {
262
+ comment {
263
+ reactions(first: AVERAGE_REACTIONS) {
264
+ }
265
+ }
266
+ }
267
+ """
268
+ op = self._get_operation()
269
+ comment = op.node(id=node_id).__as__(_schema_root.PullRequestReviewComment)
270
+ comment.id(__alias__="node_id")
271
+ comment.database_id(__alias__="id")
272
+ comment.repository.name()
273
+ comment.repository.owner.login()
274
+ self._select_reactions(comment, first, after)
275
+ return str(op)
276
+
277
+ def _select_reactions(self, comment: Selector, first: int, after: Optional[str] = None):
278
+ kwargs = {"first": first}
279
+ if after:
280
+ kwargs["after"] = after
281
+ reactions = comment.reactions(**kwargs)
282
+ reactions.page_info.__fields__(has_next_page=True, end_cursor=True)
283
+ reactions.total_count()
284
+ reactions.nodes.__fields__(id="node_id", database_id="id", content=True, created_at="created_at")
285
+ select_user_fields(reactions.nodes.user())
286
+ return reactions
287
+
288
+ def _select_comments(self, review: Selector, first: int, after: Optional[str] = None):
289
+ kwargs = {"first": first}
290
+ if after:
291
+ kwargs["after"] = after
292
+ comments = review.comments(**kwargs)
293
+ comments.page_info.__fields__(has_next_page=True, end_cursor=True)
294
+ comments.total_count()
295
+ comments.nodes.id(__alias__="node_id")
296
+ comments.nodes.database_id(__alias__="id")
297
+ return comments
298
+
299
+ def _select_reviews(self, pull_request: Selector, first: int, after: Optional[str] = None):
300
+ kwargs = {"first": first}
301
+ if after:
302
+ kwargs["after"] = after
303
+ reviews = pull_request.reviews(**kwargs)
304
+ reviews.page_info.__fields__(has_next_page=True, end_cursor=True)
305
+ reviews.total_count()
306
+ reviews.nodes.id(__alias__="node_id")
307
+ reviews.nodes.database_id(__alias__="id")
308
+ return reviews
309
+
310
+ def _get_operation(self):
311
+ return sgqlc.operation.Operation(_schema_root.query_type)
312
+
313
+
314
+ class CursorStorage:
315
+ def __init__(self, typenames):
316
+ self.typename_to_prio = {o: prio for prio, o in enumerate(reversed(typenames))}
317
+ self.count = itertools.count()
318
+ self.storage = []
319
+
320
+ def add_cursor(self, typename, cursor, total_count, parent_id=None):
321
+ priority = self.typename_to_prio[typename]
322
+ heapq.heappush(self.storage, (priority, next(self.count), (typename, cursor, total_count, parent_id)))
323
+
324
+ def get_cursor(self):
325
+ if self.storage:
326
+ _, _, c = heapq.heappop(self.storage)
327
+ return {"typename": c[0], "cursor": c[1], "total_count": c[2], "parent_id": c[3]}
source_github/run.py ADDED
@@ -0,0 +1,17 @@
1
+ #
2
+ # Copyright (c) 2023 Airbyte, Inc., all rights reserved.
3
+ #
4
+
5
+
6
+ import sys
7
+
8
+ from airbyte_cdk.entrypoint import launch
9
+ from source_github import SourceGithub
10
+ from source_github.config_migrations import MigrateBranch, MigrateRepository
11
+
12
+
13
+ def run():
14
+ source = SourceGithub()
15
+ MigrateRepository.migrate(sys.argv[1:], source)
16
+ MigrateBranch.migrate(sys.argv[1:], source)
17
+ launch(source, sys.argv[1:])
@@ -0,0 +1,63 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "repository": {
6
+ "type": "string"
7
+ },
8
+ "login": {
9
+ "type": ["null", "string"]
10
+ },
11
+ "id": {
12
+ "type": ["null", "integer"]
13
+ },
14
+ "node_id": {
15
+ "type": ["null", "string"]
16
+ },
17
+ "avatar_url": {
18
+ "type": ["null", "string"]
19
+ },
20
+ "gravatar_id": {
21
+ "type": ["null", "string"]
22
+ },
23
+ "url": {
24
+ "type": ["null", "string"]
25
+ },
26
+ "html_url": {
27
+ "type": ["null", "string"]
28
+ },
29
+ "followers_url": {
30
+ "type": ["null", "string"]
31
+ },
32
+ "following_url": {
33
+ "type": ["null", "string"]
34
+ },
35
+ "gists_url": {
36
+ "type": ["null", "string"]
37
+ },
38
+ "starred_url": {
39
+ "type": ["null", "string"]
40
+ },
41
+ "subscriptions_url": {
42
+ "type": ["null", "string"]
43
+ },
44
+ "organizations_url": {
45
+ "type": ["null", "string"]
46
+ },
47
+ "repos_url": {
48
+ "type": ["null", "string"]
49
+ },
50
+ "events_url": {
51
+ "type": ["null", "string"]
52
+ },
53
+ "received_events_url": {
54
+ "type": ["null", "string"]
55
+ },
56
+ "type": {
57
+ "type": ["null", "string"]
58
+ },
59
+ "site_admin": {
60
+ "type": ["null", "boolean"]
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "repository": {
6
+ "type": "string"
7
+ },
8
+ "name": {
9
+ "type": ["null", "string"]
10
+ },
11
+ "commit": {
12
+ "type": ["null", "object"],
13
+ "properties": {
14
+ "sha": {
15
+ "type": ["null", "string"]
16
+ },
17
+ "url": {
18
+ "type": ["null", "string"]
19
+ }
20
+ }
21
+ },
22
+ "protected": {
23
+ "type": ["null", "boolean"]
24
+ },
25
+ "protection": {
26
+ "type": ["null", "object"],
27
+ "properties": {
28
+ "required_status_checks": {
29
+ "type": ["null", "object"],
30
+ "properties": {
31
+ "enforcement_level": {
32
+ "type": ["null", "string"]
33
+ },
34
+ "contexts": {
35
+ "type": ["null", "array"],
36
+ "items": {
37
+ "type": ["null", "string"]
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ },
44
+ "protection_url": {
45
+ "type": ["null", "string"]
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,80 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "repository": {
6
+ "type": "string"
7
+ },
8
+ "login": {
9
+ "type": ["null", "string"]
10
+ },
11
+ "id": {
12
+ "type": ["null", "integer"]
13
+ },
14
+ "node_id": {
15
+ "type": ["null", "string"]
16
+ },
17
+ "avatar_url": {
18
+ "type": ["null", "string"]
19
+ },
20
+ "gravatar_id": {
21
+ "type": ["null", "string"]
22
+ },
23
+ "url": {
24
+ "type": ["null", "string"]
25
+ },
26
+ "html_url": {
27
+ "type": ["null", "string"]
28
+ },
29
+ "followers_url": {
30
+ "type": ["null", "string"]
31
+ },
32
+ "following_url": {
33
+ "type": ["null", "string"]
34
+ },
35
+ "gists_url": {
36
+ "type": ["null", "string"]
37
+ },
38
+ "starred_url": {
39
+ "type": ["null", "string"]
40
+ },
41
+ "subscriptions_url": {
42
+ "type": ["null", "string"]
43
+ },
44
+ "organizations_url": {
45
+ "type": ["null", "string"]
46
+ },
47
+ "repos_url": {
48
+ "type": ["null", "string"]
49
+ },
50
+ "events_url": {
51
+ "type": ["null", "string"]
52
+ },
53
+ "received_events_url": {
54
+ "type": ["null", "string"]
55
+ },
56
+ "type": {
57
+ "type": ["null", "string"]
58
+ },
59
+ "site_admin": {
60
+ "type": ["null", "boolean"]
61
+ },
62
+ "role_name": {
63
+ "type": ["null", "string"]
64
+ },
65
+ "permissions": {
66
+ "type": ["null", "object"],
67
+ "properties": {
68
+ "pull": {
69
+ "type": ["null", "boolean"]
70
+ },
71
+ "push": {
72
+ "type": ["null", "boolean"]
73
+ },
74
+ "admin": {
75
+ "type": ["null", "boolean"]
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,104 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "repository": {
6
+ "type": "string"
7
+ },
8
+ "id": {
9
+ "type": ["null", "integer"]
10
+ },
11
+ "node_id": {
12
+ "type": ["null", "string"]
13
+ },
14
+ "user": {
15
+ "$ref": "user.json"
16
+ },
17
+ "url": {
18
+ "type": ["null", "string"]
19
+ },
20
+ "html_url": {
21
+ "type": ["null", "string"]
22
+ },
23
+ "body": {
24
+ "type": ["null", "string"]
25
+ },
26
+ "user_id": {
27
+ "type": ["null", "integer"]
28
+ },
29
+ "created_at": {
30
+ "type": "string",
31
+ "format": "date-time"
32
+ },
33
+ "updated_at": {
34
+ "type": "string",
35
+ "format": "date-time"
36
+ },
37
+ "issue_url": {
38
+ "type": ["null", "string"]
39
+ },
40
+ "author_association": {
41
+ "type": ["null", "string"]
42
+ },
43
+ "reactions": {
44
+ "$ref": "reactions.json"
45
+ },
46
+ "performed_via_github_app": {
47
+ "type": ["null", "object"],
48
+ "properties": {
49
+ "id": {
50
+ "type": ["null", "integer"]
51
+ },
52
+ "slug": {
53
+ "type": ["null", "string"]
54
+ },
55
+ "node_id": {
56
+ "type": ["null", "string"]
57
+ },
58
+ "owner": {
59
+ "$ref": "user.json"
60
+ },
61
+ "name": {
62
+ "type": ["null", "string"]
63
+ },
64
+ "description": {
65
+ "type": ["null", "string"]
66
+ },
67
+ "external_url": {
68
+ "type": ["null", "string"]
69
+ },
70
+ "html_url": {
71
+ "type": ["null", "string"]
72
+ },
73
+ "created_at": {
74
+ "type": "string",
75
+ "format": "date-time"
76
+ },
77
+ "updated_at": {
78
+ "type": "string",
79
+ "format": "date-time"
80
+ },
81
+ "permissions": {
82
+ "type": "object",
83
+ "properties": {
84
+ "issues": {
85
+ "type": ["null", "string"]
86
+ },
87
+ "metadata": {
88
+ "type": ["null", "string"]
89
+ },
90
+ "pull_requests": {
91
+ "type": ["null", "string"]
92
+ }
93
+ }
94
+ },
95
+ "events": {
96
+ "type": "array",
97
+ "items": {
98
+ "type": ["null", "string"]
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema#",
3
+ "$ref": "reaction.json"
4
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "properties": {
5
+ "repository": {
6
+ "type": "string"
7
+ },
8
+ "html_url": {
9
+ "type": ["null", "string"]
10
+ },
11
+ "url": {
12
+ "type": ["null", "string"]
13
+ },
14
+ "id": {
15
+ "type": ["null", "integer"]
16
+ },
17
+ "node_id": {
18
+ "type": ["null", "string"]
19
+ },
20
+ "body": {
21
+ "type": ["null", "string"]
22
+ },
23
+ "path": {
24
+ "type": ["null", "string"]
25
+ },
26
+ "position": {
27
+ "type": ["null", "integer"]
28
+ },
29
+ "line": {
30
+ "type": ["null", "integer"]
31
+ },
32
+ "commit_id": {
33
+ "type": ["null", "string"]
34
+ },
35
+ "user": {
36
+ "$ref": "user.json"
37
+ },
38
+ "created_at": {
39
+ "type": "string",
40
+ "format": "date-time"
41
+ },
42
+ "updated_at": {
43
+ "type": "string",
44
+ "format": "date-time"
45
+ },
46
+ "author_association": {
47
+ "type": ["null", "string"]
48
+ },
49
+ "reactions": {
50
+ "$ref": "reactions.json"
51
+ }
52
+ }
53
+ }