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