import url from 'url' ; import { HTTPError , Response } from 'got' ; import { PrState } from '../../types' ; import { HttpOptions , HttpPostOptions , HttpResponse } from '../../util/http' ; import { BitbucketServerHttp } from '../../util/http/bitbucket-server' ; import type { BbsPr , BbsRestPr } from './types' ; const BITBUCKET_INVALID_REVIEWERS_EXCEPTION = 'com.atlassian.bitbucket.pull.InvalidPullRequestReviewersException' ; const bitbucketServerHttp = new BitbucketServerHttp ( ) ; const prStateMapping = { MERGED : PrState . Merged , DECLINED : PrState . Closed , OPEN : PrState . Open , } ; export function prInfo ( pr ) { return { version : pr . version , number : pr . id , body : pr . description , sourceBranch : pr . fromRef . displayId , targetBranch : pr . toRef . displayId , title : pr . title , state : prStateMapping [ pr . state ] , createdAt : pr . createdDate , } ; } const addMaxLength = ( inputUrl , limit = 100 ) : string => { const { search , ... parsedUrl } = url . parse ( inputUrl , true ) ; const maxedUrl = url . format ( { ... parsedUrl , query : { ... parsedUrl . query , limit } , } ) ; return maxedUrl ; } ; function callApi < T > ( apiUrl , method , options ? ) { switch ( method . toLowerCase ( ) ) { case 'post' : return bitbucketServerHttp . postJson < T > ( apiUrl , options as HttpPostOptions ) ; case 'put' : return bitbucketServerHttp . putJson < T > ( apiUrl , options as HttpPostOptions ) ; case 'patch' : return bitbucketServerHttp . patchJson < T > ( apiUrl , options as HttpPostOptions ) ; case 'head' : return bitbucketServerHttp . headJson < T > ( apiUrl , options ) ; case 'delete' : return bitbucketServerHttp . deleteJson < T > ( apiUrl , options as HttpPostOptions ) ; case 'get' : default : return bitbucketServerHttp . getJson < T > ( apiUrl , options ) ; } } export async function accumulateValues < T = any > ( reqUrl , method = 'get' , options ? , limit ? ) { let accumulator = [ ] ; let nextUrl = addMaxLength ( reqUrl , limit ) ; while ( typeof nextUrl !== 'undefined' ) { const { body } = await callApi < { values : T [ ] ; isLastPage : boolean ; nextPageStart : string ; } > ( nextUrl , method , options ) ; accumulator = [ ... accumulator , ... body . values ] ; if ( body . isLastPage !== false ) { break ; } const { search , ... parsedUrl } = url . parse ( nextUrl , true ) ; nextUrl = url . format ( { ... parsedUrl , query : { ... parsedUrl . query , start : body . nextPageStart , } , } ) ; } return accumulator ; } export interface BitbucketCommitStatus { failed : number ; inProgress : number ; successful : number ; } export type BitbucketBranchState = 'SUCCESSFUL' | 'FAILED' | 'INPROGRESS' | 'STOPPED' ; export interface BitbucketStatus { key : string ; state : BitbucketBranchState ; } interface BitbucketErrorResponse { errors ? : { exceptionName ? : string ; reviewerErrors ? : { context ? : string ; } [ ] ; } [ ] ; } interface BitbucketError extends HTTPError { readonly response : Response < BitbucketErrorResponse > ; } export function isInvalidReviewersResponse ( err ) { const errors = err ?. response ?. body ?. errors || [ ] ; return ( errors . length > 0 && errors . every ( ( error ) => error . exceptionName === BITBUCKET_INVALID_REVIEWERS_EXCEPTION ) ) ; } export function getInvalidReviewers ( err ) { const errors = err ?. response ?. body ?. errors || [ ] ; let invalidReviewers = [ ] ; for ( const error of errors ) { if ( error . exceptionName === BITBUCKET_INVALID_REVIEWERS_EXCEPTION ) { invalidReviewers = invalidReviewers . concat ( error . reviewerErrors ?. map ( ( { context } ) => context ) || [ ] ) ; } } return invalidReviewers ; }