kontent-ai / delivery-sdk-net

Kontent.ai Delivery .NET SDK
https://www.nuget.org/packages/Kontent.Ai.Delivery
MIT License
32 stars 41 forks source link

Dynamic access to elements #363

Closed Vouzamo closed 1 year ago

Vouzamo commented 1 year ago

Motivation

I would like to be able to only create a single type that I can use to programmatically traverse elements of a content item, but doesn't seem possible using the default IModelProvider.

Proposed solution

In the same way you can access IContentItemSystemAttributes I think you should be able to access a collection of elements also.

public class ContentItem
{
    public IContentItemSystemAttributes System { get; set; }
    public Dictionary<string, IContentElement> Elements { get; set; }
}

Additional context

If this is already possible via another mechanism, we can close this as resolved, but the nearest equivalent I have found it to create my own abstractions for IContentElement types and override the IModelProvider to coerce an object.

Simply007 commented 1 year ago

Hello @Vouzamo,

thank you for the issue!

Could you elaborate a bit more on your case? It is really important for us to provide a flexible solution, but providing the use can help us to point you out to an existing approach if it exists. I personally still prefer the strong types approach to prevent issues and get its benefits, but your case might have some specifics.

Now I can point you out to the value converter extensibility, or other customization techniques. But all of these are bound to strongly typed model.

Could you also provide your Model provider code, o we can explore it a bit more? I can imagine adding another IModelProvider implementation so you can register and then be able to use this functionality out of the box, but I wouldn't like to prevent making any rush decisions.

It might also be connected to #239 - it might be a nice decision logic that to use when the type provider is not registered as a fallback mechanism.

🥑 As a side note I might point you out to my personal trial for dynamic extensions for the delivery response, but it is very limited and still my personal initiative - but any feedback is welcome!

Vouzamo commented 1 year ago

My use case is to integrate Kontent AI as a provider for a delivery framework that already exists and works with content via a hierarchy of strong types to represent the composition of layout and enrichable structures along with components as leaf nodes with a dynamic IDictionary<string, object> that exposes the content in a more dynamic way.

This allows us to pass through the content as-is to the front-end or register enrichers that are invoked conditionally based on a predicate (e.g. based on content type etc) and can mutate the content. A good example would be for the CMS content to include a text field id, but use enrichment to upsert a object model into the content by interacting with a runtime integration using that text value as a parameter etc.

Your linked dynamic extensions are similar to how we would interact with content during enrichment, but don't provide as much value for the purpose of coercing the Kontent API Response into our concrete types, we also have a contract for element values that doesn't exactly match the Kontent equivalent so can't take the content as-is without some processing.

Instead of using IModelProvider, I used the JSON string in the response and ran it through my own deserialization so that we can access polymorphic models for each element and get a model that we can iterate over for mapping to our models.

This would have been much easier had there been support to just create a class with the SystemProperties (already possible) and IDictionary<string, Element> (not possible currently) that handles that polymorphic deserialization of classes for each element type, using the type as a discriminator and providing different derived Value properties of the appropriate C# types etc.

At least this way, I get the benefit of the SDK in terms of acting as a client for the API, but the freedom to work with the response in the way I need.

Simply007 commented 1 year ago

Thank you @Vouzamo for the excellent summary. I got the idea of the use case.

Just a couple of notes for potential implementation. @Vouzamo - would you go through them if you spot any problem with your use case?

For both options: We should add a new type i.e. IElements which would be a Dictionary<string, object> (to be consistent with linked items) + do not force us to wrap i.e. text element as an IContentElement which would be a breaking change, but allows us to do so in the future (might not event with a breaking change as we did ith DateTime https://github.com/kontent-ai/model-generator-net/pull/173).

Option 1 Extend the current ModeProvider

  1. On https://github.com/kontent-ai/delivery-sdk-net/blob/master/Kontent.Ai.Delivery/ContentItems/ModelProvider.cs#L101 create a new statement for the type of property of IElements and use the implemented logic.

There is one question about reference objects -> do we want to clone them when combining dynamic elements and i.e. taxonomy elements? Provide the same reference in both, or make a deep/shallow clone?

Option 2 Implement new Model provider

  1. For getting the dynamic response type. You would need to register the non-default Model provider i.e. GeneralModelProvider with the specification of what type you want to fill.
  2. This model would be asserted in the model provider and it would just return this type or raise an exception with an explanation. The logic itself would be much clearer, but you would need to Delivery Client registered for typed and Non-typed resolution. And you would not be able to combine the typed properties and the non-typed ones.
Simply007 commented 1 year ago

Hello again @Vouzamo,

I have tried to draft the solution for automapping. #367 (could be split from Model provider to the separate one).

I have a couple of questions:

  1. What do you expect as a value of the dictionaries? Would you expect the whole element context or just its value?

    • Plus: I have found out that we have two ways to represent elements -> just as it is from DAPI, or in a more structured way (i.e. for rich text, linked items, and DateTime).
  2. Currently the codename works as a key, I have used IContentElement as a base of the elements' values which has the property Codename as well, but currently, I don't set this one up.

  3. Would you expect PropertyMapper and ValueConverter for Elements property?


I have been facing the problem of object being the definition class for an element because some elements have struct (value) value => DateTime and Number (for both direct values of generically defined value in case of using the context of the whole element from 1st question).

Simply007 commented 1 year ago

What do you think @Vouzamo: https://github.com/kontent-ai/delivery-sdk-net/blob/fix/363-dynamic-values/docs/retrieving-data/universal-item-retrieval.md

If you think this is viable for testing, I can release an alpha version, or just test it from the local clone.

Simply007 commented 1 year ago

Beta version with the functionality: https://www.nuget.org/packages/Kontent.Ai.Delivery/17.7.0-beta.0

Vouzamo commented 1 year ago

Apologies for being non-responsive. I've been out on paternity for the past 6 weeks. Taking a look at the new functionality now...

Simply007 commented 1 year ago

Apologies for being non-responsive. I've been out on paternity for the past 6 weeks. Taking a look at the new functionality now...

Great @Vouzamo,

Thank you for getting back to me! Hope you enjoyed the "free" time 😄

I tried to make it as less invasive as possible for the SDK, but using most of the logic that was already in place in the SDK. I currently have some topics i would like your opinion in description of the PR: https://github.com/kontent-ai/delivery-sdk-net/pull/367#issue-1603413062

Simply007 commented 1 year ago

If you need any assistance @Vouzamo, feel free to reach out to me here, or on Kontent.ai Discord: https://kontent.ai/discord/

Simply007 commented 1 year ago

Hello @Vouzamo,

I would like to ask you whether you have any feedback on the functionality. I would like to finalize the task and make a decision on whether we would like to continue and release the feature or abandon it.

Simply007 commented 1 year ago

As we didn't get enough feedback, I am closing this issue. If we get more feedback on the beta, we will consider adding this as a part of the standard release, not just as a testable beta release.

If we missed anything on this issue or if you want to keep it open, please reply here.

Thanks for being a part of the Kontent.ai community!