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

Feature/90 known types #165

Merged
merged 52 commits into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
de3243e
157 use filescoped namespaces
Sevitas Sep 8, 2022
946f489
90 introduce option for ExtendedDeliverModels
Sevitas Sep 8, 2022
31d3c9e
90 argument and options handling
Sevitas Sep 10, 2022
2844427
90 remove preview option
Sevitas Sep 10, 2022
cf20f7b
Revert "90 remove preview option"
Sevitas Sep 15, 2022
8495eca
90 options validation
Sevitas Sep 15, 2022
4e478db
90 rename UsedMappingsType to DesiredModelsType
Sevitas Sep 15, 2022
10fbb60
90 refactorings
Sevitas Sep 15, 2022
2410e06
90 introduce ExtendedDeliveryCodeGenerator
Sevitas Sep 15, 2022
c3d3faa
90 use options instead strings in BaseClassCodeGenerator
Sevitas Sep 16, 2022
a9903da
90 introduce class code generators
Sevitas Sep 16, 2022
a5f5905
90 change GetClassDeclaration return type to TypeDeclarationSyntax
Sevitas Sep 16, 2022
b6ec2c7
90 impl + test of classcode generators
Sevitas Sep 22, 2022
422268e
90 refactoring management and delivery class code generator tests
Sevitas Sep 22, 2022
f99ca54
90 introduce TypedExtendedDeliveryClassCodeGeneratorTests
Sevitas Sep 22, 2022
3e080c5
90 TypedExtendedDeliveryClassCodeGenerator impl
Sevitas Sep 22, 2022
3f05ab6
90 introduce FileSystemOutputProviderTests
Sevitas Sep 22, 2022
f5965a2
90 introduce TypedDeliveryPropertyMapper+ impl
Sevitas Oct 13, 2022
8290e76
90 include ExtendedDeliveryClassCodeGenerator to ClassCodeGeneratorFa…
Sevitas Oct 20, 2022
c2fa2e1
90 refactor test data generation
Sevitas Nov 10, 2022
eaa0e22
90 refactor typed property mapper
Sevitas Nov 10, 2022
fb3f13a
90 fix original property name
Sevitas Nov 10, 2022
7369338
90 introduce GetEnumerableType helper method
Sevitas Nov 10, 2022
6f5f58a
90 ExtendedDeliveryCodeGenerator impl + tests
Sevitas Nov 10, 2022
342ac0f
Merge branch 'master' into feature/90_known_types
Sevitas Jan 5, 2023
bd536df
Merge branch 'master' into feature/90_known_types
Sevitas Jan 5, 2023
da544d9
90 use fluentAssertions
Sevitas Jan 5, 2023
dd0ad0e
90 refactorings
Sevitas Jan 5, 2023
1da9e84
90 update docs
Sevitas Jan 19, 2023
99a2261
90 include subpages element as well
Sevitas Jan 26, 2023
1f6b019
90 bump nuget packages
Sevitas Feb 9, 2023
1c4f102
90 remove separate generation of preview models
Sevitas Feb 9, 2023
7323107
Merge branch 'master' into feature/90_known_types
Sevitas Feb 16, 2023
396ba3f
90 intruduce structured datetime to tests for extended delivery models
Sevitas Feb 16, 2023
217b114
90 docs
Sevitas Feb 16, 2023
975dfeb
Merge branch 'master' into feature/90_known_types
Sevitas Feb 23, 2023
4f85ced
90 introduce structured modular content option
Sevitas Mar 2, 2023
483fdd1
90 refactoring
Sevitas Mar 2, 2023
1bae082
90 fix casing
Sevitas Mar 2, 2023
ba8d418
90 fix appSettings.json file
Sevitas Mar 2, 2023
95bd45c
90 docs
Sevitas Mar 3, 2023
ed0a443
90 introduce IContentItem from delivery sdk
Sevitas Mar 9, 2023
c9aae6d
90 docs
Sevitas Mar 9, 2023
41af3bf
90 review fixes
Sevitas Mar 15, 2023
0c45133
90 introduce SingleAllowedTypeAtMostSingleItem
Sevitas Mar 16, 2023
5d554c4
90 fix naming of ExtendedDeliveryModels
Sevitas Mar 16, 2023
ec03c17
90 docs
Sevitas Mar 16, 2023
546327e
90 fix warnings
Sevitas Mar 24, 2023
5409722
90 docx fix
Sevitas Mar 31, 2023
d4ad7bd
90 fix condition for itemcountlimit
Sevitas Apr 6, 2023
07ad4ca
90 fix arg problems
Sevitas Apr 13, 2023
6dfb80a
90 bump nuget packages of Delivery and Management sdks
Sevitas Apr 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 51 additions & 21 deletions README.md
Sevitas marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions src/Kontent.Ai.ModelGenerator.Core/CodeGeneratorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@ namespace Kontent.Ai.ModelGenerator.Core;

public abstract class CodeGeneratorBase
{
private string ProjectId => Options.ManagementApi ? Options.ManagementOptions.ProjectId : Options.DeliveryOptions.ProjectId;

protected readonly CodeGeneratorOptions Options;
protected readonly IOutputProvider OutputProvider;

protected string FilenameSuffix => string.IsNullOrEmpty(Options.FileNameSuffix) ? "" : $".{Options.FileNameSuffix}";
protected string NoContentTypeAvailableMessage =>
$@"No content type available for the project ({ProjectId}). Please make sure you have the Delivery API enabled at https://app.kontent.ai/.";
$@"No content type available for the project ({Options.GetProjectId()}). Please make sure you have the Delivery API enabled at https://app.kontent.ai/.";

protected CodeGeneratorBase(IOptions<CodeGeneratorOptions> options, IOutputProvider outputProvider)
{
Expand Down Expand Up @@ -125,7 +123,7 @@ private async Task GenerateBaseClass()
return;
}

var baseClassCodeGenerator = new BaseClassCodeGenerator(Options.BaseClass, Options.Namespace);
var baseClassCodeGenerator = new BaseClassCodeGenerator(Options);

foreach (var codeGenerator in classCodeGenerators)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ public static ClassCodeGenerator CreateClassCodeGenerator(CodeGeneratorOptions o
return new PartialClassCodeGenerator(classDefinition, classFilename, options.Namespace);
}

return options.ManagementApi
? new ManagementClassCodeGenerator(classDefinition, classFilename, options.Namespace)
if (options.ManagementApi())
{
return new ManagementClassCodeGenerator(classDefinition, classFilename, options.Namespace);
}

return options.ExtendedDeliveryModels()
? new ExtendedDeliveryClassCodeGenerator(classDefinition, classFilename, options.IsStructuredModelModularContent(), options.Namespace)
: new DeliveryClassCodeGenerator(classDefinition, classFilename, options.Namespace);
}
}
13 changes: 13 additions & 0 deletions src/Kontent.Ai.ModelGenerator.Core/Common/ClassDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ public void AddSystemProperty()
AddProperty(new Property("system", nameof(IContentItemSystemAttributes)));
}

public void TryAddSystemProperty()
{
try
{
AddProperty(new Property("system", nameof(IContentItemSystemAttributes)));
}
catch (InvalidOperationException)
{
Console.WriteLine(
$"Warning: Can't add 'System' property. It's in collision with existing element in Content Type '{ClassName}'.");
}
}

private bool PropertyIsAlreadyPresent(Property property)
{
return Properties.Exists(e => e.Identifier == property.Identifier);
Expand Down
88 changes: 64 additions & 24 deletions src/Kontent.Ai.ModelGenerator.Core/Common/Property.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using Kontent.Ai.Delivery.Abstractions;
using Kontent.Ai.Management.Models.LanguageVariants.Elements;
using Kontent.Ai.Management.Models.Types.Elements;
using Kontent.Ai.ModelGenerator.Core.Generators.Class;
using Kontent.Ai.ModelGenerator.Core.Helpers;

namespace Kontent.Ai.ModelGenerator.Core.Common;
Expand All @@ -13,8 +14,12 @@ public class Property
{
private const string RichTextElementType = "rich_text";
private const string DateTimeElementType = "date_time";
private const string ModularContentElementType = "modular_content";

public const string StructuredSuffix = "(structured)";

public static string ObjectType => nameof(Object).ToLower(CultureInfo.InvariantCulture);

public string Identifier => TextHelpers.GetValidPascalCaseIdentifierName(Codename);

public string Codename { get; }
Expand All @@ -26,23 +31,41 @@ public class Property
/// </summary>
public string TypeName { get; }

private static readonly Dictionary<string, string> DeliverElementTypesDictionary = new Dictionary<string, string>
private static readonly IImmutableDictionary<string, string> DeliverElementTypesDictionary = new Dictionary<string, string>
{
{ "text", "string" },
{ RichTextElementType, "string" },
{ $"{RichTextElementType}{StructuredSuffix}", nameof(IRichTextContent)},
{ "number", "decimal?" },
{ "multiple_choice", $"{nameof(IEnumerable)}<{nameof(IMultipleChoiceOption)}>"},
{ DateTimeElementType, "DateTime?" },
{ $"{DateTimeElementType}{StructuredSuffix}", nameof(IDateTimeContent) },
{ "asset", $"{nameof(IEnumerable)}<{nameof(IAsset)}>" },
{ "modular_content", $"{nameof(IEnumerable)}<{nameof(Object).ToLower(CultureInfo.InvariantCulture)}>" },
{ "taxonomy", $"{nameof(IEnumerable)}<{nameof(ITaxonomyTerm)}>" },
{ "multiple_choice", TextHelpers.GetEnumerableType(nameof(IMultipleChoiceOption))},
{ "asset", TextHelpers.GetEnumerableType(nameof(IAsset)) },
{ ModularContentElementType, TextHelpers.GetEnumerableType(ObjectType) },
{ $"{ModularContentElementType}{StructuredSuffix}", TextHelpers.GetEnumerableType(nameof(IContentItem)) },
{ "taxonomy", TextHelpers.GetEnumerableType(nameof(ITaxonomyTerm)) },
{ "url_slug", "string" },
{ "custom", "string" }
};
}.ToImmutableDictionary();

private static readonly Dictionary<ElementMetadataType, string> ManagementElementTypesDictionary = new Dictionary<ElementMetadataType, string>
internal static readonly IImmutableDictionary<string, string> ExtendedDeliverElementTypesDictionary = new Dictionary<string, string>
{
{ ElementMetadataType.Text.ToString(), "string" },
{ ElementMetadataType.RichText.ToString(), "string" },
{ $"{ElementMetadataType.RichText}{StructuredSuffix}", nameof(IRichTextContent)},
{ ElementMetadataType.Number.ToString(), "decimal?" },
{ ElementMetadataType.MultipleChoice.ToString(), TextHelpers.GetEnumerableType(nameof(IMultipleChoiceOption))},
{ ElementMetadataType.DateTime.ToString(), "DateTime?" },
{ $"{ElementMetadataType.DateTime}{StructuredSuffix}", nameof(IDateTimeContent) },
{ ElementMetadataType.Asset.ToString(), TextHelpers.GetEnumerableType(nameof(IAsset)) },
{ ElementMetadataType.LinkedItems.ToString(), null },
{ ElementMetadataType.Subpages.ToString(), null },
{ ElementMetadataType.Taxonomy.ToString(), TextHelpers.GetEnumerableType(nameof(ITaxonomyTerm)) },
{ ElementMetadataType.UrlSlug.ToString(), "string" },
{ ElementMetadataType.Custom.ToString(), "string" }
}.ToImmutableDictionary();

private static readonly IImmutableDictionary<ElementMetadataType, string> ManagementElementTypesDictionary = new Dictionary<ElementMetadataType, string>
{
{ ElementMetadataType.Text, nameof(TextElement) },
{ ElementMetadataType.RichText, nameof(RichTextElement) },
Expand All @@ -55,7 +78,7 @@ public class Property
{ ElementMetadataType.Taxonomy, nameof(TaxonomyElement) },
{ ElementMetadataType.UrlSlug,nameof(UrlSlugElement) },
{ ElementMetadataType.Custom, nameof(CustomElement) }
};
}.ToImmutableDictionary();

