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

Remember unknown values in base classes for discriminated types #650

Open
heaths opened this issue Apr 10, 2020 · 10 comments
Open

Remember unknown values in base classes for discriminated types #650

heaths opened this issue Apr 10, 2020 · 10 comments
Labels
💥 API Impact Changes that would result in API changes in default cases (customization excluded) feature-request This issue requires a new behavior in the product in order be resolved. instances5-50 Between 5 and 50 instances that need a workaround. v3 Version 3 of AutoRest C# generator. workaround-hard Working around the problem is difficult.

Comments

@heaths
Copy link
Member

heaths commented Apr 10, 2020

Just an idea from an internal discussion. We don't declare base classes for discriminated types abstract in case we need to fall back to creating something. In that case, should we store all otherwise unhandled properties in a general, private variable for round-tripping? Many different ways we could do this, even just an IDictionary<string, object> that could contain other such dictionaries for nested properties - similar to what what the JsonSerializer can do via the JsonExtensionDataAttribute.

@heaths heaths added the v3 Version 3 of AutoRest C# generator. label Apr 10, 2020
@heaths
Copy link
Member Author

heaths commented Apr 10, 2020

To note, this may not be a practical concern if service owners don't add new discriminated types to existing api-versions. Currently, we intentionally do not support arbitrary api-versions (or shouldn't; I believe Java currently does) so as long as new versions of services correspond to new packages, we'd have to have regenerated the code.

/cc @tg-msft @KrzysztofCwalina

@heaths heaths added instances5-50 Between 5 and 50 instances that need a workaround. workaround-hard Working around the problem is difficult. labels Apr 21, 2020
@heaths
Copy link
Member Author

heaths commented Apr 21, 2020

In discussing Azure/azure-sdk-for-net#9686, one idea is that we make the base classes (no matter how far down the hierarchy) abstract but then define an internal child class that basically just buckets the properties and sends them back. Users won't be able to instantiate it or the base class by design, but they are also protected from a service returning a new model - even under the same api-version - that the SDK doesn't yet handle.

@tg-msft
Copy link
Member

tg-msft commented Apr 22, 2020

To note, this may not be a practical concern if service owners don't add new discriminated types to existing api-versions.

This is a very common occurrence according to the scary stories @johanste tells around the campfire. And I get why they do it. They either have to return a new type or effectively lie about the resource when requested from an older API version. Both options are unpleasant, but at least returning the new resource gives the client a chance to work correctly.

We can't rely on updated versions of the (regenerated) client library to solve the problem. This should probably be supported on every code generated model - not just discriminated unions - because services may return new properties to old versions of the API for arbitrary resources. And it should probably have a common solution described in the guidelines and potentially exposed via Azure.Core with an explicitly implemented interface like IExtraProperties or something.

@johanste
Copy link
Member

Not sure if it very common - but I've seen it on multiple occasions. The challenge is generally whenever a service adds a new discriminated type to a server persisted resource. The choice is now for the service to hide any resource that uses the new type, map it into something that an earlier client would know about (this is most often impossible) or hide the resource from old clients (this leads to potential data loss/quota confusion) when clients delete what they think are empty containers (the old client didn't see the new typed resources contained in the container).

@heaths
Copy link
Member Author

heaths commented Apr 22, 2020

I'd personally like an interface IExtraProperties { Dictionary<string, object> Properties { get; } } that everything implements explicitly per the discussion in #650.

Ted mentioned this in the linked bug. Given "Properties" is pretty common and other patterns I've seen, I would think AdditionalProperties might be safer.

...unless we intend for it to be an explicit interface implementation, in which case it doesn't matter.

@heaths
Copy link
Member Author

heaths commented May 5, 2020

Because service teams often add properties to existing versions (whether changes are made to swagger or not), should we actually be doing this for all models by default to avoid data loss?

@MiYanni MiYanni self-assigned this May 8, 2020
@MiYanni
Copy link
Contributor

MiYanni commented May 11, 2020

Somewhat discussed offline: There already is an "additionalProperties": true swagger property that can be added to do most of the functionality described above. You can read about it in the Free-Form Object section here: https://swagger.io/docs/specification/data-models/data-types/#object

However, the abstract indication would be a very different mechanism that could be automatic. But, I'd need to investigate further to see what is involved in making that change.

@pakrym pakrym added the 💥 API Impact Changes that would result in API changes in default cases (customization excluded) label May 11, 2020
@AlexGhiondea
Copy link

@heaths can you take point on this and figure out what other code generators are planning to do this (related to Search)? If this is something that all code generators support (or would like to support) we should do this.

@heaths
Copy link
Member Author

heaths commented May 18, 2020

@AlexGhiondea, only Java has the concept of abstract classes, though you could say TypeScript could use an interface instead. I can ask what other generators might want to support, but this really is more specific to .NET and Java, and was specifically from archboard feedback about not instantiating "useless" types - i.e., that most of the abstract base classes had no members other than ODataType.

heaths added a commit to heaths/azure-sdk-for-net that referenced this issue May 21, 2020
Resolves Azure#9686 without making the classes abstract, given that a fix for Azure/autorest.csharp#650 is further out.
heaths added a commit to Azure/azure-sdk-for-net that referenced this issue May 22, 2020
Resolves #9686 without making the classes abstract, given that a fix for Azure/autorest.csharp#650 is further out.
@tg-msft
Copy link
Member

tg-msft commented Oct 26, 2020

This came up again today for Azure/azure-sdk#1892. //cc @kinelski @KrzysztofCwalina @johanste

@MiYanni MiYanni removed their assignment Nov 10, 2020
@chamons chamons added the feature-request This issue requires a new behavior in the product in order be resolved. label May 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💥 API Impact Changes that would result in API changes in default cases (customization excluded) feature-request This issue requires a new behavior in the product in order be resolved. instances5-50 Between 5 and 50 instances that need a workaround. v3 Version 3 of AutoRest C# generator. workaround-hard Working around the problem is difficult.
Projects
None yet
Development

No branches or pull requests

7 participants