Crate clawspec_core

Crate clawspec_core 

Source
Expand description

§Clawspec Core

Generate OpenAPI specifications from your HTTP client test code.

This crate provides two main ways to generate OpenAPI documentation:

  • ApiClient - Direct HTTP client for fine-grained control
  • TestClient - Test server integration with automatic lifecycle management

New to Clawspec? Start with the Tutorial for a step-by-step guide.

§Quick Start

§Using ApiClient directly

use clawspec_core::ApiClient;

let mut client = ApiClient::builder()
    .with_host("api.example.com")
    .build()?;

// Make requests - schemas are captured automatically  
let user: User = client
    .get("/users/123")?
    .await?  // ← Direct await using IntoFuture
    .as_json()  // ← Important: Must consume result for OpenAPI generation!
    .await?;

// Generate OpenAPI specification
let spec = client.collected_openapi().await;

§Using TestClient with a test server

For a complete working example, see the axum example.

use clawspec_core::test_client::{TestClient, TestServer};
use std::net::TcpListener;

#[tokio::test]
async fn test_api() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = TestClient::start(MyServer).await?;
     
    // Test your API
    let response = client.get("/users")?.await?.as_json::<serde_json::Value>().await?;
     
    // Write OpenAPI spec
    client.write_openapi("api.yml").await?;
    Ok(())
}

§Working with Parameters

use clawspec_core::{ApiClient, CallPath, CallQuery, CallHeaders, CallCookies, ParamValue, ParamStyle};

// Path parameters  
let path = CallPath::from("/users/{id}")
    .add_param("id", ParamValue::new(123));

// Query parameters
let query = CallQuery::new()
    .add_param("page", ParamValue::new(1))
    .add_param("limit", ParamValue::new(10));

// Headers
let headers = CallHeaders::new()
    .add_header("Authorization", "Bearer token");

// Cookies
let cookies = CallCookies::new()
    .add_cookie("session_id", "abc123")
    .add_cookie("user_id", 456);

// Direct await with parameters:
let response = client
    .get(path)?
    .with_query(query)
    .with_headers(headers)
    .with_cookies(cookies)
    .await?;  // Direct await using IntoFuture

§Parameter Styles

The library supports OpenAPI 3.1.0 parameter styles. Use ParamStyle for advanced serialization:

use clawspec_core::{CallPath, CallQuery, ParamValue, ParamStyle};

// Path: simple (default), label, matrix
let path = CallPath::from("/users/{id}").add_param("id", ParamValue::new(123));

// Query: form (default), spaceDelimited, pipeDelimited, deepObject
let query = CallQuery::new()
    .add_param("tags", ParamValue::with_style(vec!["a", "b"], ParamStyle::PipeDelimited));

See Chapter 4: Advanced Parameters for detailed examples.

§Authentication

Configure authentication at the client or per-request level:

use clawspec_core::{ApiClient, Authentication};

let mut client = ApiClient::builder()
    .with_host("api.example.com")
    .with_authentication(Authentication::Bearer("token".into()))
    .build()?;

// Override per-request
client.get("/admin")?.with_authentication(Authentication::Bearer("admin-token".into())).await?;

Supported types: Bearer, Basic, ApiKey. See Chapter 4 for details.

§Status Code Validation

By default, requests expect status codes in the range 200-499 (inclusive of 200, exclusive of 500). You can customize this behavior:

use clawspec_core::{ApiClient, expected_status_codes};

// Single codes
client.post("/users")?
    .with_expected_status_codes(expected_status_codes!(201, 202))
    .await?;

// Ranges
client.get("/health")?
    .with_expected_status_codes(expected_status_codes!(200-299))
    .await?;

§Response Descriptions

Add descriptive text to your OpenAPI responses for better documentation:

use clawspec_core::ApiClient;

// Set a description for the actual returned status code
client.get("/users/{id}")?
    .with_response_description("User details if found, or error information")
    .await?;

// The description applies to whatever status code is actually returned
client.post("/users")?
    .with_response_description("User created successfully or validation error")
    .await?;

§Response Redaction

Requires the redaction feature.

When generating OpenAPI examples from real API responses, dynamic values like UUIDs, timestamps, and tokens make examples unstable across test runs. The redaction feature allows you to replace these dynamic values with stable, predictable ones in the generated OpenAPI specification while preserving the actual values for assertions.

