clawspec_core/client/call/mod.rs
1use http::{Method, Uri};
2
3use super::call_parameters::OperationMetadata;
4use super::openapi::channel::CollectorSender;
5use super::response::ExpectedStatusCodes;
6use super::security::SecurityRequirement;
7use super::{CallBody, CallCookies, CallHeaders, CallPath, CallQuery};
8
9pub(in crate::client) const BODY_MAX_LENGTH: usize = 1024;
10
11mod builder;
12mod execution;
13#[cfg(test)]
14mod tests;
15
16/// Builder for configuring HTTP API calls with comprehensive parameter and validation support.
17///
18/// `ApiCall` provides a fluent interface for building HTTP requests with automatic OpenAPI schema collection.
19/// It supports query parameters, headers, request bodies, and flexible status code validation.
20///
21/// # Method Groups
22///
23/// ## Request Body Methods
24/// - [`json(data)`](Self::json) - Set JSON request body
25/// - [`form(data)`](Self::form) - Set form-encoded request body
26/// - [`multipart(form)`](Self::multipart) - Set multipart form request body
27/// - [`text(content)`](Self::text) - Set plain text request body
28/// - [`raw(bytes)`](Self::raw) - Set raw binary request body
29///
30/// ## Parameter Methods
31/// - [`with_query(query)`](Self::with_query) - Set query parameters
32/// - [`with_headers(headers)`](Self::with_headers) - Set request headers
33/// - [`with_header(name, value)`](Self::with_header) - Add single header
34///
35/// ## Status Code Validation
36/// - [`with_expected_status_codes(codes)`](Self::with_expected_status_codes) - Set expected status codes
37/// - [`with_status_range_inclusive(range)`](Self::with_status_range_inclusive) - Set inclusive range (200..=299)
38/// - [`with_status_range(range)`](Self::with_status_range) - Set exclusive range (200..300)
39/// - [`add_expected_status(code)`](Self::add_expected_status) - Add single expected status
40/// - [`add_expected_status_range_inclusive(range)`](Self::add_expected_status_range_inclusive) - Add inclusive range
41/// - [`add_expected_status_range(range)`](Self::add_expected_status_range) - Add exclusive range
42/// - [`with_client_errors()`](Self::with_client_errors) - Accept 2xx and 4xx codes
43///
44/// ## OpenAPI Metadata
45/// - [`with_operation_id(id)`](Self::with_operation_id) - Set operation ID
46/// - [`with_tags(tags)`](Self::with_tags) - Set operation tags (or use automatic tagging)
47/// - [`with_description(desc)`](Self::with_description) - Set operation description (or use automatic description)
48///
49/// ## Response Descriptions (requires `redaction` feature)
50/// - [`with_response_description(desc)`](Self::with_response_description) - Set description for the actual returned status code
51///
52/// ## Execution
53/// - `.await` - Execute the request and return response (⚠️ **must consume result for OpenAPI**)
54///
55/// # Default Behavior
56///
57/// - **Status codes**: Accepts 200-499 (inclusive of 200, exclusive of 500)
58/// - **Content-Type**: Automatically set based on body type
59/// - **Schema collection**: Request/response schemas are automatically captured
60/// - **Operation metadata**: Automatically generated if not explicitly set
61///
62/// ## Automatic OpenAPI Metadata Generation
63///
64/// When you don't explicitly set operation metadata, `ApiCall` automatically generates:
65///
66/// ### **Automatic Tags**
67/// Tags are extracted from the request path using intelligent parsing:
68///
69/// ```text
70/// Path: /api/v1/users/{id} → Tags: ["users"]
71/// Path: /users → Tags: ["users"]
72/// Path: /users/export → Tags: ["users", "export"]
73/// Path: /observations/import → Tags: ["observations", "import"]
74/// ```
75///
76/// **Path Prefix Skipping**: Common API prefixes are automatically skipped:
77/// - `api`, `v1`, `v2`, `v3`, `rest`, `service` (and more)
78/// - `/api/v1/users` becomes `["users"]`, not `["api", "v1", "users"]`
79///
80/// **Special Action Detection**: Certain path segments get their own tags:
81/// - `import`, `upload`, `export`, `search`, `bulk`
82/// - `/users/export` → `["users", "export"]`
83///
84/// ### **Automatic Descriptions**
85/// Descriptions are generated based on HTTP method and path:
86///
87/// ```text
88/// GET /users → "Retrieve users"
89/// GET /users/{id} → "Retrieve user by ID"
90/// POST /users → "Create user"
91/// PUT /users/{id} → "Update user by ID"
92/// DELETE /users/{id} → "Delete user by ID"
93/// ```
94///
95/// ### **Automatic Operation IDs**
96/// Generated from HTTP method and path: `"get-users-id"`, `"post-users"`, etc.
97///
98/// You can override any of these by calling the corresponding `with_*` methods.
99#[derive(derive_more::Debug)]
100pub struct ApiCall {
101 pub(super) client: reqwest::Client,
102 pub(super) base_uri: Uri,
103 #[debug(skip)]
104 pub(super) collector_sender: CollectorSender,
105
106 pub(super) method: Method,
107 pub(super) path: CallPath,
108 pub(super) query: CallQuery,
109 pub(super) headers: Option<CallHeaders>,
110
111 #[debug(ignore)]
112 pub(super) body: Option<CallBody>,
113
114 pub(super) authentication: Option<super::Authentication>,
115 pub(super) cookies: Option<CallCookies>,
116 /// Expected status codes for this request (default: 200..500)
117 pub(super) expected_status_codes: ExpectedStatusCodes,
118 /// Operation metadata for OpenAPI documentation
119 pub(super) metadata: OperationMetadata,
120 /// Response description for the actual returned status code (redaction feature only)
121 #[cfg(feature = "redaction")]
122 pub(super) response_description: Option<String>,
123 /// Whether to skip collection for OpenAPI documentation (default: false)
124 pub(super) skip_collection: bool,
125 /// Security requirements for this operation (None = inherit from global)
126 pub(super) security: Option<Vec<SecurityRequirement>>,
127}