netwo-io / apistos

Actix-web wrapper for automatic OpenAPI 3.0 documentation generation.
MIT License
124 stars 3 forks source link

Schema for tuples is incorrect #123

Closed Ploppz closed 1 month ago

Ploppz commented 1 month ago

apistos seems to generate incorrect schema for tuples. It seems that items of an array is not allowed to be an array, it must be an object

main.rs

use actix_web::web::{Json, Path};
use actix_web::{App, HttpServer};
use apistos::app::OpenApiWrapper;
use apistos::info::Info;
use apistos::server::Server;
use apistos::spec::Spec;
use apistos::web::{get, resource, scope};
use apistos::{api_operation, ApiComponent};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::net::Ipv4Addr;

#[derive(Serialize, Deserialize, Debug, JsonSchema, ApiComponent)]
pub struct ExperimentData {
    pub experiment_plates: (i64, String),
}

#[api_operation(summary = "Get an element from the todo list")]
pub async fn get_experiment_data(
    _path: Path<i64>,
) -> Result<Json<ExperimentData>, actix_web::Error> {
    unimplemented!()
}

#[actix_web::main]
async fn main() -> Result<(), impl Error> {
    HttpServer::new(move || {
    let spec = Spec {
      info: Info {
        title: "A well documented API".to_string(),
        description: Some(
          "This is an API documented using Apistos,\na wonderful new tool to document your actix API !".to_string(),
        ),
        ..Default::default()
      },
      servers: vec![Server {
        url: "/api/v3".to_string(),
        ..Default::default()
      }],
      ..Default::default()
    };

    App::new()
      .document(spec)
      .service( scope("/api/v1")
            .service(
                resource("/experiment_data")
                    .route(get().to(get_experiment_data))
            )
      )
      .build("/openapi.json")
  })
  .bind((Ipv4Addr::UNSPECIFIED, 8080))?
  .run()
  .await
}

Cargo.toml:

[package]
name = "t"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "4.8.0"
apistos = { version = "0.1", features = ["extras", "qs_query"] }
schemars = { package = "apistos-schemars", version = "0.8", features = ["chrono", "uuid1", "url", "rust_decimal"] }
serde = "1.0.204"

openapi.json generated:

{
   "openapi":"3.0.3",
   "info":{
      "title":"A well documented API",
      "description":"This is an API documented using Apistos,\na wonderful new tool to document your actix API !",
      "version":""
   },
   "servers":[
      {
         "url":"/api/v3"
      }
   ],
   "paths":{
      "/api/v1/experiment_data":{
         "get":{
            "summary":"Get an element from the todo list",
            "operationId":"get_api-v1-experiment_data-9b91ffd5b8bcd5ccf94d80ee114c8fc4",
            "parameters":[
               {
                  "name":"",
                  "in":"path",
                  "required":true,
                  "schema":{
                     "title":"int64",
                     "type":"integer",
                     "format":"int64"
                  }
               }
            ],
            "responses":{
               "200":{
                  "description":"",
                  "content":{
                     "application/json":{
                        "schema":{
                           "$ref":"#/components/schemas/ExperimentData"
                        }
                     }
                  }
               }
            },
            "deprecated":false
         }
      }
   },
   "components":{
      "schemas":{
         "ExperimentData":{
            "title":"ExperimentData",
            "type":"object",
            "required":[
               "experiment_plates"
            ],
            "properties":{
               "experiment_plates":{
                  "type":"array",
                  "items":[
                     {
                        "type":"integer",
                        "format":"int64"
                     },
                     {
                        "type":"string"
                     }
                  ],
                  "maxItems":2,
                  "minItems":2
               }
            }
         }
      }
   }
}

Errors in swagger.io editor:

Semantic error at paths./api/v1/experiment_data.get.parameters.0.name
Path parameter "" must have the corresponding {} segment in the "/api/v1/experiment_data" path
Jump to line 20

Structural error at components.schemas.ExperimentData.properties.experiment_plates.items
should be object
Jump to line 57

Semantic error at components.schemas.ExperimentData.properties.experiment_plates.items
`items` must be an object
Jump to line 57
rlebran commented 1 month ago

Hi ! Two things here:

Closing for now, feel free to reopen if you still encounter some issues.

Ploppz commented 4 weeks ago

Thanks. For the first point, can you elaborate what the difference is between the definition and the handler? I only know the handler function, where in my code can I add path parameters to definition?

rlebran commented 3 weeks ago

Hey ! What I mean is that you didn't specify a path parameter here:

.service( scope("/api/v1")
            .service(
                resource("/experiment_data")
                    .route(get().to(get_experiment_data))
            )
      )

Following actix's documentation, you could specify like bellow:

.service( scope("/api/v1")
            .service(
                resource("/experiment_data/{id}")
                    .route(get().to(get_experiment_data))
            )
      )

This way, both definition and handler hold a path parameter.