clawspec_core/client/call/
mod.rs

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