Module: Wurk::Api::Serializers

Defined in:
app/controllers/wurk/api/serializers.rb

Overview

Pure mapping from inspector objects → JSON-shaped Hashes for the dashboard SPA. Keeping the serializers out of the controller lets the action methods stay tiny and lets future endpoints share the same field shapes without re-implementing them.

Class Method Summary collapse

Class Method Details

.cron_row(loop_obj, now_epoch) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'app/controllers/wurk/api/serializers.rb', line 124

def cron_row(loop_obj, now_epoch)
  {
    lid: loop_obj.lid,
    schedule: loop_obj.schedule,
    klass: loop_obj.klass,
    queue: loop_obj.queue,
    tz: loop_obj.tz_name,
    paused: loop_obj.paused?,
    args: loop_obj.args,
    last_fire_at: loop_obj.last_fired_at,
    next_fire_at: loop_obj.next_fire_at(now_epoch)
  }
end

.history_point(row) ⇒ Object

One point in a cluster-total time-series (Wurk::Metrics::Query.history). ‘at` is epoch seconds at the bucket start.



144
145
146
# File 'app/controllers/wurk/api/serializers.rb', line 144

def history_point(row)
  { at: row[:at], processed: row[:p], failed: row[:f], runtime_ms: row[:ms] }
end

.job_record(record) ⇒ Object

Host-registered custom job-info rows (spec §25.2) ride along as ‘custom_rows` for the SPA’s job-detail modal (see Config#job_info_pairs, which gates on registration so the common no-extension case is free). Divergence: wurk evaluates these during job-list serialization — the SPA renders job detail client-side — not in a dedicated server detail view.



42
43
44
45
46
47
48
49
50
51
52
53
# File 'app/controllers/wurk/api/serializers.rb', line 42

def job_record(record)
  base = {
    jid: record.jid,
    klass: record.display_class,
    args: record.display_args,
    queue: record.queue,
    enqueued_at: record.enqueued_at&.to_f,
    created_at: record.created_at&.to_f
  }
  rows = ::Wurk::Web.config.job_info_pairs(record)
  rows.empty? ? base : base.merge(custom_rows: rows)
end

.limiter_row(name, meta) ⇒ Object



103
104
105
106
107
108
109
110
111
# File 'app/controllers/wurk/api/serializers.rb', line 103

def limiter_row(name, meta)
  {
    name: name,
    type: meta['type'].to_s,
    fingerprint: meta['fingerprint'].to_s,
    options: parse_options(meta['options']),
    status: limiter_status(name, meta)
  }
end

.limiter_status(name, meta) ⇒ Object

Reconstruct the limiter (read-only, ‘register: false`) just to read its uniform `{ used, limit, reset_at, available? }` status for the Limits tab. Best-effort: a malformed meta hash yields nil rather than 500-ing the whole list.



117
118
119
120
121
122
# File 'app/controllers/wurk/api/serializers.rb', line 117

def limiter_status(name, meta)
  limiter = ::Wurk::Limiter.build(name, meta['type'], parse_options(meta['options']))
  limiter&.status
rescue StandardError
  nil
end

.metric_row(klass, totals) ⇒ Object



138
139
140
# File 'app/controllers/wurk/api/serializers.rb', line 138

def metric_row(klass, totals)
  { klass: klass, processed: totals[:p], failed: totals[:f], runtime_ms: totals[:ms] }
end

.parse_options(raw) ⇒ Object



155
156
157
158
159
160
161
# File 'app/controllers/wurk/api/serializers.rb', line 155

def parse_options(raw)
  return {} if raw.nil? || raw.to_s.empty?

  ::JSON.parse(raw)
rescue ::JSON::ParserError
  {}
end

.process_row(process) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'app/controllers/wurk/api/serializers.rb', line 66

def process_row(process)
  {
    identity: process.identity,
    hostname: process['hostname'],
    pid: process['pid'],
    tag: process.tag,
    concurrency: process['concurrency'],
    busy: process['busy'],
    beat: process['beat'],
    quiet: process.stopping?,
    rss: process['rss'],
    rtt_us: process['rtt_us'],
    started_at: process['started_at'],
    cpu_model: process['cpu_model'],
    cores: process['cores'],
    memory_total_kb: process['memory_total_kb'],
    labels: process.labels,
    queues: process.queues,
    version: process.version,
    embedded: process.embedded?
  }
end

.profile_record(rec) ⇒ Object

One profile row for the Profiles pane. ‘key` drives the view/data links.



164
165
166
167
168
169
170
171
172
173
174
# File 'app/controllers/wurk/api/serializers.rb', line 164

def profile_record(rec)
  {
    key: rec.key,
    jid: rec.jid,
    token: rec.token,
    type: rec.type,
    size: rec.size,
    elapsed: rec.elapsed,
    started_at: rec.started_at&.to_i
  }
end

.queue_history_series(row) ⇒ Object

One queue’s size/latency gauge series (Wurk::Metrics::Query.queue_history). ‘points` are oldest→newest; `at` is epoch seconds at the bucket start, `size` is queue depth, `latency` is head-of-line wait in seconds.



151
152
153
# File 'app/controllers/wurk/api/serializers.rb', line 151

def queue_history_series(row)
  { name: row[:name], points: row[:points].map { |p| { at: p[:at], size: p[:size], latency: p[:latency] } } }
end

.queue_summary(summary) ⇒ Object



33
34
35
# File 'app/controllers/wurk/api/serializers.rb', line 33

def queue_summary(summary)
  { name: summary.name, size: summary.size, latency: summary.latency, paused: summary.paused? }
end

.sorted_entry(entry) ⇒ Object



55
56
57
58
59
60
61
62
63
64
# File 'app/controllers/wurk/api/serializers.rb', line 55

def sorted_entry(entry)
  job_record(entry).merge(
    score: entry.score,
    at: entry.at.to_f,
    error_class: entry['error_class'],
    error_message: entry['error_message'],
    retry_count: entry['retry_count'],
    error_backtrace: entry.error_backtrace
  )
end

.stats_payload(stats) ⇒ Object

Wire-shape consumed by the React dashboard’s landing page + SSE feed. Field names match the SPA’s ‘StatsSnapshot` interface in frontend/src/hooks/useSSE.ts — keep them in sync. The canonical Sidekiq-compatible accessors on Wurk::Stats use `_size` suffixes; this serializer renames them for the dashboard’s wire shape only.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'app/controllers/wurk/api/serializers.rb', line 17

def stats_payload(stats)
  {
    processed: stats.processed,
    failed: stats.failed,
    expired: stats.expired,
    enqueued: stats.enqueued,
    busy: stats.workers_size,
    scheduled: stats.scheduled_size,
    retries: stats.retry_size,
    dead: stats.dead_size,
    processes: stats.processes_size,
    latency: stats.default_queue_latency,
    queues: stats.queue_summaries.map { |q| queue_summary(q) }
  }
end

.work_row(process_id, thread_id, work) ⇒ Object

One in-flight job (WorkSet row) for the Busy page’s process detail.



90
91
92
93
94
95
96
97
98
99
100
101
# File 'app/controllers/wurk/api/serializers.rb', line 90

def work_row(process_id, thread_id, work)
  record = work.job
  {
    process_id: process_id,
    thread_id: thread_id,
    queue: work.queue,
    klass: record.display_class,
    args: record.display_args,
    jid: record.jid,
    run_at: work.run_at.to_f
  }
end