diff --git a/plugins/azure-devops-common/src/types.ts b/plugins/azure-devops-common/src/types.ts index eacc2af30d..458eaa76d4 100644 --- a/plugins/azure-devops-common/src/types.ts +++ b/plugins/azure-devops-common/src/types.ts @@ -126,3 +126,128 @@ export type PullRequestOptions = { top: number; status: PullRequestStatus; }; + +export interface DashboardPullRequest { + pullRequestId: number | undefined; + title: string | undefined; + description: string | undefined; + repository: Repository | undefined; + createdBy: CreatedBy | undefined; + hasAutoComplete: boolean; + policies: Policy[] | undefined; + reviewers: Reviewer[] | undefined; + creationDate: string | undefined; + status: PullRequestStatus | undefined; + isDraft: boolean | undefined; + link: string | undefined; +} + +export interface Reviewer { + id: string | undefined; + displayName: string | undefined; + imageUrl: string | undefined; + isRequired: boolean | undefined; + isContainer: boolean | undefined; + voteStatus: PullRequestVoteStatus; +} + +export interface Policy { + id: number | undefined; + type: PolicyType; + status: PolicyEvaluationStatus | undefined; + text: string | undefined; + link: string | undefined; +} + +export interface CreatedBy { + id: string | undefined; + displayName: string | undefined; + uniqueName: string | undefined; + imageUrl: string | undefined; +} + +export interface Repository { + id: string | undefined; + name: string | undefined; + url: string | undefined; +} + +export interface Team { + id: string | undefined; + name: string | undefined; + memberIds: string[] | undefined; +} + +/** + * Status of a policy which is running against a specific pull request. + */ +export enum PolicyEvaluationStatus { + /** + * The policy is either queued to run, or is waiting for some event before progressing. + */ + Queued = 0, + /** + * The policy is currently running. + */ + Running = 1, + /** + * The policy has been fulfilled for this pull request. + */ + Approved = 2, + /** + * The policy has rejected this pull request. + */ + Rejected = 3, + /** + * The policy does not apply to this pull request. + */ + NotApplicable = 4, + /** + * The policy has encountered an unexpected error. + */ + Broken = 5, +} + +export enum PolicyType { + Build = 'Build', + Status = 'Status', + MinimumReviewers = 'MinimumReviewers', + Comments = 'Comments', + RequiredReviewers = 'RequiredReviewers', + MergeStrategy = 'MergeStrategy', +} + +export const PolicyTypeId = { + /** + * This policy will require a successful build has been performed before updating protected refs. + */ + Build: '0609b952-1397-4640-95ec-e00a01b2c241', + /** + * This policy will require a successful status to be posted before updating protected refs. + */ + Status: 'cbdc66da-9728-4af8-aada-9a5a32e4a226', + /** + * This policy will ensure that a minimum number of reviewers have approved a pull request before completion. + */ + MinimumReviewers: 'fa4e907d-c16b-4a4c-9dfa-4906e5d171dd', + /** + * Check if the pull request has any active comments. + */ + Comments: 'c6a1889d-b943-4856-b76f-9e46bb6b0df2', + /** + * This policy will ensure that required reviewers are added for modified files matching specified patterns. + */ + RequiredReviewers: 'fd2167ab-b0be-447a-8ec8-39368250530e', + /** + * This policy ensures that pull requests use a consistent merge strategy. + */ + MergeStrategy: 'fa4e907d-c16b-4a4c-9dfa-4916e5d171ab', +}; + +export enum PullRequestVoteStatus { + Approved = 10, + ApprovedWithSuggestions = 5, + NoVote = 0, + WaitingForAuthor = -5, + Rejected = -10, +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCard.stories.tsx b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCard.stories.tsx index e918d37282..b948b616bd 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCard.stories.tsx +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCard.stories.tsx @@ -14,10 +14,16 @@ * limitations under the License. */ +import { + DashboardPullRequest, + PolicyEvaluationStatus, + PolicyType, + PullRequestStatus, + PullRequestVoteStatus, +} from '@backstage/plugin-azure-devops-common'; + import { MemoryRouter } from 'react-router'; -import { PullRequest } from '../../../../api/types'; import { PullRequestCard } from './PullRequestCard'; -import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; import React from 'react'; export default { @@ -25,16 +31,17 @@ export default { component: PullRequestCard, }; -const pullRequest: PullRequest = { +const pullRequest: DashboardPullRequest = { pullRequestId: 1, title: "feat(EXUX-4091): 🛂 Added the admin role authorization to the backend API's", description: 'This PR contains the following updates:\n\n| Package | Type | Update | Change |\n|---|---|---|---|\n| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint) | devDependencies | major | [`4.33.0` -> `5.0.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2feslint-plugin/4.33.0/5.0.0) |\n| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescrip', - link: '', + link: undefined, repository: { + id: undefined, name: 'backstage', - url: '', + url: undefined, }, createdBy: { id: '', @@ -44,31 +51,56 @@ const pullRequest: PullRequest = { 'https://dev.azure.com/exclaimerltd/_api/_common/identityImage?id=e6c0634b-68d2-6e6f-aa7d-adccada23216', }, reviewers: [ - { displayName: 'Marley', imageUrl: '' }, - { displayName: 'User 1', imageUrl: '' }, - { displayName: 'User 2', imageUrl: '' }, + { + id: undefined, + displayName: 'Marley', + imageUrl: '', + isRequired: true, + isContainer: false, + voteStatus: PullRequestVoteStatus.Approved, + }, + { + id: undefined, + displayName: 'User 1', + imageUrl: '', + isRequired: false, + isContainer: false, + voteStatus: PullRequestVoteStatus.WaitingForAuthor, + }, + { + id: undefined, + displayName: 'User 2', + imageUrl: '', + isRequired: true, + isContainer: false, + voteStatus: PullRequestVoteStatus.NoVote, + }, ], policies: [ { - type: 'Build', - status: 'Running', + id: undefined, + type: PolicyType.Build, + status: PolicyEvaluationStatus.Running, text: 'Build: UI (running)', + link: undefined, }, { - type: 'MinimumReviewers', + id: undefined, + type: PolicyType.MinimumReviewers, text: 'Minimum number of reviewers (2)', - status: '', + status: undefined, + link: undefined, }, { - type: 'Comments', + id: undefined, + type: PolicyType.Comments, text: 'Comment requirements', - status: '', + status: undefined, + link: undefined, }, ], hasAutoComplete: true, creationDate: new Date(Date.now() - 10000000).toISOString(), - sourceRefName: '', - targetRefName: '', status: PullRequestStatus.Active, isDraft: false, }; diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCard.tsx b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCard.tsx index 1d30504f44..cfb0a121db 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCard.tsx +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCard.tsx @@ -18,8 +18,8 @@ import { Card, CardContent, CardHeader, Link } from '@material-ui/core'; import { AutoCompleteIcon } from '../AutoCompleteIcon'; import { Avatar } from '@backstage/core-components'; +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; import { DateTime } from 'luxon'; -import { PullRequest } from '../../../../api/types'; import { PullRequestCardPolicies } from './PullRequestCardPolicies'; import { PullRequestCardReviewers } from './PullRequestCardReviewers'; import React from 'react'; @@ -53,7 +53,7 @@ const useStyles = makeStyles( ); type PullRequestCardProps = { - pullRequest: PullRequest; + pullRequest: DashboardPullRequest; simplified?: boolean; }; @@ -75,21 +75,23 @@ export const PullRequestCard = ({ const subheader = ( - {pullRequest.repository.name} + {pullRequest.repository?.name} {' '} - · {DateTime.fromISO(pullRequest.creationDate.toString()).toRelative()} + ·{' '} + {pullRequest.creationDate && + DateTime.fromISO(pullRequest.creationDate).toRelative()} ); const avatar = ( ); @@ -97,7 +99,10 @@ export const PullRequestCard = ({ const classes = useStyles(); return ( - + - + {pullRequest.policies && ( + + )} - + {pullRequest.reviewers && ( + + )} )} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardPolicies.tsx b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardPolicies.tsx index eeb78b32f8..21fe7ba034 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardPolicies.tsx +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardPolicies.tsx @@ -14,6 +14,11 @@ * limitations under the License. */ +import { + Policy, + PolicyEvaluationStatus, + PolicyType, +} from '@backstage/plugin-azure-devops-common'; import { PolicyInProgressIcon, PolicyIssueIcon, @@ -21,25 +26,24 @@ import { PullRequestCardPolicy, } from './PullRequestCardPolicy'; -import { Policy } from '../../../../api/types'; import React from 'react'; function getPullRequestCardPolicy(policy: Policy): JSX.Element | null { switch (policy.type) { - case 'Build': + case PolicyType.Build: switch (policy.status) { - case 'Running': + case PolicyEvaluationStatus.Running: return ( } /> ); - case 'Rejected': + case PolicyEvaluationStatus.Rejected: return ( } /> ); - case 'Queued': + case PolicyEvaluationStatus.Queued: return ( } /> ); - case 'Status': - case 'Comments': + case PolicyType.Status: + case PolicyType.Comments: return ( } /> ); diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardPolicy.tsx b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardPolicy.tsx index 479c31f8c1..22f9bca39b 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardPolicy.tsx +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardPolicy.tsx @@ -18,7 +18,7 @@ import { styled, withStyles } from '@material-ui/core/styles'; import CancelIcon from '@material-ui/icons/Cancel'; import GroupWorkIcon from '@material-ui/icons/GroupWork'; -import { Policy } from '../../../../api/types'; +import { Policy } from '@backstage/plugin-azure-devops-common'; import React from 'react'; import WatchLaterIcon from '@material-ui/icons/WatchLater'; @@ -57,7 +57,7 @@ const PullRequestCardPolicyContainer = styled('div')({ type PullRequestCardPolicyProps = { policy: Policy; - icon: React.ReactNode; + icon: JSX.Element; }; export const PullRequestCardPolicy = ({ diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardReviewer.tsx b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardReviewer.tsx index 87dbb6118b..1d2049728b 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardReviewer.tsx +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardReviewer.tsx @@ -16,8 +16,7 @@ import { Avatar } from '@backstage/core-components'; import React from 'react'; -import { Reviewer } from '../../../../api/types'; -import { Tooltip } from '@material-ui/core'; +import { Reviewer } from '@backstage/plugin-azure-devops-common'; type PullRequestCardReviewerProps = { reviewer: Reviewer; @@ -26,16 +25,14 @@ type PullRequestCardReviewerProps = { export const PullRequestCardReviewer = ({ reviewer, }: PullRequestCardReviewerProps) => ( - - - + ); diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardReviewers.tsx b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardReviewers.tsx index b46223e0cb..a3b87e0439 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardReviewers.tsx +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/PullRequestCard/PullRequestCardReviewers.tsx @@ -14,9 +14,13 @@ * limitations under the License. */ +import { + PullRequestVoteStatus, + Reviewer, +} from '@backstage/plugin-azure-devops-common'; + import { PullRequestCardReviewer } from './PullRequestCardReviewer'; import React from 'react'; -import { Reviewer } from '../../../../api/types'; import { styled } from '@material-ui/core/styles'; const PullRequestCardReviewersContainer = styled('div')({ @@ -34,8 +38,14 @@ export const PullRequestCardReviewers = ({ reviewers, }: PullRequestCardReviewersProps) => ( - {reviewers.map(reviewer => ( + {reviewers.filter(reviewerFilter).map(reviewer => ( ))} ); + +function reviewerFilter(reviewer: Reviewer): boolean { + return reviewer.voteStatus === PullRequestVoteStatus.NoVote + ? !!reviewer.isRequired + : !reviewer.isContainer; +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/types.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/types.ts index f05e036a58..2d936f7b64 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/types.ts +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/types.ts @@ -14,10 +14,23 @@ * limitations under the License. */ -import { PullRequest } from '../../../api/types'; +import { + DashboardPullRequest, + Team, +} from '@backstage/plugin-azure-devops-common'; export interface PullRequestGroup { title: string; - pullRequests: PullRequest[]; + pullRequests: DashboardPullRequest[]; + simplified?: boolean; +} + +export type PullRequestFilter = (pullRequest: DashboardPullRequest) => boolean; + +export type TeamFilter = (team: Team) => boolean; + +export interface PullRequestGroupConfig { + title: string; + filter: PullRequestFilter; simplified?: boolean; }