OpenAPITools / OpenAPITools/openapi-generator

[BUG] [Pistache] Validation is incomplete on model structs which have a child model struct

Open
#16,717 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

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

When an object contains another object, the generated validation method doesn't call the generated validation method of the child object.

openapi-generator version

Release 7.0.1

OpenAPI declaration file content or url

Extract of YAML description of the API :

    card_desc:
      type: object
      properties:
        vip_level:
          type: integer
          minimum: 0
        comments:
          description: Additionnal comments
          type: string
          maxLength: 100
      required:
        - vip_level
    card:
      type: object
      properties:
        id:
          description: Unique id = RFID tag id
          type: string
          minLength: 1
          maxLength: 36
        desc:
          description: Description
          $ref: "#/components/schemas/card_desc"
      required:
        - id
        - desc
Generation Details

Generated code for card_desc:

bool Card_desc::validate(std::stringstream& msg, const std::string& pathPrefix) const
{
    bool success = true;
    const std::string _pathPrefix = pathPrefix.empty() ? "Card_desc" : pathPrefix;

    /* vip_level */ {
        const int32_t& value = vip_level;
        const std::string currentValuePath = _pathPrefix + ".vipLevel";       
        if (value < 0)
        {
            success = false;
            msg << currentValuePath << ": must be greater than or equal to 0;";
        }
    }
             
    if (comments.has_value())
    {
        const std::string& value = comments.value();
        const std::string currentValuePath = _pathPrefix + ".comments"; 
        if (value.length() > 100)
        {
            success = false;
            msg << currentValuePath << ": must be at most 100 characters long;";
        }
    } 
    return success;
}

=> Ok, all fields are checked

Generated code for card:

bool Card::validate(std::stringstream& msg, const std::string& pathPrefix) const
{
    bool success = true;
    const std::string _pathPrefix = pathPrefix.empty() ? "Card" : pathPrefix;

    /* id */ {
        const std::string& value = id;
        const std::string currentValuePath = _pathPrefix + ".id";

        if (value.length() < 1)
        {
            success = false;
            msg << currentValuePath << ": must be at least 1 characters long;";
        }
        if (value.length() > 36)
        {
            success = false;
            msg << currentValuePath << ": must be at most 36 characters long;";
        }
    }
        
    return success;
}

=> Ko, the desc member is not validated!

Suggest a fix

Here is a suggestion which fixes the issue :

diff --git a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-source.mustache b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-source.mustache
index 4d7dbfe0a90..666d4599d3f 100644
--- a/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-source.mustache
+++ b/modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-source.mustache
@@ -56,18 +56,34 @@ bool {{classname}}::validate(std::stringstream& msg, const std::string& pathPref
         const std::string currentValuePath = _pathPrefix + ".{{nameInCamelCase}}";
         {{> model-validation-body }}
     }
-    {{/isArray}}{{^isArray}}{{#hasValidation}} {{! Only generate validation if necessary }}
+    {{/isArray}}
+    {{^isArray}}
+    {{#hasValidation}} {{! Only generate validation if necessary }}
     {{^required}}if ({{name}}.has_value()){{/required}}
     {{#required}}/* {{name}} */ {{/required}}{
         const {{{dataType}}}& value = {{name}}{{^required}}.value(){{/required}};
         const std::string currentValuePath = _pathPrefix + ".{{nameInCamelCase}}";
         {{> model-validation-body }}
     }
-    {{/hasValidation}}{{/isArray}}{{/vars}}{{/isEnum}}{{#vendorExtensions.x-is-string-enum-container}}{{#anyOf}}{{#-first}}
+    {{/hasValidation}}
+    {{#isModel}}
+    {{^required}}if ({{name}}.has_value()){{/required}}
+    {{#required}}/* {{name}} */ {{/required}}{
+        const {{{dataType}}}& value = {{name}}{{^required}}.value(){{/required}};
+        const std::string currentValuePath = _pathPrefix + ".{{nameInCamelCase}}";
+        if (!value.validate(msg, currentValuePath))
+        {
+            success = false;
+        }
+    }
+    {{/isModel}}
+    {{/isArray}}{{/vars}}
+    {{/isEnum}}{{#vendorExtensions.x-is-string-enum-container}}{{#anyOf}}{{#-first}}
     if (!m_value.validate(msg))
     {
         success = false;
     }{{/-first}}{{/anyOf}}{{/vendorExtensions.x-is-string-enum-container}}
+
     return success;
 }

Which leads to the following code for card :

bool Card::validate(std::stringstream& msg, const std::string& pathPrefix) const
{
    bool success = true;
    const std::string _pathPrefix = pathPrefix.empty() ? "Card" : pathPrefix;

    /* id */ {
        const std::string& value = id;
        const std::string currentValuePath = _pathPrefix + ".id";
       
        if (value.length() < 1)
        {
            success = false;
            msg << currentValuePath << ": must be at least 1 characters long;";
        }
        if (value.length() > 36)
        {
            success = false;
            msg << currentValuePath << ": must be at most 36 characters long;";
        }
    }
                
    /* desc */ {
        const org::openapitools::server::model::Card_desc& value = desc;
        const std::string currentValuePath = _pathPrefix + ".desc";
        if (!value.validate(msg, currentValuePath))
        {
            success = false;
        }
    }
    
    return success;
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with modules/openapi-generator/src/main/resources/cpp-pistache-server/model-struct-source.mustache and generate the C++ Pistache models from the supplied YAML declaration. Check that a parent model's validate method invokes validation for its child model, including the child path prefix; done means nested constraints such as vip_level and comments are reported.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
api, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.