This is particularly useful for:

  • Snapshot testing: Generated OpenAPI files remain stable across runs
  • Documentation: Examples show consistent, readable placeholder values
  • Security: Sensitive values can be masked in documentation

§Basic Usage

use clawspec_core::ApiClient;

#[derive(Deserialize, ToSchema)]
struct User {
    id: String,           // Dynamic UUID
    name: String,
    created_at: String,   // Dynamic timestamp
}

let user: User = client
    .post("/users")?
    .json(&serde_json::json!({"name": "Alice"}))?
    .await?
    .as_json_redacted()
    .await?
    // Replace dynamic UUID with stable value
    .redact("/id", "00000000-0000-0000-0000-000000000001")?
    // Replace timestamp with stable value
    .redact("/created_at", "2024-01-01T00:00:00Z")?
    .finish()
    .await
    .value;

// The actual user has real dynamic values for assertions
assert!(!user.id.is_empty());
// But the OpenAPI example shows the redacted stable values

§Redaction Operations

The redaction builder supports two operations using JSON Pointer (RFC 6901):

  • redact(pointer, redactor): Replace a value at the given path with a stable value or transformation
  • redact_remove(pointer): Remove a value entirely from the OpenAPI example
let response: Response = client
    .post("/auth/login")?
    .json(&serde_json::json!({"username": "test", "password": "secret"}))?
    .await?
    .as_json_redacted()
    .await?
    .redact("/token", "[REDACTED_TOKEN]")?
    .redact("/session_id", "session-00000000")?
    .redact_remove("/internal_ref")?  // Remove internal field from docs
    .finish()
    .await
    .value;

§Path Syntax

Paths are auto-detected based on their prefix:

  • /... → JSON Pointer (RFC 6901) - exact paths only
  • $... → JSONPath (RFC 9535) - supports wildcards

§JSON Pointer Syntax

JSON Pointers use / as a path separator. Special characters are escaped:

  • ~0 represents ~
  • ~1 represents /

Examples:

  • /id - Top-level field named “id”
  • /user/name - Nested field “name” inside “user”
  • /items/0/id - First element’s “id” in an array
  • /foo~1bar - Field named “foo/bar”

§JSONPath Wildcards

For arrays, use JSONPath syntax (starting with $) to redact all elements:

let users: Vec<User> = client
    .get("/users")?
    .await?
    .as_json_redacted()
    .await?
    .redact("$[*].id", "stable-uuid")?        // All IDs in array
    .redact("$[*].created_at", "2024-01-01T00:00:00Z")?  // All timestamps
    .finish()
    .await
    .value;

§Dynamic Transformations

Pass a closure for dynamic redaction. The closure receives the concrete JSON Pointer path and current value:

let users: Vec<User> = client
    .get("/users")?
    .await?
    .as_json_redacted()
    .await?
    // Create stable index-based IDs: user-0, user-1, user-2, ...
    .redact("$[*].id", |path: &str, _val: &Value| {
        let idx = path.split('/').nth(1).unwrap_or("0");
        serde_json::json!(format!("user-{idx}"))
    })?
    .finish()
    .await
    .value;

§Getting Both Values

The RedactedResult returned by finish() contains both:

  • value: The actual deserialized response (with real dynamic values)
  • redacted: The JSON with redacted values (as stored in OpenAPI)
let result = client
    .get("/users/123")?
    .await?
    .as_json_redacted::<User>()
    .await?
    .redact("/id", "user-00000000")?
    .finish()
    .await;

// Use actual value for test assertions
let user = result.value;
assert!(!user.id.is_empty());

// Access redacted JSON if needed
let redacted_json = result.redacted;
assert_eq!(redacted_json["id"], "user-00000000");

§Schema Registration

Schemas are automatically captured when using .json() and .as_json() methods. For nested schemas or error types, use register_schemas!:

use clawspec_core::{ApiClient, register_schemas};

register_schemas!(client, Address, ErrorResponse).await;

Note: Nested schemas may not be fully resolved automatically. Register them explicitly if they’re missing from your OpenAPI output.

§Error Handling

The library provides two main error types:

§YAML Serialization

Requires the yaml feature.

The library provides YAML serialization support using serde-saphyr, the modern replacement for the deprecated serde_yaml crate.

use clawspec_core::{ApiClient, ToYaml};

let mut client = ApiClient::builder()
    .with_host("api.example.com")
    .build()?;

