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

Custom path description #439

Closed
PierreNeumann opened this issue Jan 16, 2023 · 8 comments · Fixed by #948
Closed

Custom path description #439

PierreNeumann opened this issue Jan 16, 2023 · 8 comments · Fixed by #948

Comments

@PierreNeumann
Copy link

Hi,

I have some code documentation for my handler that I don't want to appear in the path description field.
I see that the path description field is automatically filled with everything having the doc attribute.

How can I set a custom description for the API endpoint so the user don't see all the code documentation ?

@juhaku
Copy link
Owner

juhaku commented Jan 25, 2023

Hey @PierreNeumann ,

Unfortunately this is not possible at the moment via #[utoipa::path] macro. Only way to do this now is manually edit the generated OpenApi. This is not very elegant though especially if there is many such endpoints. One way to do this is though implement path manually for the OpenApi and with that creating a custom macro rules to help with boilerplate.

What kind of functionality you had in mind? E.g. it would be quite easy to implement functionality for users to define custom description = ... attribute in utoipa path macro as seen below. But would it be sufficient?

#[utoipa::path(
    description = "This is description override",
)]

@PierreNeumann
Copy link
Author

PierreNeumann commented Jan 25, 2023

Hi,

I found a workaround.
I created another function that contain all my handler logic and the "private" documentation.
Then the handler call this function and is documented with the public utoipa documentation.

Example :

/// Do some work on a JSON Message
/// 
/// The public documentation I want to show on the swagger ui.
#[utoipa::path(
    post,
    path = "/work",
    responses(
        (status = 200, description = "Work done !", body = String)
    ),
    request_body = Message,
)]
pub async fn handler(
    item: web::Json<Message>,
    ) -> impl Responder {
    do_work(item)
}

/// Work on Json Message
///
/// All private documentation I don't want to show on the swagger ui.
/// 
/// # Arguments
/// * `item` - The JSON Message
///
/// # Example
/// ```bash
/// curl -X POST http://localhost:8080/work -d "Hello World"
/// ```
pub fn do_work(
    item: web::Json<Message>,
) -> HttpResponse {

    /*  */
    let res = work(item);

    match res {
        Ok(_) => HttpResponse::Ok().json("Work done !".to_string()),
        Err(_e) => {
            HttpResponse::InternalServerError().body("Error working on content".to_string())
        }
    }
}

@juhaku
Copy link
Owner

juhaku commented Jan 25, 2023

This is nice, and probalby most feasible solution.

@nacairns1
Copy link

nacairns1 commented Feb 24, 2023

Hey I wanted to bump this. I think having a custom Summary and Description via the macro attributes would be very nice and consistent with the rest of the API. That would look something like this:

#[utoipa::path(
    description = "This is description override",
    summary = "This is summary override",
)]

The reason for why I believe this would be really helpful is that even when I try to modify the path description at runtime, it always says the description is a None value. This happens when using the doc comments for a description. Unfortunately that means I have no means of modifying the description and therefore the summary at runtime

@juhaku
Copy link
Owner

juhaku commented Mar 13, 2023

Support for such fields could be added in future.

I'm wondering if it's possible to define a generic response body at the ApiDoc level so that Utoipa can wrap my body in the specified response body.

This sounds like a bug but it would need more investigation though. Normally it should allow updating the description during runtime after one has created the instance with let mut doc = ApiDoc::openapi(). And then updating the doc acconrding to the needs or via Modify trait.

@chrsteer
Copy link

I was also looking for a way to put the summary and description inside the path macro, because it bothered me that the first line was included in both fields.

Since this was not yet implemented, I came up with the following solution, thanks to the comment from @juhaku. It should solve all the problems discussed here, without having to define additional methods for each api endpoint that needs private documentation.

Here is the code I used to modify the description:

let mut raw_api = apidoc::ApiDoc::openapi();
raw_api
    .paths
    .paths
    .iter_mut()
    .flat_map(|(_, path)| &mut path.operations)
    .for_each(|(_, operation)| {
        if let Some(raw_description) = operation.description.as_ref() {
            let (summary, desc) = raw_description
                .split_once("\n\n")
                .unwrap_or((raw_description, ""));
            operation.summary = Some(summary.into());
            operation.description = if desc.is_empty() {
                None
            } else {
                let public_desc = desc.split("PRIVACY_SEPARATOR").next().unwrap_or(desc);
                Some(public_desc.to_string())
            };
        }
    });
let apidoc = raw_api.to_yaml()?;

... and here the dummy function with a doc string:

/// Upload file (summary only)
///
/// Upload a new file to the server (description only)
///
/// ... possibly more text to be displayed in the description
///
/// PRIVACY_SEPARATOR (not visible in the documantation)
/// This part should not be visible in the OpenApi document
pub(crate) async fn upload() -> StatusCode {
    StatusCode::OK
}

This generates the following yaml:

/upload:
  post:
    tags:
    - crate::endpoints
    summary: Upload file (summary only)
    description: |+
      Upload a new file to the server (description only)

      ... possibly more text to be displayed in the description

    operationId: upload file

You could also use a specific string to separate the summary and the description - in my example I used the first blank line as separating element.

@nacairns1:
I think the reason you got None values is that you were looking for descriptions at path level and not at operations level.

@juhaku
Copy link
Owner

juhaku commented Oct 26, 2023

Releated #781

juhaku added a commit that referenced this issue May 23, 2024
Priot to this PR the summary and description was blindly split by the
first line of the doc comment of path operaiton. This caused long
multi line summaries to be split from illogically bleeding part of the
summary to the description. More context can be found from issue #942.

This commit fixes this as from now one comments as follows will be
correctly split by paragraph. Where first paragraph will resolve to
summary and rest will become the description.
```rust
 /// This is test operation long multiline
 /// summary. That need to be correctly split.
 ///
 /// Additional info in long description
 ///
 /// With more info on separate lines
 /// containing text.
 ///
 /// Yeah
```

Follow up for #881 and #439

Fixes #942
juhaku added a commit that referenced this issue May 23, 2024
Priot to this PR the summary and description was blindly split by the
first line of the doc comment of path operaiton. This caused long
multi line summaries to be split from illogically bleeding part of the
summary to the description. More context can be found from issue #942.

This commit fixes this as from now one comments as follows will be
correctly split by paragraph. Where first paragraph will resolve to
summary and rest will become the description.
```rust
 /// This is test operation long multiline
 /// summary. That need to be correctly split.
 ///
 /// Additional info in long description
 ///
 /// With more info on separate lines
 /// containing text.
 ///
 /// Yeah
```

Follow up for #881 and #439

Fixes #942
juhaku added a commit that referenced this issue May 24, 2024
Add `description = ...` and `summary = ...` attributes for
`#[utoipa::path(...)]` attribute macro to allow description and summary
overriding. When these attributes are defined the values are not
resolved from the doc comment above the function.

The value can be either literal string or expression e.g. reference to a
`const` variable or `include_str!(...)` statement.

Relates to #802

Resolves #439 Resolves #781
@juhaku juhaku moved this to In Progress in utoipa kanban May 24, 2024
@juhaku
Copy link
Owner

juhaku commented May 24, 2024

I have now implemented support for the description = ... and summary = ... attributes here #948

juhaku added a commit that referenced this issue May 24, 2024
Add `description = ...` and `summary = ...` attributes for
`#[utoipa::path(...)]` attribute macro to allow description and summary
overriding. When these attributes are defined the values are not
resolved from the doc comment above the function.

The value can be either literal string or expression e.g. reference to a
`const` variable or `include_str!(...)` statement.

Relates to #802

Resolves #439 Resolves #781
@github-project-automation github-project-automation bot moved this from In Progress to Done in utoipa kanban May 24, 2024
@juhaku juhaku moved this from Done to Released in utoipa kanban Oct 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Released
Development

Successfully merging a pull request may close this issue.

4 participants