kontent-ai / model-generator-net

Kontent.ai .NET model generator.
https://www.nuget.org/packages/Kontent.Ai.ModelGenerator
MIT License
17 stars 18 forks source link

Skip prefixes for the field names generated from snippet #182

Closed AntonDriemin closed 1 year ago

AntonDriemin commented 1 year ago

Motivation

The name prefix will always be present for the properties generated for a model with the snippet. For example, I have a snippet with the codename "Address" with fields "Country", "City" My generated model will be Address_Country Address_City so, all my properties will be with the prefixes if they are generated from the snippet. It is not ok for the common coding conventions adopted by most organizations. And not convenient.

Proposed solution

Implement an optional argument that will let us generate properties without Snipped codename as a prefix.

Additional context

image

Sevitas commented 1 year ago

Hi @AntonDriemin , thank you for submitting the issue. We will look into it and let you know the outcome.

Sevitas commented 1 year ago

Hi @AntonDriemin, we have discussed this a bit further. We do not think it is a good idea to generate properties without a snippet prefix. The reasoning behind this is that it might generate invalid models.

Consider scenario: ContentType: image ContentTypeSnippet: image

This would result in an invalid model and also a duplicate property error. image

As an alternative you might use:

@AntonDriemin do you think this can work for you?

Sevitas commented 1 year ago

This issue was closed due to inactivity. If you would like to elaborate on it a bit further, feel free to reopen the issue.

AntonDriemin commented 1 year ago

@Sevitas I understand the duplication issue, that's why I propose to implement "optional argument that will let us generate properties without Snipped codename as a prefix." If we have 30 different page types in our project, and every page has a "SEO" tab with 8 properties, we have to create 240 properties total for each page type. Same problem for other content types.

Sevitas commented 1 year ago

Hi @AntonDriemin, thank you for explaining the use case. We need to consider your request a bit further, so I will get back you with the response. Have you considered using customization and extensibility as I have suggested in the previous comment?

AntonDriemin commented 1 year ago

Hi @AntonDriemin, thank you for explaining the use case. We need to consider your request a bit further, so I will get back you with the response. Have you considered using customization and extensibility as I have suggested in the previous comment?

Yes, I'm using interfaces for partial classes to ensure consistency of the components in a way: public partial class Eventspage : IKontentPage, IPreviewPage { } Thank you.

Sevitas commented 1 year ago

Hi @AntonDriemin, We have considered your scenario but implementing the requested feature is not a good idea. On the other hand, there is a possible workaround for your use case.

You can use Customizing the property matching and implementing your own IPropertyMapper. As an inspiration, you can use PropertyMapper. To ensure your models have the expected property name you need to generate your models with a base class option -b. In the generate base class file you can create your properties.

Note: be aware, that the base class properties will be generated for all of your content types. If you would like to prevent this you can do that by editing the {BaseClassName}Extender.cs file, but be aware of the fact, that after regenerating your models you will lose all of your changes done in {BaseClassName}Extender.cs file.

@AntonDriemin do you think this can work for you?

I can provide you with an example: CustomPropertyMapper.cs:

using System.Reflection;
using Kontent.Ai.Delivery.Abstractions;

namespace KontentAiModels
{
    public class CustomPropertyMapper : IPropertyMapper
    {
        public bool IsMatch(PropertyInfo modelProperty, string fieldName, string contentType)
        {
            return modelProperty.Name.ToLower() == fieldName || (fieldName.EndsWith(modelProperty.Name.ToLower()) && fieldName.StartsWith("snippet"));
        }
    }
}

Content type model files:

using Kontent.Ai.Delivery.Abstractions;

namespace KontentAiModels
{
    public partial class ContentType1
    {
        public const string Codename = "content_type_1";
        public const string NumberCodename = "number";
        public const string Snippet1TextCodename = "snippet_1__text";

        public decimal? Number { get; set; }
        public string Snippet1Text { get; set; }
        public IContentItemSystemAttributes System { get; set; }
    }
}
using Kontent.Ai.Delivery.Abstractions;

namespace KontentAiModels
{
    public partial class ContentType2
    {
        public const string Codename = "content_type_1";
        public const string NumberCodename = "number";
        public const string Snippet1TextCodename = "snippet_1__text";

        public decimal? Number { get; set; }
        public string Snippet1Text { get; set; }
        public IContentItemSystemAttributes System { get; set; }
    }
}

Base class file: SnippetPropertyContainer.cs

namespace KontentAiModels
{
    public partial class SnippetPropertyContainer
    {
        public string Text { get; set; }
    }
}

Base class extender file: SnippetPropertyContainerExtender.cs

using System;
using Kontent.Ai.Delivery.Abstractions;

namespace KontentAiModels
{
    // These classes extend the generated models to all inherit from the common basetype SnippetPropertyContainer.
    public partial class ContentType1 : SnippetPropertyContainer
    {
    }

    public partial class ContentType2 : SnippetPropertyContainer
    {
    }
}

Usage of a delivery client:

public class Program
{
    public static async Task<int> Main(string[] args)
    {
        var deliveryClient = DeliveryClientBuilder
            .WithProjectId("837a062d-bb42-0079-a849-9b31bf7a9fca")
            .WithTypeProvider(new CustomTypeProvider())
            .WithPropertyMapper(new CustomPropertyMapper())
            .Build();

        var items = await deliveryClient.GetItemsAsync<ContentType1>();

        return 0;
    }
}
Sevitas commented 1 year ago

Hi @AntonDriemin, I'd like to confirm that the proposed solution is working for you. If you do not have any other questions we would like to close the issue.

AntonDriemin commented 1 year ago

Hi @Sevitas it looks like I don't have another option except this one. Let's consider this as done. Thank you for your effort!