Skip to content

Commit

Permalink
internal/relui: simplify rendering of homepage
Browse files Browse the repository at this point in the history
This refactors the homepage rendering to use a nested template for each
task row. This will help simplify the template as we add more complex
layout to the outputs of workflows and tasks.

Updates golang/go#51797
Updates golang/go#40279
For golang/go#53382

Change-Id: I85a86b82bdc79c7fb4e837d884af922c7028295d
Reviewed-on: https://go-review.googlesource.com/c/build/+/412176
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Alex Rakoczy <alex@golang.org>
  • Loading branch information
toothrot committed Jun 15, 2022
1 parent f7e019d commit 2838fbb
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 106 deletions.
92 changes: 8 additions & 84 deletions internal/relui/templates/home.html
Expand Up @@ -3,14 +3,18 @@
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{template "layout" .}}

{{define "content"}}
<section class="Workflows">
<div class="Workflows-header">
<h2>Workflows</h2>
<a href="{{baseLink "/workflows/new"}}" class="Button">New</a>
</div>
<ul class="WorkflowList">
{{range $workflow := .Workflows}}
{{range $wfid := .WorkflowIDs}}
{{$detail := index $.WorkflowDetails $wfid}}
{{$workflow := $detail.Workflow}}
<li class="WorkflowList-item">
<h3 class="WorkflowList-title">
{{$workflow.Name.String}}
Expand All @@ -19,8 +23,8 @@ <h3 class="WorkflowList-title">
</span>
{{if not (or $workflow.Finished $workflow.Error)}}
<div class="WorkflowList-titleStop">
<form action="{{baseLink (printf "/workflows/%s/stop" $workflow.ID) }}" method="post">
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$workflow.ID}}" />
<form action="{{baseLink (printf "/workflows/%s/stop" $wfid)}}" method="post">
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$wfid}}" />
<input name="workflow.stop" class="Button Button--red" type="submit" value="STOP" onclick="return this.form.reportValidity() && confirm('This will stop the workflow and all in-flight tasks.\n\nAre you sure you want to proceed?')" />
</form>
</div>
Expand Down Expand Up @@ -60,87 +64,7 @@ <h3 class="WorkflowList-title">
</tbody>
</table>
<h4 class="WorkflowList-sectionTitle">Tasks</h4>
<table class="TaskList">
<thead>
<tr class="TaskList-item TaskList-itemHeader">
<th class="TaskList-itemHeaderCol TaskList-itemExpand"></th>
<th class="TaskList-itemHeaderCol TaskList-itemState">State</th>
<th class="TaskList-itemHeaderCol TaskList-itemName">Name</th>
<th class="TaskList-itemHeaderCol TaskList-itemStarted">Started</th>
<th class="TaskList-itemHeaderCol TaskList-itemUpdated">Updated</th>
<th class="TaskList-itemHeaderCol TaskList-itemResult">Result</th>
<th class="TaskList-itemHeaderCol TaskList-itemActions">Actions</th>
</tr>
</thead>
{{$tasks := index $.WorkflowTasks $workflow.ID}}
{{range $task := $tasks}}
<tbody>
<tr class="TaskList-item TaskList-itemSummary TaskList-expandableItem">
<td class="TaskList-itemCol TaskList-itemExpand">
<span class="TaskList-itemExpandClosed">
<img class="TaskList-itemExpandControl" alt="unfold more" src="{{baseLink "/static/images/chevron_right_black_24dp.svg"}}" />
</span>
<span class="TaskList-ItemExpandOpened">
<img class="TaskList-itemExpandControl" alt="unfold less" src="{{baseLink "/static/images/expand_more_black_24dp.svg"}}" />
</span>
</td>
<td class="TaskList-itemCol TaskList-itemState">
{{if $task.Error.Valid}}
<img class="TaskList-itemStateIcon" alt="error" src="{{baseLink "/static/images/error_red_24dp.svg"}}" />
{{else if $task.Finished}}
<img class="TaskList-itemStateIcon" alt="finished" src="{{baseLink "/static/images/check_circle_green_24dp.svg"}}" />
{{else}}
<img class="TaskList-itemStateIcon" alt="pending" src="{{baseLink "/static/images/pending_yellow_24dp.svg"}}" />
{{end}}
</td>
<td class="TaskList-itemCol TaskList-itemName">
{{$task.Name}}
</td>
<td class="TaskList-itemCol TaskList-itemStarted">
{{$task.CreatedAt.UTC.Format "Mon Jan _2 2006 15:04:05"}}
</td>
<td class="TaskList-itemCol TaskList-itemUpdated">
{{$task.UpdatedAt.UTC.Format "Mon Jan _2 2006 15:04:05"}}
</td>
<td class="TaskList-itemCol TaskList-itemResult">
{{$task.Result.String}}
</td>
<td class="TaskList-itemCol TaskList-itemAction">
{{if $task.Error.Valid}}
<div class="TaskList-retryTask">
<form action="{{baseLink (printf "/workflows/%s/tasks/%s/retry" $workflow.ID $task.Name) }}" method="post">
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$workflow.ID}}" />
<input class="Button Button--small" name="task.reset" type="submit" value="Retry" onclick="return this.form.reportValidity() && confirm('This will retry the task and clear workflow errors.\n\nReady to proceed?')" />
</form>
</div>
{{end}}
{{if and (not $task.Finished) (hasPrefix $task.Name "APPROVE-")}}
<div class="TaskList-approveTask">
<form action="{{baseLink (printf "/workflows/%s/tasks/%s/approve" $workflow.ID $task.Name) }}" method="post">
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$workflow.ID}}" />
<input class="Button Button--small" name="task.approve" type="submit" value="Approve" onclick="return this.form.reportValidity() && confirm('This will mark the task approved and resume the workflow.\n\nReady to proceed?')" />
</form>
</div>
{{end}}
</td>
</tr>
<tr class="TaskList-itemLogsRow">
<td class="TaskList-itemLogs" colspan="7">
{{if $task.Error.Valid}}
<div class="TaskList-itemLogLine TaskList-itemLogLineError">
{{- $task.Error.Value -}}
</div>
{{end}}
{{range $log := $.Logs $workflow.ID $task.Name}}
<div class="TaskList-itemLogLine">
{{- $log.CreatedAt.UTC.Format "2006/01/02 15:04:05"}} {{$log.Body -}}
</div>
{{end}}
</td>
</tr>
</tbody>
{{end}}
</table>
{{template "task_list" $detail}}
</li>
{{end}}
</ul>
Expand Down
4 changes: 3 additions & 1 deletion internal/relui/templates/layout.html
Expand Up @@ -3,6 +3,7 @@
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "layout"}}
<!DOCTYPE html>
<html lang="en">
<title>{{.SiteHeader.Title}}</title>
Expand All @@ -16,7 +17,8 @@ <h1 class="Header-title">{{.SiteHeader.Title}}</h1>
</div>
</header>
<main class="Site-content">
{{template "content" .}}
{{block "content" .}}{{end}}
</main>
</body>
</html>
{{end}}
2 changes: 2 additions & 0 deletions internal/relui/templates/new_workflow.html
Expand Up @@ -3,6 +3,8 @@
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{template "layout" .}}

{{define "content"}}
<section class="NewWorkflow">
<h2>New Go Release</h2>
Expand Down
94 changes: 94 additions & 0 deletions internal/relui/templates/task_list.html
@@ -0,0 +1,94 @@
<!--
Copyright 2022 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
{{define "task_list"}}
{{$workflow := .Workflow}}
<table class="TaskList">
<thead>
<tr class="TaskList-item TaskList-itemHeader">
<th class="TaskList-itemHeaderCol TaskList-itemExpand"></th>
<th class="TaskList-itemHeaderCol TaskList-itemState">State</th>
<th class="TaskList-itemHeaderCol TaskList-itemName">Name</th>
<th class="TaskList-itemHeaderCol TaskList-itemStarted">Started</th>
<th class="TaskList-itemHeaderCol TaskList-itemUpdated">Updated</th>
<th class="TaskList-itemHeaderCol TaskList-itemResult">Result</th>
<th class="TaskList-itemHeaderCol TaskList-itemActions">Actions</th>
</tr>
</thead>
{{range $task := .Tasks}}
<tbody>
<tr class="TaskList-item TaskList-itemSummary TaskList-expandableItem">
<td class="TaskList-itemCol TaskList-itemExpand">
<span class="TaskList-itemExpandClosed">
<img class="TaskList-itemExpandControl" alt="unfold more" src="{{baseLink "/static/images/chevron_right_black_24dp.svg"}}" />
</span>
<span class="TaskList-ItemExpandOpened">
<img class="TaskList-itemExpandControl" alt="unfold less" src="{{baseLink "/static/images/expand_more_black_24dp.svg"}}" />
</span>
</td>
<td class="TaskList-itemCol TaskList-itemState">
{{if $task.Error.Valid}}
<img class="TaskList-itemStateIcon" alt="error" src="{{baseLink "/static/images/error_red_24dp.svg"}}" />
{{else if $task.Finished}}
<img
class="TaskList-itemStateIcon"
alt="finished"
src="{{baseLink "/static/images/check_circle_green_24dp.svg"}}" />
{{else}}
<img
class="TaskList-itemStateIcon"
alt="pending"
src="{{baseLink "/static/images/pending_yellow_24dp.svg"}}" />
{{end}}
</td>
<td class="TaskList-itemCol TaskList-itemName">
{{$task.Name}}
</td>
<td class="TaskList-itemCol TaskList-itemStarted">
{{$task.CreatedAt.UTC.Format "Mon Jan _2 2006 15:04:05"}}
</td>
<td class="TaskList-itemCol TaskList-itemUpdated">
{{$task.UpdatedAt.UTC.Format "Mon Jan _2 2006 15:04:05"}}
</td>
<td class="TaskList-itemCol TaskList-itemResult">
{{$task.Result.String}}
</td>
<td class="TaskList-itemCol TaskList-itemAction">
{{if $task.Error.Valid}}
<div class="TaskList-retryTask">
<form action="{{baseLink (printf "/workflows/%s/tasks/%s/retry" $workflow.ID $task.Name)}}" method="post">
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$workflow.ID}}" />
<input class="Button Button--small" name="task.reset" type="submit" value="Retry" onclick="return this.form.reportValidity() && confirm('This will retry the task and clear workflow errors.\n\nReady to proceed?')" />
</form>
</div>
{{end}}
{{if and (not $task.Finished) (hasPrefix $task.Name "APPROVE-")}}
<div class="TaskList-approveTask">
<form action="{{baseLink (printf "/workflows/%s/tasks/%s/approve" $workflow.ID $task.Name)}}" method="post">
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$workflow.ID}}" />
<input class="Button Button--small" name="task.approve" type="submit" value="Approve" onclick="return this.form.reportValidity() && confirm('This will mark the task approved and resume the workflow.\n\nReady to proceed?')" />
</form>
</div>
{{end}}
</td>
</tr>
<tr class="TaskList-itemLogsRow">
<td class="TaskList-itemLogs" colspan="7">
{{if $task.Error.Valid}}
<div class="TaskList-itemLogLine TaskList-itemLogLineError">
{{- $task.Error.Value -}}
</div>
{{end}}
{{range $log := index $.TaskLogs $task.Name}}
<div class="TaskList-itemLogLine">
{{- $log.CreatedAt.UTC.Format "2006/01/02 15:04:05"}} {{$log.Body -}}
</div>
{{end}}
</td>
</tr>
</tbody>
{{end}}
</table>
{{end}}
59 changes: 38 additions & 21 deletions internal/relui/web.go
Expand Up @@ -59,6 +59,7 @@ type Server struct {
// mux used if baseURL is set
bm *http.ServeMux

templates *template.Template
homeTmpl *template.Template
newWorkflowTmpl *template.Template
}
Expand All @@ -79,9 +80,9 @@ func NewServer(p *pgxpool.Pool, w *Worker, baseURL *url.URL, header SiteHeader)
"baseLink": s.BaseLink,
"hasPrefix": strings.HasPrefix,
}
layout := template.Must(template.New("layout.html").Funcs(helpers).ParseFS(templates, "templates/layout.html"))
s.homeTmpl = template.Must(template.Must(layout.Clone()).Funcs(helpers).ParseFS(templates, "templates/home.html"))
s.newWorkflowTmpl = template.Must(template.Must(layout.Clone()).Funcs(helpers).ParseFS(templates, "templates/new_workflow.html"))
s.templates = template.Must(template.New("").Funcs(helpers).ParseFS(templates, "templates/*.html"))
s.homeTmpl = s.mustLookup("home.html")
s.newWorkflowTmpl = s.mustLookup("new_workflow.html")
s.m.POST("/workflows/:id/stop", s.stopWorkflowHandler)
s.m.POST("/workflows/:id/tasks/:name/retry", s.retryTaskHandler)
s.m.POST("/workflows/:id/tasks/:name/approve", s.approveTaskHandler)
Expand All @@ -98,6 +99,14 @@ func NewServer(p *pgxpool.Pool, w *Worker, baseURL *url.URL, header SiteHeader)
return s
}

