import { logger } from '../../logger' ; import { BitbucketHttp } from '../../util/http/bitbucket' ; import type { EnsureCommentConfig } from '../types' ; import { Config , accumulateValues } from './utils' ; const bitbucketHttp = new BitbucketHttp ( ) ; interface Comment { content : { raw : string ; } ; id : number ; } export type CommentsConfig = Pick < Config , 'repository' > ; interface EnsureBitBucketCommentConfig extends EnsureCommentConfig { config : CommentsConfig ; } async function getComments ( config , prNo ) { const comments = await accumulateValues < Comment > ( `/2.0/repositories/${ config . repository }/pullrequests/${ prNo }/comments` ) ; logger . debug ( `Found ${ comments . length } comments` ) ; return comments ; } async function addComment ( config , prNo , raw ) { await bitbucketHttp . postJson ( `/2.0/repositories/${ config . repository }/pullrequests/${ prNo }/comments` , { body : { content : { raw } } , } ) ; } async function editComment ( config , prNo , commentId , raw ) { await bitbucketHttp . putJson ( `/2.0/repositories/${ config . repository }/pullrequests/${ prNo }/comments/${ commentId }` , { body : { content : { raw } } , } ) ; } async function deleteComment ( config , prNo , commentId ) { await bitbucketHttp . deleteJson ( `/2.0/repositories/${ config . repository }/pullrequests/${ prNo }/comments/${ commentId }` ) ; } export async function ensureComment ( { config , number : prNo , topic , content , } ) { try { const comments = await getComments ( config , prNo ) ; let body ; let commentId ; let commentNeedsUpdating ; if ( topic ) { logger . debug ( `Ensuring comment "${ topic }" in #${ prNo }` ) ; body = `### ${ topic }\n\n${ content }` ; comments . forEach ( ( comment ) => { if ( comment . content . raw . startsWith ( `### ${ topic }\n\n` ) ) { commentId = comment . id ; commentNeedsUpdating = comment . content . raw !== body ; } } ) ; } else { logger . debug ( `Ensuring content-only comment in #${ prNo }` ) ; body = `${ content }` ; comments . forEach ( ( comment ) => { if ( comment . content . raw === body ) { commentId = comment . id ; commentNeedsUpdating = false ; } } ) ; } if ( ! commentId ) { await addComment ( config , prNo , body ) ; logger . info ( { repository : config . repository , prNo , topic } , 'Comment added' ) ; } else if ( commentNeedsUpdating ) { await editComment ( config , prNo , commentId , body ) ; logger . debug ( { repository : config . repository , prNo } , 'Comment updated' ) ; } else { logger . debug ( 'Comment is already update-to-date' ) ; } return true ; } catch ( err ) { logger . warn ( { err } , 'Error ensuring comment' ) ; return false ; } } export async function ensureCommentRemoval ( config , prNo , topic ? , content ? ) { try { logger . debug ( `Ensuring comment "${ topic || content }" in #${ prNo } is removed` ) ; const comments = await getComments ( config , prNo ) ; const byTopic = ( comment ) : boolean => comment . content . raw . startsWith ( `### ${ topic }\n\n` ) ; const byContent = ( comment ) : boolean => comment . content . raw . trim ( ) === content ; let commentId = null ; if ( topic ) { commentId = comments . find ( byTopic ) ?. id ; } else if ( content ) { commentId = comments . find ( byContent ) ?. id ; } if ( commentId ) { await deleteComment ( config , prNo , commentId ) ; } } catch ( err ) { logger . warn ( { err } , 'Error ensuring comment removal' ) ; } }