tamasfe / aide

An API documentation library
Apache License 2.0
417 stars 70 forks source link

Custom response type documentation #119

Closed imran-mirza79 closed 8 months ago

imran-mirza79 commented 9 months ago

How to override a response with a particular type. I want to return application/cwt (CBOR web token). I want to return and document application/cwt type using the TransformOperation for few routes.

y-haidar commented 9 months ago

I would do something like this:

use aide::openapi::{MediaType, SchemaObject};
use axum::{http::StatusCode, response::IntoResponse};
use indexmap::IndexMap;
use schemars::JsonSchema;
use serde::Serialize;

pub struct Cwt<T: Serialize>(pub T);

impl<T: Serialize> IntoResponse for Cwt<T> {
  fn into_response(self) -> axum::response::Response {
    let mut vec = Vec::new();
    // TODO: if `into_writer` is expected to fail then impl error respones
    // like `Result<axum::response::Response, CwtRejection>`
    // where `CwtRejection` impl IntoResponse
    ciborium::into_writer(&self.0, &mut vec).unwrap();
    (
      StatusCode::OK,
      [(axum::http::header::CONTENT_TYPE, "application/cwt")],
      vec,
    )
      .into_response()
  }
}

// Taken from: aide-0.13.2/src/axum/outputs.rs
impl<T: JsonSchema + Serialize> aide::OperationOutput for Cwt<T> {
  type Inner = T;

  fn operation_response(
    ctx: &mut aide::gen::GenContext,
    _operation: &mut aide::openapi::Operation,
  ) -> Option<aide::openapi::Response> {
    let mut schema = ctx.schema.subschema_for::<T>().into_object();

    Some(aide::openapi::Response {
      description: schema.metadata().description.clone().unwrap_or_default(),
      content: IndexMap::from_iter([(
        "application/cwt".into(),
        MediaType {
          schema: Some(SchemaObject {
            json_schema: schema.into(),
            example: None,
            external_docs: None,
          }),
          ..Default::default()
        },
      )]),
      ..Default::default()
    })
  }

  fn inferred_responses(
    ctx: &mut aide::gen::GenContext,
    operation: &mut aide::openapi::Operation,
  ) -> Vec<(Option<u16>, aide::openapi::Response)> {
    if let Some(res) = Self::operation_response(ctx, operation) {
      let success_response = [(Some(200), res)];
      Vec::from(success_response)

      // if ctx.all_error_responses {
      //   [
      //     &success_response,
      //     // TODO: not implemented
      //     // CwtRejection::inferred_responses(ctx, operation).as_slice(),
      //   ]
      //   .concat()
      // } else {
      //   Vec::from(success_response)
      // }
    } else {
      Vec::new()
    }
  }
}

// how to use
#[derive(Deserialize, Serialize, JsonSchema)]
pub struct Item {
  pub title: Option<String>,
}

/// My endpoint
async fn test_fn() -> Cwt<Item> {
  Cwt(Item {
    title: Some("Title".to_owned()),
  })
}

Docs result: image

Please note that I am not familiar with cwt nor ciborium crate, check if what I am doing is correct.

imran-mirza79 commented 8 months ago

ooh ok, will try it. Thanks!