Skip to content

Commit 5d1d6e6

Browse files
authored
Move the timeline methods back into the models (#7312)
* Move the timeline methods back into the models This is part 3 of adding a refresh event to the models * Fix tests
1 parent c00f7af commit 5d1d6e6

13 files changed

+219
-252
lines changed

src/github/activityBarViewProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
7979
} while (attemptsRemaining > 0 && mergability === PullRequestMergeability.Unknown);
8080

8181
const result: Partial<PullRequest> = {
82-
events: await this._item.githubRepository.getTimelineEvents(this._item),
82+
events: await this._item.getTimelineEvents(this._item),
8383
mergeable: mergability,
8484
};
8585
await this.refresh();
@@ -213,7 +213,7 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
213213
pullRequestModel.number,
214214
),
215215
this._folderRepositoryManager.getPullRequestRepositoryAccessAndMergeMethods(pullRequestModel),
216-
pullRequestModel.githubRepository.getTimelineEvents(pullRequestModel),
216+
pullRequestModel.getTimelineEvents(pullRequestModel),
217217
pullRequestModel.getReviewRequests(),
218218
this._folderRepositoryManager.getBranchNameForPullRequest(pullRequestModel),
219219
this._folderRepositoryManager.getPullRequestRepositoryDefaultBranch(pullRequestModel),

