Skip to main content

RequestBodyRedactionBuilder

Struct RequestBodyRedactionBuilder 

Source
pub struct RequestBodyRedactionBuilder<T> { /* private fields */ }
Available on crate feature redaction only.
Expand description

Builder for redacting sensitive values in JSON request bodies.

This builder allows you to apply redactions to a JSON request body before it’s used in the OpenAPI documentation. The original (unredacted) value is sent in the actual HTTP request.

§Key Principle

  • HTTP Request: Uses the original value with real data for testing
  • OpenAPI Example: Uses the redacted value with stable placeholders

This separation allows you to:

  • Test with realistic data (passwords, tokens, API keys)
  • Generate stable OpenAPI documentation (no dynamic values)
  • Hide sensitive information from documentation

§Path Syntax

Paths are auto-detected based on their prefix:

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

§Example

use clawspec_core::ApiClient;
use serde::Serialize;
use utoipa::ToSchema;

#[derive(Clone, Serialize, ToSchema)]
struct LoginRequest {
    email: String,
    password: String,
}

let mut client = ApiClient::builder().build()?;

let request = LoginRequest {
    email: "user@example.com".to_string(),
    password: "my-secret-password".to_string(),
};

client
    .post("/auth/login")?
    .json_redacted(&request)?
    .redact("/password", "[REDACTED]")?
    .await?;  // IntoFuture - no .finish() needed

Implementations§

Source§

impl<T> RequestBodyRedactionBuilder<T>

Source

pub fn redact<R: Redactor>( self, path: &str, redactor: R, ) -> Result<Self, ApiClientError>

Redacts values at the specified path using a redactor.

The path can be either JSON Pointer (RFC 6901) or JSONPath (RFC 9535). The syntax is auto-detected based on the prefix:

  • $... → JSONPath (supports wildcards)
  • /... → JSON Pointer (exact path)

The redactor can be:

  • A static value: "replacement" or serde_json::json!(...)
  • A closure: |path, val| transform(path, val)
§Arguments
  • path - Path expression (e.g., /password, $.users[*].token)
  • redactor - The redactor to apply (static value or closure)
§Errors

Returns an error if:

  • The path is invalid
  • The path matches no values
§Example
// Static value
client.post("/api")?
    .json_redacted(&Request { token: "secret".into() })?
    .redact("/token", "[REDACTED]")?
    .await?;  // IntoFuture - no .finish() needed
Source

pub fn redact_with_options<R: Redactor>( self, path: &str, redactor: R, options: RedactOptions, ) -> Result<Self, ApiClientError>

Redacts values at the specified path with configurable options.

This is like redact but allows customizing behavior through RedactOptions.

§Arguments
  • path - Path expression (e.g., /password, $.users[*].token)
  • redactor - The redactor to apply
  • options - Configuration options
§Example
use clawspec_core::RedactOptions;

// Allow empty matches for optional fields
let options = RedactOptions { allow_empty_match: true };

builder
    .redact_with_options("$.optional_field", "value", options)?
    .await?;  // IntoFuture - no .finish() needed
Source

pub fn redact_remove(self, path: &str) -> Result<Self, ApiClientError>

Removes values at the specified path from the OpenAPI example.

This completely removes the field from the OpenAPI documentation example, unlike setting it to null. The original value is still sent in the HTTP request.

The path can be either JSON Pointer (RFC 6901) or JSONPath (RFC 9535).

§Arguments
  • path - Path expression to remove
§Errors

Returns an error if:

  • The path is invalid
  • The path matches no values
§Example
client.post("/api")?
    .json_redacted(&Request {
        password: "secret".into(),
        internal_id: "internal-123".into(),
    })?
    .redact("/password", "[REDACTED]")?
    .redact_remove("/internal_id")?  // Remove entirely from docs
    .await?;  // IntoFuture - no .finish() needed
Source

pub fn redact_remove_with( self, path: &str, options: RedactOptions, ) -> Result<Self, ApiClientError>

Removes values at the specified path with configurable options.

This is like redact_remove but allows customizing behavior through RedactOptions.

§Arguments
  • path - Path expression to remove
  • options - Configuration options
§Example
use clawspec_core::RedactOptions;

// Allow empty matches for optional fields
let options = RedactOptions { allow_empty_match: true };

builder
    .redact_remove_with("$.optional_field", options)?
    .await?;  // IntoFuture - no .finish() needed
Source

pub fn finish(self) -> Result<ApiCall, ApiClientError>
where T: ToSchema + 'static,

Finalizes the redaction and returns the configured ApiCall.

This consumes the builder and returns the ApiCall with the request body configured. The body will contain:

  • HTTP data: The original (unredacted) serialized value
  • OpenAPI example: The redacted value for documentation

After calling finish(), you can .await the ApiCall to execute the HTTP request.

§Example
let response = client
    .post("/api")?
    .json_redacted(&Request { password: "secret".into() })?
    .redact("/password", "[REDACTED]")?
    .finish()?  // Returns ApiCall
    .await?;    // Executes the HTTP request
Source

pub fn original_value(&self) -> &T

Returns a reference to the original (unredacted) value.

This can be useful if you need to inspect the original value while building the redactions.

Source

pub fn redacted_value(&self) -> &Value

Returns a reference to the current redacted JSON value.

This can be useful if you need to inspect the redacted state while building the redactions.

Trait Implementations§

Source§

impl<T> Debug for RequestBodyRedactionBuilder<T>

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T> IntoFuture for RequestBodyRedactionBuilder<T>
where T: ToSchema + 'static,

Implements IntoFuture to allow direct .await on the builder.

This enables a more ergonomic API where you can write:

client
    .post("/users")?
    .json_redacted(&user)?
    .redact("/password", "[REDACTED]")?
    .await?;  // No need for .finish()?

Instead of:

client
    .post("/users")?
    .json_redacted(&user)?
    .redact("/password", "[REDACTED]")?
    .finish()?
    .await?;
Source§

type Output = Result<CallResult, ApiClientError>

The output that the future will produce on completion.
Source§

type IntoFuture = Pin<Box<dyn Future<Output = <RequestBodyRedactionBuilder<T> as IntoFuture>::Output> + Send>>

Which kind of future are we turning this into?
Source§

fn into_future(self) -> Self::IntoFuture

Creates a future from a value. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more