patient_http-sidekiq 1.1.1 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f16c7b27f88a7f84cb0b34cb6ae28a931b9be60dcfa22c8107efeff1cf9bb82c
4
- data.tar.gz: f6c20d30b05984bd72d21f4faea53f4eb2f5e08637894f09c286abc1df437640
3
+ metadata.gz: e5ae5287613de3682de817fb6c434e6a1a33c21c1b8aa1dde7b7808c1733b927
4
+ data.tar.gz: 350e8f054d1047fe70482d9c3fc2f5473b077e1e7f1c64cc6f023c8bf76f62b4
5
5
  SHA512:
6
- metadata.gz: 9d79dc80c1493605c18763b89533aeed861c3ab6e49d6ae29558d7650297752bccff9efaec56bf425366f2db79c48125351db0d1dd5b7310d353bf5af50ef9bd
7
- data.tar.gz: 84c003b789d2809bfccd9e6ef11ddf80d56712ed963bb3e5bc5769cf7b6b7bdbfc4720e260b034cb0b99d340991f30a526bdd774cba1adccf811cdf2fd8c07f3
6
+ metadata.gz: d843ce929d22f48c20bc7f487b35d11421166218081f4fea2490440781d0a00839266c3cb1dec039611a6728e2213c3a71f0a966454ba40c1efc6ab466648ca7
7
+ data.tar.gz: 006cf97e99a6d27847b038c19065a88b09e28a94c7392fbee5af4253e9483652e8022b21e42c271840cfc8bff326fcfd490b137d1b08c8984760c68f6f7bc3df
data/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 1.2.0
8
+
9
+ ### Added
10
+
11
+ - `PatientHttp::Sidekiq.with_sidekiq_options` sets Sidekiq job options (queue, retry, etc.) at runtime for the requests enqueued within a block. When the options include a queue, the callback job for the request is enqueued on that queue as well.
12
+
13
+ ## 1.1.2
14
+
15
+ ### Fixed
16
+
17
+ - The Web UI stylesheet now loads on Sidekiq 7.x. The `style_tag` helper returns the link tag on Sidekiq 7 but adds it to the page head on Sidekiq 8; the view now handles both.
18
+ - The Web UI stylesheet now uses CSS variables that the Sidekiq 8 web UI actually defines (`--color-primary`, `--color-text`, ...), with static fallback values for Sidekiq 7.x.
19
+
7
20
  ## 1.1.1
8
21
 
9
22
  ### Fixed