public Property(string codename, string typeName, string id = null)
{
Expand All @@ -68,31 +91,48 @@ public Property(string codename, string typeName, string id = null)

public static bool IsRichTextElementType(string elementType) => elementType == RichTextElementType;

private static bool IsContentTypeSupported(string elementType)
{
return DeliverElementTypesDictionary.ContainsKey(elementType);
}
public static bool IsModularContentElementType(string elementType) => elementType == ModularContentElementType;

private static bool IsContentTypeSupported(ElementMetadataType elementType)
{
return ManagementElementTypesDictionary.ContainsKey(elementType);
}
public static bool IsContentTypeSupported(string elementType, bool extendedDeliveryModels) => extendedDeliveryModels
? ExtendedDeliverElementTypesDictionary.ContainsKey(elementType)
: DeliverElementTypesDictionary.ContainsKey(elementType);

public static bool IsContentTypeSupported(string elementType) => DeliverElementTypesDictionary.ContainsKey(elementType);

public static bool IsContentTypeSupported(ElementMetadataType elementType) =>
ManagementElementTypesDictionary.ContainsKey(elementType);

public static Property FromContentTypeElement(string codename, string elementType)
public static Property FromContentTypeElement(string codename, string elementType) => IsContentTypeSupported(elementType)
? new Property(codename, DeliverElementTypesDictionary[elementType])
: throw new ArgumentException($"Unknown Content Type {elementType}", nameof(elementType));

public static Property FromContentTypeElement(ElementMetadataBase element)
{
if (IsContentTypeSupported(elementType))
if (IsContentTypeSupported(element.Type))
{
return new Property(codename, DeliverElementTypesDictionary[elementType]);
return new Property(element.Codename, ManagementElementTypesDictionary[element.Type], element.Id.ToString());
}

if (element.Type == ElementMetadataType.Guidelines)
{
throw new UnsupportedTypeException();
}

throw new ArgumentException($"Unknown Content Type {elementType}", nameof(elementType));
throw new ArgumentException($"Unknown Content Type {element.Type}", nameof(element));
}

public static Property FromContentTypeElement(ElementMetadataBase element)
public static Property FromContentTypeElement(ElementMetadataBase element, string elementType) =>
FromContentTypeElement(element, elementType, element.Codename);

public static Property FromContentTypeElement(ElementMetadataBase element, string elementType, string finalPropertyName)
{
if (IsContentTypeSupported(element.Type))
if (IsContentTypeSupported(element.Type.ToString(), true))
{
return new Property(element.Codename, ManagementElementTypesDictionary[element.Type], element.Id.ToString());
var resultElementType = element.Type is ElementMetadataType.LinkedItems or ElementMetadataType.Subpages
? elementType
: ExtendedDeliverElementTypesDictionary[elementType];

return new Property(finalPropertyName, resultElementType);
}

if (element.Type == ElementMetadataType.Guidelines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Kontent.Ai.ModelGenerator.Core.Configuration;
public class CodeGeneratorOptions
{
private const char StructuredModelSeparator = ',';
private const bool DefaultExtendedDeliveryModels = false;
private const bool DefaultGeneratePartials = true;
private const bool DefaultWithTypeProvider = true;
private const bool DefaultManagementApi = false;
Expand All @@ -24,6 +25,11 @@ public class CodeGeneratorOptions
/// </summary>
public ManagementOptions ManagementOptions { get; set; }

/// <summary>
/// Indicates whether the extended Delivery models should be generated
/// </summary>
public bool ExtendedDeliveryModels { get; set; } = DefaultExtendedDeliveryModels;

/// <summary>
/// Namespace name of the generated classes
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,36 @@

public static class CodeGeneratorOptionsExtensions
{
public static bool ManagementApi(this CodeGeneratorOptions options) =>
options.ManagementApi && !options.ExtendedDeliveryModels;

public static bool ExtendedDeliveryModels(this CodeGeneratorOptions options) =>
!options.ManagementApi && options.ExtendedDeliveryModels;

public static bool DeliveryApi(this CodeGeneratorOptions options) => !options.ManagementApi() && !options.ExtendedDeliveryModels();

public static DesiredModelsType GetDesiredModelsType(this CodeGeneratorOptions options)
{
if (options.ManagementApi())
{
return DesiredModelsType.Management;
}

return options.ExtendedDeliveryModels() ? DesiredModelsType.ExtendedDelivery : DesiredModelsType.Delivery;
}

public static string GetProjectId(this CodeGeneratorOptions options) =>
options.ManagementApi || options.ExtendedDeliveryModels
? options.ManagementOptions.ProjectId
: options.DeliveryOptions.ProjectId;

public static bool IsStructuredModelModularContent(this CodeGeneratorOptions options) =>
options.StructuredModelFlags.HasFlag(StructuredModelFlags.ModularContent);

public static bool IsStructuredModelEnabled(this CodeGeneratorOptions options) =>
options.StructuredModelFlags.HasFlag(StructuredModelFlags.RichText) ||
options.StructuredModelFlags.HasFlag(StructuredModelFlags.True) ||
options.StructuredModelFlags.HasFlag(StructuredModelFlags.ModularContent) ||
options.StructuredModelFlags.HasFlag(StructuredModelFlags.DateTime);

public static bool IsStructuredModelRichText(this CodeGeneratorOptions options) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Kontent.Ai.ModelGenerator.Core.Configuration;

public enum DesiredModelsType
{
Delivery,
Management,
ExtendedDelivery
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ public enum StructuredModelFlags
RichText = 2,
DateTime = 4,
ValidationIssue = 8,
True = 16
True = 16,
ModularContent = 32
}
37 changes: 1 addition & 36 deletions src/Kontent.Ai.ModelGenerator.Core/DeliveryCodeGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Kontent.Ai.Delivery.Abstractions;
using Kontent.Ai.ModelGenerator.Core.Common;
using Kontent.Ai.ModelGenerator.Core.Configuration;
using Kontent.Ai.ModelGenerator.Core.Generators;
using Kontent.Ai.ModelGenerator.Core.Generators.Class;
using Kontent.Ai.ModelGenerator.Core.Helpers;
using Microsoft.Extensions.Options;

namespace Kontent.Ai.ModelGenerator.Core;

public class DeliveryCodeGenerator : CodeGeneratorBase
public class DeliveryCodeGenerator : DeliveryCodeGeneratorBase
{
private readonly IDeliveryClient _deliveryClient;

Expand All @@ -27,18 +25,6 @@ public DeliveryCodeGenerator(IOptions<CodeGeneratorOptions> options, IOutputProv
_deliveryClient = deliveryClient;
}

public new async Task<int> RunAsync()
{
await base.RunAsync();

if (Options.WithTypeProvider)
{
await GenerateTypeProvider();
}

return 0;
}

protected override async Task<ICollection<ClassCodeGenerator>> GetClassCodeGenerators()
{
var deliveryTypes = (await _deliveryClient.GetTypesAsync()).Types;
Expand Down Expand Up @@ -94,27 +80,6 @@ internal ClassCodeGenerator GetClassCodeGenerator(IContentType contentType)
return ClassCodeGeneratorFactory.CreateClassCodeGenerator(Options, classDefinition, classFilename);
}

private async Task GenerateTypeProvider()
{
var classCodeGenerators = await GetClassCodeGenerators();

if (!classCodeGenerators.Any())
{
Console.WriteLine(NoContentTypeAvailableMessage);
return;
}

var typeProviderCodeGenerator = new TypeProviderCodeGenerator(Options.Namespace);

foreach (var codeGenerator in classCodeGenerators)
{
typeProviderCodeGenerator.AddContentType(codeGenerator.ClassDefinition.Codename, codeGenerator.ClassDefinition.ClassName);
}

var typeProviderCode = typeProviderCodeGenerator.GenerateCode();
WriteToOutputProvider(typeProviderCode, TypeProviderCodeGenerator.ClassName, true);
}

private static void TryAddSystemProperty(ClassDefinition classDefinition)
{
try
Expand Down
49 changes: 49 additions & 0 deletions src/Kontent.Ai.ModelGenerator.Core/DeliveryCodeGeneratorBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Kontent.Ai.ModelGenerator.Core.Configuration;
using Kontent.Ai.ModelGenerator.Core.Generators.Class;
using Kontent.Ai.ModelGenerator.Core.Generators;
using Microsoft.Extensions.Options;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace Kontent.Ai.ModelGenerator.Core;

public abstract class DeliveryCodeGeneratorBase : CodeGeneratorBase
{
protected DeliveryCodeGeneratorBase(IOptions<CodeGeneratorOptions> options, IOutputProvider outputProvider) : base(options, outputProvider)
{
}

public new async Task<int> RunAsync()
{
await base.RunAsync();

if (Options.WithTypeProvider)
{
await GenerateTypeProvider();
}

return 0;
}

private async Task GenerateTypeProvider()
{
var classCodeGenerators = await GetClassCodeGenerators();

if (!classCodeGenerators.Any())
{
Console.WriteLine(NoContentTypeAvailableMessage);
return;
}

var typeProviderCodeGenerator = new TypeProviderCodeGenerator(Options.Namespace);

foreach (var codeGenerator in classCodeGenerators)
{
typeProviderCodeGenerator.AddContentType(codeGenerator.ClassDefinition.Codename, codeGenerator.ClassDefinition.ClassName);
}

var typeProviderCode = typeProviderCodeGenerator.GenerateCode();
WriteToOutputProvider(typeProviderCode, TypeProviderCodeGenerator.ClassName, true);
}
}
Loading