NSwag client code generation: optional/required properties

170 Views Asked by At

I'm trying to generate C# client code for a 3rd-party REST API (Adobe Sign Transient Documents). The API definition provided by Adobe is swagger 1.2, so I used swagger.io tools to convert it to openapi 3.0.1.

The problem I am seeing is that the two properties in the requestBody that are optional (File-Name & Mime-Type) have arguments with a default value of null in the generated method signature but then the code throws an error if the values are null. The one required property (File) is treated the same way.

I expected to see the optional properties defined as they are in the method signature but without the error thrown when set to null in the code. I expected the required property not to have a default value set in the method signature. The arguments for the header parameters are generated as expected.

I have looked for settings that could control this behavior but couldn't find anything that applied. I tried setting "generateOptionalParameters": false but that just made everything required.

Any help for what I might be missing would be greatly appreciated.

API definition converted to openapi 3.0.1

openapi: 3.0.1
info:
  title: Adobe Sign Transient Documents API
  version: 6.0.0
servers:
  - url: https://secure.na1.adobesign.com/api/rest/v6
paths:
  /transientDocuments:
    post:
      tags:
        - transientDocuments
      summary: Uploads a document and obtains the document's ID.
      description: The document uploaded through this call is referred to as transient since it is available only for 7 days after the upload. The returned transient document ID can be used in the API calls where the uploaded file needs to be referred. The transient document request is a multipart request consisting of three parts - filename, mime type and the file stream. You can only upload one file at a time in this request.
      operationId: createTransientDocument
      parameters:
        - name: Authorization
          in: header
          description: 'An <a href="#" class="link-oauth">OAuth Access Token</a> with any of the following scopes:<ul><li style=''list-style-type: square''><a href="#" class="link-oauth" data-link-scope="scope-agreement_write">agreement_write</a></li><li style=''list-style-type: square''><a href="#" class="link-oauth" data-link-scope="scope-agreement_sign">agreement_sign</a></li><li style=''list-style-type: square''><a href="#" class="link-oauth" data-link-scope="scope-widget_write">widget_write</a></li><li style=''list-style-type: square''><a href="#" class="link-oauth" data-link-scope="scope-library_write">library_write</a></li></ul>in the format <b>''Bearer {accessToken}''.'
          required: true
          schema:
            type: string
        - name: x-api-user
          in: header
          description: The userId or email of API caller using the account or group token in the format <b>userid:{userId} OR email:{email}.</b> If it is not specified, then the caller is inferred from the token.
          schema:
            type: string
        - name: x-on-behalf-of-user
          in: header
          description: The userId or email in the format <b>userid:{userId} OR email:{email}.</b> of the user that has shared his/her account
          schema:
            type: string
      requestBody:
        content:
          multipart/form-data:
            schema:
              required:
                - File
              type: object
              properties:
                File-Name:
                  type: string
                  description: A name for the document being uploaded. Maximum number of characters in the name is restricted to 255.
                Mime-Type:
                  type: string
                  description: The mime type of the document being uploaded. If not specified here then mime type is picked up from the file object. If mime type is not present there either then mime type is inferred from file name extension.
                File:
                  type: string
                  description: The file part of the multipart request for document upload. You can upload only one file at a time.
                  format: binary
        required: true
      responses:
        '200':
          description: success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransientDocumentResponse'
        '400':
          description: 'NO_FILE_NAME: Must provide file name.'
          content: {}
        '401':
          description: 'NO_AUTHORIZATION_HEADER: Authorization header not provided.'
          content: {}
        '403':
          description: 'PERMISSION_DENIED: The API caller does not have the permission to execute this operation.'
          content: {}
        '404':
          description: 'TRANSIENT_RESOURCE_VIRUS_DETECTED: The transient document was deleted because a virus was detected in the file.'
          content: {}
        '415':
          description: 'UNSUPPORTED_MEDIA_TYPE: Content type was not provided or is not supported.'
          content: {}
        '500':
          description: 'MISC_SERVER_ERROR: Some miscellaneous error has occurred.'
          content: {}
      security:
        - oauth2:
            - agreement_write
            - agreement_sign
            - widget_write
            - library_write
components:
  securitySchemes:
    oauth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://secure.na1.adobesign.com/public/oauth/v2
          tokenUrl: https://secure.na1.adobesign.com/public/oauth/v2/token
          scopes:
            agreement_write: agreement write
            agreement_sign: agreement sign
            widget_write: widget write
            library_write: library write
  schemas:
    TransientDocumentResponse:
      type: object
      properties:
        transientDocumentId:
          type: string
          description: The unique identifier of the uploaded document that can be used in an agreement or a bulk send or web form creation call

nswag.json file

