ExecutionDialog.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import { ActionStatusDisplay } from './ActionStatusDisplay.js'
  2. import { OutputTerminal } from './OutputTerminal.js'
  3. // This ExecutionDialog is NOT a custom HTML element, but rather just picks up
  4. // the <dialog /> element out of index.html and just re-uses that - as only
  5. // one dialog can be shown at a time.
  6. export class ExecutionDialog {
  7. constructor () {
  8. this.dlg = document.querySelector('dialog#execution-results-popup')
  9. this.domIcon = document.getElementById('execution-dialog-icon')
  10. this.domTitle = document.getElementById('execution-dialog-title')
  11. this.domOutput = document.getElementById('execution-dialog-xterm')
  12. this.domOutputToggleBig = document.getElementById('execution-dialog-toggle-size')
  13. this.domOutputToggleBig.onclick = () => {
  14. this.toggleSize()
  15. }
  16. this.domBtnKill = document.getElementById('execution-dialog-kill-action')
  17. this.domDuration = document.getElementById('execution-dialog-duration')
  18. this.domStatus = new ActionStatusDisplay(document.getElementById('execution-dialog-status'))
  19. this.domExecutionBasics = document.getElementById('execution-dialog-basics')
  20. this.domExecutionDetails = document.getElementById('execution-dialog-details')
  21. window.terminal = new OutputTerminal()
  22. window.terminal.open(this.domOutput)
  23. }
  24. showOutput () {
  25. this.domOutput.hidden = false
  26. this.domOutput.hidden = false
  27. }
  28. toggleSize () {
  29. if (this.dlg.classList.contains('big')) {
  30. this.dlg.classList.remove('big')
  31. } else {
  32. this.dlg.classList.add('big')
  33. }
  34. window.terminal.fit()
  35. }
  36. reset () {
  37. this.executionSeconds = 0
  38. this.executionTrackingId = 'notset'
  39. this.dlg.classList.remove('big')
  40. this.domOutputToggleBig.hidden = false
  41. this.domIcon.innerText = ''
  42. this.domTitle.innerText = 'Waiting for result... '
  43. this.domDuration.innerText = ''
  44. // window.terminal.close()
  45. this.domBtnKill.disabled = true
  46. this.domBtnKill.onclick = () => {}
  47. this.hideDetailsOnResult = false
  48. this.domExecutionBasics.hidden = false
  49. this.domExecutionDetails.hidden = true
  50. window.terminal.reset()
  51. window.terminal.fit()
  52. }
  53. show (actionButton) {
  54. if (typeof actionButton !== 'undefined' && actionButton != null) {
  55. this.domIcon.innerText = actionButton.domIcon.innerText
  56. }
  57. this.domBtnKill.disabled = false
  58. this.domBtnKill.onclick = () => {
  59. this.killAction()
  60. }
  61. clearInterval(window.executionDialogTicker)
  62. this.executionSeconds = 0
  63. this.executionTick()
  64. window.executionDialogTicker = setInterval(() => {
  65. this.executionTick()
  66. }, 1000)
  67. if (this.dlg.open) {
  68. this.dlg.close()
  69. }
  70. this.dlg.showModal()
  71. }
  72. killAction () {
  73. const killActionArgs = {
  74. executionTrackingId: this.executionTrackingId
  75. }
  76. window.fetch(window.restBaseUrl + 'KillAction', {
  77. cors: 'cors',
  78. method: 'POST',
  79. headers: {
  80. 'Content-Type': 'application/json'
  81. },
  82. body: JSON.stringify(killActionArgs)
  83. }).then((res) => {
  84. return res.json() // This isn't used by anything. UI is updated by OnExecutionFinished like normal.
  85. }).catch(err => {
  86. throw err
  87. })
  88. }
  89. executionTick () {
  90. this.executionSeconds++
  91. this.updateDuration(this.executionSeconds + ' seconds', '')
  92. }
  93. hideEverythingApartFromOutput () {
  94. this.hideDetailsOnResult = true
  95. this.domExecutionBasics.hidden = true
  96. }
  97. fetchExecutionResult (executionTrackingId) {
  98. this.executionTrackingId = executionTrackingId
  99. const executionStatusArgs = {
  100. executionTrackingId: this.executionTrackingId
  101. }
  102. window.fetch(window.restBaseUrl + 'ExecutionStatus', {
  103. cors: 'cors',
  104. method: 'POST',
  105. headers: {
  106. 'Content-Type': 'application/json'
  107. },
  108. body: JSON.stringify(executionStatusArgs)
  109. }).then((res) => {
  110. if (res.ok) {
  111. return res.json()
  112. } else {
  113. throw new Error(res.statusText)
  114. }
  115. }
  116. ).then((json) => {
  117. this.renderExecutionResult(json)
  118. }).catch(err => {
  119. this.renderError(err)
  120. })
  121. }
  122. updateDuration (started, finished) {
  123. if (finished === '') {
  124. this.domDuration.innerHTML = started
  125. } else {
  126. let delta = ''
  127. try {
  128. delta = (new Date(finished) - new Date(started)) / 1000
  129. delta = new Intl.RelativeTimeFormat().format(delta, 'seconds').replace('in ', '').replace('ago', '')
  130. } catch (e) {
  131. console.warn('Failed to calculate delta', e)
  132. }
  133. this.domDuration.innerHTML = started + ' &rarr; ' + finished
  134. if (delta !== '') {
  135. this.domDuration.innerHTML += ' (' + delta + ')'
  136. }
  137. }
  138. }
  139. renderExecutionResult (res) {
  140. this.res = res
  141. clearInterval(window.executionDialogTicker)
  142. this.domOutput.hidden = false
  143. if (!this.hideDetailsOnResult) {
  144. this.domExecutionDetails.hidden = false
  145. }
  146. this.executionTrackingId = res.logEntry.executionTrackingId
  147. this.domBtnKill.disabled = res.logEntry.executionFinished
  148. this.domStatus.update(res.logEntry)
  149. this.domIcon.innerHTML = res.logEntry.actionIcon
  150. this.domTitle.innerText = res.logEntry.actionTitle
  151. if (res.logEntry.executionFinished) {
  152. this.updateDuration(res.logEntry.datetimeStarted, res.logEntry.datetimeFinished)
  153. } else {
  154. this.updateDuration(res.logEntry.datetimeStarted, 'Still running...')
  155. }
  156. window.terminal.reset()
  157. window.terminal.write(res.logEntry.output, () => {
  158. window.terminal.fit()
  159. })
  160. }
  161. renderError (err) {
  162. this.dlg.querySelector('pre').innerText = JSON.stringify(err)
  163. }
  164. }