Skip to content

Commit d2430a1

Browse files
authored
Show Copilot start/stop in the timeline (#6966)
* WIP Add Padawan events to PR timeline Fixes #6944 * Show Copilot start/stop in the timeline * Show link to open Padawan session Fixes #6917 * Cleanrup * Merge GraphQL and REST timeline events in order
1 parent f94a826 commit d2430a1

15 files changed

Lines changed: 234 additions & 53 deletions

File tree

.eslintrc.base.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
"no-sequences": "error",
5656
"no-template-curly-in-string": "warn",
5757
"no-throw-literal": "error",
58-
"no-unmodified-loop-condition": "warn",
5958
"no-unneeded-ternary": "error",
6059
"no-use-before-define": "off",
6160
"no-useless-call": "error",

resources/icons/briefcase.svg

Lines changed: 1 addition & 0 deletions
Loading

resources/icons/tasklist.svg

Lines changed: 1 addition & 0 deletions
Loading

src/common/timelineEvent.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export enum EventType {
2222
CrossReferenced,
2323
Closed,
2424
Reopened,
25+
CopilotStarted,
26+
CopilotFinished,
2527
Other,
2628
}
2729

@@ -75,7 +77,7 @@ export interface CommitEvent {
7577
htmlUrl: string;
7678
message: string;
7779
bodyHTML?: string;
78-
authoredDate: Date;
80+
committedDate: Date;
7981
}
8082

8183
export interface NewCommitsSinceReviewEvent {
@@ -148,4 +150,19 @@ export interface ReopenedEvent {
148150
createdAt: string;
149151
}
150152

151-
export type TimelineEvent = CommitEvent | ReviewEvent | CommentEvent | NewCommitsSinceReviewEvent | MergedEvent | AssignEvent | UnassignEvent | HeadRefDeleteEvent | CrossReferencedEvent | ClosedEvent | ReopenedEvent;
153+
export interface CopilotStartedEvent {
154+
id: string;
155+
event: EventType.CopilotStarted;
156+
createdAt: string;
157+
onBehalfOf: IAccount;
158+
sessionUrl?: string;
159+
}
160+
161+
export interface CopilotFinishedEvent {
162+
id: string;
163+
event: EventType.CopilotFinished;
164+
createdAt: string;
165+
onBehalfOf: IAccount;
166+
}
167+
168+
export type TimelineEvent = CommitEvent | ReviewEvent | CommentEvent | NewCommitsSinceReviewEvent | MergedEvent | AssignEvent | UnassignEvent | HeadRefDeleteEvent | CrossReferencedEvent | ClosedEvent | ReopenedEvent | CopilotStartedEvent | CopilotFinishedEvent;

src/github/common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export namespace OctokitCommon {
7171
export type Commit = CompareCommits['commits'][0];
7272
export type CommitFiles = CompareCommits['files']
7373
export type Notification = Endpoints['GET /notifications']['response']['data'][0];
74+
export type ListEventsForTimelineResponse = Endpoints['GET /repos/{owner}/{repo}/issues/{issue_number}/timeline']['response']['data'][0];
7475
}
7576

7677
export type Schema = { [key: string]: any, definitions: any[]; };

src/github/folderRepositoryManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ import {
5353
getOverrideBranch,
5454
getPRFetchQuery,
5555
loginComparator,
56+
parseCombinedTimelineEvents,
5657
parseGraphQLPullRequest,
57-
parseGraphQLTimelineEvents,
5858
parseGraphQLUser,
5959
teamComparator,
6060
variableSubstitution,
@@ -1728,7 +1728,7 @@ export class FolderRepositoryManager extends Disposable {
17281728
*/
17291729
this.telemetry.sendTelemetryEvent('pr.merge.success');
17301730
this._onDidMergePullRequest.fire();
1731-
return { merged: true, message: '', timeline: await parseGraphQLTimelineEvents(result.data?.mergePullRequest.pullRequest.timelineItems.nodes ?? [], pullRequest.githubRepository) };
1731+
return { merged: true, message: '', timeline: await parseCombinedTimelineEvents(result.data?.mergePullRequest.pullRequest.timelineItems.nodes ?? [], await pullRequest.getRestOnlyTimelineEvents(), pullRequest.githubRepository) };
17321732
})
17331733
.catch(e => {
17341734
/* __GDPR__

src/github/graphql.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export interface Commit {
184184
};
185185
oid: string;
186186
message: string;
187-
authoredDate: Date;
187+
committedDate: Date;
188188
};
189189

190190
url: string;
@@ -267,7 +267,7 @@ export interface TimelineEventsResponse {
267267

268268
export interface LatestCommit {
269269
commit: {
270-
authoredDate: string;
270+
committedDate: string;
271271
}
272272
}
273273

src/github/issueModel.ts

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as vscode from 'vscode';
7-
import { IComment } from '../common/comment';
7+
import { COPILOT_ACCOUNTS, IComment } from '../common/comment';
88
import Logger from '../common/logger';
99
import { Remote } from '../common/remote';
1010
import { ClosedEvent, EventType, TimelineEvent } from '../common/timelineEvent';
1111
import { formatError } from '../common/utils';
12+
import { OctokitCommon } from './common';
1213
import { GitHubRepository } from './githubRepository';
1314
import {
1415
AddIssueCommentResponse,
@@ -21,7 +22,7 @@ import {
2122
UpdateIssueResponse,
2223
} from './graphql';
2324
import { GithubItemStateEnum, IAccount, IIssueEditData, IMilestone, IProject, IProjectItem, Issue } from './interface';
24-
import { convertRESTIssueToRawPullRequest, parseGraphQlIssueComment, parseGraphQLTimelineEvents } from './utils';
25+
import { convertRESTIssueToRawPullRequest, parseCombinedTimelineEvents, parseGraphQlIssueComment, parseSelectRestTimelineEvents, restPaginate } from './utils';
2526

2627
export class IssueModel<TItem extends Issue = Issue> {
2728
static ID = 'IssueModel';
@@ -325,6 +326,32 @@ export class IssueModel<TItem extends Issue = Issue> {
325326
return this.item.projectItems;
326327
}
327328

329+
/**
330+
* TODO: @alexr00 we should delete this https://github.com/microsoft/vscode-pull-request-github/issues/6965
331+
*/
332+
async getRestOnlyTimelineEvents(): Promise<TimelineEvent[]> {
333+
if (!COPILOT_ACCOUNTS[this.author.login]) {
334+
return [];
335+
}
336+
337+
Logger.debug(`Fetch Copilot timeline events of issue #${this.number} - enter`, IssueModel.ID);
338+
339+
const { octokit, remote } = await this.githubRepository.ensure();
340+
try {
341+
const timeline = await restPaginate<typeof octokit.api.issues.listEventsForTimeline, OctokitCommon.ListEventsForTimelineResponse>(octokit.api.issues.listEventsForTimeline, {
342+
issue_number: this.number,
343+
owner: remote.owner,
344+
repo: remote.repositoryName,
345+
per_page: 100
346+
});
347+
348+
return parseSelectRestTimelineEvents(this, timeline);
349+
} catch (e) {
350+
Logger.error(`Error fetching Copilot timeline events of issue #${this.number} - ${formatError(e)}`, IssueModel.ID);
351+
return [];
352+
}
353+
}
354+
328355
async getIssueTimelineEvents(): Promise<TimelineEvent[]> {
329356
Logger.debug(`Fetch timeline events of issue #${this.number} - enter`, IssueModel.ID);
330357
const githubRepository = this.githubRepository;
@@ -345,7 +372,7 @@ export class IssueModel<TItem extends Issue = Issue> {
345372
return [];
346373
}
347374
const ret = data.repository.pullRequest.timelineItems.nodes;
348-
const events = await parseGraphQLTimelineEvents(ret, githubRepository);
375+
const events = await parseCombinedTimelineEvents(ret, await this.getRestOnlyTimelineEvents(), githubRepository);
349376

350377
return events;
351378
} catch (e) {
@@ -381,8 +408,8 @@ export class IssueModel<TItem extends Issue = Issue> {
381408
...(data.repository.pullRequest.comments.nodes.flatMap(node => node.reactions.nodes.map(reaction => new Date(reaction.createdAt)))),
382409
...(data.repository.pullRequest.timelineItems.nodes.map(node => {
383410
const latestCommit = node as Partial<LatestCommit>;
384-
if (latestCommit.commit?.authoredDate) {
385-
return new Date(latestCommit.commit.authoredDate);
411+
if (latestCommit.commit?.committedDate) {
412+
return new Date(latestCommit.commit.committedDate);
386413
}
387414
const latestReviewThread = node as Partial<LatestReviewThread>;
388415
if ((latestReviewThread.comments?.nodes.length ?? 0) > 0) {

src/github/pullRequestModel.ts

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,12 @@ import {
7676
getReactionGroup,
7777
insertNewCommitsSinceReview,
7878
parseAccount,
79+
parseCombinedTimelineEvents,
7980
parseGraphQLComment,
8081
parseGraphQLReaction,
8182
parseGraphQLReviewers,
8283
parseGraphQLReviewEvent,
8384
parseGraphQLReviewThread,
84-
parseGraphQLTimelineEvents,
8585
parseMergeability,
8686
parseMergeQueueEntry,
8787
RestAccount,
@@ -1162,40 +1162,45 @@ export class PullRequestModel extends IssueModel<PullRequest> implements IPullRe
11621162
* Get the timeline events of a pull request, including comments, reviews, commits, merges, deletes, and assigns.
11631163
*/
11641164
async getTimelineEvents(): Promise<TimelineEvent[]> {
1165-
Logger.debug(`Fetch timeline events of PR #${this.number} - enter`, PullRequestModel.ID);
1166-
const { query, remote, schema } = await this.githubRepository.ensure();
1167-
1168-
try {
1169-
const [{ data }, latestReviewCommitInfo, currentUser, reviewThreads] = await Promise.all([
1170-
query<TimelineEventsResponse>({
1165+
const getTimelineEvents = async () => {
1166+
Logger.debug(`Fetch timeline events of PR #${this.number} - enter`, PullRequestModel.ID);
1167+
const { query, remote, schema } = await this.githubRepository.ensure();
1168+
try {
1169+
const { data } = await query<TimelineEventsResponse>({
11711170
query: schema.TimelineEvents,
11721171
variables: {
11731172
owner: remote.owner,
11741173
name: remote.repositoryName,
11751174
number: this.number,
11761175
},
1177-
}),
1178-
this.getViewerLatestReviewCommit(),
1179-
(await this.githubRepository.getAuthenticatedUser()).login,
1180-
this.getReviewThreads()
1181-
]);
1176+
});
11821177

1183-
if (data.repository === null) {
1184-
Logger.error('Unexpected null repository when fetching timeline', PullRequestModel.ID);
1178+
if (data.repository === null) {
1179+
Logger.error('Unexpected null repository when fetching timeline', PullRequestModel.ID);
1180+
}
1181+
return data;
1182+
} catch (e) {
1183+
Logger.error(`Failed to get pull request timeline events: ${e}`, PullRequestModel.ID);
1184+
console.log(e);
1185+
return undefined;
11851186
}
1187+
};
11861188

1187-
const ret = data.repository?.pullRequest.timelineItems.nodes;
1188-
const events = ret ? await parseGraphQLTimelineEvents(ret, this.githubRepository) : [];
1189+
const [data, latestReviewCommitInfo, currentUser, reviewThreads] = await Promise.all([
1190+
getTimelineEvents(),
1191+
this.getViewerLatestReviewCommit(),
1192+
(await this.githubRepository.getAuthenticatedUser()).login,
1193+
this.getReviewThreads()
1194+
]);
11891195

1190-
this.addReviewTimelineEventComments(events, reviewThreads);
1191-
insertNewCommitsSinceReview(events, latestReviewCommitInfo?.sha, currentUser, this.head);
1192-
Logger.debug(`Fetch timeline events of PR #${this.number} - done`, PullRequestModel.ID);
1193-
return events;
1194-
} catch (e) {
1195-
Logger.error(`Failed to get pull request timeline events: ${e}`, PullRequestModel.ID);
1196-
console.log(e);
1197-
return [];
1198-
}
1196+
1197+
const ret = data?.repository?.pullRequest.timelineItems.nodes ?? [];
1198+
const events = await parseCombinedTimelineEvents(ret, await this.getRestOnlyTimelineEvents(), this.githubRepository);
1199+
1200+
this.addReviewTimelineEventComments(events, reviewThreads);
1201+
insertNewCommitsSinceReview(events, latestReviewCommitInfo?.sha, currentUser, this.head);
1202+
Logger.debug(`Fetch timeline events of PR #${this.number} - done`, PullRequestModel.ID);
1203+
return events;
11991204
}
12001205

12011206
protected override getUpdatesQuery(schema: any): any {

src/github/queriesShared.gql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ fragment Commit on PullRequestCommit {
121121
}
122122
oid
123123
message
124-
authoredDate
124+
committedDate
125125
}
126126
url
127127
}
@@ -480,7 +480,7 @@ query LatestUpdates($owner: String!, $name: String!, $number: Int!, $since: Date
480480
}
481481
... on PullRequestCommit {
482482
commit {
483-
authoredDate
483+
committedDate
484484
}
485485
}
486486
... on PullRequestReview {

0 commit comments

Comments
 (0)