Skip to content

Commit a308168

Browse files
authored
Add support for Timeline events (#366)
This is primarily based on the timeline API response schema (using code generated from the schema by the script in `tool/process_github_schema.dart`). Where I could find existing model classes compatible with bits of that schema I tried to re-use them, extending them as necessary. I took some liberties where the GitHub API documentation was vague, contradictory, or otherwise dubious. The comments on the new code are based on the comments in the response schema. They're not super helpful, if I'm honest. I found that some of the model classes were inconsistent in various ways and tried to clean up the code near what I touched to be more consistent. For example, I made a bunch of fields on a bunch of model classes mutable where previously they were `final`, to be more consistent with other model classes. I also moved some constructors to be above their corresponding fields to be more consistent with other code that already did that.
1 parent 043e0ed commit a308168

23 files changed

+4264
-217
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 9.12.0
2+
3+
* Add support for issue and PR timeline events via `Issue.listTimeline`.
4+
15
## 9.11.0
26

37
* expose IssueLabel.description; update labels REST APIs by @devoncarew in https://github.com/SpinlockLabs/github.dart/pull/355

CONTRIBUTING.md

+8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ GitHub.dart is committed to efficiency as much as possible. If your code is not
3030

3131
Pull Request rejections are not a bad thing. It just means you need to fix something. Perhaps it is important to define 'rejection' as it is used in this case. A rejection is when a GitHub.dart committer comments on a Pull Request with a comment like 'rejected due to incorrect formatting'.
3232

33+
## Generated code
34+
35+
To regenerate the JSON logic for the models, run:
36+
37+
```
38+
dart run build_runner build -d
39+
```
40+
3341
## Tests
3442

3543
To run the complete test suite you will need to install

lib/src/common.dart

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export 'package:github/src/common/model/repos_releases.dart';
3131
export 'package:github/src/common/model/repos_stats.dart';
3232
export 'package:github/src/common/model/repos_statuses.dart';
3333
export 'package:github/src/common/model/search.dart';
34+
export 'package:github/src/common/model/timeline.dart';
35+
export 'package:github/src/common/model/timeline_support.dart';
3436
export 'package:github/src/common/model/users.dart';
3537
export 'package:github/src/common/orgs_service.dart';
3638
export 'package:github/src/common/pulls_service.dart';

lib/src/common/issues_service.dart

+11
Original file line numberDiff line numberDiff line change
@@ -432,4 +432,15 @@ class IssuesService extends Service {
432432
.request('DELETE', '/repos/${slug.fullName}/milestones/$number')
433433
.then((response) => response.statusCode == StatusCodes.NO_CONTENT);
434434
}
435+
436+
/// Lists all timeline events for an issue.
437+
///
438+
/// API docs: https://docs.github.com/en/rest/issues/timeline?apiVersion=2022-11-28
439+
Stream<TimelineEvent> listTimeline(RepositorySlug slug, int issueNumber) {
440+
return PaginationHelper(github).objects(
441+
'GET',
442+
'/repos/${slug.fullName}/issues/$issueNumber/timeline',
443+
TimelineEvent.fromJson,
444+
);
445+
}
435446
}

lib/src/common/model/issues.dart

+81-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:github/src/common.dart';
2-
import 'package:github/src/common/model/users.dart';
32
import 'package:json_annotation/json_annotation.dart';
43

54
part 'issues.g.dart';
@@ -26,6 +25,24 @@ class Issue {
2625
this.updatedAt,
2726
this.body = '',
2827
this.closedBy,
28+
29+
// Properties from the Timeline API
30+
this.activeLockReason,
31+
this.authorAssociation,
32+
this.bodyHtml,
33+
this.bodyText,
34+
this.commentsUrl,
35+
this.draft,
36+
this.eventsUrl,
37+
this.labelsUrl,
38+
this.locked,
39+
this.nodeId,
40+
this.performedViaGithubApp,
41+
this.reactions,
42+
this.repository,
43+
this.repositoryUrl,
44+
this.stateReason,
45+
this.timelineUrl,
2946
}) {
3047
if (labels != null) {
3148
this.labels = labels;
@@ -89,6 +106,46 @@ class Issue {
89106
bool get isOpen => state == 'open';
90107
bool get isClosed => state == 'closed';
91108

109+
// The following properties were added to support the Timeline API.
110+
111+
String? activeLockReason;
112+
113+
/// How the author is associated with the repository.
114+
///
115+
/// Example: `OWNER`
116+
String? authorAssociation;
117+
118+
String? bodyHtml;
119+
120+
String? bodyText;
121+
122+
String? commentsUrl;
123+
124+
bool? draft;
125+
126+
String? eventsUrl;
127+
128+
String? labelsUrl;
129+
130+
bool? locked;
131+
132+
String? nodeId;
133+
134+
GitHubApp? performedViaGithubApp;
135+
136+
ReactionRollup? reactions;
137+
138+
Repository? repository;
139+
140+
String? repositoryUrl;
141+
142+
/// The reason for the current state
143+
///
144+
/// Example: `not_planned`
145+
String? stateReason;
146+
147+
String? timelineUrl;
148+
92149
factory Issue.fromJson(Map<String, dynamic> input) => _$IssueFromJson(input);
93150
Map<String, dynamic> toJson() => _$IssueToJson(this);
94151
}
@@ -204,6 +261,13 @@ class Milestone {
204261
this.createdAt,
205262
this.updatedAt,
206263
this.dueOn,
264+
265+
// Properties from the Timeline API
266+
this.closedAt,
267+
this.htmlUrl,
268+
this.labelsUrl,
269+
this.nodeId,
270+
this.url,
207271
});
208272

209273
/// Unique Identifier for Milestone
@@ -241,6 +305,22 @@ class Milestone {
241305
/// The due date for this milestone
242306
DateTime? dueOn;
243307

308+
// The following properties were added to support the Timeline API.
309+
310+
DateTime? closedAt;
311+
312+
/// Example: `https://github.com/octocat/Hello-World/milestones/v1.0`
313+
String? htmlUrl;
314+
315+
/// Example: `https://api.github.com/repos/octocat/Hello-World/milestones/1/labels`
316+
String? labelsUrl;
317+
318+
/// Example: `MDk6TWlsZXN0b25lMTAwMjYwNA==`
319+
String? nodeId;
320+
321+
/// Example: `https://api.github.com/repos/octocat/Hello-World/milestones/1`
322+
String? url;
323+
244324
factory Milestone.fromJson(Map<String, dynamic> input) =>
245325
_$MilestoneFromJson(input);
246326
Map<String, dynamic> toJson() => _$MilestoneToJson(this);

lib/src/common/model/issues.g.dart

+51
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)