// ... make API calls ...

let spec = client.collected_openapi().await;
let yaml = spec.to_yaml()?;

std::fs::write("openapi.yml", yaml)?;

§See Also

§Re-exports

All commonly used types are re-exported from the crate root for convenience.

Modules§

_tutorial
Tutorial: Getting Started with Clawspec
split
OpenAPI specification splitting utilities.
test_client
Generic test client framework for async server testing.

Macros§

expected_status_codes
Creates an ExpectedStatusCodes instance with the specified status codes and ranges.
register_schemas
Registers multiple schema types with the ApiClient for OpenAPI documentation.

Structs§

ApiCall
Builder for configuring HTTP API calls with comprehensive parameter and validation support.
ApiClient
ApiClientBuilder
Builder for creating ApiClient instances with comprehensive configuration options.
CallBody
Represents the body of an HTTP request with its content type and schema information.
CallCookies
Represents HTTP cookies for an API call.
CallHeaders
Represents HTTP headers for an API call.
CallPath
A parameterized HTTP path with type-safe parameter substitution.
CallQuery
A collection of query parameters for HTTP requests with OpenAPI 3.1 support.
CallResult
Represents the result of an API call with response processing capabilities.
ExpectedStatusCodes
Expected status codes for HTTP requests.
Info
OpenAPI types re-exported from utoipa for convenience. OpenAPI Info object represents metadata of the API.
InfoBuilder
OpenAPI types re-exported from utoipa for convenience. Builder for Info with chainable configuration methods to create a new Info.
OAuth2Configoauth2
OAuth2 authentication configuration.
OAuth2ConfigBuilderoauth2
Builder for OAuth2 configuration.
OAuth2Flow
OAuth2 flow configuration (for flows with token URL).
OAuth2Flows
OAuth2 flow configurations.
OAuth2ImplicitFlow
OAuth2 implicit flow configuration.
OAuth2Tokenoauth2
An OAuth2 access token with expiration tracking.
OpenApi
OpenAPI types re-exported from utoipa for convenience. Root object of the OpenAPI document.
ParamValue
A parameter value with its serialization style
Paths
OpenAPI types re-exported from utoipa for convenience. Implements OpenAPI Paths Object.
RawResult
Represents the raw response data from an HTTP request.
RedactOptionsredaction
Options for configuring redaction behavior.
RedactedResultredaction
Result of a redacted JSON response containing both the real and redacted values.
RedactionBuilderredaction
Builder for applying redactions to JSON responses.
SecureString
Secure wrapper for sensitive string data that automatically zeroes memory on drop.
SecurityRequirement
Security requirement specifying which scheme and scopes are needed.
Server
OpenAPI types re-exported from utoipa for convenience. Represents target server object. It can be used to alter server connection for path operations.
ServerBuilder
OpenAPI types re-exported from utoipa for convenience. Builder for Server with chainable configuration methods to create a new Server.
StatusCode
HTTP status codes re-exported from the http crate. An HTTP status code (status-code in RFC 9110 et al.).
ValueRedactionBuilderredaction
Builder for redacting arbitrary JSON values.

Enums§

ApiClientError
Errors that can occur when using the ApiClient.
ApiKeyLocation
Location where an API key is passed.
Authentication
Authentication configuration for API requests.
AuthenticationError
Errors that can occur during authentication processing.
OAuth2Erroroauth2
Errors that can occur during OAuth2 authentication.
ParamStyle
Parameter styles supported by OpenAPI 3.1 specification.
RawBody
Represents the body content of a raw HTTP response.
SecurityScheme
OpenAPI security scheme configuration.

Traits§

ParameterValue
A trait alias for types that can be used as parameter values.
Redactorredaction
Trait for types that can be used to redact values.
ToSchema
The ToSchema derive macro for generating OpenAPI schemas. Types used in JSON request/response bodies should derive this trait. Trait for implementing OpenAPI Schema object.
ToYamlyaml
Extension trait for serializing types to YAML.

Functions§

redact_valueredaction
Create a redaction builder for an arbitrary JSON value.

Type Aliases§

YamlErroryaml
Error type for YAML serialization operations.

Derive Macros§

ToSchema
The ToSchema derive macro for generating OpenAPI schemas. Types used in JSON request/response bodies should derive this trait. Generate reusable OpenAPI schema to be used together with OpenApi.