func (s *Server) mustLookup(name string) *template.Template {
t := template.Must(template.Must(s.templates.Clone()).ParseFS(templates, path.Join("templates", name))).Lookup(name)
if t == nil {
panic(fmt.Errorf("template %q not found", name))
}
return t
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if s.bm != nil {
s.bm.ServeHTTP(w, r)
Expand All @@ -124,19 +133,18 @@ func (s *Server) BaseLink(target string) string {
return u.String()
}

type homeResponse struct {
SiteHeader SiteHeader
Workflows []db.Workflow
WorkflowTasks map[uuid.UUID][]db.Task
TaskLogs map[uuid.UUID]map[string][]db.TaskLog
type workflowDetail struct {
Workflow db.Workflow
Tasks []db.Task
// TaskLogs is a map of all logs for a db.Task, keyed on
// (db.Task).Name
TaskLogs map[string][]db.TaskLog
}

func (h *homeResponse) Logs(workflow uuid.UUID, task string) []db.TaskLog {
t := h.TaskLogs[workflow]
if t == nil {
return nil
}
return t[task]
type homeResponse struct {
SiteHeader SiteHeader
WorkflowIDs []uuid.UUID
WorkflowDetails map[uuid.UUID]*workflowDetail
}

func (h *homeResponse) WorkflowParams(wf db.Workflow) map[string]string {
Expand Down Expand Up @@ -172,22 +180,31 @@ func (s *Server) buildHomeResponse(ctx context.Context) (*homeResponse, error) {
if err != nil {
return nil, err
}
wfTasks := make(map[uuid.UUID][]db.Task, len(ws))
hr := &homeResponse{
SiteHeader: s.header,
WorkflowDetails: make(map[uuid.UUID]*workflowDetail),
}
for _, w := range ws {
hr.WorkflowIDs = append(hr.WorkflowIDs, w.ID)
hr.WorkflowDetails[w.ID] = &workflowDetail{Workflow: w}
}
for _, t := range tasks {
wfTasks[t.WorkflowID] = append(wfTasks[t.WorkflowID], t)
wd := hr.WorkflowDetails[t.WorkflowID]
wd.Tasks = append(hr.WorkflowDetails[t.WorkflowID].Tasks, t)
wd.TaskLogs = make(map[string][]db.TaskLog)
}
tlogs, err := q.TaskLogs(ctx)
if err != nil {
return nil, err
}
wftlogs := make(map[uuid.UUID]map[string][]db.TaskLog)
for _, l := range tlogs {
if wftlogs[l.WorkflowID] == nil {
wftlogs[l.WorkflowID] = make(map[string][]db.TaskLog)
wd := hr.WorkflowDetails[l.WorkflowID]
if wd.TaskLogs == nil {
wd.TaskLogs = make(map[string][]db.TaskLog)
}
wftlogs[l.WorkflowID][l.TaskName] = append(wftlogs[l.WorkflowID][l.TaskName], l)
wd.TaskLogs[l.TaskName] = append(wd.TaskLogs[l.TaskName], l)
}
return &homeResponse{SiteHeader: s.header, Workflows: ws, WorkflowTasks: wfTasks, TaskLogs: wftlogs}, nil
return hr, nil
}

type newWorkflowResponse struct {
Expand Down

0 comments on commit 2838fbb

Please sign in to comment.