spikard 0.3.3 → 0.3.5

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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/ext/spikard_rb/Cargo.toml +1 -1
  3. data/lib/spikard/version.rb +1 -1
  4. data/vendor/crates/spikard-core/Cargo.toml +13 -13
  5. data/vendor/crates/spikard-http/Cargo.toml +31 -21
  6. data/vendor/crates/spikard-rb/Cargo.toml +1 -1
  7. metadata +1 -79
  8. data/vendor/spikard-core/Cargo.toml +0 -40
  9. data/vendor/spikard-core/src/bindings/mod.rs +0 -3
  10. data/vendor/spikard-core/src/bindings/response.rs +0 -133
  11. data/vendor/spikard-core/src/debug.rs +0 -63
  12. data/vendor/spikard-core/src/di/container.rs +0 -726
  13. data/vendor/spikard-core/src/di/dependency.rs +0 -273
  14. data/vendor/spikard-core/src/di/error.rs +0 -118
  15. data/vendor/spikard-core/src/di/factory.rs +0 -538
  16. data/vendor/spikard-core/src/di/graph.rs +0 -545
  17. data/vendor/spikard-core/src/di/mod.rs +0 -192
  18. data/vendor/spikard-core/src/di/resolved.rs +0 -411
  19. data/vendor/spikard-core/src/di/value.rs +0 -283
  20. data/vendor/spikard-core/src/http.rs +0 -153
  21. data/vendor/spikard-core/src/lib.rs +0 -28
  22. data/vendor/spikard-core/src/lifecycle.rs +0 -422
  23. data/vendor/spikard-core/src/parameters.rs +0 -719
  24. data/vendor/spikard-core/src/problem.rs +0 -310
  25. data/vendor/spikard-core/src/request_data.rs +0 -189
  26. data/vendor/spikard-core/src/router.rs +0 -249
  27. data/vendor/spikard-core/src/schema_registry.rs +0 -183
  28. data/vendor/spikard-core/src/type_hints.rs +0 -304
  29. data/vendor/spikard-core/src/validation.rs +0 -699
  30. data/vendor/spikard-http/Cargo.toml +0 -58
  31. data/vendor/spikard-http/src/auth.rs +0 -247
  32. data/vendor/spikard-http/src/background.rs +0 -249
  33. data/vendor/spikard-http/src/bindings/mod.rs +0 -3
  34. data/vendor/spikard-http/src/bindings/response.rs +0 -1
  35. data/vendor/spikard-http/src/body_metadata.rs +0 -8
  36. data/vendor/spikard-http/src/cors.rs +0 -490
  37. data/vendor/spikard-http/src/debug.rs +0 -63
  38. data/vendor/spikard-http/src/di_handler.rs +0 -423
  39. data/vendor/spikard-http/src/handler_response.rs +0 -190
  40. data/vendor/spikard-http/src/handler_trait.rs +0 -228
  41. data/vendor/spikard-http/src/handler_trait_tests.rs +0 -284
  42. data/vendor/spikard-http/src/lib.rs +0 -529
  43. data/vendor/spikard-http/src/lifecycle/adapter.rs +0 -149
  44. data/vendor/spikard-http/src/lifecycle.rs +0 -428
  45. data/vendor/spikard-http/src/middleware/mod.rs +0 -285
  46. data/vendor/spikard-http/src/middleware/multipart.rs +0 -86
  47. data/vendor/spikard-http/src/middleware/urlencoded.rs +0 -147
  48. data/vendor/spikard-http/src/middleware/validation.rs +0 -287
  49. data/vendor/spikard-http/src/openapi/mod.rs +0 -309
  50. data/vendor/spikard-http/src/openapi/parameter_extraction.rs +0 -190
  51. data/vendor/spikard-http/src/openapi/schema_conversion.rs +0 -308
  52. data/vendor/spikard-http/src/openapi/spec_generation.rs +0 -195
  53. data/vendor/spikard-http/src/parameters.rs +0 -1
  54. data/vendor/spikard-http/src/problem.rs +0 -1
  55. data/vendor/spikard-http/src/query_parser.rs +0 -369
  56. data/vendor/spikard-http/src/response.rs +0 -399
  57. data/vendor/spikard-http/src/router.rs +0 -1
  58. data/vendor/spikard-http/src/schema_registry.rs +0 -1
  59. data/vendor/spikard-http/src/server/handler.rs +0 -80
  60. data/vendor/spikard-http/src/server/lifecycle_execution.rs +0 -98
  61. data/vendor/spikard-http/src/server/mod.rs +0 -805
  62. data/vendor/spikard-http/src/server/request_extraction.rs +0 -119
  63. data/vendor/spikard-http/src/sse.rs +0 -447
  64. data/vendor/spikard-http/src/testing/form.rs +0 -14
  65. data/vendor/spikard-http/src/testing/multipart.rs +0 -60
  66. data/vendor/spikard-http/src/testing/test_client.rs +0 -285
  67. data/vendor/spikard-http/src/testing.rs +0 -377
  68. data/vendor/spikard-http/src/type_hints.rs +0 -1
  69. data/vendor/spikard-http/src/validation.rs +0 -1
  70. data/vendor/spikard-http/src/websocket.rs +0 -324
  71. data/vendor/spikard-rb/Cargo.toml +0 -42
  72. data/vendor/spikard-rb/build.rs +0 -8
  73. data/vendor/spikard-rb/src/background.rs +0 -63
  74. data/vendor/spikard-rb/src/config.rs +0 -294
  75. data/vendor/spikard-rb/src/conversion.rs +0 -392
  76. data/vendor/spikard-rb/src/di.rs +0 -409
  77. data/vendor/spikard-rb/src/handler.rs +0 -534
  78. data/vendor/spikard-rb/src/lib.rs +0 -2020
  79. data/vendor/spikard-rb/src/lifecycle.rs +0 -267
  80. data/vendor/spikard-rb/src/server.rs +0 -283
  81. data/vendor/spikard-rb/src/sse.rs +0 -231
  82. data/vendor/spikard-rb/src/test_client.rs +0 -404
  83. data/vendor/spikard-rb/src/test_sse.rs +0 -143
  84. data/vendor/spikard-rb/src/test_websocket.rs +0 -221
  85. data/vendor/spikard-rb/src/websocket.rs +0 -233
@@ -1,190 +0,0 @@
1
- //! Parameter extraction from routes and schemas for OpenAPI generation
2
-
3
- use utoipa::openapi::RefOr;
4
- use utoipa::openapi::path::Parameter;
5
- use utoipa::openapi::path::{ParameterBuilder, ParameterIn};
6
-
7
- /// Extract parameters from JSON Schema parameter_schema
8
- pub fn extract_parameters_from_schema(
9
- param_schema: &serde_json::Value,
10
- route_path: &str,
11
- ) -> Result<Vec<RefOr<Parameter>>, String> {
12
- let mut parameters = Vec::new();
13
-
14
- let path_params = extract_path_param_names(route_path);
15
-
16
- let properties = param_schema
17
- .get("properties")
18
- .and_then(|p| p.as_object())
19
- .ok_or_else(|| "Parameter schema missing 'properties' field".to_string())?;
20
-
21
- let required = param_schema
22
- .get("required")
23
- .and_then(|r| r.as_array())
24
- .map(|arr| arr.iter().filter_map(|v| v.as_str()).collect::<Vec<_>>())
25
- .unwrap_or_default();
26
-
27
- for (name, schema) in properties {
28
- let is_required = required.contains(&name.as_str());
29
- let param_in = if path_params.contains(&name.as_str()) {
30
- ParameterIn::Path
31
- } else {
32
- ParameterIn::Query
33
- };
34
-
35
- let openapi_schema = crate::openapi::schema_conversion::json_value_to_schema(schema)?;
36
-
37
- let is_path_param = matches!(param_in, ParameterIn::Path);
38
-
39
- let param = ParameterBuilder::new()
40
- .name(name)
41
- .parameter_in(param_in)
42
- .required(if is_path_param || is_required {
43
- utoipa::openapi::Required::True
44
- } else {
45
- utoipa::openapi::Required::False
46
- })
47
- .schema(Some(openapi_schema))
48
- .build();
49
-
50
- parameters.push(RefOr::T(param));
51
- }
52
-
53
- Ok(parameters)
54
- }
55
-
56
- /// Extract path parameter names from route pattern (e.g., "/users/{id}" -> ["id"])
57
- pub fn extract_path_param_names(route: &str) -> Vec<&str> {
58
- route
59
- .split('/')
60
- .filter_map(|segment| {
61
- if segment.starts_with('{') && segment.ends_with('}') {
62
- Some(&segment[1..segment.len() - 1])
63
- } else {
64
- None
65
- }
66
- })
67
- .collect()
68
- }
69
-
70
- #[cfg(test)]
71
- mod tests {
72
- use super::*;
73
- use serde_json::json;
74
-
75
- #[test]
76
- fn test_extract_path_param_names() {
77
- let names = extract_path_param_names("/users/{id}/posts/{post_id}");
78
- assert_eq!(names, vec!["id", "post_id"]);
79
-
80
- let names = extract_path_param_names("/users");
81
- assert_eq!(names, Vec::<&str>::new());
82
-
83
- let names = extract_path_param_names("/users/{user_id}");
84
- assert_eq!(names, vec!["user_id"]);
85
- }
86
-
87
- #[test]
88
- fn test_extract_parameters_from_schema_path_params() {
89
- let param_schema = json!({
90
- "type": "object",
91
- "properties": {
92
- "user_id": { "type": "integer" },
93
- "post_id": { "type": "integer" }
94
- },
95
- "required": ["user_id", "post_id"]
96
- });
97
-
98
- let result = extract_parameters_from_schema(&param_schema, "/users/{user_id}/posts/{post_id}");
99
- assert!(result.is_ok());
100
-
101
- let params = result.unwrap();
102
- assert_eq!(params.len(), 2);
103
-
104
- for param in params {
105
- if let RefOr::T(p) = param {
106
- assert!(matches!(p.parameter_in, ParameterIn::Path));
107
- assert!(matches!(p.required, utoipa::openapi::Required::True));
108
- }
109
- }
110
- }
111
-
112
- #[test]
113
- fn test_extract_parameters_from_schema_query_params() {
114
- let param_schema = json!({
115
- "type": "object",
116
- "properties": {
117
- "page": { "type": "integer" },
118
- "limit": { "type": "integer" },
119
- "search": { "type": "string" }
120
- },
121
- "required": ["page"]
122
- });
123
-
124
- let result = extract_parameters_from_schema(&param_schema, "/users");
125
- assert!(result.is_ok());
126
-
127
- let params = result.unwrap();
128
- assert_eq!(params.len(), 3);
129
-
130
- for param in &params {
131
- if let RefOr::T(p) = param {
132
- assert!(matches!(p.parameter_in, ParameterIn::Query));
133
- }
134
- }
135
-
136
- for param in params {
137
- if let RefOr::T(p) = param {
138
- if p.name == "page" {
139
- assert!(matches!(p.required, utoipa::openapi::Required::True));
140
- } else {
141
- assert!(matches!(p.required, utoipa::openapi::Required::False));
142
- }
143
- }
144
- }
145
- }
146
-
147
- #[test]
148
- fn test_extract_parameters_from_schema_mixed() {
149
- let param_schema = json!({
150
- "type": "object",
151
- "properties": {
152
- "user_id": { "type": "integer" },
153
- "page": { "type": "integer" },
154
- "limit": { "type": "integer" }
155
- },
156
- "required": ["user_id"]
157
- });
158
-
159
- let result = extract_parameters_from_schema(&param_schema, "/users/{user_id}");
160
- assert!(result.is_ok());
161
-
162
- let params = result.unwrap();
163
- assert_eq!(params.len(), 3);
164
-
165
- for param in params {
166
- if let RefOr::T(p) = param {
167
- if p.name == "user_id" {
168
- assert!(matches!(p.parameter_in, ParameterIn::Path));
169
- assert!(matches!(p.required, utoipa::openapi::Required::True));
170
- } else {
171
- assert!(matches!(p.parameter_in, ParameterIn::Query));
172
- assert!(matches!(p.required, utoipa::openapi::Required::False));
173
- }
174
- }
175
- }
176
- }
177
-
178
- #[test]
179
- fn test_extract_parameters_error_on_missing_properties() {
180
- let param_schema = json!({
181
- "type": "object"
182
- });
183
-
184
- let result = extract_parameters_from_schema(&param_schema, "/users");
185
- assert!(result.is_err());
186
- if let Err(err) = result {
187
- assert!(err.contains("properties"));
188
- }
189
- }
190
- }
@@ -1,308 +0,0 @@
1
- //! JSON Schema to OpenAPI schema conversion utilities
2
-
3
- use utoipa::openapi::{RefOr, Schema};
4
-
5
- /// Convert serde_json::Value (JSON Schema) to utoipa Schema
6
- /// OpenAPI 3.1.0 is fully compatible with JSON Schema Draft 2020-12
7
- pub fn json_value_to_schema(value: &serde_json::Value) -> Result<RefOr<Schema>, String> {
8
- if let Some(type_str) = value.get("type").and_then(|t| t.as_str()) {
9
- match type_str {
10
- "object" => {
11
- let mut object_schema = utoipa::openapi::ObjectBuilder::new();
12
-
13
- if let Some(properties) = value.get("properties").and_then(|p| p.as_object()) {
14
- for (prop_name, prop_schema) in properties {
15
- let prop_openapi_schema = json_value_to_schema(prop_schema)?;
16
- object_schema = object_schema.property(prop_name, prop_openapi_schema);
17
- }
18
- }
19
-
20
- if let Some(required) = value.get("required").and_then(|r| r.as_array()) {
21
- for field in required {
22
- if let Some(field_name) = field.as_str() {
23
- object_schema = object_schema.required(field_name);
24
- }
25
- }
26
- }
27
-
28
- Ok(RefOr::T(Schema::Object(object_schema.build())))
29
- }
30
- "array" => {
31
- let mut array_schema = utoipa::openapi::ArrayBuilder::new();
32
-
33
- if let Some(items) = value.get("items") {
34
- let items_schema = json_value_to_schema(items)?;
35
- array_schema = array_schema.items(items_schema);
36
- }
37
-
38
- Ok(RefOr::T(Schema::Array(array_schema.build())))
39
- }
40
- "string" => {
41
- let mut schema_type = utoipa::openapi::schema::Type::String;
42
-
43
- if let Some(format) = value.get("format").and_then(|f| f.as_str()) {
44
- match format {
45
- "date-time" => schema_type = utoipa::openapi::schema::Type::String,
46
- "date" => schema_type = utoipa::openapi::schema::Type::String,
47
- "email" => schema_type = utoipa::openapi::schema::Type::String,
48
- "uri" => schema_type = utoipa::openapi::schema::Type::String,
49
- _ => {}
50
- }
51
- }
52
-
53
- Ok(RefOr::T(Schema::Object(
54
- utoipa::openapi::ObjectBuilder::new().schema_type(schema_type).build(),
55
- )))
56
- }
57
- "integer" => Ok(RefOr::T(Schema::Object(
58
- utoipa::openapi::ObjectBuilder::new()
59
- .schema_type(utoipa::openapi::schema::Type::Integer)
60
- .build(),
61
- ))),
62
- "number" => Ok(RefOr::T(Schema::Object(
63
- utoipa::openapi::ObjectBuilder::new()
64
- .schema_type(utoipa::openapi::schema::Type::Number)
65
- .build(),
66
- ))),
67
- "boolean" => Ok(RefOr::T(Schema::Object(
68
- utoipa::openapi::ObjectBuilder::new()
69
- .schema_type(utoipa::openapi::schema::Type::Boolean)
70
- .build(),
71
- ))),
72
- _ => Err(format!("Unsupported schema type: {}", type_str)),
73
- }
74
- } else {
75
- Ok(RefOr::T(Schema::Object(utoipa::openapi::ObjectBuilder::new().build())))
76
- }
77
- }
78
-
79
- /// Convert JSON Schema to OpenAPI RequestBody
80
- pub fn json_schema_to_request_body(
81
- schema: &serde_json::Value,
82
- ) -> Result<utoipa::openapi::request_body::RequestBody, String> {
83
- use utoipa::openapi::content::ContentBuilder;
84
-
85
- let openapi_schema = json_value_to_schema(schema)?;
86
-
87
- let content = ContentBuilder::new().schema(Some(openapi_schema)).build();
88
-
89
- let mut request_body = utoipa::openapi::request_body::RequestBody::new();
90
- request_body.content.insert("application/json".to_string(), content);
91
-
92
- request_body.required = Some(utoipa::openapi::Required::True);
93
-
94
- Ok(request_body)
95
- }
96
-
97
- /// Convert JSON Schema to OpenAPI Response
98
- pub fn json_schema_to_response(schema: &serde_json::Value) -> Result<utoipa::openapi::Response, String> {
99
- use utoipa::openapi::content::ContentBuilder;
100
-
101
- let openapi_schema = json_value_to_schema(schema)?;
102
-
103
- let content = ContentBuilder::new().schema(Some(openapi_schema)).build();
104
-
105
- let mut response = utoipa::openapi::Response::new("Successful response");
106
- response.content.insert("application/json".to_string(), content);
107
-
108
- Ok(response)
109
- }
110
-
111
- #[cfg(test)]
112
- mod tests {
113
- use super::*;
114
-
115
- #[test]
116
- fn test_json_value_to_schema_string() {
117
- let schema_json = serde_json::json!({
118
- "type": "string"
119
- });
120
-
121
- let result = json_value_to_schema(&schema_json);
122
- assert!(result.is_ok());
123
- }
124
-
125
- #[test]
126
- fn test_json_value_to_schema_integer() {
127
- let schema_json = serde_json::json!({
128
- "type": "integer"
129
- });
130
-
131
- let result = json_value_to_schema(&schema_json);
132
- assert!(result.is_ok());
133
- }
134
-
135
- #[test]
136
- fn test_json_value_to_schema_number() {
137
- let schema_json = serde_json::json!({
138
- "type": "number"
139
- });
140
-
141
- let result = json_value_to_schema(&schema_json);
142
- assert!(result.is_ok());
143
- }
144
-
145
- #[test]
146
- fn test_json_value_to_schema_boolean() {
147
- let schema_json = serde_json::json!({
148
- "type": "boolean"
149
- });
150
-
151
- let result = json_value_to_schema(&schema_json);
152
- assert!(result.is_ok());
153
- }
154
-
155
- #[test]
156
- fn test_json_value_to_schema_object() {
157
- let schema_json = serde_json::json!({
158
- "type": "object",
159
- "properties": {
160
- "name": { "type": "string" },
161
- "age": { "type": "integer" }
162
- },
163
- "required": ["name"]
164
- });
165
-
166
- let result = json_value_to_schema(&schema_json);
167
- assert!(result.is_ok());
168
-
169
- if let Ok(RefOr::T(Schema::Object(obj))) = result {
170
- assert!(obj.properties.contains_key("name"));
171
- assert!(obj.properties.contains_key("age"));
172
- assert!(obj.required.contains(&"name".to_string()));
173
- } else {
174
- panic!("Expected Object schema");
175
- }
176
- }
177
-
178
- #[test]
179
- fn test_json_value_to_schema_array() {
180
- let schema_json = serde_json::json!({
181
- "type": "array",
182
- "items": {
183
- "type": "string"
184
- }
185
- });
186
-
187
- let result = json_value_to_schema(&schema_json);
188
- assert!(result.is_ok());
189
-
190
- if let Ok(RefOr::T(Schema::Array(_))) = result {
191
- } else {
192
- panic!("Expected Array schema");
193
- }
194
- }
195
-
196
- #[test]
197
- fn test_json_value_to_schema_nested_object() {
198
- let schema_json = serde_json::json!({
199
- "type": "object",
200
- "properties": {
201
- "user": {
202
- "type": "object",
203
- "properties": {
204
- "name": { "type": "string" },
205
- "email": { "type": "string" }
206
- }
207
- }
208
- }
209
- });
210
-
211
- let result = json_value_to_schema(&schema_json);
212
- assert!(result.is_ok());
213
- }
214
-
215
- #[test]
216
- fn test_json_schema_to_request_body() {
217
- let schema_json = serde_json::json!({
218
- "type": "object",
219
- "properties": {
220
- "title": { "type": "string" },
221
- "count": { "type": "integer" }
222
- },
223
- "required": ["title"]
224
- });
225
-
226
- let result = json_schema_to_request_body(&schema_json);
227
- assert!(result.is_ok());
228
-
229
- let request_body = result.unwrap();
230
- assert!(request_body.content.contains_key("application/json"));
231
- assert!(matches!(request_body.required, Some(utoipa::openapi::Required::True)));
232
- }
233
-
234
- #[test]
235
- fn test_json_schema_to_request_body_array() {
236
- let schema_json = serde_json::json!({
237
- "type": "array",
238
- "items": {
239
- "type": "object",
240
- "properties": {
241
- "id": { "type": "integer" }
242
- }
243
- }
244
- });
245
-
246
- let result = json_schema_to_request_body(&schema_json);
247
- assert!(result.is_ok());
248
-
249
- let request_body = result.unwrap();
250
- assert!(request_body.content.contains_key("application/json"));
251
- }
252
-
253
- #[test]
254
- fn test_json_schema_to_response() {
255
- let schema_json = serde_json::json!({
256
- "type": "object",
257
- "properties": {
258
- "id": { "type": "integer" },
259
- "name": { "type": "string" }
260
- }
261
- });
262
-
263
- let result = json_schema_to_response(&schema_json);
264
- assert!(result.is_ok());
265
-
266
- let response = result.unwrap();
267
- assert!(response.content.contains_key("application/json"));
268
- assert_eq!(response.description, "Successful response");
269
- }
270
-
271
- #[test]
272
- fn test_json_schema_to_response_array() {
273
- let schema_json = serde_json::json!({
274
- "type": "array",
275
- "items": {
276
- "type": "string"
277
- }
278
- });
279
-
280
- let result = json_schema_to_response(&schema_json);
281
- assert!(result.is_ok());
282
-
283
- let response = result.unwrap();
284
- assert!(response.content.contains_key("application/json"));
285
- }
286
-
287
- #[test]
288
- fn test_json_value_to_schema_string_with_format() {
289
- let schema_json = serde_json::json!({
290
- "type": "string",
291
- "format": "date-time"
292
- });
293
-
294
- let result = json_value_to_schema(&schema_json);
295
- assert!(result.is_ok());
296
- }
297
-
298
- #[test]
299
- fn test_json_schema_to_request_body_empty_object() {
300
- let schema_json = serde_json::json!({
301
- "type": "object",
302
- "properties": {}
303
- });
304
-
305
- let result = json_schema_to_request_body(&schema_json);
306
- assert!(result.is_ok());
307
- }
308
- }
@@ -1,195 +0,0 @@
1
- //! OpenAPI specification generation and assembly
2
-
3
- use crate::RouteMetadata;
4
- use utoipa::openapi::HttpMethod;
5
- use utoipa::openapi::security::SecurityScheme;
6
- use utoipa::openapi::{Components, Info, OpenApi, OpenApiBuilder, PathItem, Paths, RefOr, Response, Responses};
7
-
8
- /// Convert route to OpenAPI PathItem
9
- fn route_to_path_item(route: &RouteMetadata) -> Result<PathItem, String> {
10
- let operation = route_to_operation(route)?;
11
-
12
- let http_method = match route.method.to_uppercase().as_str() {
13
- "GET" => HttpMethod::Get,
14
- "POST" => HttpMethod::Post,
15
- "PUT" => HttpMethod::Put,
16
- "DELETE" => HttpMethod::Delete,
17
- "PATCH" => HttpMethod::Patch,
18
- "HEAD" => HttpMethod::Head,
19
- "OPTIONS" => HttpMethod::Options,
20
- _ => return Err(format!("Unsupported HTTP method: {}", route.method)),
21
- };
22
-
23
- let path_item = PathItem::new(http_method, operation);
24
-
25
- Ok(path_item)
26
- }
27
-
28
- /// Convert route to OpenAPI Operation
29
- fn route_to_operation(route: &RouteMetadata) -> Result<utoipa::openapi::path::Operation, String> {
30
- let mut operation = utoipa::openapi::path::Operation::new();
31
-
32
- if let Some(param_schema) = &route.parameter_schema {
33
- let parameters =
34
- crate::openapi::parameter_extraction::extract_parameters_from_schema(param_schema, &route.path)?;
35
- if !parameters.is_empty() {
36
- let unwrapped: Vec<_> = parameters
37
- .into_iter()
38
- .filter_map(|p| if let RefOr::T(param) = p { Some(param) } else { None })
39
- .collect();
40
- operation.parameters = Some(unwrapped);
41
- }
42
- }
43
-
44
- if let Some(request_schema) = &route.request_schema {
45
- let request_body = crate::openapi::schema_conversion::json_schema_to_request_body(request_schema)?;
46
- operation.request_body = Some(request_body);
47
- }
48
-
49
- let mut responses = Responses::new();
50
- if let Some(response_schema) = &route.response_schema {
51
- let response = crate::openapi::schema_conversion::json_schema_to_response(response_schema)?;
52
- responses.responses.insert("200".to_string(), RefOr::T(response));
53
- } else {
54
- responses
55
- .responses
56
- .insert("200".to_string(), RefOr::T(Response::new("Successful response")));
57
- }
58
- operation.responses = responses;
59
-
60
- Ok(operation)
61
- }
62
-
63
- /// Assemble OpenAPI specification from routes with auto-detection of security schemes
64
- pub fn assemble_openapi_spec(
65
- routes: &[RouteMetadata],
66
- config: &super::OpenApiConfig,
67
- server_config: Option<&crate::ServerConfig>,
68
- ) -> Result<OpenApi, String> {
69
- let mut info = Info::new(&config.title, &config.version);
70
- if let Some(desc) = &config.description {
71
- info.description = Some(desc.clone());
72
- }
73
- if let Some(contact_info) = &config.contact {
74
- let mut contact = utoipa::openapi::Contact::default();
75
- if let Some(name) = &contact_info.name {
76
- contact.name = Some(name.clone());
77
- }
78
- if let Some(email) = &contact_info.email {
79
- contact.email = Some(email.clone());
80
- }
81
- if let Some(url) = &contact_info.url {
82
- contact.url = Some(url.clone());
83
- }
84
- info.contact = Some(contact);
85
- }
86
- if let Some(license_info) = &config.license {
87
- let mut license = utoipa::openapi::License::new(&license_info.name);
88
- if let Some(url) = &license_info.url {
89
- license.url = Some(url.clone());
90
- }
91
- info.license = Some(license);
92
- }
93
-
94
- let servers = if config.servers.is_empty() {
95
- None
96
- } else {
97
- Some(
98
- config
99
- .servers
100
- .iter()
101
- .map(|s| {
102
- let mut server = utoipa::openapi::Server::new(&s.url);
103
- if let Some(desc) = &s.description {
104
- server.description = Some(desc.clone());
105
- }
106
- server
107
- })
108
- .collect(),
109
- )
110
- };
111
-
112
- let mut paths = Paths::new();
113
- for route in routes {
114
- let path_item = route_to_path_item(route)?;
115
- paths.paths.insert(route.path.clone(), path_item);
116
- }
117
-
118
- let mut components = Components::new();
119
- let mut global_security = Vec::new();
120
-
121
- if let Some(server_cfg) = server_config {
122
- if let Some(_jwt_cfg) = &server_cfg.jwt_auth {
123
- let jwt_scheme = SecurityScheme::Http(
124
- utoipa::openapi::security::HttpBuilder::new()
125
- .scheme(utoipa::openapi::security::HttpAuthScheme::Bearer)
126
- .bearer_format("JWT")
127
- .build(),
128
- );
129
- components.add_security_scheme("bearerAuth", jwt_scheme);
130
-
131
- let security_req = utoipa::openapi::security::SecurityRequirement::new("bearerAuth", Vec::<String>::new());
132
- global_security.push(security_req);
133
- }
134
-
135
- if let Some(api_key_cfg) = &server_cfg.api_key_auth {
136
- use utoipa::openapi::security::ApiKey;
137
- let api_key_scheme = SecurityScheme::ApiKey(ApiKey::Header(utoipa::openapi::security::ApiKeyValue::new(
138
- &api_key_cfg.header_name,
139
- )));
140
- components.add_security_scheme("apiKeyAuth", api_key_scheme);
141
-
142
- let security_req = utoipa::openapi::security::SecurityRequirement::new("apiKeyAuth", Vec::<String>::new());
143
- global_security.push(security_req);
144
- }
145
- }
146
-
147
- if !config.security_schemes.is_empty() {
148
- for (name, scheme_info) in &config.security_schemes {
149
- let scheme = crate::openapi::security_scheme_info_to_openapi(scheme_info);
150
- components.add_security_scheme(name, scheme);
151
- }
152
- }
153
-
154
- let mut openapi = OpenApiBuilder::new()
155
- .info(info)
156
- .paths(paths)
157
- .components(Some(components))
158
- .build();
159
-
160
- if let Some(servers) = servers {
161
- openapi.servers = Some(servers);
162
- }
163
-
164
- if !global_security.is_empty() {
165
- openapi.security = Some(global_security);
166
- }
167
-
168
- Ok(openapi)
169
- }
170
-
171
- #[cfg(test)]
172
- mod tests {
173
- use super::*;
174
-
175
- #[test]
176
- fn test_route_to_path_item_get() {
177
- let route = RouteMetadata {
178
- method: "GET".to_string(),
179
- path: "/users".to_string(),
180
- handler_name: "list_users".to_string(),
181
- request_schema: None,
182
- response_schema: None,
183
- parameter_schema: None,
184
- file_params: None,
185
- is_async: true,
186
- cors: None,
187
- body_param_name: None,
188
- #[cfg(feature = "di")]
189
- handler_dependencies: None,
190
- };
191
-
192
- let result = route_to_path_item(&route);
193
- assert!(result.is_ok());
194
- }
195
- }
@@ -1 +0,0 @@
1
- pub use spikard_core::parameters::*;
@@ -1 +0,0 @@
1
- pub use spikard_core::problem::*;