Closed aminya closed 1 month ago
I have found a hard workaround for this. I have to go through each of the fields of the dependencies and list them manually under components.
// later:
#[derive(OpenApi)]
#[openapi(
// ...
components(
schemas(MyType, MyTypeAttributes, MyTypeRelationships, Type),
),
)]
Another issue I had is that the schema prints models.MyTypeAttributes
because the type of the field is written as models::MyTypeAttributes
.
So I had to write a modifier to change the opanapi.json responses
/// Remove the models. prefix from the openapi json
fn modify_openapi_json(openapi_json: &str) -> String {
return openapi_json.replace("#/components/schemas/models.", "#/components/schemas/");
}
To print the fixed schema:
// in the main function
let openapi = ApiDoc::openapi();
println!("{}", modify_openapi_json(&openapi.to_json()?));
To modify the openapi response for Actix
App::new()
.service(scope("/api/v1/openapi3.json").wrap_fn(|request, service| {
let is_open_api = request.path() == "/api/v1/openapi3.json";
return service.call(request).map(move |result| {
if !is_open_api {
return result;
}
// Modify the response to remove the models. prefix from the openapi json
return result.map(|result| {
return result.map_body(|_header, body| {
// Extract the openapi json from the body
let mut bytes = body
.try_into_bytes()
.expect("Failed to convert body to bytes");
let body_str =
str::from_utf8(&mut bytes).expect("Failed to convert bytes to string");
let modified_body_str = modify_openapi_json(body_str);
return BoxBody::new(modified_body_str);
});
});
});
}))
.service(
RapiDoc::with_openapi("/api/v1/openapi3.json", openapi.clone()).path("/api/v1/docs/"),
);
I'm pretty sure that's the "intended" way. Utoipa doesn't include any types that don't derive ToSchema, see https://github.com/juhaku/utoipa/blob/master/utoipa/src/lib.rs#L528 for how the "standard" types are derived.
FWIW, I do the same in my project, but I didn't have to modify any paths
As @junlarsen stated it does work as intended. However there are two separate things going on here with your particular question.
ToSchema
trait implemented or not and then define the appropriate action for the type. Yet we could enforce a compile error when we see a token that is not recognized as a known type. But this would cause more trouble for users than benefit because there are cases where we cannot implement ToSchema
trait for a particular type that is from third-party crate. This is then to be handled the same way as you would implement serde's Serialize
and Deserialize
for external types. More about the topic can be found here https://github.com/juhaku/utoipa/issues/790#issuecomment-1787754185I have found a hard workaround for this. I have to go through each of the fields of the dependencies and list them manually under components.
// later: #[derive(OpenApi)] #[openapi( // ... components( schemas(MyType, MyTypeAttributes, MyTypeRelationships, Type), ), )]
This is not a workaround, that is as it is intended. Utoipa requires explicit declaration of types that suppose to be present in the OpenAPI spec. You might be interested of this crate https://github.com/ProbablyClem/utoipauto. It will enable automatic schema and path recognition. But still the types to be recognized must implement ToSchema
trait.
models::MyType
becoming as models.MyType
is necessary for namespacing. Users might have multiple types with same name but located in different modules which need to be distinct in OpenAPI spec. In order to achieve this we need namespacing. That is if you add type with prefix like models::
you need to reference to that type with the prefix as well. Here is link to an example https://github.com/juhaku/utoipa/issues/435#issuecomment-1405448818.From the docs https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html#struct-optional-configuration-options-for-schema:
- as = ... Can be used to define alternative path and name for the schema what will be used in the OpenAPI. E.g as =
path::to::Pet
. This would make the schema appear in the generated OpenAPI spec aspath.to.Pet
.
That is the schema path declaration used in openapi
macro attribute must match to the either name of the schema or value defined with as = ...
attribute of the type that implements ToSchema
.
#[openapi(
schema(models::MyType)
)]
@juhaku Regarding Point 2. Is it possible to rename the referenced Schama name for Named Fields
?
id:
$ref: '#/components/schemas/crate.reference_id.ReferenceId'
to
id:
$ref: '#/components/schemas/ReferenceId'
I have added for
ToSchema
for aMyType
defined in a Cargo dependency. However, when I declareMyType
as a components of the schema in my project, the fields of the third party type, have missing models.Here you can see that
MyTypeAttributes
,MyTypeRelationships
, andType
are not generated in the openapi schema.It seems that the macro only goes deep one level, and doesn't consider that the fields of a struct can have schemas themselves.