Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ target
.local*
.vscode
Cargo.lock
.zed
18 changes: 9 additions & 9 deletions crates/aide/src/axum/inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ where
T: axum_extra::headers::Header,
{
fn operation_input(ctx: &mut crate::generate::GenContext, operation: &mut Operation) {
let s = ctx.schema.subschema_for::<String>();
let s = ctx.input_generator.subschema_for::<String>();
add_parameters(
ctx,
operation,
Expand Down Expand Up @@ -72,8 +72,8 @@ fn operation_input_json<T: JsonSchema>(
ctx: &mut crate::generate::GenContext,
operation: &mut Operation,
) {
let json_schema = ctx.schema.subschema_for::<T>();
let resolved_schema = ctx.resolve_schema(&json_schema);
let json_schema = ctx.input_generator.subschema_for::<T>();
let resolved_schema = ctx.resolve_input_schema(&json_schema);

set_body(
ctx,
Expand Down Expand Up @@ -126,8 +126,8 @@ where
T: JsonSchema,
{
fn operation_input(ctx: &mut crate::generate::GenContext, operation: &mut Operation) {
let schema = ctx.schema.subschema_for::<T>();
let resolved_schema = ctx.resolve_schema(&schema);
let schema = ctx.input_generator.subschema_for::<T>();
let resolved_schema = ctx.resolve_input_schema(&schema);

set_body(
ctx,
Expand Down Expand Up @@ -160,7 +160,7 @@ where
T: JsonSchema,
{
fn operation_input(ctx: &mut crate::generate::GenContext, operation: &mut Operation) {
let schema = ctx.schema.subschema_for::<T>();
let schema = ctx.input_generator.subschema_for::<T>();
let params = parameters_from_schema(ctx, schema, ParamLocation::Path);
add_parameters(ctx, operation, params);
}
Expand All @@ -172,7 +172,7 @@ where
T: JsonSchema,
{
fn operation_input(ctx: &mut crate::generate::GenContext, operation: &mut Operation) {
let schema = ctx.schema.subschema_for::<T>();
let schema = ctx.input_generator.subschema_for::<T>();
let params = parameters_from_schema(ctx, schema, ParamLocation::Query);
add_parameters(ctx, operation, params);
}
Expand Down Expand Up @@ -363,7 +363,7 @@ where
T: JsonSchema,
{
fn operation_input(ctx: &mut crate::generate::GenContext, operation: &mut Operation) {
let schema = ctx.schema.subschema_for::<T>();
let schema = ctx.input_generator.subschema_for::<T>();
let resolved_schema = ctx.resolve_schema(&schema);

set_body(
Expand Down Expand Up @@ -397,7 +397,7 @@ where
T: JsonSchema,
{
fn operation_input(ctx: &mut crate::generate::GenContext, operation: &mut Operation) {
let schema = ctx.schema.subschema_for::<T>();
let schema = ctx.input_generator.subschema_for::<T>();
let params = parameters_from_schema(ctx, schema, ParamLocation::Query);
add_parameters(ctx, operation, params);
}
Expand Down
34 changes: 19 additions & 15 deletions crates/aide/src/axum/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
//! and the documented routes will be updated as expected.
//!

use std::{convert::Infallible, future::Future, pin::Pin};
use std::{convert::Infallible, future::Future, iter::Map, pin::Pin};

use crate::{
generate::{self, in_context},
Expand All @@ -189,6 +189,7 @@ use axum::{
};
use indexmap::map::Entry;
use indexmap::IndexMap;
use serde_json::Value;
use tower_layer::Layer;
use tower_service::Service;

Expand Down Expand Up @@ -447,21 +448,24 @@ where
return false;
}

let components = api.components.get_or_insert_with(Default::default);
components
.schemas
.extend(ctx.schema.take_definitions(true).into_iter().map(
|(name, json_schema)| {
(
name,
SchemaObject {
json_schema: json_schema.try_into().expect("Invalid schema"),
example: None,
external_docs: None,
},
)
let mut definitions = ctx.input_generator.take_definitions(true);
definitions.extend(ctx.output_generator.take_definitions(true));

let mapped_definitions = definitions.into_iter().map(|(name, json_schema)| {
(
name,
SchemaObject {
json_schema: json_schema.try_into().expect("Invalid schema"),
example: None,
external_docs: None,
},
));
)
});

api.components
.get_or_insert_with(Default::default)
.schemas
.extend(mapped_definitions);

true
});
Expand Down
8 changes: 4 additions & 4 deletions crates/aide/src/axum/outputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ where
type Inner = T;

fn operation_response(ctx: &mut GenContext, _operation: &mut Operation) -> Option<Response> {
let json_schema = ctx.schema.subschema_for::<T>();
let resolved_schema = ctx.resolve_schema(&json_schema);
let json_schema = ctx.output_generator.subschema_for::<T>();
let resolved_schema = ctx.resolve_output_schema(&json_schema);

Some(Response {
description: resolved_schema
Expand Down Expand Up @@ -91,8 +91,8 @@ where
type Inner = T;

fn operation_response(ctx: &mut GenContext, _operation: &mut Operation) -> Option<Response> {
let json_schema = ctx.schema.subschema_for::<T>();
let resolved_schema = ctx.resolve_schema(&json_schema);
let json_schema = ctx.output_generator.subschema_for::<T>();
let resolved_schema = ctx.resolve_output_schema(&json_schema);

Some(Response {
description: resolved_schema
Expand Down
2 changes: 1 addition & 1 deletion crates/aide/src/axum/routing/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ where
{
fn operation_input(ctx: &mut crate::generate::GenContext, operation: &mut Operation) {
// `subschema_for` `description` is none, while `root_schema_for` is some
let schema = ctx.schema.root_schema_for::<T>();
let schema = ctx.input_generator.root_schema_for::<T>();
operation.description = schema
.get("description")
.and_then(|d| d.as_str())
Expand Down
72 changes: 55 additions & 17 deletions crates/aide/src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,13 @@ pub fn reset_context() {
/// A context for API document generation
/// that provides settings and a [`SchemaGenerator`].
pub struct GenContext {
/// Schema generator that should be used
/// for generating JSON schemas.
pub schema: SchemaGenerator,
/// Generator that should be used
/// for generating operation input schemas.
pub input_generator: SchemaGenerator,

/// Generator that should be used
/// for generating operation output schemas.
pub output_generator: SchemaGenerator,

pub(crate) infer_responses: bool,

Expand Down Expand Up @@ -130,8 +134,11 @@ impl GenContext {
}
}

let schema_settings = SchemaSettings::draft07();

let mut this = Self {
schema: SchemaGenerator::new(SchemaSettings::draft07()),
input_generator: SchemaGenerator::new(schema_settings.clone().for_deserialize()),
output_generator: SchemaGenerator::new(schema_settings.for_serialize()),
infer_responses: true,
all_error_responses: false,
extract_schemas: true,
Expand All @@ -148,15 +155,21 @@ impl GenContext {
}
fn set_extract_schemas(&mut self, extract: bool) {
if extract {
self.schema = SchemaGenerator::new(SchemaSettings::draft07().with(|s| {
let settings = SchemaSettings::draft07().with(|s| {
s.inline_subschemas = false;
s.definitions_path = "#/components/schemas/".into();
}));
});

self.input_generator = SchemaGenerator::new(settings.clone().for_deserialize());
self.output_generator = SchemaGenerator::new(settings.for_serialize());
self.extract_schemas = true;
} else {
self.schema = SchemaGenerator::new(SchemaSettings::draft07().with(|s| {
let settings = SchemaSettings::draft07().with(|s| {
s.inline_subschemas = true;
}));
});

self.input_generator = SchemaGenerator::new(settings.clone().for_deserialize());
self.output_generator = SchemaGenerator::new(settings.for_serialize());
self.extract_schemas = false;
}
}
Expand Down Expand Up @@ -185,18 +198,43 @@ impl GenContext {
/// schema objects are references.
#[must_use]
pub fn resolve_schema<'s>(&'s self, schema_or_ref: &'s Schema) -> &'s Schema {
match &schema_or_ref.as_object().and_then(|o| o.get("$ref")) {
Some(Value::String(r)) => self
.schema
.definitions()
.get(r.strip_prefix("#/components/schemas/").unwrap_or(r))
.and_then(|s| Into::<serde_json::Result<&Schema>>::into(s.try_into()).ok())
.unwrap_or(schema_or_ref),
_ => schema_or_ref,
}
self.resolve_output_schema(self.resolve_input_schema(schema_or_ref))
}

/// Resolve a schema reference to an input schema
/// generated by the input schema generator.
///
/// This function behaves like [`resolve_schema`] but only
/// for input schemas.
#[must_use]
pub fn resolve_input_schema<'s>(&'s self, schema_or_ref: &'s Schema) -> &'s Schema {
extract_schema_by_ref(&self.input_generator, schema_or_ref).unwrap_or(schema_or_ref)
}

/// Resolve a schema reference to an output schema
/// generated by the output schema generator.
///
/// This function behaves like [`resolve_schema`] but only
/// for output schemas.
#[must_use]
pub fn resolve_output_schema<'s>(&'s self, schema_or_ref: &'s Schema) -> &'s Schema {
extract_schema_by_ref(&self.output_generator, schema_or_ref).unwrap_or(schema_or_ref)
}
}

fn default_error_filter(_: &Error) -> bool {
true
}

fn extract_schema_by_ref<'s>(
generator: &'s SchemaGenerator,
schema_or_ref: &'s Schema,
) -> Option<&'s Schema> {
match &schema_or_ref.as_object().and_then(|o| o.get("$ref")) {
Some(Value::String(r)) => generator
.definitions()
.get(r.strip_prefix("#/components/schemas/").unwrap_or(r))
.and_then(|s| Into::<serde_json::Result<&Schema>>::into(s.try_into()).ok()),
_ => None,
}
}
4 changes: 2 additions & 2 deletions crates/aide/src/impls/serde_qs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ where
T: JsonSchema,
{
fn operation_input(ctx: &mut crate::generate::GenContext, operation: &mut Operation) {
let schema = ctx.schema.subschema_for::<T>();
let schema = ctx.input_generator.subschema_for::<T>();
let params = parameters_from_schema(ctx, schema, ParamLocation::Query);
add_parameters(ctx, operation, params);
}
Expand All @@ -24,7 +24,7 @@ where
T: JsonSchema,
{
fn operation_input(ctx: &mut crate::generate::GenContext, operation: &mut Operation) {
let schema = ctx.schema.subschema_for::<T>();
let schema = ctx.input_generator.subschema_for::<T>();
let params = parameters_from_schema(ctx, schema, ParamLocation::Query);
add_parameters(ctx, operation, params);
}
Expand Down