cosmonats 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +208 -156
- data/lib/cosmo/active_job/adapter.rb +46 -0
- data/lib/cosmo/active_job/executor.rb +16 -0
- data/lib/cosmo/active_job/options.rb +50 -0
- data/lib/cosmo/active_job.rb +29 -0
- data/lib/cosmo/api/busy.rb +2 -2
- data/lib/cosmo/api/counter.rb +2 -2
- data/lib/cosmo/api/cron/entry.rb +99 -0
- data/lib/cosmo/api/cron.rb +118 -0
- data/lib/cosmo/api/kv.rb +35 -13
- data/lib/cosmo/api/stream.rb +10 -5
- data/lib/cosmo/api.rb +1 -0
- data/lib/cosmo/cli.rb +27 -10
- data/lib/cosmo/client.rb +48 -2
- data/lib/cosmo/config.rb +9 -0
- data/lib/cosmo/job/data.rb +1 -1
- data/lib/cosmo/job/limit.rb +51 -0
- data/lib/cosmo/job/processor.rb +49 -5
- data/lib/cosmo/job.rb +51 -2
- data/lib/cosmo/processor.rb +1 -1
- data/lib/cosmo/railtie.rb +21 -0
- data/lib/cosmo/stream/processor.rb +2 -2
- data/lib/cosmo/stream.rb +2 -1
- data/lib/cosmo/utils/hash.rb +13 -0
- data/lib/cosmo/utils/overrides.rb +1 -1
- data/lib/cosmo/version.rb +1 -1
- data/lib/cosmo/web/assets/app.css +42 -0
- data/lib/cosmo/web/controllers/crons.rb +41 -0
- data/lib/cosmo/web/controllers/jobs.rb +7 -3
- data/lib/cosmo/web/controllers/streams.rb +1 -1
- data/lib/cosmo/web/helpers/application.rb +4 -0
- data/lib/cosmo/web/views/actions/index.erb +1 -1
- data/lib/cosmo/web/views/crons/_table.erb +58 -0
- data/lib/cosmo/web/views/crons/index.erb +10 -0
- data/lib/cosmo/web/views/jobs/_busy.erb +54 -49
- data/lib/cosmo/web/views/jobs/_dead.erb +70 -65
- data/lib/cosmo/web/views/jobs/_enqueued.erb +82 -56
- data/lib/cosmo/web/views/jobs/_scheduled.erb +53 -48
- data/lib/cosmo/web/views/jobs/_tabs.erb +6 -0
- data/lib/cosmo/web/views/jobs/busy.erb +8 -6
- data/lib/cosmo/web/views/jobs/dead.erb +6 -5
- data/lib/cosmo/web/views/jobs/enqueued.erb +8 -6
- data/lib/cosmo/web/views/jobs/index.erb +1 -1
- data/lib/cosmo/web/views/jobs/scheduled.erb +6 -5
- data/lib/cosmo/web/views/layout.erb +1 -1
- data/lib/cosmo/web.rb +5 -0
- data/lib/cosmo.rb +1 -0
- data/sig/cosmo/active_job/adapter.rbs +13 -0
- data/sig/cosmo/active_job/executor.rbs +9 -0
- data/sig/cosmo/active_job/options.rbs +14 -0
- data/sig/cosmo/api/cron/entry.rbs +30 -0
- data/sig/cosmo/api/cron.rbs +25 -0
- data/sig/cosmo/api/kv.rbs +4 -6
- data/sig/cosmo/client.rbs +9 -1
- data/sig/cosmo/job/data.rbs +1 -1
- data/sig/cosmo/job/limit.rbs +18 -0
- data/sig/cosmo/job/processor.rbs +3 -1
- data/sig/cosmo/job.rbs +9 -4
- data/sig/cosmo/railtie.rbs +4 -0
- data/sig/cosmo/utils/hash.rbs +4 -0
- metadata +20 -1
|
@@ -1,65 +1,70 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<% end -%>
|
|
1
|
+
<div id="dead-poller"
|
|
2
|
+
hx-get="<%= url_for('/jobs/_dead') %>"
|
|
3
|
+
hx-trigger="every 5s"
|
|
4
|
+
hx-swap="outerHTML">
|
|
5
|
+
<% if @jobs.empty? -%>
|
|
6
|
+
<div class="alert alert-success">No dead jobs found</div>
|
|
7
|
+
<% else -%>
|
|
8
|
+
<div class="table-container">
|
|
9
|
+
<table>
|
|
10
|
+
<thead>
|
|
11
|
+
<tr>
|
|
12
|
+
<th>Job</th>
|
|
13
|
+
<th>JID[Args]</th>
|
|
14
|
+
<th>Stream</th>
|
|
15
|
+
<th>Error</th>
|
|
16
|
+
<th>Failed</th>
|
|
17
|
+
<th>Actions</th>
|
|
18
|
+
</tr>
|
|
19
|
+
</thead>
|
|
20
|
+
<tbody>
|
|
21
|
+
<% @jobs.each do |job| -%>
|
|
22
|
+
<tr class="dead-row" id="dead-row-<%= job.seq %>">
|
|
23
|
+
<td>
|
|
24
|
+
<div class="job-class">
|
|
25
|
+
<%= h(job.data[:class].to_s) %>
|
|
26
|
+
</div>
|
|
27
|
+
</td>
|
|
28
|
+
<td>
|
|
29
|
+
<div class="job-id">
|
|
30
|
+
<details>
|
|
31
|
+
<summary>
|
|
32
|
+
<code><%= h(job.data[:jid].to_s) %></code>
|
|
33
|
+
</summary>
|
|
34
|
+
<code><%= h((JSON.pretty_generate(job.data[:args]) rescue job.data[:args].to_s)) %></code>
|
|
35
|
+
</details>
|
|
36
|
+
</div>
|
|
37
|
+
</td>
|
|
38
|
+
<td>
|
|
39
|
+
<code><%= h(job.stream) %></code>
|
|
40
|
+
</td>
|
|
41
|
+
<td style="max-width: 400px;">
|
|
42
|
+
<% if job.data[:error] -%>
|
|
43
|
+
<div class="error-message">
|
|
44
|
+
<strong><%= h(job.data[:error].to_s.split(":").first || "Error") %></strong>
|
|
45
|
+
<p><%= h(job.data[:error]) %></p>
|
|
46
|
+
</div>
|
|
47
|
+
<% end -%>
|
|
48
|
+
</td>
|
|
49
|
+
<td style="white-space: nowrap;">
|
|
50
|
+
<time><%= h(job.timestamp) %></time>
|
|
51
|
+
</td>
|
|
52
|
+
<td>
|
|
53
|
+
<a hx-patch="<%= url_for("/jobs/retry/#{job.seq}") %>"
|
|
54
|
+
hx-target="#dead-row-<%= job.seq %>"
|
|
55
|
+
hx-swap="innerHTML"
|
|
56
|
+
class="btn btn-primary"
|
|
57
|
+
style="width: 100%; text-align: center; margin-bottom: 3px">↻ Retry</a>
|
|
58
|
+
<a hx-delete="<%= url_for("/jobs/delete/#{job.seq}") %>"
|
|
59
|
+
hx-target="#dead-row-<%= job.seq %>"
|
|
60
|
+
hx-swap="innerHTML"
|
|
61
|
+
class="btn btn-danger"
|
|
62
|
+
style="width: 100%; text-align: center; margin-bottom: 3px">✕ Delete</a>
|
|
63
|
+
</td>
|
|
64
|
+
</tr>
|
|
65
|
+
<% end -%>
|
|
66
|
+
</tbody>
|
|
67
|
+
</table>
|
|
68
|
+
</div>
|
|
69
|
+
<% end -%>
|
|
70
|
+
</div>
|
|
@@ -1,60 +1,86 @@
|
|
|
1
|
-
<div
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
<div id="enqueued-poller">
|
|
2
|
+
<div class="nav">
|
|
3
|
+
<% @stream_names.each do |stream_name| -%>
|
|
4
|
+
<button hx-get="<%= url_for('/jobs/enqueued', { stream_name: }) %>"
|
|
5
|
+
hx-target="#content"
|
|
6
|
+
hx-swap="innerHTML"
|
|
7
|
+
hx-push-url="true"
|
|
8
|
+
class="btn <%= "btn-primary" if @stream_name.to_s == stream_name.to_s %>">
|
|
9
|
+
<%= h(stream_name) %>
|
|
10
|
+
</button>
|
|
11
|
+
<% end -%>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="pending">
|
|
15
|
+
<span></span>
|
|
16
|
+
<span class="text-muted"><%= @total %> message(s) pending</span>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<% if @jobs.empty? -%>
|
|
20
|
+
<div class="alert alert-success">Stream <code><%= h(@stream_name) %></code> is empty</div>
|
|
21
|
+
<% else -%>
|
|
22
|
+
<div class="table-container">
|
|
23
|
+
<table>
|
|
24
|
+
<thead>
|
|
25
|
+
<tr>
|
|
26
|
+
<th>Job</th>
|
|
27
|
+
<th>JID[Args]</th>
|
|
28
|
+
<th>Stream</th>
|
|
29
|
+
<th>Seq</th>
|
|
30
|
+
</tr>
|
|
31
|
+
</thead>
|
|
32
|
+
<tbody>
|
|
33
|
+
<% @jobs.each do |job| -%>
|
|
34
|
+
<tr>
|
|
35
|
+
<td>
|
|
36
|
+
<div class="job-class">
|
|
37
|
+
<%= h(job.data[:class].to_s) %>
|
|
38
|
+
</div>
|
|
39
|
+
</td>
|
|
40
|
+
<td>
|
|
41
|
+
<div class="job-id">
|
|
42
|
+
<details>
|
|
43
|
+
<summary>
|
|
44
|
+
<code><%= h(job.data[:jid].to_s) %></code>
|
|
45
|
+
</summary>
|
|
46
|
+
<code><%= h((JSON.pretty_generate(job.data[:args]) rescue job.data[:args].to_s)) %></code>
|
|
47
|
+
</details>
|
|
48
|
+
</div>
|
|
49
|
+
</td>
|
|
50
|
+
<td>
|
|
51
|
+
<code><%= h(job.stream) %></code>
|
|
52
|
+
</td>
|
|
53
|
+
<td>
|
|
54
|
+
<code><%= job.seq %></code>
|
|
55
|
+
</td>
|
|
56
|
+
</tr>
|
|
57
|
+
<% end -%>
|
|
58
|
+
</tbody>
|
|
59
|
+
</table>
|
|
60
|
+
</div>
|
|
10
61
|
<% end -%>
|
|
11
|
-
</div>
|
|
12
62
|
|
|
13
|
-
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
63
|
+
<% if @total_pages > 1 -%>
|
|
64
|
+
<div class="pagination">
|
|
65
|
+
<% if @page > 1 -%>
|
|
66
|
+
<a hx-get="<%= url_for('/jobs/_enqueued', stream_name: @stream_name, page: @page - 1, limit: @limit) %>"
|
|
67
|
+
hx-target="#enqueued-poller"
|
|
68
|
+
hx-swap="outerHTML"
|
|
69
|
+
class="btn">← Prev</a>
|
|
70
|
+
<% else -%>
|
|
71
|
+
<span class="btn btn-disabled">← Prev</span>
|
|
72
|
+
<% end -%>
|
|
73
|
+
|
|
74
|
+
<span class="text-muted">Page <%= @page %> of <%= @total_pages %></span>
|
|
17
75
|
|
|
18
|
-
<% if @
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<th>JID[Args]</th>
|
|
27
|
-
<th>Stream</th>
|
|
28
|
-
<th>Seq</th>
|
|
29
|
-
</tr>
|
|
30
|
-
</thead>
|
|
31
|
-
<tbody>
|
|
32
|
-
<% @jobs.each do |job| -%>
|
|
33
|
-
<tr>
|
|
34
|
-
<td>
|
|
35
|
-
<div class="job-class">
|
|
36
|
-
<%= h(job.data[:class].to_s) %>
|
|
37
|
-
</div>
|
|
38
|
-
</td>
|
|
39
|
-
<td>
|
|
40
|
-
<div class="job-id">
|
|
41
|
-
<details>
|
|
42
|
-
<summary>
|
|
43
|
-
<code><%= h(job.data[:jid].to_s) %></code>
|
|
44
|
-
</summary>
|
|
45
|
-
<code><%= h((JSON.pretty_generate(job.data[:args]) rescue job.data[:args].to_s)) %></code>
|
|
46
|
-
</details>
|
|
47
|
-
</div>
|
|
48
|
-
</td>
|
|
49
|
-
<td>
|
|
50
|
-
<code><%= h(job.stream) %></code>
|
|
51
|
-
</td>
|
|
52
|
-
<td>
|
|
53
|
-
<code><%= job.seq %></code>
|
|
54
|
-
</td>
|
|
55
|
-
</tr>
|
|
56
|
-
<% end -%>
|
|
57
|
-
</tbody>
|
|
58
|
-
</table>
|
|
76
|
+
<% if @page < @total_pages -%>
|
|
77
|
+
<a hx-get="<%= url_for('/jobs/_enqueued', stream_name: @stream_name, page: @page + 1, limit: @limit) %>"
|
|
78
|
+
hx-target="#enqueued-poller"
|
|
79
|
+
hx-swap="outerHTML"
|
|
80
|
+
class="btn">Next →</a>
|
|
81
|
+
<% else -%>
|
|
82
|
+
<span class="btn btn-disabled">Next →</span>
|
|
83
|
+
<% end -%>
|
|
59
84
|
</div>
|
|
60
|
-
<% end -%>
|
|
85
|
+
<% end -%>
|
|
86
|
+
</div>
|
|
@@ -1,49 +1,54 @@
|
|
|
1
|
-
<div
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<% else -%>
|
|
9
|
-
<div class="table-container">
|
|
10
|
-
<table>
|
|
11
|
-
<thead>
|
|
12
|
-
<tr>
|
|
13
|
-
<th>Job</th>
|
|
14
|
-
<th>JID[Args]</th>
|
|
15
|
-
<th>Stream</th>
|
|
16
|
-
<th>When</th>
|
|
17
|
-
</tr>
|
|
18
|
-
</thead>
|
|
19
|
-
<tbody>
|
|
20
|
-
<% @jobs.each do |job| -%>
|
|
21
|
-
<tr>
|
|
22
|
-
<td>
|
|
23
|
-
<div class="job-class">
|
|
24
|
-
<%= h(job.data[:class].to_s) %>
|
|
25
|
-
</div>
|
|
26
|
-
</td>
|
|
27
|
-
<td>
|
|
28
|
-
<div class="job-id">
|
|
29
|
-
<details>
|
|
30
|
-
<summary>
|
|
31
|
-
<code><%= h(job.data[:jid].to_s) %></code>
|
|
32
|
-
</summary>
|
|
33
|
-
<code><%= h((JSON.pretty_generate(job.data[:args]) rescue job.data[:args].to_s)) %></code>
|
|
34
|
-
</details>
|
|
35
|
-
</div>
|
|
36
|
-
</td>
|
|
37
|
-
<td>
|
|
38
|
-
<code><%= h(job.x_stream) %></code>
|
|
39
|
-
</td>
|
|
40
|
-
<td style="white-space: nowrap;">
|
|
41
|
-
<div class="time-badge"><%= h(time_until(job.execute_at)) %></div>
|
|
42
|
-
<time><%= h(format_timestamp(job.execute_at)) %></time>
|
|
43
|
-
</td>
|
|
44
|
-
</tr>
|
|
45
|
-
<% end -%>
|
|
46
|
-
</tbody>
|
|
47
|
-
</table>
|
|
1
|
+
<div id="scheduled-poller"
|
|
2
|
+
hx-get="<%= url_for('/jobs/_scheduled') %>"
|
|
3
|
+
hx-trigger="every 5s"
|
|
4
|
+
hx-swap="outerHTML">
|
|
5
|
+
<div class="pending">
|
|
6
|
+
<span></span>
|
|
7
|
+
<span class="text-muted"><%= @total %> job(s) scheduled</span>
|
|
48
8
|
</div>
|
|
49
|
-
|
|
9
|
+
|
|
10
|
+
<% if @jobs.empty? -%>
|
|
11
|
+
<div class="alert alert-success">No scheduled jobs found</div>
|
|
12
|
+
<% else -%>
|
|
13
|
+
<div class="table-container">
|
|
14
|
+
<table>
|
|
15
|
+
<thead>
|
|
16
|
+
<tr>
|
|
17
|
+
<th>Job</th>
|
|
18
|
+
<th>JID[Args]</th>
|
|
19
|
+
<th>Stream</th>
|
|
20
|
+
<th>When</th>
|
|
21
|
+
</tr>
|
|
22
|
+
</thead>
|
|
23
|
+
<tbody>
|
|
24
|
+
<% @jobs.each do |job| -%>
|
|
25
|
+
<tr>
|
|
26
|
+
<td>
|
|
27
|
+
<div class="job-class">
|
|
28
|
+
<%= h(job.data[:class].to_s) %>
|
|
29
|
+
</div>
|
|
30
|
+
</td>
|
|
31
|
+
<td>
|
|
32
|
+
<div class="job-id">
|
|
33
|
+
<details>
|
|
34
|
+
<summary>
|
|
35
|
+
<code><%= h(job.data[:jid].to_s) %></code>
|
|
36
|
+
</summary>
|
|
37
|
+
<code><%= h((JSON.pretty_generate(job.data[:args]) rescue job.data[:args].to_s)) %></code>
|
|
38
|
+
</details>
|
|
39
|
+
</div>
|
|
40
|
+
</td>
|
|
41
|
+
<td>
|
|
42
|
+
<code><%= h(job.x_stream) %></code>
|
|
43
|
+
</td>
|
|
44
|
+
<td style="white-space: nowrap;">
|
|
45
|
+
<div class="time-badge"><%= h(time_until(job.execute_at)) %></div>
|
|
46
|
+
<time><%= h(format_timestamp(job.execute_at)) %></time>
|
|
47
|
+
</td>
|
|
48
|
+
</tr>
|
|
49
|
+
<% end -%>
|
|
50
|
+
</tbody>
|
|
51
|
+
</table>
|
|
52
|
+
</div>
|
|
53
|
+
<% end -%>
|
|
54
|
+
</div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<section>
|
|
2
|
-
<header
|
|
2
|
+
<header><%= render('jobs/_tabs') %></header>
|
|
3
3
|
|
|
4
4
|
<div hx-get="<%= url_for('/jobs/_stats') %>"
|
|
5
5
|
hx-trigger="load, every 5s"
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
<div class="alert alert-info">Loading statistics…</div>
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
|
-
<div id="content"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
<div id="content">
|
|
11
|
+
<div id="busy-poller"
|
|
12
|
+
hx-get="<%= url_for('/jobs/_busy') %>"
|
|
13
|
+
hx-trigger="load"
|
|
14
|
+
hx-swap="outerHTML">
|
|
15
|
+
<div class="alert alert-info">Loading busy jobs…</div>
|
|
16
|
+
</div>
|
|
15
17
|
</div>
|
|
16
18
|
</section>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<section>
|
|
2
|
-
<header
|
|
2
|
+
<header><%= render('jobs/_tabs') %></header>
|
|
3
3
|
|
|
4
4
|
<div hx-get="<%= url_for('/jobs/_stats') %>"
|
|
5
5
|
hx-trigger="load, every 5s"
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
10
|
<div id="content">
|
|
11
|
-
<div
|
|
12
|
-
hx-
|
|
13
|
-
hx-
|
|
14
|
-
|
|
11
|
+
<div id="dead-poller"
|
|
12
|
+
hx-get="<%= url_for('/jobs/_dead') %>"
|
|
13
|
+
hx-trigger="load"
|
|
14
|
+
hx-swap="outerHTML">
|
|
15
|
+
<div class="alert alert-info">Loading dead jobs…</div>
|
|
15
16
|
</div>
|
|
16
17
|
</div>
|
|
17
18
|
</section>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<section>
|
|
2
|
-
<header
|
|
2
|
+
<header><%= render('jobs/_tabs') %></header>
|
|
3
3
|
|
|
4
4
|
<div hx-get="<%= url_for('/jobs/_stats') %>"
|
|
5
5
|
hx-trigger="load, every 5s"
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
<div class="alert alert-info">Loading statistics…</div>
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
|
-
<div id="content"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
<div id="content">
|
|
11
|
+
<div id="enqueued-poller"
|
|
12
|
+
hx-get="<%= url_for('/jobs/_enqueued', stream_name: @stream_name) %>"
|
|
13
|
+
hx-trigger="load"
|
|
14
|
+
hx-swap="outerHTML">
|
|
15
|
+
<div class="alert alert-info">Loading enqueued jobs…</div>
|
|
16
|
+
</div>
|
|
15
17
|
</div>
|
|
16
18
|
</section>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<section>
|
|
2
|
-
<header
|
|
2
|
+
<header><%= render('jobs/_tabs') %></header>
|
|
3
3
|
|
|
4
4
|
<div hx-get="<%= url_for('/jobs/_stats') %>"
|
|
5
5
|
hx-trigger="load, every 5s"
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
10
|
<div id="content">
|
|
11
|
-
<div
|
|
12
|
-
hx-
|
|
13
|
-
hx-
|
|
14
|
-
|
|
11
|
+
<div id="scheduled-poller"
|
|
12
|
+
hx-get="<%= url_for('/jobs/_scheduled') %>"
|
|
13
|
+
hx-trigger="load"
|
|
14
|
+
hx-swap="outerHTML">
|
|
15
|
+
<div class="alert alert-info">Loading scheduled jobs…</div>
|
|
15
16
|
</div>
|
|
16
17
|
</div>
|
|
17
18
|
</section>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<a href="<%= url_for('/jobs') %>" class="navbar-brand">🚀 Cosmo</a>
|
|
15
15
|
<ul class="nav-list">
|
|
16
16
|
<li>
|
|
17
|
-
<a href="<%= url_for('/jobs') %>" class="<%= "active" if
|
|
17
|
+
<a href="<%= url_for('/jobs') %>" class="<%= "active" if path_prefix?('/jobs', '/crons') %>">Jobs</a>
|
|
18
18
|
</li>
|
|
19
19
|
<li>
|
|
20
20
|
<a href="<%= url_for('/streams') %>" class="<%= "active" if current_page?('/streams') %>">Streams</a>
|
data/lib/cosmo/web.rb
CHANGED
|
@@ -9,6 +9,7 @@ require "cosmo/web/controllers/application"
|
|
|
9
9
|
require "cosmo/web/controllers/jobs"
|
|
10
10
|
require "cosmo/web/controllers/streams"
|
|
11
11
|
require "cosmo/web/controllers/actions"
|
|
12
|
+
require "cosmo/web/controllers/crons"
|
|
12
13
|
|
|
13
14
|
module Cosmo
|
|
14
15
|
class Web
|
|
@@ -43,6 +44,10 @@ module Cosmo
|
|
|
43
44
|
in [:get, "/streams/_info"] then [Controllers::Streams, :_info]
|
|
44
45
|
in [:patch, "/streams/pause"] then [Controllers::Streams, :pause]
|
|
45
46
|
in [:patch, "/streams/unpause"] then [Controllers::Streams, :unpause]
|
|
47
|
+
in [:get, "/crons"] then [Controllers::Crons, :index]
|
|
48
|
+
in [:get, "/crons/_table"] then [Controllers::Crons, :_table]
|
|
49
|
+
in [:delete, "/crons/delete"] then [Controllers::Crons, :delete]
|
|
50
|
+
in [:post, "/crons/run"] then [Controllers::Crons, :run_now]
|
|
46
51
|
in [:get, "/actions"] then [Controllers::Actions, :index]
|
|
47
52
|
in [:get, "/assets/htmx.min.js.gz"] then serve("htmx.2.0.8.min.js.gz",
|
|
48
53
|
"application/javascript",
|
data/lib/cosmo.rb
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Cosmo
|
|
2
|
+
module ActiveJobAdapter
|
|
3
|
+
class Adapter
|
|
4
|
+
def enqueue: (untyped job) -> String
|
|
5
|
+
def enqueue_at: (untyped job, Numeric timestamp) -> String
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def publish: (untyped job, Numeric? timestamp) -> String
|
|
10
|
+
def job_cosmo_options: (untyped job) -> Hash[Symbol, untyped]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Cosmo
|
|
2
|
+
module ActiveJobAdapter
|
|
3
|
+
VALID_OPTIONS: ::Array[::Symbol]
|
|
4
|
+
|
|
5
|
+
module Options
|
|
6
|
+
def self.included: (::Module base) -> void
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
def cosmo_options: (**untyped opts) -> ::Hash[::Symbol, untyped]
|
|
10
|
+
def get_cosmo_options: () -> ::Hash[::Symbol, untyped]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Cosmo
|
|
2
|
+
module API
|
|
3
|
+
class Cron
|
|
4
|
+
class Entry
|
|
5
|
+
SUBJECT_PREFIX: ::String
|
|
6
|
+
|
|
7
|
+
attr_reader class_name: ::String
|
|
8
|
+
attr_reader stream: ::String
|
|
9
|
+
attr_reader expression: ::String
|
|
10
|
+
attr_reader args: ::Array[untyped]
|
|
11
|
+
attr_reader timezone: ::String?
|
|
12
|
+
attr_reader name: ::String?
|
|
13
|
+
|
|
14
|
+
def self.normalize_expression: (untyped expr) -> ::String
|
|
15
|
+
|
|
16
|
+
def initialize: (class_name: ::String, stream: ::String, expression: ::String,
|
|
17
|
+
?args: ::Array[untyped], ?timezone: ::String?, ?name: (::String | ::Symbol)?) -> void
|
|
18
|
+
|
|
19
|
+
def schedule_subject: () -> ::String
|
|
20
|
+
def target_subject: () -> ::String
|
|
21
|
+
def job_name: () -> ::String
|
|
22
|
+
def job_payload: () -> ::String
|
|
23
|
+
def as_json: () -> Hash[Symbol, untyped]
|
|
24
|
+
def to_s: () -> ::String
|
|
25
|
+
alias inspect to_s
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Cosmo
|
|
2
|
+
module API
|
|
3
|
+
class Cron
|
|
4
|
+
def all: () -> ::Array[Hash[Symbol, untyped]]
|
|
5
|
+
|
|
6
|
+
def upsert!: (?Cosmo::API::Cron::Entry schedule_obj,
|
|
7
|
+
?class_name: ::String?,
|
|
8
|
+
?stream: ::String?,
|
|
9
|
+
?expression: ::String?,
|
|
10
|
+
?args: ::Array[untyped],
|
|
11
|
+
?timezone: ::String?,
|
|
12
|
+
?name: ::String?) -> Hash[Symbol, untyped]?
|
|
13
|
+
|
|
14
|
+
def delete!: (::String subject) -> void
|
|
15
|
+
|
|
16
|
+
def run_now!: (::String schedule_subject) -> void
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def schedules_from_stream: (::String stream_name) -> ::Array[Hash[Symbol, untyped]]
|
|
21
|
+
def build_from_nats: (::String stream_name, ::String subject) -> Hash[Symbol, untyped]?
|
|
22
|
+
def name_from_subject: (::String subject) -> ::String?
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|