OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
20.59k stars 6.29k forks source link

[BUG] [C#] Validate method throws ArgumentNullException #16001

Open witoldsz opened 1 year ago

witoldsz commented 1 year ago

Bug Report Checklist

Description

The model code produced by the YAML file looks like this:

        /// <summary>
        /// To validate all properties of the instance
        /// </summary>
        /// <param name="validationContext">Validation context</param>
        /// <returns>Validation Result</returns>
        IEnumerable<System.ComponentModel.DataAnnotations.ValidationResult> IValidatableObject.Validate(ValidationContext validationContext)
        {
            // paymentId (string) pattern
            Regex regexpaymentId = new Regex(@"^[a-zA-Z0-9_-]{1,36}$", RegexOptions.CultureInvariant);
            if (false == regexpaymentId.Match(this.paymentId).Success)
            {
                yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for paymentId, must match a pattern of " + regexpaymentId, new [] { "paymentId" });
            }

            // targetAmount (string) pattern
            Regex regextargetAmount = new Regex(@"^[0-9]{1,10}([.][0-9]{2})?$", RegexOptions.CultureInvariant);
            if (false == regextargetAmount.Match(this.targetAmount).Success) // EXCEPTION HERE
            {
                yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for targetAmount, must match a pattern of " + regextargetAmount, new [] { "targetAmount" });
            }
[...]

The code above will throw

Value cannot be null. (Parameter 'input')</h1><br/>System.ArgumentNullException: Value cannot be null. (Parameter 'input')
   at System.Text.RegularExpressions.ThrowHelper.ThrowArgumentNullException(ExceptionArgument arg)
   at System.Text.RegularExpressions.Regex.Match(String input)
   at PblInitRequest.System.ComponentModel.DataAnnotations.IValidatableObject.Validate(ValidationContext validationContext)

The this.targetAmount is null (optional field) but it is pushed into a Regex#Match method anyway.

openapi-generator version

6.6.0 Additionaly checked with csharp codegen (former csharp-core) of https://oss.sonatype.org/content/repositories/snapshots/org/openapitools/openapi-generator-cli/7.0.0-SNAPSHOT/openapi-generator-cli-7.0.0-20230703.163913-170.jar

OpenAPI declaration file content or url
openapi: 3.0.0
paths:
  /api/tpp/payment-gate/init:
    post:
      tags:
        - PBL
      summary: xxSummary
      operationId: pblInitRequest
      description: xxDescription
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PblInitRequest'
      responses:
        '200':
          description: >-
            xxDescription
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericInitResponseSuccess'

components:
  requestBodies:
    PblInitRequest:
      type: object
      required:
        - paymentId
      properties:
        paymentId:
          type: string
          pattern: /^[a-zA-Z0-9_-]{1,36}$/
          description: Unique payment id used to reference it all through the process.
        targetAmount:
          type: string
          pattern: /^[0-9]{1,10}([.][0-9]{2})?$/
          description: ------------ THIS FIELD IS OPTIONAL ------------ 
Generation Details
TPP_YAML = main/wwwroot/tpp/tpp.yaml
java -jar generated/openapi-generator-cli.jar generate \
-i $(TPP_YAML) \
-g csharp-netcore \
--additional-properties targetFramework=net6.0 \
--additional-properties packageName=${TPP_PKG} \
--additional-properties netCoreProjectFile=true \
--additional-properties library=generichost \
--additional-properties modelPropertyNaming=original \
--additional-properties dateTimeFormat=yyyy-MM-ddTHH:mm:ss.fff\'Z\' \
-o generated/tpp__temp
Related issues/PRs

Somehow related: #2760

Suggest a fix

Missing optional fields should not be validated against patterns (and possibly other formatting rules). Or maybe validation should always be protected with if (this.{{{name}}} != null && ....

witoldsz commented 1 year ago

This simple fix seems to work, for me at least:

$ git diff
diff --git a/modules/openapi-generator/src/main/resources/csharp/validatable.mustache b/modules/openapi-generator/src/main/resources/csharp/validatable.mustache
index cf24f2f94fd..f0e167c41a7 100644
--- a/modules/openapi-generator/src/main/resources/csharp/validatable.mustache
+++ b/modules/openapi-generator/src/main/resources/csharp/validatable.mustache
@@ -75,7 +75,7 @@
             {{^isByteArray}}
             // {{{name}}} ({{{dataType}}}) pattern
             Regex regex{{{name}}} = new Regex(@"{{{vendorExtensions.x-regex}}}"{{#vendorExtensions.x-modifiers}}{{#-first}}, {{/-first}}RegexOptions.{{{.}}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}});
-            if (false == regex{{{name}}}.Match(this.{{{name}}}{{#isUuid}}.ToString(){{/isUuid}}).Success)
+            if (this.{{{name}}} != null && false == regex{{{name}}}.Match(this.{{{name}}}{{#isUuid}}.ToString(){{/isUuid}}).Success)
             {
                 yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must match a pattern of " + regex{{{name}}}, new [] { "{{{name}}}" });
             }

if (this.{{{name}}} != null && was added before false == regex{{{name}}}.Match(…

wing328 commented 1 year ago

Can you please file a PR with the suggested fix? Thanks.

witoldsz commented 1 year ago

Can you please file a PR with the suggested fix? Thanks.

https://github.com/OpenAPITools/openapi-generator/pull/16021