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

Missing schema properties in models from interfaces #909

Closed
iKadmium opened this issue Aug 26, 2018 · 32 comments
Closed

Missing schema properties in models from interfaces #909

iKadmium opened this issue Aug 26, 2018 · 32 comments

Comments

@iKadmium
Copy link

When a controller specifies its return type as an interface, NSwag generates an empty model for it. The following is an interface, IFixtureData:

public interface IFixtureData
{
    ushort Address { get; set; }
    FixtureDefinitionSkeleton Type { get; set; }
    FixtureOptions Options { get; set; }
    string Group { get; set; }
}

In the swagger.json file, the following is generated:

"IFixtureDefinition": {
  "type": "object",
  "x-abstract": true,
  "additionalProperties": false
}

All of my concrete classes are generated properly, but the interfaces are all like this.

@RicoSuter
Copy link
Owner

Not sure if this feature is actually implemented by NJsonSchema... we use Newtonsoft.Json's ContractResolver to enumerate properties - I'd expect that interfaces are also supported by the resolver... but we need to verify this in http://njsonschema.org with a unit test

@mjeanrichard
Copy link
Contributor

This seems to be a Problem with NJsonSchema. Getter and Setter methods are abstract in an Interface and these are not generated by default (see https://github.com/RSuter/NJsonSchema/blob/f7224a3ee33333b3cd439df536f80c5d551dcb21/src/NJsonSchema/Generation/JsonSchemaGenerator.cs#L598 ).

Everything works fine if I just set JsonSchemaGeneratorSettings.GenerateAbstractProperties to true, but this might not work in all situations.

@RicoSuter
Copy link
Owner

NJsonSchema uses the Newtonsoft.Json contract resolver to enumerate properties and do "reflection" over DTOs: This is done to ensure that the schema and the serialized/deserialized data match... The option GenerateAbstractProperties overrides this behavior and manually uses reflection to add abstract/interface properties - but because it is not used often (also not recommended) it might come with some problems etc. but NJsonSchema would be the place to test and fix this issue...

@mjeanrichard
Copy link
Contributor

Should I create a corresponding issue in NJsonSchema? I could probably help with writing the UnitTest and potential fix...

@RicoSuter
Copy link
Owner

I’ll move this issue over to NJS, but it would be great if you create a (WIP) PR with some tests there...

@RicoSuter RicoSuter transferred this issue from RicoSuter/NSwag Feb 25, 2019
@RicoSuter RicoSuter changed the title NSwag ASP.Net Core middleware not generating models from interfaces Missing schema properties in models from interfaces Feb 25, 2019
@RicoSuter
Copy link
Owner

Moved to NJS - please create a PR with tests (& maybe a fix) so we can discuss further...

@mjeanrichard
Copy link
Contributor

I added a PR with one test to show the Problem. Also included a potential fix, but i am unsure if this might have any side effects.

@mjeanrichard
Copy link
Contributor

@RSuter : Could you have a look at this fix? Is this the way to go?

RicoSuter added a commit that referenced this issue Mar 19, 2019
* Add test #929

* Fix property type resolution for any schemas in definitions, RicoSuter/NSwag#2028

* Test and potential Fix for Interfaces. (#909) (#921)

* Add legacy support, #921

* v9.13.25
@mjeanrichard
Copy link
Contributor

mjeanrichard commented Apr 3, 2019

I still have a problem with this. The Properties are now generated, but in an abstract class an can therefore not be deserialized. Am I doing something wrong or is this behavior correct?

In my API I have a Interface as a return type. The gerenated TypeScript Class contains all the Properties, but fromJS Method contains the Line throw new Error("The abstract class '...' cannot be instantiated.");

Everthing else seems to be there (e.g. the correct init method).

Can I specify somehow, that I want non abstract classes to be genereated for interfaces?

@RicoSuter
Copy link
Owner

Theres an option to turn of x-abstract generation

@mjeanrichard
Copy link
Contributor

Could you point me to that option? I can just see the assignment of schema.IsAbstract in the JsonSchemaGenerator on line 352. But there seems to be no condition there...

@RicoSuter
Copy link
Owner

Ah, maybe i was mistaken and there is no such option. Would this solve the problem? I think just allowing abstract class instances would then break on the server as they cannot be deserialized... have you also had a look at the inheritance handling in tandem with abstract classes/interfaces?

@RicoSuter
Copy link
Owner

Can you create a pr with your tests for discussions?

@mjeanrichard
Copy link
Contributor

Hmm, yes, I had a quick look at inheritance handling. I currently only use interfaces in return types of my API and have therefore no issue on the server side :-). But I do not use any inheritance (at least I don't want it to be exposed in my schema).

How is NJsonSchema involved on the server side?

Could it be a solution to generate interfaces and (optionally) an implementation of it to be used? Because just an abstract class (or an interface for that matter) cannot be used at all on the client side.

@RicoSuter
Copy link
Owner

NJS is only used for serializing inheritance otherwise its mainly used for generating schemas...

Can you provide a comprehensive sample for what you want to achieve?

@mjeanrichard
Copy link
Contributor

Lets assume I have a Controller like this:

[RoutePrefix("users")]
public class UserController : ApiController
{
  [HttpGet]
  [Route("")]
  public async Task<IUser> GetCurrentUser()
  {
    return await GetCurrentUserAsync();
  }
}

With the IUser Interface as follows:

public interface IUser
{
  string Lastname { get; set; }
  string Firstname { get; set; }
}

Of course there is an implementation of this interface on the server somewhere, but I do not want to expose it and in my case the API-Layer does not even know about it.

I now want to be able to generate TypeScript-Clients that can call this API and handle the Response. Even if I had a Controller like this

[RoutePrefix("users")]
public class UserController : ApiController
{
  [HttpPost]
  [Route("")]
  public async Task UpdateUser(IUser user)
  {
    ...
  }
}

I think clients should be gernerated with concrete DTOs (not abstract ones). How this is deserialized into something meaningful on the server is the servers problem.

@RicoSuter
Copy link
Owner

RicoSuter commented Apr 3, 2019

I think clients should be gernerated with concrete DTOs (not abstract ones). How this is deserialized into something meaningful on the server is the servers problem.

I agree.

Need to think about what this was actually trying to solve. But i think x-abstract should only be generated in an inheritance tree (ie when there’s a discriminator etc) - otherwise omitted

@mjeanrichard
Copy link
Contributor

Is the inheritance tree opt-in? Is it only relevant if the JsonInheritanceConverter is used? What would be a good way to detect this?

@RicoSuter
Copy link
Owner

Yes opt-in with JsonInheritanceConverter, so i’d say we only generate x-abstract if its in an inheritance with JsonInheritanceConverter on the type or any base types

@RicoSuter
Copy link
Owner

But i need to check the tests and the code whether that is the correct behavior - not on pc right now

@mjeanrichard
Copy link
Contributor

Sounds good to me, thanks. Tell me if and how I can be of help.

@RicoSuter
Copy link
Owner

Should be quite simple to implement/change but i need to ensure that nothing breaks and there are no regressions

@mjeanrichard
Copy link
Contributor

Any news on this?

@RicoSuter
Copy link
Owner

Closed by 4643d38

I've added JsonSchemaGeneratorSettings to turn abstract off completely and an JsonSchemaAbstractAttribute to explicitely opt-in or opt-out...

is that ok?

@mjeanrichard
Copy link
Contributor

Thanks for the Update (sorry for being late, i did somehow not get an update from github).

I am not sure how I should use this.
Can I currently set the new Property of JsonSchemaGeneratorSettings from NSwag, or should I create a PR there to include this?
Also I cannot add the new Attribute to Interfaces, because it has a [AttributeUsage(AttributeTargets.Class)] Attribute. Is that on purpose?

@RicoSuter
Copy link
Owner

Can I currently set the new Property of JsonSchemaGeneratorSettings from NSwag, or should I create a PR there to include this?

Yes, should be available

Also I cannot add the new Attribute to Interfaces, because it has a [AttributeUsage(AttributeTargets.Class)] Attribute. Is that on purpose?

That's bad, we need to add that

@mjeanrichard
Copy link
Contributor

Could you point me to this option? I cannot see this in NSwagStudio (UI) nor see where I would change that in the nswag.json file.

@RicoSuter
Copy link
Owner

The setting has not yet been added to the command (CLI) and UI, but you can already set it in the ASP.NET Core middleware settings...

@RicoSuter
Copy link
Owner

see RicoSuter/NSwag@1a43cb4

@mjeanrichard
Copy link
Contributor

Cool, thanks a lot for your work!

@mmalyska
Copy link

mmalyska commented Jun 4, 2019

Also I cannot add the new Attribute to Interfaces, because it has a [AttributeUsage(AttributeTargets.Class)] Attribute. Is that on purpose?

That's bad, we need to add that

So, when it will be added?

@RicoSuter
Copy link
Owner

Just added it.

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

No branches or pull requests

4 participants