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

The generated nswag contract from NSwag v14.0.0-preview009 failed to build #4572

Open
JinjinM opened this issue Nov 6, 2023 · 8 comments
Open

Comments

@JinjinM
Copy link

JinjinM commented Nov 6, 2023

Generated the c# contract from the NSwagStudio (v14.0.00) based on the API spec from NSwag v14.0.0-preview009.
The generated contracts code failed to compile due to the below issues:
(1) new generated partial class IntPtr confused the complier
Issue message: The call is ambitious between the following methods or properties.
Screen shot:
image

It looks like it got confused with the new introduced class IntPtr at the "contracts.nswag.cs"
(2) Several new properties in the new class IntPtr errors

Issue message: Attribute 'xxx' is not valid on this declaration type. It is only valid on 'property, indexer' declarations.
Screen shot:
image

Update: It looks like the problem is caused by difference with the swagger json generated between the old version and the newer version:
Using the older version of Nswag( NSwag.AspNetCore version: 13.18.0 and NSwag.MSBuild version: 13.15.0)
The swagger json has the below section:

 "Exception": {
      "type": "object",
      "required": [
        "Message"
      ],
      "properties": {
        "Message": {
          "type": "string"
        },
        "InnerException": {
          "$ref": "#/definitions/Exception"
        },
        "Source": {
          "type": "string"
        },
        "StackTrace": {
          "type": "string"
        }
      }
}

Whereas using the newer version of Nswag (NSwag.AspNetCore version: 14.0.0-preview009 and NSwag.MSBuild version: 14.0.0-preview009)
The swagger json has added extra bit required fields for the "Exception" and also have extra new models, such as "MethodBase", "MethodAttributes" and ect which are the public properties of the class "System.Exception":

 "Exception": {
      "type": "object",
      "required": [
        "hasBeenThrown",
        "message",
        "data",
        "hResult"
      ],
      "properties": {
        "targetSite": {
          "$ref": "#/definitions/MethodBase"
        },
        "hasBeenThrown": {
          "type": "boolean"
        },
        "serializationWatsonBuckets": {
          
        },
        "message": {
          "type": "string"
        },
        "data": {
          "type": "array",
          "items": {
            "additionalProperties": {
              
            }
          }
        },
        "innerException": {
          "$ref": "#/definitions/Exception"
        },
        "helpLink": {
          "type": "string"
        },
        "source": {
          "type": "string"
        },
        "hResult": {
          "type": "integer",
          "format": "int32"
        },
        "stackTrace": {
          "type": "string"
        },
        "serializationStackTraceString": {
          "type": "string"
        }
      }
    },
    "MethodBase": {
      "allOf": [
        {
          "$ref": "#/definitions/MemberInfo"
        },
        {
          "type": "object",
          "x-abstract": true,
          "required": [
            "attributes",
            "methodImplementationFlags",
            "callingConvention",
            "isAbstract",
            "isConstructor",
            "isFinal",
            "isHideBySig",
            "isSpecialName",
            "isStatic",
            "isVirtual",
            "isAssembly",
            "isFamily",
            "isFamilyAndAssembly",
            "isFamilyOrAssembly",
            "isPrivate",
            "isPublic",
            "isConstructedGenericMethod",
            "isGenericMethod",
            "isGenericMethodDefinition",
            "containsGenericParameters",
            "methodHandle",
            "isSecurityCritical",
            "isSecuritySafeCritical",
            "isSecurityTransparent"
          ],
          "properties": {
            "attributes": {
              "$ref": "#/definitions/MethodAttributes"
            },
            "methodImplementationFlags": {
              "$ref": "#/definitions/MethodImplAttributes"
            },
            "callingConvention": {
              "$ref": "#/definitions/CallingConventions"
            },
            "isAbstract": {
              "type": "boolean"
            },
            "isConstructor": {
              "type": "boolean"
            },
            "isFinal": {
              "type": "boolean"
            },
            "isHideBySig": {
              "type": "boolean"
            },
            "isSpecialName": {
              "type": "boolean"
            },
            "isStatic": {
              "type": "boolean"
            },
            "isVirtual": {
              "type": "boolean"
            },
            "isAssembly": {
              "type": "boolean"
            },
            "isFamily": {
              "type": "boolean"
            },
            "isFamilyAndAssembly": {
              "type": "boolean"
            },
            "isFamilyOrAssembly": {
              "type": "boolean"
            },
            "isPrivate": {
              "type": "boolean"
            },
            "isPublic": {
              "type": "boolean"
            },
            "isConstructedGenericMethod": {
              "type": "boolean"
            },
            "isGenericMethod": {
              "type": "boolean"
            },
            "isGenericMethodDefinition": {
              "type": "boolean"
            },
            "containsGenericParameters": {
              "type": "boolean"
            },
            "methodHandle": {
              "$ref": "#/definitions/RuntimeMethodHandle"
            },
            "isSecurityCritical": {
              "type": "boolean"
            },
            "isSecuritySafeCritical": {
              "type": "boolean"
            },
            "isSecurityTransparent": {
              "type": "boolean"
            }
          }
        }
      ]
    },

image

.....

When I tried to use the contracts that generated by the new version of Nswag, these extra class models have been conflicting the same class models from "system.namespace", which caused the project failed to build. I was expecting the models would stay unchanged because my code stays same.

@michaelSant0s
Copy link

I have the same problem, is there any workaround?

@simeyla
Copy link

simeyla commented Feb 13, 2024

Same issue. Interestingly I only discovered it because it generated the following invalid code. Yes it's duplicates some enum values and not put a | separator between them.

export type MethodAttributes = "PrivateScope""PrivateScope" | "Private" | "FamANDAssem" | "Assembly" | "Family" | "FamORAssem" | "Public" | "MemberAccessMask" | "UnmanagedExport" | "Static" | "Final" | "Virtual" | "HideBySig" | "NewSlot" | "NewSlot" | "CheckAccessOnOverride" | "Abstract" | "SpecialName" | "RTSpecialName" | "PinvokeImpl" | "HasSecurity" | "RequireSecObject" | "ReservedMask";

export type MethodImplAttributes = "IL""IL" | "Native" | "OPTIL" | "CodeTypeMask" | "CodeTypeMask" | "ManagedMask" | "ManagedMask" | "NoInlining" | "ForwardRef" | "Synchronized" | "NoOptimization" | "PreserveSig" | "AggressiveInlining" | "AggressiveOptimization" | "InternalCall" | "MaxMethodImplVal";

@simeyla
Copy link

simeyla commented Feb 13, 2024

I couldn't find any workable solution right now other than to switch to using NewtonsoftJsonSchemaGeneratorSettings. Your settings may vary.

        services.AddOpenApiDocument(document =>
        {
            document.DocumentName = "a";

            document.SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings()
            {
                SchemaType = NJsonSchema.SchemaType.OpenApi3,

                SerializerSettings = new JsonSerializerSettings()
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver(),
                    Converters = new[]
                    {
                        new StringEnumConverter()
                    }
                }
            };

By default in v14.0 it uses the System.Text generator settings, which for my project at least I found incredibly frustrating to try to get it to match what I had in v13 - especially given years of work.

  • Could not figure out how to get a required property that was also nullable. (I need prop: string | null in my Angular client)
  • Couldn't see a way to not have the extra properties on Exception such as property of type MethodBase to be serialized
  • Very unexpected behavior with private setter only fields.

This allowed me to get back to where I was before in .NET Core 7.

Be careful to check for places where you have JsonPropertyName() but not JsonProperty(). Do a diff between your old .json and new .json open API specs.

@JinjinM
Copy link
Author

JinjinM commented Feb 20, 2024

I parked this issue for a while until now I am back to look at this again.
The project I worked on is using System.Text.Json which was being agreed with peers, so it would be better to stick with it unless the road is dead.
I will post updates if I can find something useful

@RanjithkumarRajendran
Copy link

RanjithkumarRajendran commented Feb 28, 2024

Tried with the version: 14.0.3, still having same issue
I am also facing the same issue while generating the client code for Typescript. any one got any workaround or solution for this issues?

@JinjinM
Copy link
Author

JinjinM commented Feb 29, 2024

@RanjithkumarRajendran Unfortunately, I have not find a workable solution yet

@devinlyons
Copy link

I was able to work around it by adding the following to my NSwagStudio configuration. I'm not sure why NSwag was trying to generate code for a bunch of built-in reflection types, but here we are...

"codeGenerators": {
    "excludedTypeNames": [
        "MemberInfo",
        "EventInfo",
        "Anonymous",
        "TypeInfo",
        "MethodBase",
        "RuntimeMethodHandle",
        "IntPtr",
        "MethodAttributes",
        "MethodImplAttributes",
        "CallingConventions",
        "MemberTypes",
        "Module",
        "Assembly",
        "ConstructorInfo",
        "EventAttributes",
        "MethodInfo",
        "ParameterInfo",
        "ParameterAttributes",
        "CustomAttributeData",
        "CustomAttributeTypedArgument",
        "PropertyInfo",
        "FieldAttributes",
        "RuntimeFieldHandle",
        "PropertyAttributes",
        "SecurityRuleSet",
        "ModuleHandle",
        "FieldInfo",
        "ICustomAttributeProvider",
        "CustomAttributeNamedArgument"
      ],

I, also, had to correct some of the generated code at the end of the client file. The commented-out code and the line beneath it are the key. The rest is provided for context.

private void SetExceptionFieldValue(Newtonsoft.Json.Linq.JObject jObject, string propertyName, object value, string fieldName, Newtonsoft.Json.Serialization.IContractResolver resolver, Newtonsoft.Json.JsonSerializer serializer)
{
    var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(System.Exception)).GetDeclaredField(fieldName);
    var jsonPropertyName = resolver is Newtonsoft.Json.Serialization.DefaultContractResolver ? ((Newtonsoft.Json.Serialization.DefaultContractResolver)resolver).GetResolvedPropertyName(propertyName) : propertyName;
    // Workaround due to bug in NSwagStudio 14.0.3.0
    // foreach (var p in jObject.Properties())
    foreach (var property in jObject.Properties())
    {
        if (System.String.Equals(property.Name, jsonPropertyName, System.StringComparison.OrdinalIgnoreCase))
        {
            var fieldValue = property.Value.ToObject(field.FieldType, serializer);
            field.SetValue(value, fieldValue);
            break;
        }
    }
}

@devinlyons
Copy link

devinlyons commented Mar 22, 2024

On further investigation, it looks like NSwagStudio is trying to generate these types because NSwag.AspNetCore is generating an OpenAPI spec with an endpoint that uses the Exception type as a payload for 5xx errors. I'm not sure why it's doing that, but I am going to see if I can figure it out.

UPDATE:
One of my controllers was responding with the Exception type in certain cases. At least that's what the ProducesResponseType attribute said. Once I fixed this, everything started working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants