2021-01-25 00:24:31 +01:00
function initBuildActionsForm ( )
{
const buildActionsForm = document . getElementById ( 'build-action-form' ) ;
if ( buildActionsForm . dataset . initialized ) {
return true ;
}
queryBuildActions ( ) ;
handleBuildActionTypeChange ( ) ;
buildActionsForm . dataset . initialized = true ;
return true ;
}
function queryBuildActions ( )
{
queryRoute ( 'GET' , '/build-action' , showBuildActions ) ;
return true ;
}
function queryBuildActionDetails ( ids )
{
queryRoute ( 'GET' , '/build-action/details?' + makeIdParams ( ids ) , showBuildActionDetails ) ;
return true ;
}
function cloneBuildAction ( ids )
{
queryRoute ( 'POST' , '/build-action/clone?' + makeIdParams ( ids ) , function ( xhr , success ) {
if ( ! success ) {
return showAjaxError ( xhr , 'clone build action' ) ;
}
const cloneIDs = JSON . parse ( xhr . responseText ) ;
if ( cloneIDs . length === 1 && typeof cloneIDs [ 0 ] === 'number' ) {
if ( window . confirm ( 'Build action cloned as ' + cloneIDs [ 0 ] + '. Show cloned action?' ) ) {
queryBuildActionDetails ( cloneIDs [ 0 ] ) ;
}
} else {
window . alert ( 'Build action(s) have been cloned.' ) ;
}
} ) ;
return true ;
}
function deleteBuildAction ( ids )
{
queryRoute ( 'DELETE' , '/build-action?' + makeIdParams ( ids ) , function ( xhr , success ) {
success ? window . alert ( 'Build action has been deleted.' ) : showAjaxError ( xhr , 'delete build action' ) ;
} ) ;
return true ;
}
function stopBuildAction ( ids )
{
queryRoute ( 'POST' , '/build-action/stop?' + makeIdParams ( ids ) , function ( xhr , success ) {
success ? window . alert ( 'Build action has been stopped.' ) : showAjaxError ( xhr , 'stop build action' ) ;
} ) ;
return true ;
}
function startBuildAction ( ids )
{
queryRoute ( 'POST' , '/build-action/start?' + makeIdParams ( ids ) , function ( xhr , success ) {
success ? window . alert ( 'Build action has been started.' ) : showAjaxError ( xhr , 'start build action' ) ;
} ) ;
return true ;
}
function selectOptions ( selectElement , values )
{
Array . from ( selectElement . options ) . forEach ( function ( option ) {
option . selected = values . includes ( option . value ) ;
} ) ;
}
function prepareBuildActionBasedOnExistingOne ( existingBuildAction )
{
if ( ! globalInfo ) {
window . alert ( 'Global info needs to be retrieved first.' ) ;
return ;
}
const typeId = existingBuildAction . type ;
const typeInfo = globalInfo . buildActionTypes [ typeId ] ;
const typeName = typeInfo . type ;
document . getElementById ( 'build-action-task-none' ) . selected = true ;
document . getElementById ( 'build-action-type' ) . value = typeName ;
handleBuildActionPresetChange ( ) ;
handleBuildActionTypeChange ( ) ;
document . getElementById ( 'build-action-directory' ) . value = existingBuildAction . directory ;
document . getElementById ( 'build-action-package-names' ) . value = existingBuildAction . packageNames . join ( ', ' ) ;
selectOptions ( document . getElementById ( 'build-action-source-repo' ) , existingBuildAction . sourceDbs ) ;
selectOptions ( document . getElementById ( 'build-action-destination-repo' ) , existingBuildAction . destinationDbs ) ;
const presentFlags = existingBuildAction . flags ;
typeInfo . flags . forEach ( function ( flag ) {
const input = document . getElementById ( 'build-action-' + typeName + '-' + flag . param ) ;
input . checked = parseInt ( input . dataset . id ) & presentFlags ;
} ) ;
typeInfo . settings . forEach ( function ( setting ) {
document . getElementById ( 'build-action-' + typeName + '-' + setting . param ) . value = existingBuildAction . settings [ setting . param ] || '' ;
} ) ;
}
function handleBuildActionTypeChange ( )
{
if ( ! globalInfo ) {
return ;
}
const buildActionType = document . getElementById ( 'build-action-type' ) . value ;
const typeInfo = globalInfo . buildActionTypesByParam [ buildActionType ] ;
document . getElementById ( 'build-action-directory' ) . disabled = ! typeInfo . directory ;
document . getElementById ( 'build-action-source-repo' ) . disabled = ! typeInfo . sourceDb ;
document . getElementById ( 'build-action-destination-repo' ) . disabled = ! typeInfo . destinationDb ;
document . getElementById ( 'build-action-package-names' ) . disabled = ! typeInfo . packageNames ;
Array . from ( document . getElementById ( 'build-action-flags' ) . getElementsByTagName ( 'label' ) ) . forEach ( function ( label ) {
const relevantType = label . dataset . relevantType ;
if ( ! relevantType ) {
return ;
}
label . control . style . display = label . style . display = buildActionType === relevantType ? 'inline' : 'none' ;
} ) ;
Array . from ( document . getElementById ( 'build-action-settings' ) . getElementsByTagName ( 'tr' ) ) . forEach ( function ( tr ) {
const relevantType = tr . dataset . relevantType ;
if ( ! relevantType ) {
return ;
}
const isRelevant = buildActionType === relevantType ;
tr . style . display = isRelevant ? 'table-row' : 'none' ;
Array . from ( tr . getElementsByTagName ( 'input' ) ) . forEach ( function ( input ) {
input . style . display = isRelevant ? 'inline' : 'none' ;
} ) ;
} ) ;
}
function handleBuildActionPresetChange ( )
{
if ( ! globalInfo ) {
return ;
}
const task = document . getElementById ( 'build-action-task' ) . value ;
const taskInfo = globalInfo . presets . tasks [ task ] ;
const taskInfoElement = getAndEmptyElement ( 'build-action-task-info' ) ;
const actionSelect = document . getElementById ( 'build-action-type' ) ;
if ( ! taskInfo ) {
actionSelect . disabled = false ;
taskInfoElement . style . fontStyle = 'italic' ;
taskInfoElement . appendChild ( document . createTextNode ( 'Start a single action (no predefined task selected)' ) ) ;
handleBuildActionTypeChange ( ) ;
return ;
}
actionSelect . disabled = true ;
taskInfoElement . style . fontStyle = 'normal' ;
taskInfoElement . appendChild ( document . createTextNode ( taskInfo . desc || taskInfo . name ) ) ;
document . getElementById ( 'build-action-directory' ) . disabled = false ;
document . getElementById ( 'build-action-source-repo' ) . disabled = true ;
document . getElementById ( 'build-action-destination-repo' ) . disabled = true ;
document . getElementById ( 'build-action-package-names' ) . disabled = false ;
Array . from ( document . getElementById ( 'build-action-flags' ) . getElementsByTagName ( 'label' ) ) . forEach ( function ( label ) {
label . control . style . display = label . style . display = 'none' ;
} ) ;
Array . from ( document . getElementById ( 'build-action-settings' ) . getElementsByTagName ( 'tr' ) ) . forEach ( function ( tr ) {
tr . style . display = 'none' ;
Array . from ( tr . getElementsByTagName ( 'input' ) ) . forEach ( function ( input ) {
input . style . display = 'none' ;
} ) ;
} ) ;
}
function renderBuildActionActions ( actionValue , buildAction , refresh )
{
const container = document . createElement ( 'span' ) ;
if ( ! refresh ) {
container . className = 'table-row-actions' ;
}
const id = buildAction . id ;
container . appendChild ( renderIconLink ( refresh ? 'table-refresh' : 'magnify' , buildAction , function ( ) {
queryBuildActionDetails ( id ) ;
return false ;
2021-04-17 12:04:40 +02:00
} , refresh ? 'Refresh details table' : 'Show details' , undefined , '#build-action-details-section?' + id ) ) ;
2021-01-25 00:24:31 +01:00
container . appendChild ( renderIconLink ( 'restart' , buildAction , function ( ) {
if ( window . confirm ( 'Do you really want to clone/restart action ' + id + '?' ) ) {
cloneBuildAction ( id ) ;
}
} , 'Clone ' + id ) ) ;
container . appendChild ( renderIconLink ( 'plus' , buildAction , function ( ) {
prepareBuildActionBasedOnExistingOne ( buildAction ) ;
switchToBuildActions ( ) ;
} , 'Create new build action based on ' + id ) ) ;
container . appendChild ( renderIconLink ( 'delete' , buildAction , function ( ) {
if ( window . confirm ( 'Do you really want to delete action ' + id + '?' ) ) {
deleteBuildAction ( id ) ;
}
} , 'Delete ' + id ) ) ;
if ( buildAction . status !== 0 && buildAction . status !== 4 ) {
container . appendChild ( renderIconLink ( 'stop' , buildAction , function ( ) {
if ( window . confirm ( 'Do you really want to stop/decline action ' + id + '?' ) ) {
stopBuildAction ( id ) ;
}
} , 'Stop/decline ' + id ) ) ;
}
if ( buildAction . status === 0 ) {
container . appendChild ( renderIconLink ( 'play' , buildAction , function ( ) {
if ( window . confirm ( 'Do you really want to start action ' + id + '?' ) ) {
startBuildAction ( id ) ;
}
} , 'Start ' + id ) ) ;
}
return container ;
}
function showBuildActions ( ajaxRequest )
{
if ( ! window . globalInfo ) {
window . functionsPostponedUntilGlobalInfo . push ( showBuildActions . bind ( this , ... arguments ) ) ;
return ;
}
const buildActionsList = getAndEmptyElement ( 'build-actions-list' ) ;
if ( ajaxRequest . status !== 200 ) {
buildActionsList . appendChild ( document . createTextNode ( 'Unable to load build actions: ' + ajaxRequest . responseText ) ) ;
buildActionsList . appendChild ( renderReloadButton ( queryBuildActions ) ) ;
return ;
}
const responseJson = JSON . parse ( ajaxRequest . responseText ) ;
if ( ! Array . isArray ( responseJson ) ) {
buildActionsList . appendChild ( document . createTextNode ( 'Unable to load build actions: response is no array' ) ) ;
buildActionsList . appendChild ( renderReloadButton ( queryBuildActions ) ) ;
return ;
}
// compute clusters of actions which run in sequence
const buildActionsById = { } ;
responseJson . forEach ( function ( buildAction ) {
buildActionsById [ buildAction . id ] = buildAction ;
} ) ;
responseJson . forEach ( function ( buildAction ) {
if ( ! Array . isArray ( buildAction . startAfter ) ) {
return ;
}
const currentActionId = buildAction . id ;
let clusterId = buildAction . _clusterId ;
let clusterActions = buildAction . _clusterActions ;
buildAction . startAfter . forEach ( function ( previousActionId ) {
const previousAction = buildActionsById [ previousActionId ] ;
if ( ! previousAction ) {
return ;
}
const previousActionClusterId = previousAction . _clusterId ;
const previousClusterActions = previousAction . _clusterActions ;
if ( clusterId !== undefined ) {
if ( previousActionClusterId === undefined ) {
clusterActions . splice ( clusterActions . indexOf ( buildAction ) , 0 , previousAction ) ;
previousAction . _clusterId = clusterId ;
previousAction . _clusterActions = clusterActions ;
} else if ( previousActionClusterId !== clusterId ) {
clusterActions . splice ( clusterActions . indexOf ( buildAction ) , 0 , ... previousClusterActions ) ;
previousClusterActions . forEach ( function ( previousClusterAction ) {
previousClusterAction . _clusterId = clusterId ;
previousClusterAction . _clusterActions = clusterActions ;
} ) ;
}
} else if ( previousActionClusterId !== undefined ) {
clusterId = buildAction . _clusterId = previousAction . _clusterId ;
clusterActions = buildAction . _clusterActions = previousAction . _clusterActions ;
clusterActions . push ( buildAction ) ;
} else {
clusterId = buildAction . _clusterId = previousAction . _clusterId = currentActionId ;
clusterActions = buildAction . _clusterActions = previousAction . _clusterActions = [ previousAction , buildAction ] ;
}
} ) ;
} ) ;
// compute cluster offsets and make up a color for each cluster
responseJson . forEach ( function ( buildAction ) {
const clusterActions = buildAction . _clusterActions ;
const clusterOffset = buildAction . _clusterOffset = clusterActions ? clusterActions . indexOf ( buildAction ) : 0 ;
buildAction . _cc = buildAction . created + ':' + clusterOffset . toString ( ) . padStart ( 4 , '0' ) ;
if ( ! clusterActions ) { return ; }
if ( buildAction . _clusterColor ) {
return ;
}
const clusterColor = '#' + Math . floor ( ( Math . abs ( Math . sin ( buildAction . _clusterId ) * 16777215 ) ) % 16777215 ) . toString ( 16 ) ;
clusterActions . forEach ( a => a . _clusterColor = clusterColor ) ;
} ) ;
// define function to toggle a row
const toggleRow = function ( trElement ) {
trElement . cells [ 0 ] . getElementsByTagName ( 'input' ) [ 0 ] . click ( ) ;
} ;
// render the table
const container = renderTableFromJsonArray ( {
rows : responseJson ,
rowsPerPage : 10 ,
columnHeaders : [ '' , 'ID' , 'Task' , 'Type' , 'Status' , 'Result' , 'Created' , 'Started' , 'Runtime' , 'Directory' , 'Source repo' , 'Destination repo' , 'Packages' , 'Actions' ] ,
columnAccessors : [ 'checkbox' , 'id' , 'taskName' , 'type' , 'status' , 'result' , 'created' , 'started' , 'finished' , 'directory' , 'sourceDbs' , 'destinationDbs' , 'packageNames' , 'actions' ] ,
columnSortAccessors : [ null , null , null , null , null , null , '_cc' ] ,
customRenderer : {
checkbox : function ( value , row ) {
return renderCheckBoxForTableRow ( value , row , function ( row ) {
return row . name ;
} ) ;
} ,
actions : renderBuildActionActions ,
id : function ( value , row ) {
return renderLink ( value , row , function ( ) {
queryBuildActionDetails ( row . id ) ;
return false ;
2021-03-22 16:51:57 +01:00
} , 'Show details' , undefined , '#build-action-details-section?' + row . id ) ;
2021-01-25 00:24:31 +01:00
} ,
taskName : function ( value ) {
if ( ! value ) {
return renderNoneInGrey ( ) ;
}
return document . createTextNode ( getProperty ( globalInfo . presets . tasks [ value ] , 'name' , value ) ) ;
} ,
status : function ( value ) {
return document . createTextNode ( getProperty ( globalInfo . buildActionStates [ value ] , 'name' , 'Invalid/unknown' ) ) ;
} ,
result : function ( value ) {
return renderNoneInGrey ( getProperty ( globalInfo . buildActionResults [ value ] , 'name' , 'Invalid/unknown' ) ) ;
} ,
type : function ( value ) {
return document . createTextNode ( getProperty ( globalInfo . buildActionTypes [ value ] , 'name' , 'Invalid/debugging' ) ) ;
} ,
created : renderShortTimeStamp ,
started : renderShortTimeStamp ,
finished : function ( value , row ) {
return renderTimeSpan ( row . started , value ) ;
} ,
sourceDbs : renderArrayElidedAsCommaSeparatedString ,
destinationDbs : renderArrayElidedAsCommaSeparatedString ,
packageNames : function ( value ) {
return renderNoneInGrey ( ! Array . isArray ( value ) || value . length <= 0 ? 'none' : value . length ) ;
} ,
note : function ( rows ) {
const note = document . createElement ( 'p' ) ;
note . appendChild ( document . createTextNode ( rows . length + ' build actions present ' ) ) ;
note . appendChild ( renderReloadButton ( queryBuildActions ) ) ;
return note ;
} ,
} ,
rowHandler : function ( row , tr ) {
// toggle checkbox on click; toggle checkbox for whole cluster when clicking edge
const dataset = tr . dataset ;
dataset . id = row . id ;
if ( ! dataset . initialized ) {
dataset . initialized = true ;
tr . onclick = function ( e ) {
if ( e . defaultPrevented || e . target . type === 'checkbox' ) { return ; }
const dataset = tr . dataset ;
const clusterClass = e . clientX <= 30 ? dataset . clusterClass : undefined ;
if ( ! clusterClass ) { return toggleRow ( tr ) ; }
const relevantAction = buildActionsById [ dataset . id ] ;
const newSelected = ! relevantAction . selected ;
const clusterActions = relevantAction . _clusterActions || [ ] ;
clusterActions . forEach ( clusterAction => clusterAction . selected = newSelected ) ;
Array . from ( tr . parentElement . getElementsByClassName ( clusterClass ) ) . forEach ( toggleRow ) ;
} ;
}
// apply highlighting of clusters
const oldClusterClass = dataset . clusterClass ;
const clusterId = row . _clusterId ;
const className = clusterId === undefined ? undefined : 'ba-tr-cluster-' + clusterId ;
if ( oldClusterClass === className ) {
return ;
}
if ( oldClusterClass ) {
delete tr . dataset . clusterClass ;
tr . classList . remove ( oldClusterClass ) ;
tr . firstChild . style . borderLeft = '' ;
}
if ( clusterId === undefined ) {
return ;
}
tr . dataset . clusterClass = className ;
tr . classList . add ( className ) ;
tr . firstChild . style . borderLeft = '10px solid ' + row . _clusterColor ;
if ( oldClusterClass ) {
return ;
}
const updateRowHighlighting = function ( row , highlight ) {
const clusterClass = row . dataset . clusterClass ;
if ( ! clusterClass ) {
return ;
}
const fn = highlight ? 'add' : 'remove' ;
Array . from ( document . getElementsByClassName ( clusterClass ) ) . forEach ( function ( tr ) {
tr . classList [ fn ] ( 'table-row-highlighted' ) ;
} ) ;
}
tr . addEventListener ( 'mouseover' , function ( ) { updateRowHighlighting ( this , true ) ; } ) ;
tr . addEventListener ( 'mouseout' , function ( ) { updateRowHighlighting ( this , false ) ; } ) ;
} ,
} ) ;
container . table . sort ( '_cc' , true ) ;
buildActionsList . appendChild ( container ) ;
}
function switchToBuildActionDetails ( buildActionIds )
{
sections [ 'build-action-details' ] . state . ids = buildActionIds ;
2021-03-22 16:51:57 +01:00
updateHashPreventingSectionInitializer ( ! Array . isArray ( buildActionIds ) || buildActionIds . length === 0
? '#build-action-details-section'
: '#build-action-details-section?' + encodeURIComponent ( buildActionIds . join ( ',' ) ) ) ;
2021-01-25 00:24:31 +01:00
}
function switchToBuildActions ( )
{
2021-03-22 16:51:57 +01:00
updateHashPreventingSectionInitializer ( '#build-action-section' ) ;
2021-01-25 00:24:31 +01:00
}
function showBuildActionDetails ( ajaxRequest )
{
if ( ! window . globalInfo ) {
window . functionsPostponedUntilGlobalInfo . push ( showBuildActionDetails . bind ( this , ... arguments ) ) ;
return ;
}
if ( checkForAjaxError ( ajaxRequest , 'show build action' ) ) {
return ;
}
const responseJSON = JSON . parse ( ajaxRequest . responseText ) ;
return showBuildActionDetails2 ( Array . isArray ( responseJSON ) ? responseJSON : [ responseJSON ] ) ;
}
function showBuildActionDetails2 ( buildActions )
{
const buildActionResults = getAndEmptyElement ( 'build-action-results' ) ;
let buildActionActions = getAndEmptyElement ( 'build-action-details-actions' ) ;
buildActions . forEach ( function ( buildActionDetails ) {
if ( ! buildActionActions ) {
buildActionActions = document . createElement ( 'span' ) ;
buildActionActions . className = 'heading-actions' ;
buildActionResults . appendChild ( buildActionActions ) ;
}
buildActionResults . appendChild ( renderBuildActionDetailsTable ( buildActionDetails ) ) ;
buildActionActions . appendChild ( renderBuildActionActions ( undefined , buildActionDetails , true ) ) ;
buildActionActions = undefined ;
} ) ;
switchToBuildActionDetails ( buildActions . map ( buildAction => buildAction . id ) ) ;
}
function renderBuildActionDetailsTable ( buildActionDetails )
{
return renderTableFromJsonObject ( {
data : buildActionDetails ,
displayLabels : [ 'ID' , 'Task' , 'Type' , 'Status' , 'Result' , 'Result data' , 'Created' , 'Started' , 'Finished' , 'Start after' , 'Directory' , 'Source repo' , 'Destination repo' , 'Packages' , 'Flags' , 'Settings' , 'Log files' , 'Artefacts' , 'Output' ] ,
fieldAccessors : [ 'id' , 'taskName' , 'type' , 'status' , 'result' , 'resultData' , 'created' , 'started' , 'finished' , 'startAfter' , 'directory' , 'sourceDbs' , 'destinationDbs' , 'packageNames' , 'flags' , 'settings' , 'logfiles' , 'artefacts' , 'output' ] ,
customRenderer : {
taskName : function ( value ) {
if ( ! value ) {
return renderNoneInGrey ( ) ;
}
return document . createTextNode ( getProperty ( globalInfo . presets . tasks [ value ] , 'name' , value ) ) ;
} ,
status : function ( value ) {
return document . createTextNode ( getProperty ( globalInfo . buildActionStates [ value ] , 'name' , 'Invalid/unknown' ) ) ;
} ,
result : function ( value ) {
return renderNoneInGrey ( getProperty ( globalInfo . buildActionResults [ value ] , 'name' , 'Invalid/unknown' ) ) ;
} ,
type : function ( value ) {
return document . createTextNode ( getProperty ( globalInfo . buildActionTypes [ value ] , 'name' , 'Invalid/debugging' ) ) ;
} ,
created : renderTimeStamp ,
started : renderTimeStamp ,
finished : renderTimeStamp ,
startAfter : renderArrayAsCommaSeparatedString ,
sourceDbs : renderArrayAsCommaSeparatedString ,
destinationDbs : renderArrayAsCommaSeparatedString ,
packageNames : renderArrayAsCommaSeparatedString ,
flags : function ( value , row ) {
const flagNames = [ ] ;
const typeInfo = globalInfo . buildActionTypes [ row . type ] ;
typeInfo . flags . forEach ( function ( flag ) {
if ( flag . id & value ) {
flagNames . push ( flag . name ) ;
}
} ) ;
return renderArrayAsCommaSeparatedString ( flagNames ) ;
} ,
settings : function ( value , row ) {
const typeInfo = globalInfo . buildActionTypes [ row . type ] ;
if ( typeInfo . settingNames . length === 0 ) {
return renderNoneInGrey ( ) ;
}
return renderTableFromJsonObject ( {
data : value ,
displayLabels : typeInfo . settingNames ,
fieldAccessors : typeInfo . settingParams ,
defaultRenderer : function ( arg1 , arg2 , arg3 ) {
return renderNoneInGrey ( arg1 , arg2 , arg3 , 'default/none' ) ;
} ,
} ) ;
} ,
resultData : function ( value , row ) {
switch ( value . index ) {
2021-02-13 17:14:49 +01:00
case 3 : { // update info
2021-01-25 00:24:31 +01:00
const formElement = document . createElement ( 'form' ) ;
formElement . className = 'update-info-form' ;
formElement . appendChild ( renderTableFromJsonObject ( {
data : value . data ,
relatedRow : row ,
displayLabels : [ 'Version updates' , 'Package updates' , 'Downgrades' , 'Orphans' ] ,
fieldAccessors : [ 'versionUpdates' , 'packageUpdates' , 'downgrades' , 'orphans' ] ,
customRenderer : {
orphans : renderOrphanPackage ,
versionUpdates : renderUpdateOrDowngrade ,
packageUpdates : renderUpdateOrDowngrade ,
downgrades : renderUpdateOrDowngrade ,
} ,
} ) ) ;
const addSelectedInput = document . createElement ( 'input' ) ;
addSelectedInput . type = 'button' ;
addSelectedInput . value = 'Add selected packages for new build action' ;
addSelectedInput . onclick = function ( ) {
const buildActionForm = document . getElementById ( 'build-action-form' ) ;
const packageNamesTextArea = buildActionForm [ 'package-names' ] ;
const elements = formElement . elements ;
const packageNames = [ ] ;
for ( let i = 0 , count = elements . length ; i != count ; ++ i ) {
const element = elements [ i ] ;
if ( element . type === 'checkbox' && element . checked ) {
packageNames . push ( element . value ) ;
}
}
if ( packageNamesTextArea . value !== '' ) {
packageNamesTextArea . value += ' ' ;
}
packageNamesTextArea . value += packageNames . join ( ' ' ) ;
} ;
formElement . appendChild ( addSelectedInput ) ;
return formElement ;
2021-02-13 17:14:49 +01:00
}
2021-01-25 00:24:31 +01:00
case 4 : // build preparation info
return renderTableFromJsonObject ( {
data : value . data ,
displayLabels : [ 'Error' , 'Warnings' , 'Database config' , 'Batches' , 'Cyclic leftovers' , 'Build data' ] ,
fieldAccessors : [ 'error' , 'warnings' , 'dbConfig' , 'batches' , 'cyclicLeftovers' , 'buildData' ] ,
customRenderer : {
buildData : renderBuildPreparationResultData ,
cyclicLeftovers : renderArrayAsCommaSeparatedString ,
batches : function ( batch ) {
return renderCustomList ( batch , renderArrayAsCommaSeparatedString ) ;
} ,
} ,
} ) ;
2021-07-03 19:59:57 +02:00
case 7 : { // repository problems
2021-01-25 00:24:31 +01:00
const container = document . createElement ( 'div' ) ;
container . className = 'repo-problems' ;
for ( const [ database , problems ] of Object . entries ( value . data ) ) {
const table = renderTableFromJsonArray ( {
rows : problems ,
columnHeaders : [ 'Related package' , 'Problem description' ] ,
columnAccessors : [ 'pkg' , 'desc' ] ,
customRenderer : {
2021-07-03 19:59:57 +02:00
note : problems . length + ' problems in repository ' + database ,
2021-01-25 00:24:31 +01:00
desc : function ( value ) {
switch ( value . index ) {
case 1 :
return renderTableFromJsonObject ( {
data : value . data ,
displayLabels : [ 'Missing dependencies' , 'Missing libraries' ] ,
fieldAccessors : [ 'deps' , 'libs' ] ,
customRenderer : {
deps : renderDependency ,
} ,
} ) ;
default :
return renderStandardTableCell ( value . data ) ;
}
} ,
} ,
} ) ;
table . table . sort ( 'pkg' , false ) ;
container . appendChild ( table ) ;
}
return container ;
2021-02-13 17:14:49 +01:00
}
2021-01-25 00:24:31 +01:00
default :
return renderStandardTableCell ( value . data ) ;
}
} ,
logfiles : renderBuildActionLogFiles ,
artefacts : renderBuildActionArtefacts ,
output : function ( value , row ) {
const isFinished = row . status === 4 ;
if ( ! value && isFinished ) {
return renderNoneInGrey ( ) ;
}
const targetElement = document . createElement ( 'div' ) ;
if ( isFinished ) {
const terminal = makeTerminal ( ) ;
setupTerminalLater ( terminal , targetElement , value ) ;
return targetElement ;
}
const streamingSetup = setupTerminalForStreaming ( {
id : 'output-' + row . id ,
targetElement : targetElement ,
path : '/build-action/output?id=' + encodeURIComponent ( row . id ) + '&offset=' + encodeURIComponent ( value . length ) ,
} ) ;
streamingSetup . startStreaming ( ) ;
const terminal = streamingSetup . terminal ( ) ;
terminal . setOption ( 'convertEol' , true ) ;
terminal . write ( value ) ;
return streamingSetup . elements ;
} ,
} ,
} ) ;
}
function setupTerminalForStreaming ( args )
{
const id = args . id ; // a page-unique ID to make up DOM element IDs as needed
const targetElement = args . targetElement ; // the DOM element to stream contents into
const path = args . path ; // the path to GET contents from via streamRouteIntoTerminal()
let terminal ;
let ajaxRequest ;
return {
elements : [ targetElement ] ,
startStreaming : function ( ) {
terminal = makeTerminal ( ) ;
ajaxRequest = streamRouteIntoTerminal ( 'GET' , path , terminal ) ;
setupTerminalLater ( terminal , targetElement ) ;
} ,
stopStreaming : function ( ) {
ajaxRequest . abort ( ) ;
ajaxRequest = undefined ;
terminal . dispose ( ) ;
terminal = undefined ;
} ,
isStreaming : function ( ) {
return ajaxRequest !== undefined ;
} ,
terminal : function ( ) {
return terminal ;
} ,
} ;
}
function fillBuildActionFromPackageSearch ( )
{
const packageNamesTextArea = document . getElementById ( 'build-action-form' ) [ 'package-names' ] ;
const data = getFormTableData ( 'package-results-form' ) ;
if ( data === undefined ) {
return ;
}
packageNamesTextArea . value = getSelectedRowProperties ( data , 'name' ) . join ( ' ' ) ;
location . hash = '#build-action-section' ;
}
function submitBuildAction ( )
{
startFormQuery ( 'build-action-form' , handleBuildActionResponse ) ;
}
function handleBuildActionResponse ( ajaxRequest )
{
const results = getAndEmptyElement ( 'build-action-results' ) ;
if ( ajaxRequest . status !== 200 ) {
results . appendChild ( document . createTextNode ( 'unable to create build action: ' + ajaxRequest . responseText ) ) ;
switchToBuildActionDetails ( ) ;
return ;
}
showBuildActionDetails ( ajaxRequest ) ;
}
function renderBuildActionLogFiles ( array , obj )
{
return renderCustomList ( array , function ( arrayElement ) {
const params = 'id=' + encodeURIComponent ( obj . id ) + '&name=' + encodeURIComponent ( arrayElement ) ;
const logFilePath = '/build-action/logfile?' + params ;
const newWindowPath = 'log.html#' + params ;
const targetElement = document . createElement ( 'div' ) ;
const streamingSetup = setupTerminalForStreaming ( {
id : 'logfile-' + obj . id + '-' + arrayElement ,
targetElement : targetElement ,
path : logFilePath ,
} ) ;
const basicElements = streamingSetup . elements ;
const openInNewWindowLinkElement = renderIconLink ( 'dock-window' , obj , function ( ) { window . open ( newWindowPath ) ; } , 'Open in new window' ) ;
const downloadLinkElement = renderIconLink ( 'download' , obj , function ( ) { window . open ( apiPrefix + logFilePath ) ; } , 'Download log' ) ;
const stopStreamingLinkElement = renderIconLink ( 'stop' , obj , function ( ) {
if ( ! streamingSetup . isStreaming ( ) ) {
return ;
}
streamingSetup . stopStreaming ( ) ;
targetElement . style . display = stopStreamingLinkElement . style . display = 'none' ;
emptyDomElement ( targetElement ) ;
} , 'Close log' ) ;
const startStreamingLinkElement = renderLink ( arrayElement , obj , function ( ) {
if ( streamingSetup . isStreaming ( ) ) {
return ;
}
emptyDomElement ( targetElement ) ;
streamingSetup . startStreaming ( ) ;
targetElement . style . display = 'block' ;
stopStreamingLinkElement . style . display = 'inline-block' ;
} , 'Show log file' , apiPrefix + logFilePath ) ;
targetElement . style . display = stopStreamingLinkElement . style . display = 'none' ;
[ downloadLinkElement , stopStreamingLinkElement ] . forEach ( function ( element ) {
element . classList . add ( 'streaming-link' ) ;
} ) ;
return [ startStreamingLinkElement , stopStreamingLinkElement , downloadLinkElement , openInNewWindowLinkElement , ... basicElements ] ;
} ) ;
}
function renderBuildActionArtefacts ( array , obj )
{
return renderCustomList ( array , function ( arrayElement ) {
const path = apiPrefix + '/build-action/artefact?id=' + encodeURIComponent ( obj . id ) + '&name=' + encodeURIComponent ( arrayElement ) ;
return renderLink ( arrayElement , obj , function ( ) {
window . open ( path ) ;
} , 'Download artefact' , path ) ;
} ) ;
}
2021-02-09 17:49:31 +01:00
function renderUpdateInfoWithCheckbox ( id , packageName , newPackageName , versionInfo , sourceDbs , newVersion )
2021-01-25 00:24:31 +01:00
{
const inputElement = document . createElement ( 'input' ) ;
inputElement . type = 'checkbox' ;
inputElement . id = id ;
inputElement . value = packageName ;
const labelElement = document . createElement ( 'label' ) ;
labelElement . htmlFor = id ;
2021-02-09 17:49:31 +01:00
if ( newVersion && newPackageName ) {
2021-01-25 00:24:31 +01:00
const packageNameLink = document . createElement ( 'a' ) ;
2021-02-09 17:49:31 +01:00
let from = newVersion . db ;
if ( newVersion . db === 'aur' ) {
packageNameLink . href = 'https://aur.archlinux.org/packages/' + encodeURIComponent ( newPackageName ) ;
packageNameLink . target = '_blank' ;
from = 'AUR' ;
} else {
2021-03-22 16:51:57 +01:00
packageNameLink . href = '#package-details-section?' + encodeURIComponent ( newVersion . db + '@' + newVersion . arch + '/' + newPackageName ) ;
2021-02-09 17:49:31 +01:00
}
2021-01-31 19:56:40 +01:00
packageNameLink . appendChild ( document . createTextNode ( newPackageName ) ) ;
if ( newPackageName !== packageName ) {
labelElement . appendChild ( document . createTextNode ( packageName + ' (' ) ) ;
}
2021-01-25 00:24:31 +01:00
labelElement . appendChild ( packageNameLink ) ;
2021-01-31 19:56:40 +01:00
if ( newPackageName !== packageName ) {
2021-02-09 17:49:31 +01:00
labelElement . appendChild ( document . createTextNode ( sourceDbs . length ? ' from ' + from + ')' : ')' ) ) ;
} else if ( sourceDbs . length ) {
labelElement . appendChild ( document . createTextNode ( ' from ' + from ) ) ;
2021-01-31 19:56:40 +01:00
}
2021-01-25 00:24:31 +01:00
labelElement . appendChild ( document . createTextNode ( ': ' + versionInfo ) ) ;
2021-01-31 19:56:40 +01:00
} else if ( newPackageName && packageName !== newPackageName ) {
labelElement . appendChild ( document . createTextNode ( packageName + ' (' + newPackageName + '): ' + versionInfo ) ) ;
2021-01-25 00:24:31 +01:00
} else {
labelElement . appendChild ( document . createTextNode ( packageName + ': ' + versionInfo ) ) ;
}
return [ inputElement , labelElement ] ;
}
function renderPackageList ( packageList )
{
return renderCustomList ( packageList , renderPackage ) ;
}
function renderBuildPreparationBuildData ( buildDataForPackage )
{
return renderTableFromJsonObject ( {
data : buildDataForPackage ,
displayLabels : [ 'Error' , 'Warnings' , 'Has source' , 'Source directory' , 'Original/local source directory' , 'New packages' , 'Existing packages' , 'Specified index' ] ,
fieldAccessors : [ 'error' , 'warnings' , 'hasSource' , 'sourceDirectory' , 'originalSourceDirectory' , 'packages' , 'existingPackages' , 'specifiedIndex' ] ,
customRenderer : {
packages : renderPackageList ,
existingPackages : renderPackageList ,
} ,
} ) ;
}
function makeVersionsString ( packages )
{
const versions = packages . map ( package => package . version ) ;
if ( versions . length === 0 ) {
return '?' ;
} else if ( versions . length === 1 ) {
return versions [ 0 ] ;
} else {
return '(' + versions . join ( ', ' ) + ')' ;
}
}
function renderBuildPreparationResultData ( buildPreparationData )
{
const elements = [ ] ;
for ( const [ packageName , buildDataForPackage ] of Object . entries ( buildPreparationData ) ) {
const heading = document . createElement ( 'h4' ) ;
let table ;
heading . className = 'compact-heading' ;
heading . appendChild ( renderLink ( packageName , undefined , function ( ) {
if ( table === undefined ) {
table = renderBuildPreparationBuildData ( buildDataForPackage ) ;
heading . insertAdjacentElement ( 'afterEnd' , table ) ;
} else {
table . style . display = table . style . display === 'none' ? 'table' : 'none' ;
}
} ) ) ;
const versionSpan = document . createElement ( 'span' ) ;
const newVersions = makeVersionsString ( buildDataForPackage . packages ) ;
const existingVersions = makeVersionsString ( buildDataForPackage . existingPackages ) ;
versionSpan . style . fontWeight = 'lighter' ;
versionSpan . style . float = 'right' ;
versionSpan . appendChild ( document . createTextNode ( ' ' + existingVersions + ' → ' + newVersions ) ) ;
heading . appendChild ( versionSpan ) ;
elements . push ( heading ) ;
}
return elements ;
}
function renderOrphanPackage ( value , obj , level , row )
{
return renderCustomList ( value , function ( package ) {
const packageName = package . name ;
return renderUpdateInfoWithCheckbox (
'update-info-checkbox-' + packageName + '-' + package . version ,
packageName ,
2021-01-31 19:56:40 +01:00
undefined ,
2021-02-09 17:49:31 +01:00
package . version ,
row . sourceDbs ,
2021-01-25 00:24:31 +01:00
) ;
} , function ( package1 , package2 ) {
return package1 . name . localeCompare ( package2 . name ) ;
} ) ;
}
function renderUpdateOrDowngrade ( value , obj , level , row )
{
return renderCustomList ( value , function ( updateInfo ) {
const oldVersion = updateInfo . oldVersion ;
const newVersion = updateInfo . newVersion ;
const packageName = oldVersion . name ;
return renderUpdateInfoWithCheckbox (
'update-info-checkbox-' + packageName + '-' + oldVersion . version + '-' + newVersion . version ,
packageName ,
2021-01-31 19:56:40 +01:00
newVersion . name ,
2021-01-25 00:24:31 +01:00
oldVersion . version + ' → ' + newVersion . version ,
2021-02-09 17:49:31 +01:00
row . sourceDbs ,
newVersion
2021-01-25 00:24:31 +01:00
) ;
} , function ( updateInfo1 , updateInfo2 ) {
return updateInfo1 . oldVersion . name . localeCompare ( updateInfo2 . oldVersion . name ) ;
} ) ;
}
function triggerToolbarAction ( toolbarElement )
{
const confirmQuestion = toolbarElement . dataset . confirmation ;
if ( confirmQuestion !== undefined && ! window . confirm ( confirmQuestion ) ) {
return false ;
}
toolbarElement . disabled = true ;
queryRoute ( toolbarElement . dataset . method , toolbarElement . dataset . action , function ( ajaxRequest ) {
window . alert ( ajaxRequest . responseText ) ;
toolbarElement . disabled = false ;
} ) ;
return false ;
}
function deleteSelectedActions ( )
{
const data = getFormTableData ( 'build-actions-list' ) ;
if ( data === undefined ) {
return ;
}
const ids = getSelectedRowProperties ( data , 'id' ) ;
if ( ids . length && window . confirm ( 'Do you really want to delete the build action(s) ' + ids . join ( ', ' ) + '?' ) ) {
deleteBuildAction ( ids ) ;
}
}
function showSelectedActions ( )
{
const data = getFormTableData ( 'build-actions-list' ) ;
if ( data === undefined ) {
return ;
}
const ids = getSelectedRowProperties ( data , 'id' ) ;
if ( ids . length ) {
queryBuildActionDetails ( ids ) ;
}
}
function initBuildActionDetails ( sectionElement , sectionData , newHashParts )
{
const currentBuildActionIds = sectionData . state . ids ;
const hasCurrentlyBuildActions = Array . isArray ( currentBuildActionIds ) && currentBuildActionIds . length !== 0 ;
if ( ! newHashParts . length ) {
if ( hasCurrentlyBuildActions ) {
2021-04-05 20:29:03 +02:00
updateHashPreventingChangeHandler ( '#build-action-details-section?' + encodeURIComponent ( currentBuildActionIds . join ( ',' ) ) ) ;
2021-01-25 00:24:31 +01:00
}
return true ;
}
const newBuildActionIds = newHashParts [ 0 ] . split ( ',' ) ;
2021-04-05 20:29:03 +02:00
if ( ! hasCurrentlyBuildActions || newBuildActionIds . some ( id => currentBuildActionIds . find ( currentId => id == currentId ) === undefined ) ) { // possible type conversion wanted
2021-01-25 00:24:31 +01:00
queryBuildActionDetails ( newBuildActionIds ) ;
}
return true ;
2021-02-13 17:14:49 +01:00
}