import './RunCard.renderCell.scss' ; import * as React from 'react' ; import { Fragment } from 'react' ; import * as ReactMarkdown from 'react-markdown' ; import { Result } from 'sarif' ; import { Hi } from './Hi' ; import { tryOr , tryLink } from './try' ; import { Rule , More , ResultOrRuleOrMore } from './Viewer.Types' ; import { Snippet } from './Snippet' ; import { css } from 'azure-devops-ui/Util' ; import { Link } from 'azure-devops-ui/Link' ; import { ObservableLike } from 'azure-devops-ui/Core/Observable' ; import { Status , Statuses , StatusSize } from "azure-devops-ui/Status" ; import { PillSize , Pill } from 'azure-devops-ui/Pill' ; import { ISimpleTableCell , TableCell } from 'azure-devops-ui/Table' ; import { ExpandableTreeCell , ITreeColumn } from 'azure-devops-ui/TreeEx' ; import { ITreeItemEx , ITreeItem } from 'azure-devops-ui/Utilities/TreeItemProvider' ; import { Icon , IconSize } from 'azure-devops-ui/Icon' ; import { renderPathCell } from './RunCard.renderPathCell' ; import { getRepoUri } from './getRepoUri' ; const colspan = 99 ; export function renderCell < T extends ISimpleTableCell > ( rowIndex , columnIndex , treeColumn , treeItem ) { const data = ObservableLike . getValue ( treeItem . underlyingItem . data ) ; const commonProps = { className : treeColumn . className , columnIndex , treeItem , treeColumn , } ; const isAge = ( item => item . isAge ) as ( item : any ) => item is { name : string ; treeItem : ITreeItem < ResultOrRuleOrMore > ; } ; if ( isAge ( data ) ) { const age = data ; return columnIndex === 0 ? ExpandableTreeCell ( { children : < div className = "swcRowRule" > { } { age . name } < Pill size = { PillSize . compact } > { age . treeItem . childItemsAll . length } < /Pill> < /div>, colspan , ... commonProps , } ) : null ; } const isRule = ( item => item . isRule ) as ( item : any ) => item is Rule ; if ( isRule ( data ) ) { const rule = data ; return columnIndex === 0 ? ExpandableTreeCell ( { children : < div className = "swcRowRule" > { } { tryLink ( ( ) => rule . helpUri , < Hi > { rule . id || rule . guid } < /Hi>)} { tryOr ( ( ) => rule . name && < > : < Hi > { rule . name } < /Hi></ > ) } { tryOr ( ( ) => rule . relationships . map ( ( rel , i ) => { const taxon = rule . run . taxonomies [ rel . target . toolComponent . index ] . taxa [ rel . target . index ] ; return < Fragment key = { rel . target . id } > { i > 0 ? ',' : '' } { tryLink ( ( ) => taxon . helpUri , taxon . name ) } < /Fragment>; } ) ) } < Pill size = { PillSize . compact } > { rule . treeItem . childItemsAll . length } < /Pill> < /div>, colspan , ... commonProps , } ) : null ; } const capitalize = str => `${ str [ 0 ] . toUpperCase ( ) }${ str . slice ( 1 ) }` ; const isResult = ( item => item . message !== undefined ) as ( item : any ) => item is Result ; if ( isResult ( data ) ) { const result = data ; const status = { none : result . kind === 'pass' ? Statuses . Success : Statuses . Queued , note : Statuses . Information , error : Statuses . Failed , } [ result . level ] || Statuses . Warning ; return columnIndex === 0 ? ExpandableTreeCell ( { children : < > < Status { ... status } className = "bolt-table-two-line-cell-icon flex-noshrink bolt-table-status-icon" size = { StatusSize . m } ariaLabel = { result . level || 'warning' } / > { renderPathCell ( result ) } < />, ... commonProps , } ) : TableCell ( { children : ( ( ) => { const rule = result . _rule ; switch ( treeColumn . id ) { case 'Details' : const messageFromRule = result . _rule ?. messageStrings ?. [ result . message . id ?? - 1 ] ?? result . message ; const formattedMessage = format ( messageFromRule . text || result . message ?. text , result . message . arguments ) ?? '' ; const formattedMarkdown = format ( messageFromRule . markdown || result . message ?. markdown , result . message . arguments ) ; return < > { formattedMarkdown ? < div className = "swcMarkDown" > < ReactMarkdown source = { formattedMarkdown } renderers = { { link : ( { href , children } ) => < a href = { href } target = "_blank" > { children } < /a> }}/ > < /div> / / Div to cancel out containers display flex row . : < span style = { { whiteSpace : 'pre-line' } } > < Hi > { renderMessageWithEmbeddedLinks ( result , formattedMessage ) } < /Hi></span > || '' } < Snippet ploc = { result . locations ?. [ 0 ] ?. physicalLocation } /> < />; case 'Rule' : return < > { tryLink ( ( ) => rule . helpUri , < Hi > { rule . id || rule . guid } < /Hi>)} { tryOr ( ( ) => rule . name && < > : < Hi > { rule . name } < /Hi></ > ) } < />; case 'Baseline' : return < Hi > { result . baselineState && capitalize ( result . baselineState ) || 'New' } < /Hi>; case 'Bug' : return tryOr ( ( ) => < Link href = { result . workItemUris [ 0 ] } target = "_blank" > < Icon iconName = "LadybugSolid" size = { IconSize . medium } style = { { color : '#E81123' } } /> < /Link>); case 'Age' : return < Hi > { result . sla } < /Hi>; case 'FirstObserved' : return < Hi > { result . firstDetection . toLocaleDateString ( ) } < /Hi>; } } ) ( ) , className : css ( treeColumn . className , 'font-size' ) , columnIndex , } ) ; } const isMore = ( item => item . onClick !== undefined ) as ( item : any ) => item is More ; if ( isMore ( data ) ) { return columnIndex === 0 ? ExpandableTreeCell ( { children : < Link onClick = { data . onClick } tabIndex = { - 1 } > Show All < /Link>, colspan , ... commonProps } ) : null ; } return null ; } function renderMessageWithEmbeddedLinks ( result , message ) { const rxLink = /\[([^\]]*)\]\(([^\)]+)\)/ ; return message . match ( rxLink ) ? message . split ( /(\[[^\]]*\]\([^\)]+\))/g ) . map ( ( item , i ) => { if ( i % 2 === 0 ) return item ; const [ _ , text , id ] = item . match ( rxLink ) ; const href = ( ( ) => { if ( isNaN ( id as any ) ) return id ; const physicalLocation = result . relatedLocations ?. find ( location => location . id === + id ) ?. physicalLocation ; return getRepoUri ( physicalLocation ?. artifactLocation ?. uri , result . run , physicalLocation ?. region ) ; } ) ( ) ; return href ? < a key = { i } href = { href } target = "_blank" > { text } < /a> : text ; } ) : message ; } function format ( template , args ? ) { if ( ! template ) return undefined ; if ( ! args ) return template ; return template . replace ( /{(\d+)}/g , ( _ , group ) => args [ group ] ) ; }