data/README.md CHANGED
@@ -232,6 +232,21 @@ PatientHttp.execute(request: request, callback: MyCallback, callback_args: {user
232
232
 
233
233
  See the [patient_http docs](https://github.com/bdurand/patient_http) for the full `Request` and `Response` API reference.
234
234
 
235
+ ### Setting Sidekiq Options At Runtime
236
+
237
+ You can set Sidekiq job options for individual requests with `PatientHttp::Sidekiq.with_sidekiq_options`. The options apply to all requests enqueued within the block. Use this, for example, to route urgent requests to a higher priority queue:
238
+
239
+ ```ruby
240
+ PatientHttp::Sidekiq.with_sidekiq_options(queue: "high_priority") do
241
+ PatientHttp.get("https://api.example.com/users/123", callback: MyCallback)
242
+ end
243
+ ```
244
+
245
+ The options are applied with Sidekiq's `set` method, so any Sidekiq job option (`queue`, `retry`, etc.) is allowed. Notes on the behavior:
246
+
247
+ - If the options include a `queue`, the callback job that invokes your `on_complete`/`on_error` methods is enqueued on that queue as well, so the whole request keeps one priority end to end.
248
+ - Nested blocks merge their options, and the innermost values take precedence. The previous options are restored when the block exits, even if the block raises an error.
249
+
235
250
  ### Using Request Templates
236
251
 
237
252
  For repeated requests to the same API, use `PatientHttp::RequestTemplate` to share configuration:
@@ -446,6 +461,7 @@ PatientHttp::Sidekiq.configure do |config|
446
461
  config.payload_store_threshold = 64 * 1024
447
462
 
448
463
  # Sidekiq options for RequestWorker and CallbackWorker
464
+ # (use PatientHttp::Sidekiq.with_sidekiq_options to override per request)
449
465
  config.sidekiq_options = {queue: "patient_http", retry: 5}
450
466
 
451
467
  # Handler called when a callback job exhausts all Sidekiq retries
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.1
1
+ 1.2.0
@@ -28,7 +28,7 @@ module PatientHttp
28
28
  # @return [void]
29
29
  def on_complete(response, callback)
30
30
  data = store_if_needed(response.as_json)
31
- CallbackWorker.perform_async(data, "response", callback)
31
+ callback_worker.perform_async(data, "response", callback)
32
32
  delete_stored_request_payload
33
33
  end
34
34
 
@@ -42,7 +42,7 @@ module PatientHttp
42
42
  # @return [void]
43
43
  def on_error(error, callback)
44
44
  data = store_if_needed(error.as_json)
45
- CallbackWorker.perform_async(data, "error", callback)
45
+ callback_worker.perform_async(data, "error", callback)
46
46
  delete_stored_request_payload
47
47
  end
48
48
 
@@ -69,6 +69,18 @@ module PatientHttp
69
69
 
70
70
  private
71
71
 
72
+ # Returns CallbackWorker or a Sidekiq job setter that routes the callback
73
+ # job to the same queue as the request job when a runtime queue was set
74
+ # at enqueue time.
75
+ def callback_worker
76
+ queue = @sidekiq_job["patient_http_callback_queue"]
77
+ if queue
78
+ CallbackWorker.set(queue: queue)
79
+ else
80
+ CallbackWorker
81
+ end
82
+ end
83
+
72
84
  # Delete the externally stored request payload once the request has
73
85
  # completed. Until then the payload must remain fetchable because the
74
86
  # Sidekiq job hash referencing it can be re-pushed by Sidekiq retries,
@@ -1,18 +1,29 @@
1
1
  /* PatientHttp Dashboard Stylesheet */
2
2
 
3
3
  .patient-http-dashboard {
4
- --patient-http-primary: var(--color-brand);
5
- --patient-http-success: var(--color-ok);
6
- --patient-http-error: var(--color-err);
7
- --patient-http-warning: var(--color-warn);
8
- --patient-http-bg: var(--color-bg);
9
- --patient-http-text: var(--color-fg);
10
- --patient-http-border: var(--color-border);
11
- --patient-http-subtle: var(--color-subtle);
4
+ /* The --color-* variables come from the Sidekiq 8 web UI stylesheet. The
5
+ fallback values are for Sidekiq 7.x, which defines no CSS variables.
6
+ Status colors are static because Sidekiq 8 only defines pastel label
7
+ backgrounds, which are unreadable as text colors. */
8
+ --patient-http-primary: var(--color-primary, #b91d47);
9
+ --patient-http-success: #28a745;
10
+ --patient-http-error: #dc3545;
11
+ --patient-http-warning: #b45309;
12
+ --patient-http-bg: var(--color-bg, transparent);
13
+ --patient-http-text: var(--color-text, inherit);
14
+ --patient-http-border: var(--color-border, #dee2e6);
15
+ --patient-http-subtle: var(--color-text-light, #757575);
16
+ --patient-http-card-bg: var(--color-elevated, transparent);
17
+ --patient-http-card-hover: var(--color-selected, transparent);
18
+
19
+ /* Reset the base font size because the Bootstrap CSS in the Sidekiq 7.x
20
+ web UI sets a 10px root font size. Font sizes below are in em so they
21
+ scale from this base on every Sidekiq version. */
22
+ font-size: var(--font-size, 16px);
12
23
  }
13
24
 
14
25
  .patient-http-dashboard .section {
15
- margin-top: 1.5rem;
26
+ margin-top: 24px;
16
27
  background: var(--patient-http-bg);
17
28
  }
18
29
 
@@ -20,7 +31,7 @@
20
31
  display: flex;
21
32
  justify-content: space-between;
22
33
  align-items: center;
23
- margin-bottom: var(--space-lg, 1.5rem);
34
+ margin-bottom: 24px;
24
35
  }
25
36
 
26
37
  .patient-http-dashboard .header-with-button h2 {
@@ -32,30 +43,30 @@
32
43
  }
33
44
 
34
45
  .patient-http-dashboard .clear-stats-form .btn {
35
- font-size: 0.875rem;
36
- padding: 0.5rem 1rem;
46
+ font-size: 0.875em;
47
+ padding: 8px 16px;
37
48
  }
38
49
 
39
50
  .patient-http-dashboard h2 {
40
- margin: 0 0 var(--space-lg, 1.5rem) 0;
51
+ margin: 0 0 24px 0;
41
52
  color: var(--patient-http-text);
42
- font-size: 1.75rem;
53
+ font-size: 1.75em;
43
54
  font-weight: 600;
44
55
  letter-spacing: -0.015em;
45
56
  }
46
57
 
47
58
  .patient-http-dashboard h3 {
48
- margin: var(--space-xl, 2rem) 0 var(--space-lg, 1.5rem) 0;
59
+ margin: 32px 0 24px 0;
49
60
  color: var(--patient-http-text);
50
- font-size: 1.25rem;
61
+ font-size: 1.25em;
51
62
  font-weight: 600;
52
63
  letter-spacing: -0.01em;
53
64
  }
54
65
 
55
66
  .patient-http-dashboard h4 {
56
- margin: var(--space-md, 1rem) 0 var(--space-sm, 0.75rem) 0;
67
+ margin: 16px 0 12px 0;
57
68
  color: var(--patient-http-text);
58
- font-size: 1rem;
69
+ font-size: 1em;
59
70
  font-weight: 600;
60
71
  }
61
72
 
@@ -63,30 +74,30 @@
63
74
  .stats-grid {
64
75
  display: grid;
65
76
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
66
- gap: var(--space-lg, 1.5rem);
67
- margin-bottom: var(--space-xl, 2rem);
77
+ gap: 24px;
78
+ margin-bottom: 32px;
68
79
  }
69
80
 
70
81
  .stat-card {
71
- padding: var(--space-lg, 1.5rem);
72
- background: var(--color-card-bg, rgb(255 255 255));
82
+ padding: 24px;
83
+ background: var(--patient-http-card-bg);
73
84
  border: 1px solid var(--patient-http-border);
74
- border-radius: 0.375rem;
85
+ border-radius: 6px;
75
86
  display: flex;
76
87
  flex-direction: column;
77
88
  justify-content: space-between;
78
89
  }
79
90
 
80
91
  .stat-label {
81
- font-size: 0.875rem;
82
- color: var(--color-subtle, rgb(128 128 128));
92
+ font-size: 0.875em;
93
+ color: var(--patient-http-subtle);
83
94
  font-weight: 500;
84
- margin-bottom: var(--space-sm, 0.75rem);
95
+ margin-bottom: 12px;
85
96
  letter-spacing: 0.02em;
86
97
  }
87
98
 
88
99
  .stat-value {
89
- font-size: 2rem;
100
+ font-size: 2em;
90
101
  font-weight: 700;
91
102
  color: var(--patient-http-primary);
92
103
  letter-spacing: -0.02em;
@@ -99,75 +110,75 @@
99
110
 
100
111
  /* Capacity Section */
101
112
  .capacity-section {
102
- margin: var(--space-xl, 2rem) 0;
103
- padding: var(--space-lg, 1.5rem);
104
- background: var(--color-card-bg, rgb(255 255 255));
113
+ margin: 32px 0;
114
+ padding: 24px;
115
+ background: var(--patient-http-card-bg);
105
116
  border: 1px solid var(--patient-http-border);
106
- border-radius: 0.375rem;
117
+ border-radius: 6px;
107
118
  }
108
119
 
109
120
  .capacity-cards {
110
121
  display: grid;
111
122
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
112
- gap: var(--space-lg, 1.5rem);
113
- margin-bottom: var(--space-xl, 2rem);
123
+ gap: 24px;
124
+ margin-bottom: 32px;
114
125
  }
115
126
 
116
127
  .capacity-card {
117
- padding: var(--space-md, 1rem);
118
- background: var(--patient-http-bg, rgb(255 255 255));
128
+ padding: 16px;
129
+ background: var(--patient-http-card-bg);
119
130
  border: 1px solid var(--patient-http-border);
120
- border-radius: 0.25rem;
131
+ border-radius: 4px;
121
132
  text-align: center;
122
133
  }
123
134
 
124
135
  .capacity-label {
125
- font-size: 0.875rem;
126
- color: var(--color-subtle, rgb(128 128 128));
136
+ font-size: 0.875em;
137
+ color: var(--patient-http-subtle);
127
138
  font-weight: 500;
128
- margin-bottom: 0.5rem;
139
+ margin-bottom: 8px;
129
140
  display: block;
130
141
  }
131
142
 
132
143
  .capacity-value {
133
144
  display: block;
134
- font-size: 1.75rem;
145
+ font-size: 1.75em;
135
146
  font-weight: 700;
136
147
  color: var(--patient-http-primary);
137
148
  line-height: 1;
138
- margin-bottom: 0.25rem;
149
+ margin-bottom: 4px;
139
150
  }
140
151
 
141
152
  .capacity-unit {
142
- font-size: 0.75rem;
143
- color: var(--color-subtle, rgb(128 128 128));
153
+ font-size: 0.75em;
154
+ color: var(--patient-http-subtle);
144
155
  text-transform: uppercase;
145
156
  letter-spacing: 0.05em;
146
157
  }
147
158
 
148
159
  /* Process Breakdown Table */
149
160
  .process-breakdown {
150
- margin-top: var(--space-lg, 1.5rem);
161
+ margin-top: 24px;
151
162
  }
152
163
 
153
164
  /* Breakdown Sections (HTTP Status Codes, Error Types) */
154
165
  .breakdown-section {
155
- margin: var(--space-xl, 2rem) 0;
156
- padding: var(--space-lg, 1.5rem);
157
- background: var(--color-card-bg, rgb(255 255 255));
166
+ margin: 32px 0;
167
+ padding: 24px;
168
+ background: var(--patient-http-card-bg);
158
169
  border: 1px solid var(--patient-http-border);
159
- border-radius: 0.375rem;
170
+ border-radius: 6px;
160
171
  }
161
172
 
162
173
  .breakdown-section h3 {
163
- margin: 0 0 var(--space-lg, 1.5rem) 0;
174
+ margin: 0 0 24px 0;
164
175
  }
165
176
 
166
177
  /* Table Styling */
167
178
  .patient-http-dashboard .table {
168
179
  width: 100%;
169
180
  border-collapse: collapse;
170
- margin-top: var(--space-md, 1rem);
181
+ margin-top: 16px;
171
182
  }
172
183
 
173
184
  .patient-http-dashboard .table thead {
@@ -176,16 +187,16 @@
176
187
  }
177
188
 
178
189
  .patient-http-dashboard .table th {
179
- padding: var(--space-md, 1rem);
190
+ padding: 16px;
180
191
  text-align: left;
181
192
  font-weight: 600;
182
193
  color: var(--patient-http-text);
183
- font-size: 0.875rem;
194
+ font-size: 0.875em;
184
195
  letter-spacing: 0.01em;
185
196
  }
186
197
 
187
198
  .patient-http-dashboard .table td {
188
- padding: var(--space-md, 1rem);
199
+ padding: 16px;
189
200
  border-bottom: 1px solid var(--patient-http-border);
190
201
  color: var(--patient-http-text);
191
202
  }
@@ -196,7 +207,7 @@
196
207
 
197
208
  .patient-http-dashboard .table .empty-state {
198
209
  text-align: center;
199
- color: var(--color-subtle, rgb(128 128 128));
210
+ color: var(--patient-http-subtle);
200
211
  font-style: italic;
201
212
  }
202
213
 
@@ -211,15 +222,15 @@
211
222
  }
212
223
 
213
224
  .patient-http-dashboard h2 {
214
- font-size: 1.5rem;
225
+ font-size: 1.5em;
215
226
  }
216
227
 
217
228
  .patient-http-dashboard h3 {
218
- font-size: 1.125rem;
229
+ font-size: 1.125em;
219
230
  }
220
231
 
221
232
  .stat-value {
222
- font-size: 1.5rem;
233
+ font-size: 1.5em;
223
234
  }
224
235
  }
225
236
 
@@ -233,17 +244,10 @@
233
244
  }
234
245
 
235
246
  .capacity-value {
236
- font-size: 1.5rem;
247
+ font-size: 1.5em;
237
248
  }
238
249
  }
239
250
 
240
- /* Dark Mode Support (via CSS variables) */
241
- @media (prefers-color-scheme: dark) {
242
- .stat-card {
243
- background: var(--color-card-bg);
244
- }
245
-
246
- .stat-card:hover {
247
- background: var(--color-card-hover);
248
- }
251
+ .stat-card:hover {
252
+ background: var(--patient-http-card-hover);
249
253
  }
@@ -138,5 +138,7 @@
138
138
  </div>
139
139
  </div>
140
140
 
141
- <%# Note: style tag does not use the "=" to send output %>
142
- <% style_tag("patient-http/css/patient_http.css") %>
141
+ <%# Sidekiq 8 adds the link tag to the page head and returns an Array;
142
+ Sidekiq 7 returns the link tag as a String that must be output in place. %>
143
+ <% stylesheet = style_tag("patient-http/css/patient_http.css") %>
144
+ <%= stylesheet if stylesheet.is_a?(String) %>
@@ -18,6 +18,12 @@ require "patient_http"
18
18
  # callback_args: {user_id: 123}
19
19
  # )
20
20
  #
21
+ # Set Sidekiq job options at runtime for the requests enqueued within a block:
22
+ #
23
+ # PatientHttp::Sidekiq.with_sidekiq_options(queue: "high_priority") do
24
+ # PatientHttp::Sidekiq.execute(request, callback: MyCallback)
25
+ # end
26
+ #
21
27
  # Define a callback service class with +on_complete+ and +on_error+ methods:
22
28
  #
23
29
  # class MyCallback
@@ -209,6 +215,32 @@ module PatientHttp
209
215
  configuration.encryptor.decrypt(data)
210
216
  end
211
217
 
218
+ # Set Sidekiq job options for HTTP requests enqueued within the block.
219
+ # Options are applied with Sidekiq's `set` method (queue, retry, etc.).
220
+ # Nested calls merge options with the innermost values taking precedence.
221
+ # If the options include a queue, the callback job for the request is
222
+ # enqueued on that queue as well. Options only apply to requests enqueued
223
+ # in the same fiber as the block. This method has no effect
224
+ # when jobs run inline with Sidekiq::Testing.inline!.
225
+ #
226
+ # @param options [Hash] Sidekiq job options (symbol or string keys)
227
+ # @yield block within which enqueued requests use the options
228
+ # @return [Object] the return value of the block
229
+ def with_sidekiq_options(options)
230
+ unless options.is_a?(Hash)
231
+ raise ArgumentError.new("options must be a Hash, got: #{options.class}")
232
+ end
233
+ raise ArgumentError.new("with_sidekiq_options requires a block") unless block_given?
234
+
235
+ previous = Thread.current[:patient_http_sidekiq_options]
236
+ begin
237
+ Thread.current[:patient_http_sidekiq_options] = (previous || {}).merge(options.transform_keys(&:to_s))
238
+ yield
239
+ ensure
240
+ Thread.current[:patient_http_sidekiq_options] = previous
241
+ end
242
+ end
243
+
212
244
  # Execute an async HTTP request.
213
245
  #
214
246
  # @param request [PatientHttp::Request] the HTTP request to execute
@@ -236,7 +268,14 @@ module PatientHttp
236
268
  encrypted
237
269
  end
238
270
 
239
- RequestWorker.perform_async(data, callback_name, raise_error_responses, callback_args, request_id)
271
+ options = current_sidekiq_options
272
+ if options&.any?
273
+ queue = options["queue"]
274
+ options = options.merge("patient_http_callback_queue" => queue.to_s) if queue
275
+ RequestWorker.set(options).perform_async(data, callback_name, raise_error_responses, callback_args, request_id)
276
+ else
277
+ RequestWorker.perform_async(data, callback_name, raise_error_responses, callback_args, request_id)
278
+ end
240
279
 
241
280
  request_id
242
281
  end
@@ -347,6 +386,15 @@ module PatientHttp
347
386
  # @return [PatientHttp::Processor, nil]
348
387
  # @api private
349
388
  attr_accessor :processor
389
+
390
+ private
391
+
392
+ # Current scoped Sidekiq options, if any.
393
+ #
394
+ # @return [Hash, nil]
395
+ def current_sidekiq_options
396
+ Thread.current[:patient_http_sidekiq_options]
397
+ end
350
398
  end
351
399
  end
352
400
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: patient_http-sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand