diff --git a/OliveTin.proto b/OliveTin.proto index 7ba4a64..8071938 100644 --- a/OliveTin.proto +++ b/OliveTin.proto @@ -137,6 +137,7 @@ message WatchExecutionUpdate { message ExecutionStatusRequest { string execution_tracking_id = 1; + string action_id = 2; } message ExecutionStatusResponse { diff --git a/internal/grpcapi/grpcApi.go b/internal/grpcapi/grpcApi.go index c185583..4987fb5 100644 --- a/internal/grpcapi/grpcApi.go +++ b/internal/grpcapi/grpcApi.go @@ -142,16 +142,40 @@ func internalLogEntryToPb(logEntry *executor.InternalLogEntry) *pb.LogEntry { } } +func getExecutionStatusByTrackingID(api *oliveTinAPI, executionTrackingId string) *executor.InternalLogEntry { + logEntry, ok := api.executor.Logs[executionTrackingId] + + if !ok { + return nil + } + + return logEntry +} + +func getMostRecentExecutionStatusById(api *oliveTinAPI, actionId string) *executor.InternalLogEntry { + var ile *executor.InternalLogEntry + + for _, candidateLe := range api.executor.Logs { + if actionId == candidateLe.ActionId { + ile = candidateLe + } + } + + return ile +} + func (api *oliveTinAPI) ExecutionStatus(ctx ctx.Context, req *pb.ExecutionStatusRequest) (*pb.ExecutionStatusResponse, error) { res := &pb.ExecutionStatusResponse{} - logEntry, ok := api.executor.Logs[req.ExecutionTrackingId] + var ile *executor.InternalLogEntry - if !ok { - return res, nil + if req.ExecutionTrackingId != "" { + ile = getExecutionStatusByTrackingID(api, req.ExecutionTrackingId) + } else { + ile = getMostRecentExecutionStatusById(api, req.ActionId) } - res.LogEntry = internalLogEntryToPb(logEntry) + res.LogEntry = internalLogEntryToPb(ile) return res, nil } diff --git a/internal/grpcapi/grpcApiDashboard.go b/internal/grpcapi/grpcApiDashboard.go index f657a99..0769aac 100644 --- a/internal/grpcapi/grpcApiDashboard.go +++ b/internal/grpcapi/grpcApiDashboard.go @@ -3,6 +3,7 @@ package grpcapi import ( pb "github.com/OliveTin/OliveTin/gen/grpc" config "github.com/OliveTin/OliveTin/internal/config" + "golang.org/x/exp/slices" ) func dashboardCfgToPb(res *pb.GetDashboardComponentsResponse, dashboards []*config.DashboardComponent) { @@ -37,14 +38,19 @@ func getDashboardComponentContents(dashboard *config.DashboardComponent) []*pb.D } func getDashboardComponentType(item *config.DashboardComponent) string { + allowedTypes := []string{ + "stdout-most-recent-execution", + "display", + } + if len(item.Contents) > 0 { if item.Type != "fieldset" { return "directory" } return "fieldset" - } else if item.Type == "display" { - return "display" + } else if slices.Contains(allowedTypes, item.Type) { + return item.Type } return "link" diff --git a/webui.dev/js/marshaller.js b/webui.dev/js/marshaller.js index c6fa460..ae527af 100644 --- a/webui.dev/js/marshaller.js +++ b/webui.dev/js/marshaller.js @@ -199,6 +199,50 @@ function marshalLink (item, fieldset) { fieldset.appendChild(btn) } +function marshalMreOutput (dashboardComponent, fieldset) { + const pre = document.createElement('pre') + pre.classList.add('mre-output') + pre.innerHTML = 'Waiting...' + + const executionStatus = { + actionId: dashboardComponent.title + } + + window.fetch(window.restBaseUrl + 'ExecutionStatus', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(executionStatus) + }).then((res) => { + if (res.ok) { + return res.json() + } else { + pre.innerHTML = 'error' + + throw new Error(res.statusText) + } + }).then((json) => { + updateMre(pre, json.logEntry) + }) + + const updateMre = (pre, json) => { + pre.innerHTML = json.stdout + } + + window.addEventListener('ExecutionFinished', (e) => { + // The dashboard component "title" field is used for lots of things + // and in this context for MreOutput it's just to refer an an actionId. + // + // So this is not a typo. + if (e.payload.actionId === dashboardComponent.title) { + updateMre(pre, e.payload) + } + }) + + fieldset.appendChild(pre) +} + function marshalContainerContents (json, section, fieldset, parentDashboard) { for (const item of json.contents) { switch (item.type) { @@ -212,6 +256,9 @@ function marshalContainerContents (json, section, fieldset, parentDashboard) { case 'display': marshalDisplay(item, fieldset) break + case 'stdout-most-recent-execution': + marshalMreOutput(item, fieldset) + break case 'link': marshalLink(item, fieldset) break diff --git a/webui.dev/style.css b/webui.dev/style.css index 2038e7b..210e3b2 100644 --- a/webui.dev/style.css +++ b/webui.dev/style.css @@ -429,6 +429,7 @@ pre { padding: 1em; min-height: 1em; overflow: auto; + text-align: left; } td.exit-code {