Skip to content

Commit

Permalink
Fix #137
Browse files Browse the repository at this point in the history
Fix definition holder overriding components when multiple are defined at root level.
  • Loading branch information
rlebran committed Dec 21, 2024
1 parent 48aeeac commit c31a9b9
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 11 deletions.
18 changes: 12 additions & 6 deletions apistos/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,12 +251,18 @@ where
#[allow(clippy::unwrap_used)]
fn update_from_def_holder<D: DefinitionHolder>(&mut self, definition_holder: &mut D) {
let mut open_api_spec = self.open_api_spec.write().unwrap();
let mut components = definition_holder.components().into_iter().reduce(|mut acc, component| {
acc.schemas.extend(component.schemas);
acc.responses.extend(component.responses);
acc.security_schemes.extend(component.security_schemes);
acc
});
let components = mem::take(&mut open_api_spec.components);

let mut components = definition_holder
.components()
.into_iter()
.chain(components)
.reduce(|mut acc, component| {
acc.schemas.extend(component.schemas);
acc.responses.extend(component.responses);
acc.security_schemes.extend(component.security_schemes);
acc
});
definition_holder.update_path_items(&mut open_api_spec.paths.paths);
let mut paths = IndexMap::new();
for (path, mut item) in mem::take(&mut open_api_spec.paths.paths) {
Expand Down
79 changes: 74 additions & 5 deletions apistos/tests/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ use actix_web::middleware::Logger;
use actix_web::web::{Json, Path};
use actix_web::{App, Error};

use actix_web::test::{call_service, init_service, try_read_body_json, TestRequest};
use apistos::app::OpenApiWrapper;
use apistos::spec::Spec;
use apistos::web::{delete, get, patch, post, put, resource, scope, tagged_resource, tagged_scope, ServiceConfig};
use apistos_gen::api_operation;
use apistos_gen::{api_operation, ApiComponent};
use apistos_models::info::Info;
use apistos_models::tag::Tag;
use apistos_models::OpenApi;
use schemars::JsonSchema;
use serde::Serialize;

#[actix_web::test]
async fn actix_routing() {
Expand Down Expand Up @@ -97,12 +101,79 @@ async fn actix_routing() {
);
}

#[actix_web::test]
async fn actix_routing_multiple_root_definition_holder() {
#[derive(ApiComponent, JsonSchema, Serialize)]
pub(crate) struct TestResponse {
pub(crate) value: String,
}

#[derive(ApiComponent, JsonSchema, Serialize)]
pub(crate) struct TestResponse2 {
pub(crate) value: String,
}

#[api_operation(tag = "pet")]
pub(crate) async fn test(_params: Path<(u32, String)>) -> Result<Json<TestResponse>, Error> {
Ok(Json(TestResponse {
value: "plop".to_string(),
}))
}

#[api_operation(tag = "pet")]
pub(crate) async fn test2(_params: Path<String>) -> Result<Json<TestResponse2>, Error> {
Ok(Json(TestResponse2 {
value: "plop".to_string(),
}))
}

let info = Info {
title: "A well documented API".to_string(),
description: Some("Really well document I mean it".to_string()),
terms_of_service: Some("https://terms.com".to_string()),
..Default::default()
};
let tags = vec![
Tag {
name: "pet".to_owned(),
..Default::default()
},
Tag {
name: "A super tag".to_owned(),
..Default::default()
},
Tag {
name: "Another super tag".to_owned(),
..Default::default()
},
];
let spec = Spec {
info: info.clone(),
tags: tags.clone(),
..Default::default()
};

let app = App::new()
.document(spec)
.service(scope("test").service(resource("/{plop_id}/{clap_name}").route(get().to(test))))
.service(scope("test2").service(resource("/{clap_name}").route(get().to(test2))))
.build("/openapi.json");
let app = init_service(app).await;

let req = TestRequest::get().uri("/openapi.json").to_request();
let resp = call_service(&app, req).await;
assert!(resp.status().is_success());

let body: OpenApi = try_read_body_json(resp).await.expect("Unable to read body");
let components = body.components.expect("Unable to get components");

assert_eq!(components.schemas.len(), 2);
}

// Imports bellow aim at making clippy happy. Those dependencies are necessary for integration-test.
use actix_service as _;
use actix_web::test::{call_service, init_service, try_read_body_json, TestRequest};
use actix_web_lab as _;
use apistos_core as _;
use apistos_models::OpenApi;
use apistos_plugins as _;
use apistos_rapidoc as _;
use apistos_redoc as _;
Expand All @@ -115,6 +186,4 @@ use log as _;
use md5 as _;
use once_cell as _;
use regex as _;
use schemars as _;
use serde as _;
use serde_json as _;

0 comments on commit c31a9b9

Please sign in to comment.