fix(web): job log polls while active, one service history limit (#7)
This commit is contained in:
@@ -23,6 +23,9 @@ import (
|
|||||||
//go:embed templates/*.html static/*
|
//go:embed templates/*.html static/*
|
||||||
var assets embed.FS
|
var assets embed.FS
|
||||||
|
|
||||||
|
// serviceJobsLimit is how many jobs the service page lists.
|
||||||
|
const serviceJobsLimit = 20
|
||||||
|
|
||||||
var agentNamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,62}$`)
|
var agentNamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,62}$`)
|
||||||
|
|
||||||
// Store is the storage the UI reads and writes. *store.Store satisfies it.
|
// Store is the storage the UI reads and writes. *store.Store satisfies it.
|
||||||
@@ -203,7 +206,7 @@ func (s *Server) serviceDetail(r *http.Request, id int64) (pageData, error) {
|
|||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
data.Jobs, err = s.store.ListJobs(r.Context(), model.JobFilter{ServiceID: id, Limit: 20})
|
data.Jobs, err = s.store.ListJobs(r.Context(), model.JobFilter{ServiceID: id, Limit: serviceJobsLimit})
|
||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +228,7 @@ func (s *Server) serviceJobsFragment(w http.ResponseWriter, r *http.Request) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jobs, err := s.store.ListJobs(r.Context(), model.JobFilter{ServiceID: id, Limit: 20})
|
jobs, err := s.store.ListJobs(r.Context(), model.JobFilter{ServiceID: id, Limit: serviceJobsLimit})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.fail(w, err)
|
s.fail(w, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -482,6 +482,19 @@ func TestJobFragment_RereadsFromStore(t *testing.T) {
|
|||||||
assert.Contains(t, body, "step 2")
|
assert.Contains(t, body, "step 2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJobFragment_PollsOnlyWhileActive(t *testing.T) {
|
||||||
|
srv, store, _, _, _ := newTestEnv(t)
|
||||||
|
store.jobs = []model.Job{
|
||||||
|
{ID: 42, ServiceID: 1, Status: model.JobRunning, CreatedAt: time.Now()},
|
||||||
|
{ID: 43, ServiceID: 1, Status: model.JobSucceeded, CreatedAt: time.Now()},
|
||||||
|
}
|
||||||
|
_, body := get(t, srv, "/fragments/jobs/42")
|
||||||
|
assert.Contains(t, body, `hx-trigger="sse:job-42, every 2s"`)
|
||||||
|
_, body = get(t, srv, "/fragments/jobs/43")
|
||||||
|
assert.Contains(t, body, `hx-trigger="sse:job-43"`)
|
||||||
|
assert.NotContains(t, body, "every 2s")
|
||||||
|
}
|
||||||
|
|
||||||
func TestJob_NotFound(t *testing.T) {
|
func TestJob_NotFound(t *testing.T) {
|
||||||
srv, _, _, _ := newTestServer(t)
|
srv, _, _, _ := newTestServer(t)
|
||||||
for _, path := range []string{"/jobs/99", "/fragments/jobs/99"} {
|
for _, path := range []string{"/jobs/99", "/fragments/jobs/99"} {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<h1>Job #{{.Job.ID}}</h1>
|
<h1>Job #{{.Job.ID}}</h1>
|
||||||
<p><a href="/services/{{.Job.ServiceID}}">{{.Job.ServiceName}}</a></p>
|
<p><a href="/services/{{.Job.ServiceID}}">{{.Job.ServiceName}}</a></p>
|
||||||
<div id="job" hx-get="/fragments/jobs/{{.Job.ID}}" hx-trigger="sse:job-{{.Job.ID}}" hx-swap="innerHTML">
|
{{template "job" .}}
|
||||||
{{template "job" .}}
|
|
||||||
</div>
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{/* The wrapper is part of the fragment and swapped as outerHTML, so a finished job drops the polling timer. The timer recovers from a dropped SSE signal. */}}
|
||||||
{{define "job"}}
|
{{define "job"}}
|
||||||
|
<div id="job" hx-get="/fragments/jobs/{{.Job.ID}}" hx-trigger="sse:job-{{.Job.ID}}{{if .Job.Active}}, every 2s{{end}}" hx-swap="outerHTML">
|
||||||
<table class="details">
|
<table class="details">
|
||||||
<tr><th>Status</th><td class="status status-job-{{.Job.Status}}">{{.Job.Status}}</td></tr>
|
<tr><th>Status</th><td class="status status-job-{{.Job.Status}}">{{.Job.Status}}</td></tr>
|
||||||
<tr><th>From</th><td><code>{{shortRef .Job.FromRef}}</code></td></tr>
|
<tr><th>From</th><td><code>{{shortRef .Job.FromRef}}</code></td></tr>
|
||||||
@@ -18,4 +18,5 @@
|
|||||||
{{if .Job.Error}}<p class="error">{{.Job.Error}}</p>{{end}}
|
{{if .Job.Error}}<p class="error">{{.Job.Error}}</p>{{end}}
|
||||||
{{if .Job.OutputTruncated}}<p class="muted">Earlier output truncated.</p>{{end}}
|
{{if .Job.OutputTruncated}}<p class="muted">Earlier output truncated.</p>{{end}}
|
||||||
<pre class="log">{{.Job.Output}}</pre>
|
<pre class="log">{{.Job.Output}}</pre>
|
||||||
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
Reference in New Issue
Block a user