Skip to content

Instantly share code, notes, and snippets.

@platy
Created October 1, 2020 11:03
Show Gist options
  • Save platy/e0bc44e0ce91c8268dcf0d9689e1f974 to your computer and use it in GitHub Desktop.
Save platy/e0bc44e0ce91c8268dcf0d9689e1f974 to your computer and use it in GitHub Desktop.
Changing status codes in paperclip actix-web plugin
use std::fmt;
use actix_http::{Response, Error};
use actix_web::{HttpRequest, Responder};
use actix_web::http::StatusCode;
use futures::future::{err, ok, Ready};
use paperclip::v2::schema::Apiv2Schema;
use paperclip::actix::OperationModifier;
use paperclip::v2::models::{Response as PaperclipResponse, Either, DefaultOperationRaw, DefaultSchemaRaw};
use serde::Serialize;
macro_rules! json_with_status {
($name:ident => $status:expr) => {
pub struct $name<T: Serialize + Apiv2Schema>(pub T);
impl<T> fmt::Debug for $name<T>
where
T: fmt::Debug + Serialize + Apiv2Schema,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let status: StatusCode = $status;
let status_str = status.canonical_reason().unwrap_or(status.as_str());
write!(f, "{} Json: {:?}", status_str, self.0)
}
}
impl<T> fmt::Display for $name<T>
where
T: fmt::Display + Serialize + Apiv2Schema,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<T> Responder for $name<T>
where
T: Serialize + Apiv2Schema,
{
type Error = Error;
type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future {
let body = match serde_json::to_string(&self.0) {
Ok(body) => body,
Err(e) => return err(e.into()),
};
ok(Response::build(StatusCode::CREATED)
.content_type("application/json")
.body(body))
}
}
impl<T> Apiv2Schema for $name<T>
where
T: Serialize + Apiv2Schema,
{
const NAME: Option<&'static str> = T::NAME;
fn raw_schema() -> DefaultSchemaRaw {
T::raw_schema()
}
}
impl<T> OperationModifier for $name<T>
where
T: Serialize + Apiv2Schema,
{
fn update_response(op: &mut DefaultOperationRaw) {
let status: StatusCode = $status;
op.responses.insert(
status.as_str().into(),
Either::Right(PaperclipResponse {
description: status.canonical_reason().map(ToString::to_string),
schema: Some({
let mut def = T::schema_with_ref();
def.retain_ref();
def
}),
..Default::default()
}),
);
}
}
};
}
json_with_status!(CreatedJson => StatusCode::CREATED);
json_with_status!(AcceptedJson => StatusCode::ACCEPTED);
#[derive(Debug)]
pub struct NoContent;
impl fmt::Display for NoContent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("No Content")
}
}
impl Responder for NoContent {
type Error = Error;
type Future = Ready<Result<Response, Error>>;
fn respond_to(self, _: &HttpRequest) -> Self::Future {
ok(Response::build(StatusCode::NO_CONTENT)
.content_type("application/json")
.finish())
}
}
impl Apiv2Schema for NoContent {}
impl OperationModifier for NoContent {
fn update_response(op: &mut DefaultOperationRaw) {
let status = StatusCode::NO_CONTENT;
op.responses.insert(
status.as_str().into(),
Either::Right(PaperclipResponse {
description: status.canonical_reason().map(ToString::to_string),
schema: None,
..Default::default()
}),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment