RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.82k stars 1.3k forks source link

NSwag v14 preview announcement & breaking changes #4524

Open RicoSuter opened 1 year ago

RicoSuter commented 1 year ago

Please test the preview packages for NSwag v14 and NJsonSchema v11 and report problems here.

Note: NSwag/NJS still uses Newtonsoft.Json for serializing/deserializing schemas internally, so for now it still requires newtonsoft as a reference, internal serialization will hopefully migrated to STJ in the next major version.

This whole major version is about dropping old stuff to make the project maintainable again. I'm testing it with my more or less modern ASP.NET Core applications. The goal is that the tooling still works with them and there are no regressions or very hard to mitigate breaking changes.

NSwag v14

PR: https://github.com/RicoSuter/NSwag/pull/3758

Breaking changes

NJsonSchema v11

PR: https://github.com/RicoSuter/NJsonSchema/pull/1450 (merged)

Breaking changes

Other changes:

Numpsy commented 1 year ago

Hi,

The 14.0.0-preview004 package for NSwag.ApiDescription.Client seems to have a dependency of

NSwag.MSBuild (>= 14.0.0)

and when I try to update in Visual Studio it complains because NSwag.MSBuild only has 14.0.0-preview00N packages

RicoSuter commented 1 year ago

@Numpsy try v14.0.0-preview005

Numpsy commented 1 year ago

Ok, the new version has installed ok

olegd-superoffice commented 1 year ago

Both .NET Core 3.1 and .NET 5 are out of support already and .NET 8 is around the corner. So maybe drop support for old versions?

lahma commented 1 year ago

@olegd-superoffice there's https://github.com/RicoSuter/NSwag/pull/4001 for that

Numpsy commented 1 year ago

fwiw I've dropped the v14 test packages into a project that contains an asp .net core 3.1 web service and a mix of client builds using different .net versions, but one of the reasons for that is that I'm trying to see what's going on with the Mend complaints described at https://github.com/RicoSuter/NSwag/issues/2824 - and as long as I can generate client code that can build as .NET Standard 2.0 it doesn't matter if the code generator itself needs .NET 6+ and I don't have any pressing need to update the server side itself

lahma commented 1 year ago
  • nswag.json now only supports .csproj based specification generation (reflection/assembly based removed, only aspnetcore2openapi)

I wonder if this now makes it impossible to generate API specification and clients via MSBuild. Seems that now MSBuild will invoke NSwag's run target (with nswag.json etc) like documented in MSBuild integration guidance which in turn now needs to gather the project metadata by invoking MSBuild against the csproj (DLL no longer supported), which in turn will again invoke the nswag run and the recursion will continue...

There's also quite a lot of performance penalty from invoking the build pipeline multiple times.

EDIT, so the way nswag.json needs to be configured is to have "noBuild": true in it when invoked from csproj.

RicoSuter commented 1 year ago

EDIT, so the way nswag.json needs to be configured is to have "noBuild": true in it when invoked from csproj.

Exactly, if you run this as part of the csproj build (after build) then you need to enable noBuild

Tomius commented 1 year ago

nswag.json now only supports .csproj based specification generation (reflection/assembly based removed, only aspnetcore2openapi)

Nit: the docs will eventually need to be updated to reflect this, e.g. WebApiToOpenApiCommand

Out of curiosity, what was the reason to remove this feature?

I mean, I understand that having fewer commands makes the code easier to maintain. But it feels very sad to have start up the application to generate swagger from it, when it's used at large scale. E.g. the build and prod enviornments might be very different and the app might not start up on the build machine. Or it might do some very expensive intialisation, like read from databases, that might significantly increase build times (if the nswag generation happens during the build).

I am aware that I could put all the controllers into a library and have two Asp.Net core apps, one for prod doing the real initialisation, and a "dummy one" just for nswag. But having to do this just feels so painful compared to how easy it was to use NSwag v13.

RicoSuter commented 1 year ago

Out of curiosity, what was the reason to remove this feature?

Main reason is that all this legacy stuff puts a lot of maintenance effort. The main pain is this whole dynamic assembly loading stuff. I cannot maintain legacy frameworks, .net runtimes forever alone without even being paid for it...

The web api generator is still available, so the simplest solution for you would be to just build your own simple CLI tool which uses the https://github.com/RicoSuter/NSwag/wiki/WebApiOpenApiDocumentGenerator and references the project with the controllers...

I am aware that I could put all the controllers into a library and have two Asp.Net core apps

Did you use WebApiToOpenApiCommand for ASP.NET Core? That's a very bad idea as it uses reflection and not the API explorer which might lead to completely wrong results...

m-demydiuk commented 1 year ago

Version 14.0.0-preview008 has a reference to Microsoft.Extensions.ApiDescription.Server v6.0.3. Is it correct? image

RicoSuter commented 1 year ago

Is this a problem?

m-demydiuk commented 1 year ago

@RicoSuter we have all other packages (also transitive) in our project updated to the latest version, except this. Is there any blocker not to update this one to latest?

olegd-superoffice commented 1 year ago

@m-demydiuk This reference is only setting minimal supported version of the package, but you always can use newer ones in your project if you reference the dependency directly of if you use NuGet's central package management with CentralPackageTransitivePinningEnabled. But there are many use cases where it is not possible to use the latest version and keeping this reference to minimal supported version allows it to be used in those cases.

Herdo commented 1 year ago

@RicoSuter Will there also be a preview of the NSwagStudio 14.x ? 😄

lahma commented 1 year ago

@Herdo the studio setup MSI should now be part of the release assets.

Gigas002 commented 1 year ago

What's about OpenAPI v3.1.0/swagger-ui v5? This is being constantly asked since 2021 in a #3761, but haven't been answered still. Are these a planned features, at least?

vipwan commented 1 year ago

In v14 preview AspNetCoreOpenApiDocumentGeneratorSettings removed the SchemaProcessors property , how should I register a custom ISchemaProcessor

chriscameron-vertexinc commented 1 year ago

It looks like JsonSchemaGeneratorSettings is gone, removing the UseXmlDocumentation option on AddSwaggerDocument. Should we not be using XML documentation to drive the Swagger documentation anymore?

lahma commented 1 year ago

@vipwan @chriscameron-vertexinc

services.AddSwaggerDocument(document =>
{
    document.SchemaSettings.SchemaProcessors.Add(null!);
    document.SchemaSettings.UseXmlDocumentation = true;
});
chriscameron-vertexinc commented 1 year ago

nswag.json Here's an odd one. I'm using a post build step to run NSwagExe_Net80:

  <Target Name="NSwag" AfterTargets="Build">
    <Exec Command="$(NSwagExe_Net80) run nswag.json /variables:Configuration=$(Configuration)" />
  </Target>

My nswag.json aspNetCoreToOpenApi document generator has the following assembly path:

      "assemblyPaths": [
        "bin/$(Configuration)/net8.0/CNR.Example.API.dll"
      ],

Since following the advice above to set noBuild to true this is working perfectly in VS2022 when I build for Debug or Release.

In my CI/CD pipeline I'm doing the following:

dotnet restore ./src/CNR.Example.API.sln
dotnet build ./src/CNR.Example.API.sln --maxcpucount --configuration Release --no-restore

This also works perfectly from my local command line, however on my build machine I get:

  NSwag command line tool for .NET Core Net80, toolchain v14.0.0.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))
  Visit http://NSwag.org for more information.
  NSwag bin directory: /root/.nuget/packages/nswag.msbuild/14.0.0-preview009/tools/Net80

  Executing file 'nswag.json' with variables 'Configuration=Release'...
  System.InvalidOperationException: Project outputs could not be located in '/home/ubuntu/actions-runner/_work/cnr-example-api/cnr-example-api/src/CNR.Example.API/bin/Debug/net8.0/'. Ensure that the project has been built.
     at NSwag.Commands.Generation.AspNetCore.AspNetCoreToOpenApiCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Generation/AspNetCore/AspNetCoreToOpenApiCommand.cs:line 82
     at NSwag.Commands.NSwagDocumentBase.GenerateSwaggerDocumentAsync() in /_/src/NSwag.Commands/NSwagDocumentBase.cs:line 270
     at NSwag.Commands.NSwagDocument.ExecuteAsync() in /_/src/NSwag.Commands/NSwagDocument.cs:line 67
     at NSwag.Commands.Document.ExecuteDocumentCommand.ExecuteDocumentAsync(IConsoleHost host, String filePath) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 76
     at NSwag.Commands.Document.ExecuteDocumentCommand.RunAsync(CommandLineProcessor processor, IConsoleHost host) in /_/src/NSwag.Commands/Commands/Document/ExecuteDocumentCommand.cs:line 33
     at NConsole.CommandLineProcessor.ProcessSingleAsync(String[] args, Object input)
     at NConsole.CommandLineProcessor.ProcessAsync(String[] args, Object input)
     at NSwag.Commands.NSwagCommandProcessor.ProcessAsync(String[] args) in /_/src/NSwag.Commands/NSwagCommandProcessor.cs:line 62
/home/ubuntu/actions-runner/_work/cnr-example-api/cnr-example-api/src/CNR.Example.API/CNR.Example.API.csproj(54,5): error MSB3073: The command "dotnet "/root/.nuget/packages/nswag.msbuild/14.0.0-preview009/buildTransitive/../tools/Net80/dotnet-nswag.dll" run nswag.json /variables:Configuration=Release" exited with code 255.

It looks like NSwagExe_Net80 isn't replacing the $(Configuration) value in my nswag.json properly. It clearly says Executing file 'nswag.json' with variables 'Configuration=Release' and then complains that it can't find the file in the Debug directory.

The exact same script, and exact same machine, when working with .net7.0:

  NSwag command line tool for .NET Core Net70, toolchain v13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0))
  Visit http://NSwag.org for more information.
  NSwag bin directory: /root/.nuget/packages/nswag.msbuild/13.20.0/tools/Net70

  Executing file 'nswag.json' with variables 'Configuration=Release'...
  Done.

  Duration: 00:00:01.3889592
RicoSuter commented 1 year ago

@chriscameron-vertexinc assembly path loading has been removed, use “project” with path to csproj instead

RicoSuter commented 1 year ago

JsonSchemaGeneratorSettings Is part of NJsonSchema and now a property and not inheritance anymore.. mainly because there are now two variants: system.text.json and newtonsoft..

chriscameron-vertexinc commented 1 year ago

@chriscameron-vertexinc assembly path loading has been removed, use “project” with path to csproj instead

I've now updated my nswag.json to use project instead of assemblyPaths and I'm seeing the exact same problem.

It looks like I misspoke when I said I couldn't reproduce the problem locally. After deleting my bin directories to clear out the Debug output my post build step is failing in VS2022 release builds.

image

yfital commented 1 year ago

Sorry, i would really appreivcate a clarification on how we are supposed to work here with the removal of the WebApi2Swagger. We have 2 use cases:

  1. We have an internal SDK which includes our hosting capabilities and various controllers 1.2. These are dlls, not .exes which do not contain startup 1.3. All our of programs inherit these 1.4 Our UI uses a document create from the SDK for all general purpose controllers and specific code from documents from the console applications

  2. We have a multi dll console application, where the controllers sit in the DLLs and not in the console

Our current solution was simple, in the SDK, we used WebApi2Swagger to create the openapi

In our DLLs

(Notice, in the DLL we also exported the document for the SDK counterpart)

  • Our actual .exe doesn't have any reference to nswag.msbuild, it doesn't need it
  • Our .exe(s) are heavy one, i prefer to create the openapi it without having to run it

am i missing something here? are we working completly wrong?

lahma commented 1 year ago

@chriscameron-vertexinc

We had to tune the configuration a bit after enabling <UseArtifactsOutput>true</UseArtifactsOutput> (docs) . Maybe this can give some configuration clues for you, key is to point msBuildProjectExtensionsPath to obj folder and have configuration passed in as variable, our mileage may vary.

{
  "runtime": "Net70",
  "defaultVariables": null,
  "documentGenerator": {
    "aspNetCoreToOpenApi": {
      "project": null,
      "documentName": "v1",
      "msBuildProjectExtensionsPath": "../../artifacts/obj/TheProjectFolder",
      "configuration": "$(Configuration)",
chriscameron-vertexinc commented 1 year ago

Thanks so much @lahma !

In summary, I've had to change the following to get NSwagExe_80 to work for me:

LarsKemmann commented 1 year ago

After upgrading my Minimal APIs app (which is using STJ for serialization) from .NET 7 and NSwag 13.20.0 to .NET 8 and NSwag 14.0.0-preview010, the OpenAPI output is no longer using camelCase but PascalCase (mirroring my .NET property names' casing); this is frustrating because the actual ASP.NET Core serialization output is using camelCase which results in my generated TypeScript client not seeing any of the properties in the server's responses. I hadn't ever specified the name handling anywhere prior to this (and didn't even have to create any .nswag files in the past -- the default setup just worked which was wonderful), but now I'm having to add the following to get my code to work again:

builder.Services.AddOpenApiDocument(config =>
  {
    config.PostProcess = document =>
    {
      // Generate camelCase output for properties
      foreach (var schema in document.Components.Schemas)
      {
        var priorProperties = schema.Value.Properties.ToImmutableList();
        schema.Value.Properties.Clear();
        schema.Value.Properties.AddRange(priorProperties
          .Select(property =>
          {
            return new KeyValuePair<string, JsonSchemaProperty>(
              char.ToLowerInvariant(property.Key[0]) + property.Key.Substring(1),
              property.Value);
          }));
      }
    };
  });

Any thoughts as to why this might be?

lahma commented 1 year ago

@LarsKemmann sound almost like that your serializer options don't default to web, you could try something like this (and debug what's the default value):

services.AddSwaggerDocument(settings =>
{
    ((SystemTextJsonSchemaGeneratorSettings) settings.SchemaSettings).SerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);

If settings are set for JsonSerializerDefaults.General naming policy won't be set to JsonNamingPolicy.CamelCase which is what NSwag uses to determine the name (if it's present).

kev-andrews commented 1 year ago

i am having an issue using OpenApiReference since v14

https://github.com/RicoSuter/NSwag/blob/57859684e1e7446bda9113f92de0d96102e6205d/src/NSwag.ApiDescription.Client/NSwag.ApiDescription.Client.targets#L41

is trying to call swagger2tsclient, which seems to have been replaced by openapi2tsclient

LarsKemmann commented 1 year ago

@LarsKemmann sound almost like that your serializer options don't default to web, you could try something like this (and debug what's the default value):

services.AddSwaggerDocument(settings =>
{
    ((SystemTextJsonSchemaGeneratorSettings) settings.SchemaSettings).SerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);

If settings are set for JsonSerializerDefaults.General naming policy won't be set to JsonNamingPolicy.CamelCase which is what NSwag uses to determine the name (if it's present).

@lahma Interesting!! I couldn't find a way to see the initial value of the JsonSerializerDefaults in the debugger as it didn't appear to be saved on JsonSerializerOptions as a field/property, but constructing a new instance and setting the defaults to .Web like you suggested instantly fixed the issue! So, you're right, the default must be set to .General somehow--only, I have no idea how that would be, as this is literally my app's entire code up to this point:

var builder = WebApplication.CreateBuilder(args);

if (builder.Environment.IsDevelopment() || builder.Environment.EnvironmentName == "NSwag")
{
  builder.Services.AddEndpointsApiExplorer();
  builder.Services.AddOpenApiDocument(config =>
  {
    ((SystemTextJsonSchemaGeneratorSettings)config.SchemaSettings).SerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);
  }
}

Some design feedback: If there's any way to have that 'settings' argument in AddSwaggerDocument/AddOpenApiDocument be passed as the actual derived type (SystemTextJsonSchemaGeneratorSettings in this case), that would be so helpful for the developer experience. I spent two hours last night trying to figure out why all the guidance was telling me to access the SerializerOptions property when that wasn't available on JsonSchemaGeneratorSettings.

TimKras commented 1 year ago

@RicoSuter What is the planned release date for v14?

geskill commented 1 year ago

Generating for tuples seems a bit off:

public IList<(string, int, List<(string, int)>)> Data { get; set; }

v13

export class XYZ implements IXZY {
    item1!: string;
    item2!: number;
    item3!: ValueTupleOfStringAndInteger[];

v14

export class XYZ implements IXZY {
    systemRuntimeCompilerServicesITupleLength?: number;
    systemRuntimeCompilerServicesITupleItem?: any | undefined;

issues:

-> using named parameters has no effect

or maybe i have a miss configuration 🤔

SchemaSettings ``` SchemaSettings = new SystemTextJsonSchemaGeneratorSettings { SerializerOptions = new JsonSerializerOptions { Converters = { new JsonStringEnumConverter() }, IncludeFields = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, } } ```
mikoskinen commented 1 year ago

We're trying to update from 13.18.0 to 14.0.0-preview010 and... uh, I'm not sure if we will get this to work ever again.

The biggest problem for now seems to be the removal of webApiToOpenApi. We used to list multiple assemblies in the nswag.json's assemblyPaths, for example:

  "assemblyPaths": [
    "bin/Debug/net7.0/Host.Backend.dll",
    "bin/Debug/net7.0/Host.Modules.Agents.Backend.dll",
    "bin/Debug/net7.0/Host.Modules.Events.Store.Backend.dll"
   ]

This was used to generate one swagger.json from multiple projects. Now in 14.0.0 as webApiToOpenApi is removed we checked how we could use aspnetcore2openapi. The problem is that this seems to only support a single project? If our application is split between multiple modules, each containing controllers, how can we create a single swagger.json?

So how to replace webApiToOpenApi with aspnetcore2openapi if we previously had multiple assemblies listed in the webApiToOpenApi/assemblyPaths?

Thank you in advance for the help.

dylanvdmerwe commented 1 year ago

Can't update to net8.0 until nswag 14 is released it appears. Conflicting dependencies with <Project Sdk="Microsoft.NET.Sdk.Web">

lahma commented 1 year ago

This was used to generate one swagger.json from multiple projects. Now in 14.0.0 as webApiToOpenApi is removed we checked how we could use aspnetcore2openapi. The problem is that this seems to only support a single project? If our application is split between multiple modules, each containing controllers, how can we create a single swagger.json?

I'm afraid I don't have the answer to how to get this work, but I'm a bit curious how you handle the swagger API routing? I would think that you would have single endpoint taking the requests as they are aggregated from multiple dlls but accessed via single API? Just sounds like there's some "main router" that could be the source of the swagger generation too.

lahma commented 1 year ago

Can't update to net8.0 until nswag 14 is released it appears. Conflicting dependencies with <Project Sdk="Microsoft.NET.Sdk.Web">

But you probably could update to the latest 14.0.0-preview010, you can run diff tool and check if output for schema and/or clients is the same (or close enough) to determine if it's production ready, in a way.

mikoskinen commented 1 year ago

This was used to generate one swagger.json from multiple projects. Now in 14.0.0 as webApiToOpenApi is removed we checked how we could use aspnetcore2openapi. The problem is that this seems to only support a single project? If our application is split between multiple modules, each containing controllers, how can we create a single swagger.json?

I'm afraid I don't have the answer to how to get this work, but I'm a bit curious how you handle the swagger API routing? I would think that you would have single endpoint taking the requests as they are aggregated from multiple dlls but accessed via single API? Just sounds like there's some "main router" that could be the source of the swagger generation too.

Thank you for the reply! We don't use anything fancy, just Controllers & Actions split between multiple projects, built so that there isn't duplicate routes. We have the following structure:

  • Module 1
  • Module 2
  • Module 3

Each module is just a Class Library (Project Sdk="Microsoft.NET.Sdk") and the modules contain Controllers, services, repositories etc. So instead of using ASP.NET Core areas, we are using separate projects to split the app into smaller units.

ASP.NET Core automatically adds the controllers & actions from all the projects that the main app references (or we call AddApplicationParts, I'll have to check this). So when we build the modules, we just have to make sure that we don't duplicate the routes.

If Module1 has route:

GET /api/Invoices

And Module2:

POST /api/customers

And the Main app:

GET /api/user

When the main app is run, it's going to have three routes/endpoints:

GET /api/Invoices POST /api/customers GET /api/user

Previously we had nswag.json and the Nswag MSBuild package in the main app. Nswag.json was configured so that webApiToOpenApi's assemblyPaths pointed to the all the modules + to the main app.

We also sometimes used the webApiToOpenApi to generate partial swagger files. So for example we could use assemblyPaths to create an "all endpoints" swagger.json and a "minimal" swagger.json which contained only module1 + main app.

lahma commented 1 year ago

Previously we had nswag.json and the Nswag MSBuild package in the main app. Nswag.json was configured so that webApiToOpenApi's assemblyPaths pointed to the all the modules + to the main app.

I probably don't fully grasp your setup, but my initial thought would be just to have aspNetCoreToOpenApi pointed to the main Main ASP.NET Core App (Project Sdk="Microsoft.NET.Sdk.Web") and it would have the OpenApi generators producting the swagger output combining all the services that are discoverable from the web app. That is basically what NSwag.MSBuild does with nswag.json pointed to main web app DLL (I guess).

Of course this would not solve your partial/minimal APIs. Just spitballing here.

simeyla commented 1 year ago

I managed to get things up and running using aspnetcore2openapi using /project: mode on dotnet 8 (previously was using /assembly: on dotnet 7). However I'm getting errors that don't point me in the direction of controller has a problem.

System.InvalidOperationException: This operation is only valid on generic types.

I'm running my nswag json file from the command line (normally done in the build). Currently NSwagStudio is completely hanging for me (but that's not really my issue). I'm using preview 10.

To aid in testing, I created a simple IOperationProcessor:

public class IncludeControllersInSwagger : IOperationProcessor
{
    public bool Process(OperationProcessorContext context)
    {
        // only process TestController
        return context.ControllerType.Name.ToUpper().Contains("TESTCONTROLLER");
    }
}

This is comforting insofar as it gave me the expected output (and my TS client):

{
  "x-generator": "NSwag v14.0.0.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))",
  "openapi": "3.0.0",
  "info": {
    "title": "My Title",
    "version": "1.0.0"
  },
  "paths": {
    "/api/test/test": {
      "get": {
        "tags": [
          "Test"
        ],
        "operationId": "Test_Test1",
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {}
}

But when I change back to including all controllers I get all kinds of errors that don't tell me anything. I have no idea which controller / model type this is referring to:

 ---> System.InvalidOperationException: This operation is only valid on generic types.
   at System.RuntimeType.GetGenericTypeDefinition()
   at Namotion.Reflection.ContextualType.<get_Fields>b__40_0(FieldInfo field)
   at System.Linq.Enumerable.SelectArrayIterator`2.ToArray()
   at Namotion.Reflection.ContextualType.get_Fields()
   at NJsonSchema.Generation.SystemTextJsonReflectionService.GenerateProperties(JsonSchema schema, ContextualType contextualType, SystemTextJsonSchemaGeneratorSettings settings, JsonSchemaGenerator schemaGenerator, JsonSchemaResolver schemaResolver)
   at NJsonSchema.Generation.ReflectionServiceBase`1.NJsonSchema.Generation.IReflectionService.GenerateProperties(JsonSchema schema, ContextualType contextualType, JsonSchemaGeneratorSettings settings, JsonSchemaGenerator schemaGenerator, JsonSchemaResolver schemaResolver)
   at NJsonSchema.Generation.JsonSchemaGenerator.GenerateInheritance(ContextualType type, JsonSchema schema, JsonSchemaResolver schemaResolver)
   at NJsonSchema.Generation.JsonSchemaGenerator.GenerateObject(JsonSchema schema, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver)

@RicoSuter Can these errors be improved or is there some additional logging I can enable to find the source of the problem?

trejjam commented 1 year ago

Hi, I was looking at how that may happen and it would be useful to know the shape of the object passed into GenerateObject. If I am not wrong the stack trace was longer, speaking of Generate<T> the T is interesting to know (not by value, but by the type of its fields). Since the error, we are probably looking for a generic type.

You can try to find the problematic field by placing [STJ.JsonIgnore] on fields and try to run it until you find the field Type.

marcobudde commented 1 year ago

We have justed ported our code from .NET 6 using nswag 13.20 to .NET 8 using nswag v14.0.0-preview010.

There is one difference in the generated OpenAPI 3 files, which I do not understand.

We are using

[JsonProperty(Required = Required.AllowNull)]

or

 [JsonProperty(Required = Required.Always)]

to control the "required" and "nullable" elements.

With the nswag preview this does not longer work. All "required" elements are removed from the generated OpenAPI file.

If have tried to use [JsonRequired] or "required" instead, but this does not help.

Is this an expected behaviour?

RicoSuter commented 1 year ago

@marcobudde did you also switch eg from Newtonsoft to STJ? Can you maybe post a simple example DTO where this can be tested?

RicoSuter commented 1 year ago

@simeyla it seems that your problem is with a generatic DTO which has fields? Could you pinpoint it and can you maybe provide a sample DTO?

marcobudde commented 1 year ago

We are using STJ for our complete code. This has not been changed.

Out DTO looks like this:

 public interface ITransferData
 {
     public DateTime Timestamp { get; set; }
     public long Crc32 { get; set; }
 }

 public class TransferData<T> : ITransferData
 {      
   [Description("bla")]
   [JsonProperty(Required = Required.AllowNull)]
   public T? Data { get; set; }

   [Description("bla")]
   [JsonProperty(Required = Required.Always)]
   public DateTime Timestamp { get; set; }

   [Description("bla")]
   [JsonProperty(Required = Required.Always)]
   public long Crc32 { get; set; }
}

Usage like this:


[ProducesResponseType(typeof(TransferData<object>), 200)]
[ProducesResponseType(typeof(TransferData<Error>), 400)]
[HttpPost]
public IActionResult SetBla(TransferData<bool> request) {}

We are using STJ "Source Generation" feature.

simeyla commented 1 year ago

@RicoSuter ok managed to narrow it down to an attempt to serialize private fields, which didn't happen before.

Not sure if I need to set an option to skip private properties now or if this is a bug, or nuance of .NET 8? I've had these fields private for years and they've been ignored. Trying to find every place I did this wouldn't be fun!

public class DTOObject
{
    private RRDBC.RRStoreContext RRContext { get; }    // EF Core generated context
}

Can confirm adding [System.Text.Json.Serialization.JsonIgnore] for this private property made the error go away.

This is for a controller method with:

    [SwaggerResponse(HttpStatusCode.OK, typeof(DTOObject), IsNullable = false)]

The broader issue is the error message not pointing me at the class in question.

dylanvdmerwe commented 11 months ago

Is the npm version of nswag (preview003) meant to be so far behind nuget's NSwag.AspNetCore preview010?

RicoSuter commented 11 months ago

NPM version publishing has been disabled for previews because they are not marked as preview automatically...

kev-andrews commented 11 months ago

still having the issue in preview012 that openapireference is trying to call swagger2tsclient, which seems to have been renamed openapi2tsclient

schnerring commented 11 months ago

@geskill Did you find a solution regarding value tuples?

I'm also seeing similar issues for generated C# clients:

 [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.0.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")]
    public partial record ValueTupleOfModelCreatedResponseAndIStartStream
    {
        [System.Text.Json.Serialization.JsonConstructor]

        public ValueTupleOfModelCreatedResponseAndIStartStream(object? @system.Runtime.CompilerServices.ITuple.Item, int @system.Runtime.CompilerServices.ITuple.Length)

        {

            this.SystemRuntimeCompilerServicesITupleLength = @system.Runtime.CompilerServices.ITuple.Length;

            this.SystemRuntimeCompilerServicesITupleItem = @system.Runtime.CompilerServices.ITuple.Item;

        }
        [System.Text.Json.Serialization.JsonPropertyName("System.Runtime.CompilerServices.ITuple.Length")]

        [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]   
        public int SystemRuntimeCompilerServicesITupleLength { get; init; }

        [System.Text.Json.Serialization.JsonPropertyName("System.Runtime.CompilerServices.ITuple.Item")]

        [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)]   
        public object? SystemRuntimeCompilerServicesITupleItem { get; init; }

    }