Skip to content

Commit 16b4307

Browse files
Improves pull request conversion and models
1 parent 8bf34fe commit 16b4307

File tree

3 files changed

+160
-38
lines changed

3 files changed

+160
-38
lines changed

src/plus/integrations/providers/azure/azure.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export class AzureDevOpsApi implements Disposable {
102102
const pr = prResult?.value.find(pr => pr.sourceRefName.endsWith(branch));
103103
if (pr == null) return undefined;
104104

105-
return fromAzurePullRequest(pr, provider);
105+
return fromAzurePullRequest(pr, provider, owner, projectName);
106106
} catch (ex) {
107107
Logger.error(ex, scope);
108108
return undefined;

src/plus/integrations/providers/azure/models.ts

Lines changed: 153 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
import { RepositoryAccessLevel } from '../../../../git/models/issue';
12
import type { IssueOrPullRequestState } from '../../../../git/models/issueOrPullRequest';
2-
import { PullRequest } from '../../../../git/models/pullRequest';
3+
import type { PullRequestMember, PullRequestReviewer } from '../../../../git/models/pullRequest';
4+
import {
5+
PullRequest,
6+
PullRequestMergeableState,
7+
PullRequestReviewDecision,
8+
PullRequestReviewState,
9+
} from '../../../../git/models/pullRequest';
310
import type { Provider } from '../../../../git/models/remoteProvider';
411

512
export type AzureWorkItemStateCategory = 'Proposed' | 'InProgress' | 'Resolved' | 'Completed' | 'Removed';
@@ -36,15 +43,24 @@ export interface AzureUser {
3643
id: string;
3744
uniqueName: string;
3845
imageUrl: string;
39-
descriptor: string;
46+
descriptor?: string;
4047
}
4148

4249
export interface AzureUserWithVote extends AzureUser {
43-
isFlagged: boolean;
44-
isReapprove: boolean;
45-
isRequired: boolean;
50+
isFlagged?: boolean;
51+
hasDeclined?: boolean;
52+
isReapprove?: boolean;
53+
isRequired?: boolean;
54+
vote?: AzurePullRequestVote;
4655
}
4756

57+
export type AzurePullRequestVote =
58+
| 10 // approved
59+
| 5 // approved with suggestions
60+
| 0 // no vote
61+
| -5 // waiting for author
62+
| -10; // rejected
63+
4864
export interface AzureWorkItemCommentVersionRef {
4965
commentId: number;
5066
createdInRevision: number;
@@ -133,19 +149,6 @@ export interface AzureProject {
133149
lastUpdateTime: string;
134150
}
135151

136-
export type AzureProjectState = 'createPending' | 'deleted' | 'deleting' | 'new' | 'unchanged' | 'wellFormed';
137-
export type AzureProjectVisibility = 'private' | 'public';
138-
139-
export interface AzureProject {
140-
id: string;
141-
name: string;
142-
url: string;
143-
state: AzureProjectState;
144-
revision: number;
145-
visibility: AzureProjectVisibility;
146-
lastUpdateTime: string;
147-
}
148-
149152
export interface AzureRepository {
150153
id: string;
151154
name: string;
@@ -238,10 +241,13 @@ export interface AzurePullRequest {
238241
closedBy?: AzureUser; // Can be missed even if closedDate is presented.
239242
title: string;
240243
description: string;
244+
forkSource?: AzureGitForkRef;
241245
sourceRefName: string;
242246
targetRefName: string;
243247
isDraft: boolean;
244248
mergeId: string;
249+
mergeStatus?: AzurePullRequestAsyncStatus;
250+
lastMergeCommit?: AzureGitCommitRef;
245251
lastMergeSourceCommit: AzureGitCommitRef;
246252
lastMergeTargetCommit: AzureGitCommitRef;
247253
reviewers: AzureUserWithVote[];
@@ -267,14 +273,11 @@ export interface AzurePullRequestWithLinks extends AzurePullRequest {
267273
commits?: AzureGitCommitRef[];
268274
completionOptions?: AzurePullRequestCompletionOptions;
269275
completionQueueTime?: string;
270-
forkSource?: AzureGitForkRef;
271276
hasMultipleMergeBases?: boolean;
272277
labels?: AzureWebApiTagDefinition[];
273-
lastMergeCommit?: AzureGitCommitRef;
274278
mergeFailureMessage?: string;
275279
mergeFailureType?: 'caseSensitive' | 'none' | 'objectTooLarge' | 'unknown';
276280
mergeOptions?: AzureGitPullRequestMergeOptions;
277-
mergeStatus?: AzurePullRequestAsyncStatus;
278281
remoteUrl?: string;
279282
workItemRefs?: AzureResourceRef[];
280283
}
@@ -306,7 +309,91 @@ export function getAzurePullRequestWebUrl(pr: AzurePullRequest): string {
306309
return `${baseUrl}/${owner}/${repoPath}/pullrequest/${pr.pullRequestId}`;
307310
}
308311

309-
export function fromAzurePullRequest(pr: AzurePullRequest, provider: Provider): PullRequest {
312+
export function fromAzurePullRequestMergeStatusToMergeableState(
313+
mergeStatus: AzurePullRequestAsyncStatus,
314+
): PullRequestMergeableState {
315+
switch (mergeStatus) {
316+
case 'conflicts':
317+
return PullRequestMergeableState.Conflicting;
318+
case 'failure':
319+
return PullRequestMergeableState.FailingChecks;
320+
case 'rejectedByPolicy':
321+
return PullRequestMergeableState.BlockedByPolicy;
322+
case 'succeeded':
323+
return PullRequestMergeableState.Mergeable;
324+
case 'notSet':
325+
case 'queued':
326+
default:
327+
return PullRequestMergeableState.Unknown;
328+
}
329+
}
330+
331+
export function fromAzurePullRequestVoteToReviewState(vote: AzurePullRequestVote): PullRequestReviewState {
332+
switch (vote) {
333+
case 10:
334+
case 5:
335+
return PullRequestReviewState.Approved;
336+
case 0:
337+
return PullRequestReviewState.ReviewRequested;
338+
case -5:
339+
case -10:
340+
return PullRequestReviewState.ChangesRequested;
341+
default:
342+
return PullRequestReviewState.ReviewRequested;
343+
}
344+
}
345+
346+
export function fromAzureUserWithVoteToReviewer(reviewer: AzureUserWithVote): PullRequestReviewer {
347+
return {
348+
isCodeOwner: undefined,
349+
reviewer: {
350+
avatarUrl: reviewer.imageUrl,
351+
id: reviewer.id,
352+
name: reviewer.displayName,
353+
url: reviewer.url,
354+
},
355+
state: fromAzurePullRequestVoteToReviewState(reviewer.vote ?? 0),
356+
};
357+
}
358+
359+
export function getAzurePullRequestReviewDecision(
360+
votes: AzurePullRequestVote[],
361+
): PullRequestReviewDecision | undefined {
362+
const reviewStates = votes.map(vote => fromAzurePullRequestVoteToReviewState(vote));
363+
if (reviewStates.includes(PullRequestReviewState.ChangesRequested)) {
364+
return PullRequestReviewDecision.ChangesRequested;
365+
}
366+
367+
if (reviewStates.includes(PullRequestReviewState.ReviewRequested)) {
368+
return PullRequestReviewDecision.ReviewRequired;
369+
}
370+
371+
if (reviewStates.includes(PullRequestReviewState.Approved)) {
372+
return PullRequestReviewDecision.Approved;
373+
}
374+
375+
return undefined;
376+
}
377+
378+
export function fromAzureReviewerToPullRequestMember(reviewer: AzureUser): PullRequestMember {
379+
return {
380+
avatarUrl: reviewer.imageUrl,
381+
id: reviewer.id,
382+
name: reviewer.displayName,
383+
url: reviewer.url,
384+
};
385+
}
386+
387+
function normalizeAzureBranchName(branchName: string): string {
388+
return branchName.startsWith('refs/heads/') ? branchName.replace('refs/heads/', '') : branchName;
389+
}
390+
391+
export function fromAzurePullRequest(
392+
pr: AzurePullRequest,
393+
provider: Provider,
394+
orgName: string,
395+
projectName: string,
396+
): PullRequest {
310397
const url = new URL(pr.url);
311398
return new PullRequest(
312399
provider,
@@ -322,10 +409,52 @@ export function fromAzurePullRequest(pr: AzurePullRequest, provider: Provider):
322409
getAzurePullRequestWebUrl(pr),
323410
{
324411
owner: getAzureOwner(url),
325-
repo: getAzureRepo(pr),
412+
repo: pr.repository.name,
413+
id: pr.repository.id,
414+
// TODO: Remove this assumption once actual access level is available
415+
accessLevel: RepositoryAccessLevel.Write,
326416
},
327417
azurePullRequestStatusToState(pr.status),
328418
new Date(pr.creationDate),
329-
new Date(pr.creationDate),
419+
new Date(pr.closedDate || pr.creationDate),
420+
pr.closedDate ? new Date(pr.closedDate) : undefined,
421+
pr.closedDate && pr.status === 'completed' ? new Date(pr.closedDate) : undefined,
422+
fromAzurePullRequestMergeStatusToMergeableState(pr.mergeStatus ?? 'notSet'),
423+
undefined,
424+
{
425+
base: {
426+
branch: pr.targetRefName ? normalizeAzureBranchName(pr.targetRefName) : '',
427+
sha: pr.lastMergeTargetCommit?.commitId ?? '',
428+
repo: pr.repository.name,
429+
owner: getAzureOwner(url),
430+
exists: pr.targetRefName != null,
431+
url: pr.repository.webUrl,
432+
},
433+
head: {
434+
branch: pr.sourceRefName ? normalizeAzureBranchName(pr.sourceRefName) : '',
435+
sha: pr.lastMergeSourceCommit?.commitId ?? '',
436+
repo: pr.forkSource?.repository != null ? pr.forkSource.repository.name : pr.repository.name,
437+
owner: getAzureOwner(url),
438+
exists: pr.sourceRefName != null,
439+
url: pr.forkSource?.repository != null ? pr.forkSource.repository.webUrl : pr.repository.webUrl,
440+
},
441+
isCrossRepository: pr.forkSource != null,
442+
},
443+
pr.isDraft,
444+
undefined,
445+
undefined,
446+
undefined,
447+
undefined,
448+
getAzurePullRequestReviewDecision(pr.reviewers?.filter(r => r.isRequired).map(r => r.vote ?? 0) ?? []),
449+
pr.reviewers.filter(r => r.vote == null || r.vote === 0).map(r => fromAzureUserWithVoteToReviewer(r)),
450+
pr.reviewers.filter(r => r.vote != null && r.vote !== 0).map(r => fromAzureUserWithVoteToReviewer(r)),
451+
pr.reviewers.map(r => fromAzureReviewerToPullRequestMember(r)),
452+
undefined,
453+
{
454+
id: pr.repository?.project?.id,
455+
name: projectName,
456+
resourceId: '', // TODO: This is a workaround until we can get the org id here.
457+
resourceName: orgName,
458+
},
330459
);
331460
}

src/plus/integrations/providers/azureDevOps.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -273,24 +273,17 @@ export class AzureDevOpsIntegration extends HostingIntegration<
273273
}
274274

275275
protected override async getProviderPullRequestForBranch(
276-
_session: AuthenticationSession,
277-
_repo: AzureRepositoryDescriptor,
278-
_branch: string,
276+
{ accessToken }: AuthenticationSession,
277+
repo: AzureRepositoryDescriptor,
278+
branch: string,
279279
_options?: {
280280
avatarSize?: number;
281281
include?: PullRequestState[];
282282
},
283283
): Promise<PullRequest | undefined> {
284-
return (await this.container.azure)?.getPullRequestForBranch(
285-
this,
286-
_session.accessToken,
287-
_repo.owner,
288-
_repo.name,
289-
_branch,
290-
{
291-
baseUrl: this.apiBaseUrl,
292-
},
293-
);
284+
return (await this.container.azure)?.getPullRequestForBranch(this, accessToken, repo.owner, repo.name, branch, {
285+
baseUrl: this.apiBaseUrl,
286+
});
294287
}
295288

296289
protected override async getProviderPullRequestForCommit(

0 commit comments

Comments
 (0)