{
  "runtime": "Net60",
  "defaultVariables": null,
  "documentGenerator": {
    "fromDocument": {
      "json": "openapi.json",
      "output": null,
      "newLineBehavior": "Auto"
    }
  },
  "codeGenerators": {
    "openApiToCSharpClient": {
      "clientBaseClass": null,
      "configurationClass": null,
      "generateClientClasses": true,
      "generateClientInterfaces": true,
      "clientBaseInterface": null,
      "injectHttpClient": true,
      "disposeHttpClient": true,
      "protectedMethods": [],
      "generateExceptionClasses": true,
      "exceptionClass": "ApiException",
      "wrapDtoExceptions": true,
      "useHttpClientCreationMethod": false,
      "httpClientType": "System.Net.Http.HttpClient",
      "useHttpRequestMessageCreationMethod": false,
      "useBaseUrl": false,
      "generateBaseUrlProperty": true,
      "generateSyncMethods": true,
      "generatePrepareRequestAndProcessResponseAsAsyncMethods": false,
      "exposeJsonSerializerSettings": false,
      "clientClassAccessModifier": "public",
      "typeAccessModifier": "public",
      "generateContractsOutput": false,
      "contractsNamespace": null,
      "contractsOutputFilePath": null,
      "parameterDateTimeFormat": "s",
      "parameterDateFormat": "yyyy-MM-dd",
      "generateUpdateJsonSerializerSettingsMethod": true,
      "useRequestAndResponseSerializationSettings": false,
      "serializeTypeInformation": false,
      "queryNullValue": "",
      "className": "Client",
      "operationGenerationMode": "MultipleClientsFromOperationId",
      "additionalNamespaceUsages": [],
      "additionalContractNamespaceUsages": [],
      "generateOptionalParameters": true,
      "generateJsonMethods": false,
      "enforceFlagEnums": false,
      "parameterArrayType": "System.Collections.Generic.IEnumerable",
      "parameterDictionaryType": "System.Collections.Generic.IDictionary",
      "responseArrayType": "System.Collections.Generic.ICollection",
      "responseDictionaryType": "System.Collections.Generic.IDictionary",
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "SwaggerResponse",
      "namespace": "Adobe.AcrobatSign.WebService",
      "requiredPropertiesMustBeDefined": true,
      "dateType": "System.DateTimeOffset",
      "jsonConverters": null,
      "anyType": "object",
      "dateTimeType": "System.DateTimeOffset",
      "timeType": "System.TimeSpan",
      "timeSpanType": "System.TimeSpan",
      "arrayType": "System.Collections.Generic.ICollection",
      "arrayInstanceType": "System.Collections.ObjectModel.Collection",
      "dictionaryType": "System.Collections.Generic.IDictionary",
      "dictionaryInstanceType": "System.Collections.Generic.Dictionary",
      "arrayBaseType": "System.Collections.ObjectModel.Collection",
      "dictionaryBaseType": "System.Collections.Generic.Dictionary",
      "classStyle": "Poco",
      "jsonLibrary": "NewtonsoftJson",
      "generateDefaultValues": true,
      "generateDataAnnotations": true,
      "excludedTypeNames": [],
      "excludedParameterNames": [],
      "handleReferences": false,
      "generateImmutableArrayProperties": false,
      "generateImmutableDictionaryProperties": false,
      "jsonSerializerSettingsTransformationMethod": null,
      "inlineNamedArrays": false,
      "inlineNamedDictionaries": false,
      "inlineNamedTuples": true,
      "inlineNamedAny": false,
      "generateDtoTypes": true,
      "generateOptionalPropertiesAsNullable": false,
      "generateNullableReferenceTypes": false,
      "templateDirectory": null,
      "typeNameGeneratorType": null,
      "propertyNameGeneratorType": null,
      "enumNameGeneratorType": null,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": "openapi.cs",
      "newLineBehavior": "Auto"
    }
  }
}

Generated code (relevant portions)

        public virtual async System.Threading.Tasks.Task<TransientDocumentResponse> CreateTransientDocumentAsync(string authorization, string x_api_user = null, string x_on_behalf_of_user = null, string file_Name = null, string mime_Type = null, FileParameter file = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
        {
            ...
                    if (file_Name == null)
                        throw new System.ArgumentNullException("file_Name");
                    else
                    {
                        content_.Add(new System.Net.Http.StringContent(ConvertToString(file_Name, System.Globalization.CultureInfo.InvariantCulture)), "File-Name");
                    }

                    if (mime_Type == null)
                        throw new System.ArgumentNullException("mime_Type");
                    else
                    {
                        content_.Add(new System.Net.Http.StringContent(ConvertToString(mime_Type, System.Globalization.CultureInfo.InvariantCulture)), "Mime-Type");
                    }
            ...
        }
0

There are 0 best solutions below