src/github/copilotPrWatcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ export class CopilotPRWatcher extends Disposable {
186186
const prs = await folderManager.getPullRequestsForCategory(githubRepository, await variableSubstitution(query, undefined, await folderManager.getPullRequestDefaults(), await this._getCurrentUser(folderManager)));
187187
for (const pr of prs?.items ?? []) {
188188
unseenKeys.delete(this._model.makeKey(pr.remote.owner, pr.remote.repositoryName, pr.number));
189-
const copilotEvents = await pr.githubRepository.getCopilotTimelineEvents(pr);
189+
const copilotEvents = await pr.getCopilotTimelineEvents(pr);
190190
if (copilotEvents.length === 0) {
191191
continue;
192192
}

src/github/folderRepositoryManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1737,7 +1737,7 @@ export class FolderRepositoryManager extends Disposable {
17371737
*/
17381738
this.telemetry.sendTelemetryEvent('pr.merge.success');
17391739
this._onDidMergePullRequest.fire();
1740-
return { merged: true, message: '', timeline: await parseCombinedTimelineEvents(result.data?.mergePullRequest.pullRequest.timelineItems.nodes ?? [], await pullRequest.githubRepository.getCopilotTimelineEvents(pullRequest), pullRequest.githubRepository) };
1740+
return { merged: true, message: '', timeline: await parseCombinedTimelineEvents(result.data?.mergePullRequest.pullRequest.timelineItems.nodes ?? [], await pullRequest.getCopilotTimelineEvents(pullRequest), pullRequest.githubRepository) };
17411741
})
17421742
.catch(e => {
17431743
/* __GDPR__
@@ -2524,7 +2524,7 @@ export class FolderRepositoryManager extends Disposable {
25242524
private async promptPullBrach(pr: PullRequestModel, branch: Branch, autoStashSetting?: boolean) {
25252525
if (!this._updateMessageShown || autoStashSetting) {
25262526
// When the PR is from Copilot, we only want to show the notification when Copilot is done working
2527-
const copilotStatus = await pr.githubRepository.copilotWorkingStatus(pr);
2527+
const copilotStatus = await pr.copilotWorkingStatus(pr);
25282528
if (copilotStatus === CopilotWorkingStatus.InProgress) {
25292529
return;
25302530
}

src/github/githubRepository.ts

Lines changed: 0 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@ import { ApolloQueryResult, DocumentNode, FetchResult, MutationOptions, NetworkS
88
import LRUCache from 'lru-cache';
99
import * as vscode from 'vscode';
1010
import { AuthenticationError, AuthProvider, GitHubServerType, isSamlError } from '../common/authentication';
11-
import { COPILOT_ACCOUNTS, IComment, IReviewThread } from '../common/comment';
1211
import { Disposable, disposeAll } from '../common/lifecycle';
1312
import Logger from '../common/logger';
1413
import { GitHubRemote, parseRemote } from '../common/remote';
1514
import { ITelemetry } from '../common/telemetry';
16-
import * as Common from '../common/timelineEvent';
17-
import { compareIgnoreCase, formatError } from '../common/utils';
1815
import { PRCommentControllerRegistry } from '../view/pullRequestCommentControllerRegistry';
1916
import { mergeQuerySchemaWithShared, OctokitCommon, Schema } from './common';
2017
import { CredentialStore, GitHub } from './credentials';
@@ -43,7 +40,6 @@ import {
4340
RepoProjectsResponse,
4441
RevertPullRequestResponse,
4542
SuggestedActorsResponse,
46-
TimelineEventsResponse,
4743
UserResponse,
4844
ViewerPermissionResponse,
4945
} from './graphql';
@@ -70,20 +66,16 @@ import * as limitedSchema from './queriesLimited.gql';
7066
import * as sharedSchema from './queriesShared.gql';
7167
import {
7268
convertRESTPullRequestToRawPullRequest,
73-
eventTime,
7469
getAvatarWithEnterpriseFallback,
7570
getOverrideBranch,
76-
insertNewCommitsSinceReview,
7771
isInCodespaces,
7872
parseAccount,
79-
parseCombinedTimelineEvents,
8073
parseGraphQLIssue,
8174
parseGraphQLPullRequest,
8275
parseGraphQLUser,
8376
parseGraphQLViewerPermission,
8477
parseMergeMethod,
8578
parseMilestone,
86-
parseSelectRestTimelineEvents,
8779
restPaginate,
8880
} from './utils';
8981

@@ -1516,229 +1508,6 @@ export class GitHubRepository extends Disposable {
15161508
return this._credentialStore.isCurrentUser(authProviderId, login);
15171509
}
15181510

1519-
1520-
/**
1521-
* TODO: @alexr00 we should delete this https://github.com/microsoft/vscode-pull-request-github/issues/6965
1522-
*/
1523-
async getCopilotTimelineEvents(issueModel: IssueModel, skipMerge: boolean = false): Promise<Common.TimelineEvent[]> {
1524-
if (!COPILOT_ACCOUNTS[issueModel.author.login]) {
1525-
return [];
1526-
}
1527-
1528-
Logger.debug(`Fetch Copilot timeline events of issue #${issueModel.number} - enter`, GitHubRepository.ID);
1529-
1530-
const { octokit, remote } = await this.ensure();
1531-
try {
1532-
const timeline = await restPaginate<typeof octokit.api.issues.listEventsForTimeline, OctokitCommon.ListEventsForTimelineResponse>(octokit.api.issues.listEventsForTimeline, {
1533-
issue_number: issueModel.number,
1534-
owner: remote.owner,
1535-
repo: remote.repositoryName,
1536-
per_page: 100
1537-
});
1538-
1539-
const timelineEvents = parseSelectRestTimelineEvents(issueModel, timeline);
1540-
if (timelineEvents.length === 0) {
1541-
return [];
1542-
}
1543-
if (!skipMerge) {
1544-
const oldLastEvent = issueModel.timelineEvents.length > 0 ? issueModel.timelineEvents[issueModel.timelineEvents.length - 1] : undefined;
1545-
let allEvents: Common.TimelineEvent[];
1546-
if (!oldLastEvent) {
1547-
allEvents = timelineEvents;
1548-
} else {
1549-
const oldEventTime = (eventTime(oldLastEvent) ?? 0);
1550-
const newEvents = timelineEvents.filter(event => (eventTime(event) ?? 0) > oldEventTime);
1551-
allEvents = [...issueModel.timelineEvents, ...newEvents];
1552-
}
1553-
const oldTimeline = issueModel.timelineEvents;
1554-
issueModel.timelineEvents = allEvents;
1555-
if (oldLastEvent && (allEvents.length !== oldTimeline.length)) {
1556-
this._onDidChangePullRequests.fire([issueModel]);
1557-
}
1558-
}
1559-
return timelineEvents;
1560-
} catch (e) {
1561-
Logger.error(`Error fetching Copilot timeline events of issue #${issueModel.number} - ${formatError(e)}`, GitHubRepository.ID);
1562-
return [];
1563-
}
1564-
}
1565-
1566-
async getIssueTimelineEvents(issueModel: IssueModel): Promise<Common.TimelineEvent[]> {
1567-
Logger.debug(`Fetch timeline events of issue #${issueModel.number} - enter`, GitHubRepository.ID);
1568-
const { query, remote, schema } = await this.ensure();
1569-
1570-
try {
1571-
const { data } = await query<TimelineEventsResponse>({
1572-
query: schema.IssueTimelineEvents,
1573-
variables: {
1574-
owner: remote.owner,
1575-
name: remote.repositoryName,
1576-
number: issueModel.number,
1577-
},
1578-
});
1579-
1580-
if (data.repository === null) {
1581-
Logger.error('Unexpected null repository when getting issue timeline events', GitHubRepository.ID);
1582-
return [];
1583-
}
1584-
const ret = data.repository.pullRequest.timelineItems.nodes;
1585-
const events = await parseCombinedTimelineEvents(ret, await this.getCopilotTimelineEvents(issueModel, true), this);
1586-
1587-
const crossRefs = new Map(events
1588-
.filter((event): event is Common.CrossReferencedEvent => {
1589-
if ((event.event === Common.EventType.CrossReferenced) && !event.source.isIssue) {
1590-
return (compareIgnoreCase(event.source.owner, issueModel.remote.owner) === 0 && compareIgnoreCase(event.source.repo, issueModel.remote.repositoryName) === 0);
1591-
}
1592-
return false;
1593-
1594-
}).map((event: Common.CrossReferencedEvent) => {
1595-
return [event.source.url, event];
1596-
}));
1597-
1598-
for (const pr of this._pullRequestModelsByNumber.values()) {
1599-
if (crossRefs.has(pr.model.html_url)) {
1600-
crossRefs.delete(pr.model.html_url);
1601-
}
1602-
}
1603-
const oldEvents = issueModel.timelineEvents;
1604-
issueModel.timelineEvents = events;
1605-
if (crossRefs.size > 0) {
1606-
this._onDidChangePullRequests.fire([issueModel]);
1607-
} else if (oldEvents.length !== events.length) {
1608-
this._onDidChangePullRequests.fire([issueModel]);
1609-
}
1610-
return events;
1611-
} catch (e) {
1612-
console.log(e);
1613-
return [];
1614-
}
1615-
}
1616-
1617-
async copilotWorkingStatus(issueModel: IssueModel): Promise<CopilotWorkingStatus | undefined> {
1618-
const copilotEvents = await this.getCopilotTimelineEvents(issueModel);
1619-
if (copilotEvents.length > 0) {
1620-
const lastEvent = copilotEvents[copilotEvents.length - 1];
1621-
if (lastEvent.event === Common.EventType.CopilotFinished) {
1622-
return CopilotWorkingStatus.Done;
1623-
} else if (lastEvent.event === Common.EventType.CopilotStarted) {
1624-
return CopilotWorkingStatus.InProgress;
1625-
} else if (lastEvent.event === Common.EventType.CopilotFinishedError) {
1626-
return CopilotWorkingStatus.Error;
1627-
}
1628-
}
1629-
return CopilotWorkingStatus.NotCopilotIssue;
1630-
}
1631-
1632-
/**
1633-
* Get the timeline events of a pull request, including comments, reviews, commits, merges, deletes, and assigns.
1634-
*/
1635-
async getTimelineEvents(pullRequestModel: PullRequestModel): Promise<Common.TimelineEvent[]> {
1636-
const getTimelineEvents = async () => {
1637-
Logger.debug(`Fetch timeline events of PR #${pullRequestModel.number} - enter`, PullRequestModel.ID);
1638-
const { query, remote, schema } = await this.ensure();
1639-
try {
1640-
const { data } = await query<TimelineEventsResponse>({
1641-
query: schema.TimelineEvents,
1642-
variables: {
1643-
owner: remote.owner,
1644-
name: remote.repositoryName,
1645-
number: pullRequestModel.number,
1646-
},
1647-
});
1648-
1649-
if (data.repository === null) {
1650-
Logger.error('Unexpected null repository when fetching timeline', PullRequestModel.ID);
1651-
}
1652-
return data;
1653-
} catch (e) {
1654-
Logger.error(`Failed to get pull request timeline events: ${e}`, PullRequestModel.ID);
1655-
console.log(e);
1656-
return undefined;
1657-
}
1658-
};
1659-
1660-
const [data, latestReviewCommitInfo, currentUser, reviewThreads] = await Promise.all([
1661-
getTimelineEvents(),
1662-
pullRequestModel.getViewerLatestReviewCommit(),
1663-
(await this.getAuthenticatedUser()).login,
1664-
pullRequestModel.getReviewThreads()
1665-
]);
1666-
1667-
1668-
const ret = data?.repository?.pullRequest.timelineItems.nodes ?? [];
1669-
const events = await parseCombinedTimelineEvents(ret, await this.getCopilotTimelineEvents(pullRequestModel, true), this);
1670-
1671-
this.addReviewTimelineEventComments(events, reviewThreads);
1672-
insertNewCommitsSinceReview(events, latestReviewCommitInfo?.sha, currentUser, pullRequestModel.head);
1673-
Logger.debug(`Fetch timeline events of PR #${pullRequestModel.number} - done`, PullRequestModel.ID);
1674-
const oldEvents = pullRequestModel.timelineEvents;
1675-
pullRequestModel.timelineEvents = events;
1676-
if (oldEvents.length !== events.length) {
1677-
this._onDidChangePullRequests.fire([pullRequestModel]);
1678-
}
1679-
return events;
1680-
}
1681-
1682-
private addReviewTimelineEventComments(events: Common.TimelineEvent[], reviewThreads: IReviewThread[]): void {
1683-
interface CommentNode extends IComment {
1684-
childComments?: CommentNode[];
1685-
}
1686-
1687-
const reviewEvents = events.filter((e): e is Common.ReviewEvent => e.event === Common.EventType.Reviewed);
1688-
const reviewComments = reviewThreads.reduce((previous, current) => (previous as IComment[]).concat(current.comments), []);
1689-
1690-
const reviewEventsById = reviewEvents.reduce((index, evt) => {
1691-
index[evt.id] = evt;
1692-
evt.comments = [];
1693-
return index;
1694-
}, {} as { [key: number]: Common.ReviewEvent });
1695-
1696-
const commentsById = reviewComments.reduce((index, evt) => {
1697-
index[evt.id] = evt;
1698-
return index;
1699-
}, {} as { [key: number]: CommentNode });
1700-
1701-
const roots: CommentNode[] = [];
1702-
let i = reviewComments.length;
1703-
while (i-- > 0) {
1704-
const c: CommentNode = reviewComments[i];
1705-
if (!c.inReplyToId) {
1706-
roots.unshift(c);
1707-
continue;
1708-
}
1709-
const parent = commentsById[c.inReplyToId];
1710-
parent.childComments = parent.childComments || [];
1711-
parent.childComments = [c, ...(c.childComments || []), ...parent.childComments];
1712-
}
1713-
1714-
roots.forEach(c => {
1715-
const review = reviewEventsById[c.pullRequestReviewId!];
1716-
if (review) {
1717-
review.comments = review.comments.concat(c).concat(c.childComments || []);
1718-
}
1719-
});
1720-
1721-
reviewThreads.forEach(thread => {
1722-
if (!thread.prReviewDatabaseId || !reviewEventsById[thread.prReviewDatabaseId]) {
1723-
return;
1724-
}
1725-
const prReviewThreadEvent = reviewEventsById[thread.prReviewDatabaseId];
1726-
prReviewThreadEvent.reviewThread = {
1727-
threadId: thread.id,
1728-
canResolve: thread.viewerCanResolve,
1729-
canUnresolve: thread.viewerCanUnresolve,
1730-
isResolved: thread.isResolved
1731-
};
1732-
1733-
});
1734-
1735-
const pendingReview = reviewEvents.filter(r => r.state?.toLowerCase() === 'pending')[0];
1736-
if (pendingReview) {
1737-
// Ensures that pending comments made in reply to other reviews are included for the pending review
1738-
pendingReview.comments = reviewComments.filter(c => c.isDraft);
1739-
}
1740-
}
1741-
17421511
/**
17431512
* Get the status checks of the pull request, those for the last commit.
17441513
*

0 commit comments

Comments
 (0)