Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Component from external crate #507

Open
samchouse opened this issue Mar 8, 2023 · 6 comments
Open

Component from external crate #507

samchouse opened this issue Mar 8, 2023 · 6 comments

Comments

@samchouse
Copy link
Contributor

How would I use a struct from an external crate as a component. I want to use the ValidationErrors struct from the validator library.

@Hastaroth1
Copy link

Hastaroth1 commented Mar 11, 2023

There are 3 ways I can think of to do this. I'm not sure if there are easier ways.

  1. Have a struct that has the same structure as the external one and make the schema point to it instead
#[derive(OpenApi)]
#[openapi(components(schemas(Wawa, FakeWowoStruct)))] // <-- Can't add external::Wowo to schemas
struct ApiDoc;

#[derive(ToSchema)]
struct Wawa {
    wowo: Wowo,
}

#[derive(ToSchema)]
#[schema(as = Wowo)] // <-- change the name in the schema to the external struct
struct FakeWowoStruct {
    name: String,
}

mod external {
    // Can't derive ToSchema
    pub struct Wowo {
        name: String,
    }
}
  1. Merging schemas:
fn get_extra_schema() -> openapi::OpenApi {
    utoipa::openapi::OpenApiBuilder::new()
        .components(Some(
            utoipa::openapi::ComponentsBuilder::new()
                .schema(
                    // Manually construct external schema
                    "Wowo",
                    utoipa::openapi::ObjectBuilder::new()
                        .property(
                            "name",
                            utoipa::openapi::ObjectBuilder::new()
                                .schema_type(utoipa::openapi::SchemaType::String),
                        )
                        .required("name"),
                )
                .build(),
        ))
        .build()
}

fn main() {
    let mut openapi = ApiDoc::openapi();

    let merge = get_extra_schema();

    openapi.merge(merge);

    println!("{}", openapi.to_pretty_json().unwrap());
}
  1. Implement ToSchema for a dummy struct:
struct FakeWowo {}

impl<'s> utoipa::ToSchema<'s> for FakeWowo {
    fn schema() -> (
        &'s str,
        utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
    ) {
        (
            "Wowo", // <-- Set the name to the external struct
            utoipa::openapi::ObjectBuilder::new()
                .property(
                    "name",
                    utoipa::openapi::ObjectBuilder::new()
                        .schema_type(utoipa::openapi::SchemaType::String),
                )
                .required("name")
                .into(),
        )
    }
}

#[derive(OpenApi)]
#[openapi(components(schemas(Wawa, FakeWowo)))] // <-- Add the dummy struct

All 3 should give you a document like this:

image

Another option is to contribute to the external library and add the necessary derives for Utoipa.

The first option isn't too different from what Serde does when you want to deserialize a struct from an external crate https://serde.rs/remote-derive.html

@samchouse
Copy link
Contributor Author

The #[schema(as = )] doesn't work for enums, it has an error of unexpected attribute: as, expected any of: example, default, rename_all. Is there any way to make this work?

@juhaku
Copy link
Owner

juhaku commented Mar 13, 2023

@Hastaroth1 Thanks for detailed explanation, those are the options to choose from to provide ToSchema for an external crate type.

@juhaku
Copy link
Owner

juhaku commented Mar 13, 2023

The #[schema(as = )] doesn't work for enums, it has an error of unexpected attribute: as, expected any of: example, default, rename_all. Is there any way to make this work?

This seems like a bug, I will shortly test it out, it should work. I looked the code and the As attribute declaration is defined on simple enums but is missing from complex enums (enums that nested type arguments). I'll add a test for this and try to get it working.

@that-ambuj
Copy link

that-ambuj commented Jun 15, 2024

@Hastaroth1 Is there any way we can have a macro that we pass the type to and it automatically generates a 'local' type with a Suffix so that we don't have to maintain fields of the external type?

We are having this problem because a certain crate we are using update their types without notice and our local types becomes out of sync.

Something like this:

use external::Wawa;

the_macro_in_question!(Wawa); // generates a local type called `WawaSchema`

@juhaku
Copy link
Owner

juhaku commented Sep 4, 2024

@that-ambuj There is no such macro and creating one would be too much effort. Such macro would most likely need a path to the file where such type is located then to only parse the file and find the wanted type and try to create a schema for it. E.g. something like this.

magic_macro!("path/to/file/of/the/type.rs" => struct Wawa);

This is because there is no reflection and simply relying on token stream cannot access to external crates files unless explicitly told so. (What to find and where).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants