openapi-generator icon indicating copy to clipboard operation
openapi-generator copied to clipboard

[BUG][Rust-Axum] Validation creates invalid code with certain schemas

Open myz-dev opened this issue 1 year ago • 0 comments

Bug Report Checklist

  • [x] Have you provided a full/minimal spec to reproduce the issue?
  • [x] Have you validated the input using an OpenAPI validator (example)?
  • [x] Have you tested with the latest master to confirm the issue still exists?
  • [x] Have you searched for related issues/PRs?
  • [x] What's the actual output vs expected output? Expected: code that compiles. Actual: code with a signature conflict.
  • [ ] [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

A schema that defines a type with a validation pattern causes the code generation step to produce code that fails to compile:

   |
29 | #[derive(validator::Validate)]
   |          ^^^^^^^^^^^^^^^^^^^ expected `&[u8]`, found `&Email`
...
32 |     #[validate(custom(function = "validate_byte_mailputbodyvalidator"))]
   |                                  ------------------------------------ arguments to this function are incorrect
   |
   = note: expected reference `&[u8]`
              found reference `&'a Email`
note: function defined here
  --> output/rust-axum-validation-test/src/server/mod.rs:39:4
   |
39 | fn validate_byte_mailputbodyvalidator(
   |    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40 |     b: &[u8],
   |     --------
   = note: this error originates in the derive macro `validator::Validate` (in Nightly builds, run with -Z macro-backtrace for more info)
openapi-generator version

7.6.0-SNAPSHOT

OpenAPI declaration file content or url
openapi: 3.0.1
info:
  title: Test to check that validation logic is not rendered when disabled.
  version: 0.0.1
paths:
   /mail:
    put:
      description: Updates the email.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Email"
      responses:
        "204":
          description: OK.
components:
  schemas:
    Email:
      type: string
      pattern: '^[\w\-\.]+@([\w-]+\.)+[\w-]{2,}$'
      example: [email protected]
Generation Details

This happens also when the CLI option disableValidator is set to true. This option makes the code generation step not call the function that causes the compilation error. But it still generates the faulty code so the project can not be compiled.

Steps to reproduce

java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -i validation_example.yaml -g rust-axum -o out --additional-properties disableValidator=true

Suggest a fix

The first step is to not create the validation code at all when disableValidator is set to true. This is an easy fix that I can provide quickly. The second step would be to create valid code when it needs to be created. Manually this can be solved with a different function signature and the implementation of a conversion trait, but I have not yet understood how to solve this generally in the code generation process. Manual fix example:

Faulty:

    #[derive(validator::Validate)]
    #[allow(dead_code)]
    struct MailPutBodyValidator<'a> {
          #[validate(
                  custom(function = "validate_byte_mailputbodyvalidator"),
              )]
          body: &'a models::Email,
    }

    lazy_static::lazy_static! {
        static ref RE_MAILPUTBODYVALIDATOR: regex::bytes::Regex = regex::bytes::Regex::new(r"^[\\w\\-\\.]+@([\\w-]+\\.)+[\\w-]{2,}$").unwrap();
    }
    fn validate_byte_mailputbodyvalidator(
        b: &[u8]
    ) -> std::result::Result<(), validator::ValidationError> {
        if !RE_MAILPUTBODYVALIDATOR.is_match(b) {
            return Err(validator::ValidationError::new("Character not allowed"));
        }
        Ok(())
    }

Fix:

#[derive(validator::Validate)]
#[allow(dead_code)]
struct MailPutBodyValidator<'a> {
    #[validate(custom(function = "validate_byte_mailputbodyvalidator"))]
    body: &'a models::Email,
}

lazy_static::lazy_static! {
    static ref RE_MAILPUTBODYVALIDATOR: regex::bytes::Regex = regex::bytes::Regex::new(r"^[\\w\\-\\.]+@([\\w-]+\\.)+[\\w-]{2,}$").unwrap();
}
fn validate_byte_mailputbodyvalidator(
    b: &models::Email, // <-- change signature
) -> std::result::Result<(), validator::ValidationError> {
    let b = b.into();  // <-- call conversion
    if !RE_MAILPUTBODYVALIDATOR.is_match(b) {
        return Err(validator::ValidationError::new("Character not allowed"));
    }
    Ok(())
}

impl<'a> From<&'a models::Email> for &'a [u8] {   // <-- implement conversion
    fn from(value: &'a models::Email) -> Self {
        value.as_bytes()
    }
}

This manual solution fails as soon as the model to convert does not implement a as_bytes() method. So I am trying to figure out how to get to a more general solution.

myz-dev avatar May 06 '24 14:05 myz-dev