spikard 0.3.6 → 0.5.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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -6
  3. data/ext/spikard_rb/Cargo.toml +2 -2
  4. data/lib/spikard/app.rb +33 -14
  5. data/lib/spikard/testing.rb +47 -12
  6. data/lib/spikard/version.rb +1 -1
  7. data/vendor/crates/spikard-bindings-shared/Cargo.toml +63 -0
  8. data/vendor/crates/spikard-bindings-shared/examples/config_extraction.rs +132 -0
  9. data/vendor/crates/spikard-bindings-shared/src/config_extractor.rs +752 -0
  10. data/vendor/crates/spikard-bindings-shared/src/conversion_traits.rs +194 -0
  11. data/vendor/crates/spikard-bindings-shared/src/di_traits.rs +246 -0
  12. data/vendor/crates/spikard-bindings-shared/src/error_response.rs +401 -0
  13. data/vendor/crates/spikard-bindings-shared/src/handler_base.rs +238 -0
  14. data/vendor/crates/spikard-bindings-shared/src/lib.rs +24 -0
  15. data/vendor/crates/spikard-bindings-shared/src/lifecycle_base.rs +292 -0
  16. data/vendor/crates/spikard-bindings-shared/src/lifecycle_executor.rs +616 -0
  17. data/vendor/crates/spikard-bindings-shared/src/response_builder.rs +305 -0
  18. data/vendor/crates/spikard-bindings-shared/src/test_client_base.rs +248 -0
  19. data/vendor/crates/spikard-bindings-shared/src/validation_helpers.rs +351 -0
  20. data/vendor/crates/spikard-bindings-shared/tests/comprehensive_coverage.rs +454 -0
  21. data/vendor/crates/spikard-bindings-shared/tests/error_response_edge_cases.rs +383 -0
  22. data/vendor/crates/spikard-bindings-shared/tests/handler_base_integration.rs +280 -0
  23. data/vendor/crates/spikard-core/Cargo.toml +4 -4
  24. data/vendor/crates/spikard-core/src/debug.rs +64 -0
  25. data/vendor/crates/spikard-core/src/di/container.rs +3 -27
  26. data/vendor/crates/spikard-core/src/di/factory.rs +1 -5
  27. data/vendor/crates/spikard-core/src/di/graph.rs +8 -47
  28. data/vendor/crates/spikard-core/src/di/mod.rs +1 -1
  29. data/vendor/crates/spikard-core/src/di/resolved.rs +1 -7
  30. data/vendor/crates/spikard-core/src/di/value.rs +2 -4
  31. data/vendor/crates/spikard-core/src/errors.rs +30 -0
  32. data/vendor/crates/spikard-core/src/http.rs +262 -0
  33. data/vendor/crates/spikard-core/src/lib.rs +1 -1
  34. data/vendor/crates/spikard-core/src/lifecycle.rs +764 -0
  35. data/vendor/crates/spikard-core/src/metadata.rs +389 -0
  36. data/vendor/crates/spikard-core/src/parameters.rs +1962 -159
  37. data/vendor/crates/spikard-core/src/problem.rs +34 -0
  38. data/vendor/crates/spikard-core/src/request_data.rs +966 -1
  39. data/vendor/crates/spikard-core/src/router.rs +263 -2
  40. data/vendor/crates/spikard-core/src/validation/error_mapper.rs +688 -0
  41. data/vendor/crates/spikard-core/src/{validation.rs → validation/mod.rs} +26 -268
  42. data/vendor/crates/spikard-http/Cargo.toml +12 -16
  43. data/vendor/crates/spikard-http/examples/sse-notifications.rs +148 -0
  44. data/vendor/crates/spikard-http/examples/websocket-chat.rs +92 -0
  45. data/vendor/crates/spikard-http/src/auth.rs +65 -16
  46. data/vendor/crates/spikard-http/src/background.rs +1614 -3
  47. data/vendor/crates/spikard-http/src/cors.rs +515 -0
  48. data/vendor/crates/spikard-http/src/debug.rs +65 -0
  49. data/vendor/crates/spikard-http/src/di_handler.rs +1322 -77
  50. data/vendor/crates/spikard-http/src/handler_response.rs +711 -0
  51. data/vendor/crates/spikard-http/src/handler_trait.rs +607 -5
  52. data/vendor/crates/spikard-http/src/handler_trait_tests.rs +6 -0
  53. data/vendor/crates/spikard-http/src/lib.rs +33 -28
  54. data/vendor/crates/spikard-http/src/lifecycle/adapter.rs +81 -0
  55. data/vendor/crates/spikard-http/src/lifecycle.rs +765 -0
  56. data/vendor/crates/spikard-http/src/middleware/mod.rs +372 -117
  57. data/vendor/crates/spikard-http/src/middleware/multipart.rs +836 -10
  58. data/vendor/crates/spikard-http/src/middleware/urlencoded.rs +409 -43
  59. data/vendor/crates/spikard-http/src/middleware/validation.rs +513 -65
  60. data/vendor/crates/spikard-http/src/openapi/parameter_extraction.rs +345 -0
  61. data/vendor/crates/spikard-http/src/openapi/schema_conversion.rs +1055 -0
  62. data/vendor/crates/spikard-http/src/openapi/spec_generation.rs +473 -3
  63. data/vendor/crates/spikard-http/src/query_parser.rs +455 -31
  64. data/vendor/crates/spikard-http/src/response.rs +321 -0
  65. data/vendor/crates/spikard-http/src/server/handler.rs +1572 -9
  66. data/vendor/crates/spikard-http/src/server/lifecycle_execution.rs +136 -0
  67. data/vendor/crates/spikard-http/src/server/mod.rs +875 -178
  68. data/vendor/crates/spikard-http/src/server/request_extraction.rs +674 -23
  69. data/vendor/crates/spikard-http/src/server/routing_factory.rs +599 -0
  70. data/vendor/crates/spikard-http/src/sse.rs +983 -21
  71. data/vendor/crates/spikard-http/src/testing/form.rs +38 -0
  72. data/vendor/crates/spikard-http/src/testing/test_client.rs +0 -2
  73. data/vendor/crates/spikard-http/src/testing.rs +7 -7
  74. data/vendor/crates/spikard-http/src/websocket.rs +1055 -4
  75. data/vendor/crates/spikard-http/tests/background_behavior.rs +832 -0
  76. data/vendor/crates/spikard-http/tests/common/handlers.rs +309 -0
  77. data/vendor/crates/spikard-http/tests/common/mod.rs +26 -0
  78. data/vendor/crates/spikard-http/tests/di_integration.rs +192 -0
  79. data/vendor/crates/spikard-http/tests/doc_snippets.rs +5 -0
  80. data/vendor/crates/spikard-http/tests/lifecycle_execution.rs +1093 -0
  81. data/vendor/crates/spikard-http/tests/multipart_behavior.rs +656 -0
  82. data/vendor/crates/spikard-http/tests/server_config_builder.rs +314 -0
  83. data/vendor/crates/spikard-http/tests/sse_behavior.rs +620 -0
  84. data/vendor/crates/spikard-http/tests/websocket_behavior.rs +663 -0
  85. data/vendor/crates/spikard-rb/Cargo.toml +10 -4
  86. data/vendor/crates/spikard-rb/build.rs +196 -5
  87. data/vendor/crates/spikard-rb/src/config/mod.rs +5 -0
  88. data/vendor/crates/spikard-rb/src/{config.rs → config/server_config.rs} +100 -109
  89. data/vendor/crates/spikard-rb/src/conversion.rs +121 -20
  90. data/vendor/crates/spikard-rb/src/di/builder.rs +100 -0
  91. data/vendor/crates/spikard-rb/src/{di.rs → di/mod.rs} +12 -46
  92. data/vendor/crates/spikard-rb/src/handler.rs +100 -107
  93. data/vendor/crates/spikard-rb/src/integration/mod.rs +3 -0
  94. data/vendor/crates/spikard-rb/src/lib.rs +467 -1428
  95. data/vendor/crates/spikard-rb/src/lifecycle.rs +1 -0
  96. data/vendor/crates/spikard-rb/src/metadata/mod.rs +5 -0
  97. data/vendor/crates/spikard-rb/src/metadata/route_extraction.rs +447 -0
  98. data/vendor/crates/spikard-rb/src/runtime/mod.rs +5 -0
  99. data/vendor/crates/spikard-rb/src/runtime/server_runner.rs +324 -0
  100. data/vendor/crates/spikard-rb/src/server.rs +47 -22
  101. data/vendor/crates/spikard-rb/src/{test_client.rs → testing/client.rs} +187 -40
  102. data/vendor/crates/spikard-rb/src/testing/mod.rs +7 -0
  103. data/vendor/crates/spikard-rb/src/testing/websocket.rs +635 -0
  104. data/vendor/crates/spikard-rb/src/websocket.rs +178 -37
  105. metadata +46 -13
  106. data/vendor/crates/spikard-http/src/parameters.rs +0 -1
  107. data/vendor/crates/spikard-http/src/problem.rs +0 -1
  108. data/vendor/crates/spikard-http/src/router.rs +0 -1
  109. data/vendor/crates/spikard-http/src/schema_registry.rs +0 -1
  110. data/vendor/crates/spikard-http/src/type_hints.rs +0 -1
  111. data/vendor/crates/spikard-http/src/validation.rs +0 -1
  112. data/vendor/crates/spikard-rb/src/test_websocket.rs +0 -221
  113. /data/vendor/crates/spikard-rb/src/{test_sse.rs → testing/sse.rs} +0 -0
@@ -88,6 +88,9 @@ pub struct RouteMetadata {
88
88
  #[cfg(feature = "di")]
89
89
  #[serde(skip_serializing_if = "Option::is_none")]
90
90
  pub handler_dependencies: Option<Vec<String>>,
91
+ /// JSON-RPC method metadata (if this route is exposed as a JSON-RPC method)
92
+ #[serde(skip_serializing_if = "Option::is_none")]
93
+ pub jsonrpc_method: Option<Value>,
91
94
  }
92
95
 
93
96
  /// Compression configuration shared across runtimes
@@ -151,3 +154,262 @@ impl Default for RateLimitConfig {
151
154
  }
152
155
  }
153
156
  }
157
+
158
+ #[cfg(test)]
159
+ mod tests {
160
+ use super::*;
161
+ use std::str::FromStr;
162
+
163
+ #[test]
164
+ fn test_method_as_str_get() {
165
+ assert_eq!(Method::Get.as_str(), "GET");
166
+ }
167
+
168
+ #[test]
169
+ fn test_method_as_str_post() {
170
+ assert_eq!(Method::Post.as_str(), "POST");
171
+ }
172
+
173
+ #[test]
174
+ fn test_method_as_str_put() {
175
+ assert_eq!(Method::Put.as_str(), "PUT");
176
+ }
177
+
178
+ #[test]
179
+ fn test_method_as_str_patch() {
180
+ assert_eq!(Method::Patch.as_str(), "PATCH");
181
+ }
182
+
183
+ #[test]
184
+ fn test_method_as_str_delete() {
185
+ assert_eq!(Method::Delete.as_str(), "DELETE");
186
+ }
187
+
188
+ #[test]
189
+ fn test_method_as_str_head() {
190
+ assert_eq!(Method::Head.as_str(), "HEAD");
191
+ }
192
+
193
+ #[test]
194
+ fn test_method_as_str_options() {
195
+ assert_eq!(Method::Options.as_str(), "OPTIONS");
196
+ }
197
+
198
+ #[test]
199
+ fn test_method_as_str_trace() {
200
+ assert_eq!(Method::Trace.as_str(), "TRACE");
201
+ }
202
+
203
+ #[test]
204
+ fn test_method_display_get() {
205
+ assert_eq!(Method::Get.to_string(), "GET");
206
+ }
207
+
208
+ #[test]
209
+ fn test_method_display_post() {
210
+ assert_eq!(Method::Post.to_string(), "POST");
211
+ }
212
+
213
+ #[test]
214
+ fn test_method_display_put() {
215
+ assert_eq!(Method::Put.to_string(), "PUT");
216
+ }
217
+
218
+ #[test]
219
+ fn test_method_display_patch() {
220
+ assert_eq!(Method::Patch.to_string(), "PATCH");
221
+ }
222
+
223
+ #[test]
224
+ fn test_method_display_delete() {
225
+ assert_eq!(Method::Delete.to_string(), "DELETE");
226
+ }
227
+
228
+ #[test]
229
+ fn test_method_display_head() {
230
+ assert_eq!(Method::Head.to_string(), "HEAD");
231
+ }
232
+
233
+ #[test]
234
+ fn test_method_display_options() {
235
+ assert_eq!(Method::Options.to_string(), "OPTIONS");
236
+ }
237
+
238
+ #[test]
239
+ fn test_method_display_trace() {
240
+ assert_eq!(Method::Trace.to_string(), "TRACE");
241
+ }
242
+
243
+ #[test]
244
+ fn test_from_str_get() {
245
+ assert_eq!(Method::from_str("GET"), Ok(Method::Get));
246
+ }
247
+
248
+ #[test]
249
+ fn test_from_str_post() {
250
+ assert_eq!(Method::from_str("POST"), Ok(Method::Post));
251
+ }
252
+
253
+ #[test]
254
+ fn test_from_str_put() {
255
+ assert_eq!(Method::from_str("PUT"), Ok(Method::Put));
256
+ }
257
+
258
+ #[test]
259
+ fn test_from_str_patch() {
260
+ assert_eq!(Method::from_str("PATCH"), Ok(Method::Patch));
261
+ }
262
+
263
+ #[test]
264
+ fn test_from_str_delete() {
265
+ assert_eq!(Method::from_str("DELETE"), Ok(Method::Delete));
266
+ }
267
+
268
+ #[test]
269
+ fn test_from_str_head() {
270
+ assert_eq!(Method::from_str("HEAD"), Ok(Method::Head));
271
+ }
272
+
273
+ #[test]
274
+ fn test_from_str_options() {
275
+ assert_eq!(Method::from_str("OPTIONS"), Ok(Method::Options));
276
+ }
277
+
278
+ #[test]
279
+ fn test_from_str_trace() {
280
+ assert_eq!(Method::from_str("TRACE"), Ok(Method::Trace));
281
+ }
282
+
283
+ #[test]
284
+ fn test_from_str_lowercase() {
285
+ assert_eq!(Method::from_str("get"), Ok(Method::Get));
286
+ }
287
+
288
+ #[test]
289
+ fn test_from_str_mixed_case() {
290
+ assert_eq!(Method::from_str("PoSt"), Ok(Method::Post));
291
+ }
292
+
293
+ #[test]
294
+ fn test_from_str_invalid_method() {
295
+ let result = Method::from_str("INVALID");
296
+ assert!(result.is_err());
297
+ assert_eq!(result.unwrap_err(), "Unknown HTTP method: INVALID");
298
+ }
299
+
300
+ #[test]
301
+ fn test_from_str_empty_string() {
302
+ let result = Method::from_str("");
303
+ assert!(result.is_err());
304
+ assert_eq!(result.unwrap_err(), "Unknown HTTP method: ");
305
+ }
306
+
307
+ #[test]
308
+ fn test_compression_config_default() {
309
+ let config = CompressionConfig::default();
310
+ assert!(config.gzip);
311
+ assert!(config.brotli);
312
+ assert_eq!(config.min_size, 1024);
313
+ assert_eq!(config.quality, 6);
314
+ }
315
+
316
+ #[test]
317
+ fn test_default_true() {
318
+ assert!(default_true());
319
+ }
320
+
321
+ #[test]
322
+ fn test_default_compression_min_size() {
323
+ assert_eq!(default_compression_min_size(), 1024);
324
+ }
325
+
326
+ #[test]
327
+ fn test_default_compression_quality() {
328
+ assert_eq!(default_compression_quality(), 6);
329
+ }
330
+
331
+ #[test]
332
+ fn test_rate_limit_config_default() {
333
+ let config = RateLimitConfig::default();
334
+ assert_eq!(config.per_second, 100);
335
+ assert_eq!(config.burst, 200);
336
+ assert!(config.ip_based);
337
+ }
338
+
339
+ #[test]
340
+ fn test_method_equality() {
341
+ assert_eq!(Method::Get, Method::Get);
342
+ assert_ne!(Method::Get, Method::Post);
343
+ }
344
+
345
+ #[test]
346
+ fn test_method_clone() {
347
+ let method = Method::Post;
348
+ let cloned = method.clone();
349
+ assert_eq!(method, cloned);
350
+ }
351
+
352
+ #[test]
353
+ fn test_compression_config_custom_values() {
354
+ let config = CompressionConfig {
355
+ gzip: false,
356
+ brotli: false,
357
+ min_size: 2048,
358
+ quality: 11,
359
+ };
360
+ assert!(!config.gzip);
361
+ assert!(!config.brotli);
362
+ assert_eq!(config.min_size, 2048);
363
+ assert_eq!(config.quality, 11);
364
+ }
365
+
366
+ #[test]
367
+ fn test_rate_limit_config_custom_values() {
368
+ let config = RateLimitConfig {
369
+ per_second: 50,
370
+ burst: 100,
371
+ ip_based: false,
372
+ };
373
+ assert_eq!(config.per_second, 50);
374
+ assert_eq!(config.burst, 100);
375
+ assert!(!config.ip_based);
376
+ }
377
+
378
+ #[test]
379
+ fn test_cors_config_construction() {
380
+ let cors = CorsConfig {
381
+ allowed_origins: vec!["http://localhost:3000".to_string()],
382
+ allowed_methods: vec!["GET".to_string(), "POST".to_string()],
383
+ allowed_headers: vec![],
384
+ expose_headers: None,
385
+ max_age: None,
386
+ allow_credentials: None,
387
+ };
388
+ assert_eq!(cors.allowed_origins.len(), 1);
389
+ assert_eq!(cors.allowed_methods.len(), 2);
390
+ assert_eq!(cors.allowed_headers.len(), 0);
391
+ }
392
+
393
+ #[test]
394
+ fn test_route_metadata_construction() {
395
+ let metadata = RouteMetadata {
396
+ method: "GET".to_string(),
397
+ path: "/api/users".to_string(),
398
+ handler_name: "get_users".to_string(),
399
+ request_schema: None,
400
+ response_schema: None,
401
+ parameter_schema: None,
402
+ file_params: None,
403
+ is_async: true,
404
+ cors: None,
405
+ body_param_name: None,
406
+ #[cfg(feature = "di")]
407
+ handler_dependencies: None,
408
+ jsonrpc_method: None,
409
+ };
410
+ assert_eq!(metadata.method, "GET");
411
+ assert_eq!(metadata.path, "/api/users");
412
+ assert_eq!(metadata.handler_name, "get_users");
413
+ assert!(metadata.is_async);
414
+ }
415
+ }
@@ -24,6 +24,6 @@ pub use lifecycle::{HookResult, LifecycleHook, LifecycleHooks, LifecycleHooksBui
24
24
  pub use parameters::ParameterValidator;
25
25
  pub use problem::ProblemDetails;
26
26
  pub use request_data::RequestData;
27
- pub use router::{Route, RouteHandler, Router};
27
+ pub use router::{JsonRpcMethodInfo, Route, RouteHandler, Router};
28
28
  pub use schema_registry::SchemaRegistry;
29
29
  pub use validation::{SchemaValidator, ValidationError, ValidationErrorDetail};