From 474a5a4fb4fe7e5ee5247d29c0b5c4135075e224 Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 22 Jul 2021 20:27:09 +0200 Subject: [PATCH 01/12] introduce management client options --- .../Configuration/CodeGeneratorOptions.cs | 6 ++++ src/Kentico.Kontent.ModelGenerator/Program.cs | 3 +- .../ValidationExtensions.cs | 29 ++++++++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/Kentico.Kontent.ModelGenerator.Core/Configuration/CodeGeneratorOptions.cs b/src/Kentico.Kontent.ModelGenerator.Core/Configuration/CodeGeneratorOptions.cs index f49c4d0f..ea83c080 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/Configuration/CodeGeneratorOptions.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/Configuration/CodeGeneratorOptions.cs @@ -1,4 +1,5 @@ using Kentico.Kontent.Delivery.Abstractions; +using Kentico.Kontent.Management; namespace Kentico.Kontent.ModelGenerator.Core.Configuration { @@ -15,6 +16,11 @@ public class CodeGeneratorOptions /// public DeliveryOptions DeliveryOptions { get; set; } + /// + /// Management Client configuration. + /// + public ManagementOptions ManagementOptions { get; set; } + /// /// Namespace name of the generated classes /// diff --git a/src/Kentico.Kontent.ModelGenerator/Program.cs b/src/Kentico.Kontent.ModelGenerator/Program.cs index 7b492d1f..baf189e2 100644 --- a/src/Kentico.Kontent.ModelGenerator/Program.cs +++ b/src/Kentico.Kontent.ModelGenerator/Program.cs @@ -38,7 +38,7 @@ public static async Task Main(string[] args) var serviceProvider = services.BuildServiceProvider(); // Validate configuration of the Delivery Client - serviceProvider.GetService>().Value.DeliveryOptions.Validate(); + serviceProvider.GetService>().Value.Validate(); // Code generator entry point return await serviceProvider.GetService().RunAsync(); @@ -72,6 +72,7 @@ private static IDictionary GetSwitchMappings() {"-t", nameof(CodeGeneratorOptions.WithTypeProvider) }, {"-s", nameof(CodeGeneratorOptions.StructuredModel) }, {"-c", nameof(CodeGeneratorOptions.ContentManagementApi) }, + {"-k", $"{nameof(CodeGeneratorOptions.ManagementOptions)}:{nameof(CodeGeneratorOptions.ManagementOptions.ApiKey)}" }, {"-b", nameof(CodeGeneratorOptions.BaseClass) } }; return mappings; diff --git a/src/Kentico.Kontent.ModelGenerator/ValidationExtensions.cs b/src/Kentico.Kontent.ModelGenerator/ValidationExtensions.cs index fabed1a2..3e2547f4 100644 --- a/src/Kentico.Kontent.ModelGenerator/ValidationExtensions.cs +++ b/src/Kentico.Kontent.ModelGenerator/ValidationExtensions.cs @@ -1,6 +1,8 @@ using System; using Kentico.Kontent.Delivery.Abstractions; using Kentico.Kontent.Delivery.Configuration; +using Kentico.Kontent.Management; +using Kentico.Kontent.ModelGenerator.Core.Configuration; namespace Kentico.Kontent.ModelGenerator { @@ -9,11 +11,36 @@ namespace Kentico.Kontent.ModelGenerator /// public static class ValidationExtensions { + private const string SeePart = "See http://bit.ly/k-params for more details on configuration."; + + /// + /// Validates that CodeGeneratorOptions are initialized and performs some extra integrity validations. + /// + /// CodeGeneratorOptions object to be validated + public static void Validate(this CodeGeneratorOptions codeGeneratorOptions) + { + codeGeneratorOptions.DeliveryOptions.Validate(); + + if (codeGeneratorOptions.ContentManagementApi) + { + codeGeneratorOptions.ManagementOptions.ProjectId = codeGeneratorOptions.DeliveryOptions.ProjectId; + if (codeGeneratorOptions.ManagementOptions == null || codeGeneratorOptions.ManagementOptions.ProjectId == null) + { + throw new Exception($"You have to provide the '{nameof(ManagementOptions.ProjectId)}' to generate type for Content Management SDK. {SeePart}"); + } + + if (string.IsNullOrWhiteSpace(codeGeneratorOptions.ManagementOptions.ApiKey)) + { + throw new Exception($"You have to provide the '{nameof(ManagementOptions.ApiKey)}' to generate type for Content Management SDK. {SeePart}"); + } + } + } + /// /// Validates that DeliveryOptions are initialized and performs some extra integrity validations. /// /// DeliveryOptions object to be validated - public static void Validate(this DeliveryOptions deliveryOptions) + private static void Validate(this DeliveryOptions deliveryOptions) { if (deliveryOptions == null) { From a6ed3f1fe3a8f8392cb61dde5b4a5a2dc3a0a54d Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 22 Jul 2021 20:40:45 +0200 Subject: [PATCH 02/12] introduce custom management client --- .../IManagementClient.cs | 12 +++++ .../ManagementClient.cs | 52 +++++++++++++++++++ src/Kentico.Kontent.ModelGenerator/Program.cs | 3 ++ 3 files changed, 67 insertions(+) create mode 100644 src/Kentico.Kontent.ModelGenerator.Core/IManagementClient.cs create mode 100644 src/Kentico.Kontent.ModelGenerator.Core/ManagementClient.cs diff --git a/src/Kentico.Kontent.ModelGenerator.Core/IManagementClient.cs b/src/Kentico.Kontent.ModelGenerator.Core/IManagementClient.cs new file mode 100644 index 00000000..2afb9dff --- /dev/null +++ b/src/Kentico.Kontent.ModelGenerator.Core/IManagementClient.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Kentico.Kontent.ModelGenerator.Core.Configuration; +using Newtonsoft.Json.Linq; + +namespace Kentico.Kontent.ModelGenerator.Core +{ + public interface IManagementClient + { + Task> GetAllContentTypesAsync(CodeGeneratorOptions options); + } +} diff --git a/src/Kentico.Kontent.ModelGenerator.Core/ManagementClient.cs b/src/Kentico.Kontent.ModelGenerator.Core/ManagementClient.cs new file mode 100644 index 00000000..a7ba86cb --- /dev/null +++ b/src/Kentico.Kontent.ModelGenerator.Core/ManagementClient.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Kentico.Kontent.ModelGenerator.Core.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Kentico.Kontent.ModelGenerator.Core +{ + public class ManagementClient : IManagementClient + { + private const int MilisecondsDelay = 1000; + private readonly HttpClient _httpClient; + + public ManagementClient(HttpClient httpClient) + { + _httpClient = httpClient; + } + + public async Task> GetAllContentTypesAsync(CodeGeneratorOptions options) + { + var contentTypes = new List(); + string continuationToken = null; + do + { + _httpClient.DefaultRequestHeaders.Clear(); + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", options.ManagementOptions.ApiKey); + if (continuationToken != null) + { + _httpClient.DefaultRequestHeaders.Add("x-continuation", continuationToken); + } + + var response = await _httpClient.GetAsync(new Uri($"https://manage.kontent.ai/v2/projects/{options.ManagementOptions.ProjectId}/types"), HttpCompletionOption.ResponseContentRead); + + var responseStream = await response.Content.ReadAsStreamAsync(); + var responseObject = await JObject.ReadFromAsync(new JsonTextReader(new StreamReader(responseStream))); + + continuationToken = responseObject["pagination"]["continuation_token"].ToObject(); + + contentTypes.AddRange(responseObject["types"].ToObject>()); + + await Task.Delay(MilisecondsDelay); + } + while (continuationToken != null); + + return contentTypes; + } + } +} diff --git a/src/Kentico.Kontent.ModelGenerator/Program.cs b/src/Kentico.Kontent.ModelGenerator/Program.cs index baf189e2..e5f755bd 100644 --- a/src/Kentico.Kontent.ModelGenerator/Program.cs +++ b/src/Kentico.Kontent.ModelGenerator/Program.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Options; using System; using System.Collections.Generic; +using System.Net.Http; using System.Threading.Tasks; using Kentico.Kontent.Delivery; using Kentico.Kontent.Delivery.Abstractions; @@ -31,6 +32,8 @@ public static async Task Main(string[] args) // Fill the DI container services.Configure(configuration); services.AddDeliveryClient(configuration); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); From 30dac019fa2a27bed22ed4ceec05192dd628d70c Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 22 Jul 2021 20:43:54 +0200 Subject: [PATCH 03/12] introduce management client in tests --- .../CodeGeneratorTests.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/CodeGeneratorTests.cs b/test/Kentico.Kontent.ModelGenerator.Tests/CodeGeneratorTests.cs index 4fc44e00..9342f741 100644 --- a/test/Kentico.Kontent.ModelGenerator.Tests/CodeGeneratorTests.cs +++ b/test/Kentico.Kontent.ModelGenerator.Tests/CodeGeneratorTests.cs @@ -77,9 +77,10 @@ public async Task IntegrationTest(bool cmApi) ContentManagementApi = cmApi }); - var client = DeliveryClientBuilder.WithProjectId(PROJECT_ID).WithDeliveryHttpClient(new DeliveryHttpClient(httpClient)).Build(); + var deliveryClient = DeliveryClientBuilder.WithProjectId(PROJECT_ID).WithDeliveryHttpClient(new DeliveryHttpClient(httpClient)).Build(); + var managementClient = new Mock(); - var codeGenerator = new CodeGenerator(mockOptions.Object, client, new FileSystemOutputProvider(mockOptions.Object)); + var codeGenerator = new CodeGenerator(mockOptions.Object, deliveryClient, new FileSystemOutputProvider(mockOptions.Object), managementClient.Object); await codeGenerator.GenerateContentTypeModels(); await codeGenerator.GenerateTypeProvider(); @@ -87,7 +88,7 @@ public async Task IntegrationTest(bool cmApi) Assert.True(Directory.GetFiles(Path.GetFullPath(TEMP_DIR)).Length > 10); Assert.NotEmpty(Directory.EnumerateFiles(Path.GetFullPath(TEMP_DIR), "*.Generated.cs")); - Assert.NotEmpty(Directory.EnumerateFiles(Path.GetFullPath(TEMP_DIR)).Where(p=> !p.Contains("*.Generated.cs"))); + Assert.NotEmpty(Directory.EnumerateFiles(Path.GetFullPath(TEMP_DIR)).Where(p => !p.Contains("*.Generated.cs"))); Assert.NotEmpty(Directory.EnumerateFiles(Path.GetFullPath(TEMP_DIR), "*TypeProvider.cs")); // Cleanup @@ -117,9 +118,10 @@ public async Task IntegrationTestWithGeneratedSuffix(bool cmApi) ContentManagementApi = cmApi }); - var client = DeliveryClientBuilder.WithProjectId(PROJECT_ID).WithDeliveryHttpClient(new DeliveryHttpClient(httpClient)).Build(); + var deliveryClient = DeliveryClientBuilder.WithProjectId(PROJECT_ID).WithDeliveryHttpClient(new DeliveryHttpClient(httpClient)).Build(); + var managementClient = new Mock(); - var codeGenerator = new CodeGenerator(mockOptions.Object, client, new FileSystemOutputProvider(mockOptions.Object)); + var codeGenerator = new CodeGenerator(mockOptions.Object, deliveryClient, new FileSystemOutputProvider(mockOptions.Object), managementClient.Object); await codeGenerator.GenerateContentTypeModels(); @@ -157,9 +159,10 @@ public async Task IntegrationTestWithGeneratePartials(bool cmApi) ContentManagementApi = cmApi }); - var client = DeliveryClientBuilder.WithProjectId(PROJECT_ID).WithDeliveryHttpClient(new DeliveryHttpClient(httpClient)).Build(); + var deliveryClient = DeliveryClientBuilder.WithProjectId(PROJECT_ID).WithDeliveryHttpClient(new DeliveryHttpClient(httpClient)).Build(); + var managementClient = new Mock(); - var codeGenerator = new CodeGenerator(mockOptions.Object, client, new FileSystemOutputProvider(mockOptions.Object)); + var codeGenerator = new CodeGenerator(mockOptions.Object, deliveryClient, new FileSystemOutputProvider(mockOptions.Object), managementClient.Object); await codeGenerator.GenerateContentTypeModels(); From 5c21973c90164876c0a5e3bc442dce1ed2d4e7c0 Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 22 Jul 2021 22:37:38 +0200 Subject: [PATCH 04/12] introduce attribute KontentElementId --- .../ClassCodeGenerator.cs | 12 +- .../CodeGenerator.cs | 29 +- .../ContentTypeJObjectHelper.cs | 15 + .../ManagementClient.cs | 5 + .../Property.cs | 9 +- ...CompleteContentType_CompiledCode_CMAPI.txt | 10 + .../ClassCodeGeneratorTests.cs | 20 +- .../CodeGeneratorTests.cs | 28 +- .../{types.json => delivery_types.json} | 0 .../Fixtures/management_types.json | 1097 +++++++++++++++++ ...entico.Kontent.ModelGenerator.Tests.csproj | 5 +- 11 files changed, 1200 insertions(+), 30 deletions(-) create mode 100644 src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs rename test/Kentico.Kontent.ModelGenerator.Tests/Fixtures/{types.json => delivery_types.json} (100%) create mode 100644 test/Kentico.Kontent.ModelGenerator.Tests/Fixtures/management_types.json diff --git a/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs b/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs index 06893c3b..9b8a3ba3 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs @@ -75,7 +75,17 @@ public string GenerateCode(bool cmApi = false) SyntaxFactory.AttributeArgument( SyntaxFactory.LiteralExpression( SyntaxKind.StringLiteralExpression, - SyntaxFactory.Literal(element.Codename))))))))); + SyntaxFactory.Literal(element.Codename)))))))), + SyntaxFactory.AttributeList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("KontentElementId")) + .WithArgumentList( + SyntaxFactory.AttributeArgumentList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.AttributeArgument( + SyntaxFactory.LiteralExpression( + SyntaxKind.StringLiteralExpression, + SyntaxFactory.Literal(element.Id))))))))); } return property; diff --git a/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs b/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs index 696e811c..be802493 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs @@ -5,6 +5,7 @@ using Kentico.Kontent.Delivery.Abstractions; using Kentico.Kontent.ModelGenerator.Core.Configuration; using Microsoft.Extensions.Options; +using Newtonsoft.Json.Linq; namespace Kentico.Kontent.ModelGenerator.Core { @@ -12,13 +13,15 @@ public class CodeGenerator { private readonly CodeGeneratorOptions _options; private readonly IDeliveryClient _client; + private readonly IManagementClient _managementClient; private readonly IOutputProvider _outputProvider; - public CodeGenerator(IOptions options, IDeliveryClient deliveryClient, IOutputProvider outputProvider) + public CodeGenerator(IOptions options, IDeliveryClient deliveryClient, IOutputProvider outputProvider, IManagementClient managementClient) { _options = options.Value; _client = deliveryClient; _outputProvider = outputProvider; + _managementClient = managementClient; } public async Task RunAsync() @@ -85,11 +88,13 @@ internal async Task GenerateTypeProvider() internal async Task> GetClassCodeGenerators(bool structuredModel = false) { - IEnumerable contentTypes = (await _client.GetTypesAsync()).Types; + IEnumerable deliveryTypes = (await _client.GetTypesAsync()).Types; + var managementTypes = await _managementClient.GetAllContentTypesAsync(_options); + var codeGenerators = new List(); - if (contentTypes != null) + if (deliveryTypes != null) { - foreach (var contentType in contentTypes) + foreach (var contentType in deliveryTypes) { try { @@ -97,7 +102,12 @@ internal async Task> GetClassCodeGenerators(bool { codeGenerators.Add(GetCustomClassCodeGenerator(contentType)); } - codeGenerators.Add(GetClassCodeGenerator(contentType, structuredModel)); + + var managementContentType = _options.ContentManagementApi + ? managementTypes.FirstOrDefault(ct => ct["codename"].ToObject() == contentType.System.Codename) + : null; + + codeGenerators.Add(GetClassCodeGenerator(contentType, structuredModel, managementContentType)); } catch (InvalidIdentifierException) { @@ -108,7 +118,7 @@ internal async Task> GetClassCodeGenerators(bool return codeGenerators; } - internal ClassCodeGenerator GetClassCodeGenerator(IContentType contentType, bool structuredModel) + internal ClassCodeGenerator GetClassCodeGenerator(IContentType contentType, bool structuredModel, JObject managementContentType = null) { var classDefinition = new ClassDefinition(contentType.System.Codename); @@ -121,7 +131,12 @@ internal ClassCodeGenerator GetClassCodeGenerator(IContentType contentType, bool { elementType += Property.STRUCTURED_SUFFIX; } - var property = Property.FromContentType(element.Codename, elementType, _options.ContentManagementApi); + + var elementId = _options.ContentManagementApi + ? ContentTypeJObjectHelper.GetElementId(managementContentType, element.Codename) + : null; + + var property = Property.FromContentType(element.Codename, elementType, _options.ContentManagementApi, elementId); classDefinition.AddPropertyCodenameConstant(element); classDefinition.AddProperty(property); } diff --git a/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs b/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs new file mode 100644 index 00000000..c238ac62 --- /dev/null +++ b/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json.Linq; + +namespace Kentico.Kontent.ModelGenerator.Core +{ + public static class ContentTypeJObjectHelper + { + public static string GetElementId(JObject managementContentType, string elementCodename) + { + return managementContentType["elements"].ToObject>() + .FirstOrDefault(el => el["codename"].ToObject() == elementCodename)["id"].ToObject(); + } + } +} diff --git a/src/Kentico.Kontent.ModelGenerator.Core/ManagementClient.cs b/src/Kentico.Kontent.ModelGenerator.Core/ManagementClient.cs index a7ba86cb..5d3b5a36 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/ManagementClient.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/ManagementClient.cs @@ -22,6 +22,11 @@ public ManagementClient(HttpClient httpClient) public async Task> GetAllContentTypesAsync(CodeGeneratorOptions options) { + if (!options.ContentManagementApi) + { + return null; + } + var contentTypes = new List(); string continuationToken = null; do diff --git a/src/Kentico.Kontent.ModelGenerator.Core/Property.cs b/src/Kentico.Kontent.ModelGenerator.Core/Property.cs index 99d3c638..be3b2ca2 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/Property.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/Property.cs @@ -14,6 +14,8 @@ public class Property public string Codename { get; } + public string Id { get; } + /// /// Returns return type of the property in a string format (e.g.: "string"). /// @@ -51,10 +53,11 @@ public class Property private static Dictionary ContentTypeToTypeName(bool cmApi) => cmApi ? ContentManagementTypes : DeliverTypes; - public Property(string codename, string typeName) + public Property(string codename, string typeName, string id = null) { Codename = codename; TypeName = typeName; + Id = id; } public static bool IsContentTypeSupported(string contentType, bool cmApi = false) @@ -62,14 +65,14 @@ public static bool IsContentTypeSupported(string contentType, bool cmApi = false return ContentTypeToTypeName(cmApi).ContainsKey(contentType); } - public static Property FromContentType(string codename, string contentType, bool cmApi = false) + public static Property FromContentType(string codename, string contentType, bool cmApi = false, string id = null) { if (!IsContentTypeSupported(contentType, cmApi)) { throw new ArgumentException($"Unknown Content Type {contentType}", nameof(contentType)); } - return new Property(codename, ContentTypeToTypeName(cmApi)[contentType]); + return new Property(codename, ContentTypeToTypeName(cmApi)[contentType], id); } } } diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt b/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt index 501fa526..46c9a46d 100644 --- a/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt +++ b/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt @@ -15,24 +15,34 @@ namespace KenticoKontentModels public partial class CompleteContentType { [JsonProperty("asset")] + [KontentElementId("asset_element_id")] public IEnumerable Asset { get; set; } [JsonProperty("custom")] + [KontentElementId("custom_element_id")] public string Custom { get; set; } [JsonProperty("date_time")] + [KontentElementId("date_time_element_id")] public DateTime? DateTime { get; set; } [JsonProperty("modular_content")] + [KontentElementId("linked_items_element_id")] public IEnumerable ModularContent { get; set; } [JsonProperty("multiple_choice")] + [KontentElementId("multiple_choice_element_id")] public IEnumerable MultipleChoice { get; set; } [JsonProperty("number")] + [KontentElementId("number_element_id")] public decimal? Number { get; set; } [JsonProperty("rich_text")] + [KontentElementId("rich_text_element_id")] public string RichText { get; set; } [JsonProperty("taxonomy")] + [KontentElementId("taxonomy_element_id")] public IEnumerable Taxonomy { get; set; } [JsonProperty("text")] + [KontentElementId("text_element_id")] public string Text { get; set; } [JsonProperty("url_slug")] + [KontentElementId("url_slug_element_id")] public string UrlSlug { get; set; } } } \ No newline at end of file diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/ClassCodeGeneratorTests.cs b/test/Kentico.Kontent.ModelGenerator.Tests/ClassCodeGeneratorTests.cs index fa0b8e5f..e3dc95ba 100644 --- a/test/Kentico.Kontent.ModelGenerator.Tests/ClassCodeGeneratorTests.cs +++ b/test/Kentico.Kontent.ModelGenerator.Tests/ClassCodeGeneratorTests.cs @@ -60,16 +60,16 @@ public void Build_CreatesClassWithCompleteContentType() public void Build_CreatesClassWithCompleteContentType_CMAPI() { var classDefinition = new ClassDefinition("Complete content type"); - classDefinition.AddProperty(Property.FromContentType("text", "text", true)); - classDefinition.AddProperty(Property.FromContentType("rich_text", "rich_text", true)); - classDefinition.AddProperty(Property.FromContentType("number", "number", true)); - classDefinition.AddProperty(Property.FromContentType("multiple_choice", "multiple_choice", true)); - classDefinition.AddProperty(Property.FromContentType("date_time", "date_time", true)); - classDefinition.AddProperty(Property.FromContentType("asset", "asset", true)); - classDefinition.AddProperty(Property.FromContentType("modular_content", "modular_content", true)); - classDefinition.AddProperty(Property.FromContentType("taxonomy", "taxonomy", true)); - classDefinition.AddProperty(Property.FromContentType("url_slug", "url_slug", true)); - classDefinition.AddProperty(Property.FromContentType("custom", "custom", true)); + classDefinition.AddProperty(Property.FromContentType("text", "text", true, "text_element_id")); + classDefinition.AddProperty(Property.FromContentType("rich_text", "rich_text", true, "rich_text_element_id")); + classDefinition.AddProperty(Property.FromContentType("number", "number", true, "number_element_id")); + classDefinition.AddProperty(Property.FromContentType("multiple_choice", "multiple_choice", true, "multiple_choice_element_id")); + classDefinition.AddProperty(Property.FromContentType("date_time", "date_time", true, "date_time_element_id")); + classDefinition.AddProperty(Property.FromContentType("asset", "asset", true, "asset_element_id")); + classDefinition.AddProperty(Property.FromContentType("modular_content", "modular_content", true, "linked_items_element_id")); + classDefinition.AddProperty(Property.FromContentType("taxonomy", "taxonomy", true, "taxonomy_element_id")); + classDefinition.AddProperty(Property.FromContentType("url_slug", "url_slug", true, "url_slug_element_id")); + classDefinition.AddProperty(Property.FromContentType("custom", "custom", true, "custom_element_id")); var classCodeGenerator = new ClassCodeGenerator(classDefinition, classDefinition.ClassName); diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/CodeGeneratorTests.cs b/test/Kentico.Kontent.ModelGenerator.Tests/CodeGeneratorTests.cs index 9342f741..4425a1c5 100644 --- a/test/Kentico.Kontent.ModelGenerator.Tests/CodeGeneratorTests.cs +++ b/test/Kentico.Kontent.ModelGenerator.Tests/CodeGeneratorTests.cs @@ -11,6 +11,8 @@ using Kentico.Kontent.ModelGenerator.Core.Configuration; using Xunit; using System.Linq; +using Kentico.Kontent.Management; +using ManagementClient = Kentico.Kontent.ModelGenerator.Core.ManagementClient; namespace Kentico.Kontent.ModelGenerator.Tests { @@ -66,7 +68,9 @@ public async Task IntegrationTest(bool cmApi) { var mockHttp = new MockHttpMessageHandler(); mockHttp.When("https://deliver.kontent.ai/*") - .Respond("application/json", await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "Fixtures/types.json"))); + .Respond("application/json", await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "Fixtures/delivery_types.json"))); + mockHttp.When("https://manage.kontent.ai/v2/projects/*") + .Respond("application/json", await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "Fixtures/management_types.json"))); var httpClient = mockHttp.ToHttpClient(); var mockOptions = new Mock>(); @@ -74,11 +78,12 @@ public async Task IntegrationTest(bool cmApi) { Namespace = "CustomNamespace", OutputDir = TEMP_DIR, - ContentManagementApi = cmApi + ContentManagementApi = cmApi, + ManagementOptions = new ManagementOptions { ApiKey = "apiKey" } }); var deliveryClient = DeliveryClientBuilder.WithProjectId(PROJECT_ID).WithDeliveryHttpClient(new DeliveryHttpClient(httpClient)).Build(); - var managementClient = new Mock(); + var managementClient = new Mock(httpClient); var codeGenerator = new CodeGenerator(mockOptions.Object, deliveryClient, new FileSystemOutputProvider(mockOptions.Object), managementClient.Object); @@ -102,7 +107,9 @@ public async Task IntegrationTestWithGeneratedSuffix(bool cmApi) { var mockHttp = new MockHttpMessageHandler(); mockHttp.When("https://deliver.kontent.ai/*") - .Respond("application/json", await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "Fixtures/types.json"))); + .Respond("application/json", await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "Fixtures/delivery_types.json"))); + mockHttp.When("https://manage.kontent.ai/v2/projects/*") + .Respond("application/json", await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "Fixtures/management_types.json"))); var httpClient = mockHttp.ToHttpClient(); const string transformFilename = "CustomSuffix"; @@ -111,6 +118,7 @@ public async Task IntegrationTestWithGeneratedSuffix(bool cmApi) mockOptions.Setup(x => x.Value).Returns(new CodeGeneratorOptions { DeliveryOptions = new DeliveryOptions { ProjectId = PROJECT_ID }, + ManagementOptions = new ManagementOptions { ApiKey = "apiKey" }, Namespace = "CustomNamespace", OutputDir = TEMP_DIR, GeneratePartials = false, @@ -119,7 +127,7 @@ public async Task IntegrationTestWithGeneratedSuffix(bool cmApi) }); var deliveryClient = DeliveryClientBuilder.WithProjectId(PROJECT_ID).WithDeliveryHttpClient(new DeliveryHttpClient(httpClient)).Build(); - var managementClient = new Mock(); + var managementClient = new Mock(httpClient); var codeGenerator = new CodeGenerator(mockOptions.Object, deliveryClient, new FileSystemOutputProvider(mockOptions.Object), managementClient.Object); @@ -143,7 +151,9 @@ public async Task IntegrationTestWithGeneratePartials(bool cmApi) { var mockHttp = new MockHttpMessageHandler(); mockHttp.When("https://deliver.kontent.ai/*") - .Respond("application/json", await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "Fixtures/types.json"))); + .Respond("application/json", await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "Fixtures/delivery_types.json"))); + mockHttp.When("https://manage.kontent.ai/v2/projects/*") + .Respond("application/json", await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "Fixtures/management_types.json"))); var httpClient = mockHttp.ToHttpClient(); const string transformFilename = "Generated"; @@ -156,11 +166,13 @@ public async Task IntegrationTestWithGeneratePartials(bool cmApi) OutputDir = TEMP_DIR, FileNameSuffix = transformFilename, GeneratePartials = true, - ContentManagementApi = cmApi + ContentManagementApi = cmApi, + ManagementOptions = new ManagementOptions { ApiKey = "apiKey" } }); var deliveryClient = DeliveryClientBuilder.WithProjectId(PROJECT_ID).WithDeliveryHttpClient(new DeliveryHttpClient(httpClient)).Build(); - var managementClient = new Mock(); + var managementClient = new Mock(httpClient); + var codeGenerator = new CodeGenerator(mockOptions.Object, deliveryClient, new FileSystemOutputProvider(mockOptions.Object), managementClient.Object); diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/Fixtures/types.json b/test/Kentico.Kontent.ModelGenerator.Tests/Fixtures/delivery_types.json similarity index 100% rename from test/Kentico.Kontent.ModelGenerator.Tests/Fixtures/types.json rename to test/Kentico.Kontent.ModelGenerator.Tests/Fixtures/delivery_types.json diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/Fixtures/management_types.json b/test/Kentico.Kontent.ModelGenerator.Tests/Fixtures/management_types.json new file mode 100644 index 00000000..18492952 --- /dev/null +++ b/test/Kentico.Kontent.ModelGenerator.Tests/Fixtures/management_types.json @@ -0,0 +1,1097 @@ +{ + "types": [ + { + "id": "edb8cc01-f1bf-47e0-b164-08ded6831f6c", + "codename": "home", + "last_modified": "2017-08-02T20:21:48.2943193Z", + "name": "Home", + "content_groups": [], + "elements": [ + { + "item_count_limit": null, + "allowed_content_types": [], + "name": "Hero unit", + "guidelines": null, + "is_required": false, + "type": "modular_content", + "id": "738273ab-6159-4b93-a735-89d599d19f49", + "codename": "hero_unit" + }, + { + "item_count_limit": null, + "allowed_content_types": [], + "name": "Articles", + "guidelines": null, + "is_required": false, + "type": "modular_content", + "id": "d6160dd3-41b6-450a-b4ef-201e224ddde8", + "codename": "articles" + }, + { + "item_count_limit": null, + "allowed_content_types": [], + "name": "Our story", + "guidelines": null, + "is_required": false, + "type": "modular_content", + "id": "c0f58add-94be-4366-95c4-a080993b7a4c", + "codename": "our_story" + }, + { + "item_count_limit": null, + "allowed_content_types": [], + "name": "Cafes", + "guidelines": null, + "is_required": false, + "type": "modular_content", + "id": "cd2d33cb-0e7e-4099-a735-cf34b8ae4a8c", + "codename": "cafes" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Contact", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "c02fda77-09ea-49b1-8f4d-e8a2469926c4", + "codename": "contact" + }, + { + "depends_on": { + "element": { + "id": "bb35716f-90e0-4c70-83f2-a3d86fd1c20d" + } + }, + "name": "URL pattern", + "guidelines": null, + "is_required": false, + "type": "url_slug", + "id": "27915b97-90ae-423f-a634-4df875cbf24d", + "codename": "url_pattern" + } + ] + }, + { + "id": "0ec190d7-f5a5-46f2-838f-f1c097df2fe3", + "codename": "hosted_video", + "last_modified": "2017-08-02T20:22:47.5896826Z", + "name": "Hosted video", + "content_groups": [], + "elements": [ + { + "maximum_text_length": null, + "name": "Video ID", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "e325af27-1b33-4627-8c16-5dc60ba3ab55", + "codename": "video_id" + }, + { + "mode": "single", + "options": [ + { + "id": "6a8ea1c4-ee2a-40f0-9333-6295b183e6de", + "codename": "youtube", + "name": "Youtube" + }, + { + "id": "68737eb6-30c3-48e7-92c4-2abb5e8b3e6a", + "codename": "vimeo", + "name": "Vimeo" + } + ], + "name": "Video host", + "guidelines": null, + "is_required": false, + "type": "multiple_choice", + "id": "5846f3ea-73cf-452d-bfc9-6979b5c53c23", + "codename": "video_host" + } + ] + }, + { + "id": "9406db34-bbb1-4b03-951d-c5f058e2b978", + "codename": "brewer", + "last_modified": "2017-08-02T20:15:56.8920291Z", + "name": "Brewer", + "content_groups": [], + "elements": [ + { + "maximum_text_length": null, + "name": "Product name", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "2cab96ed-960a-4c88-acfc-ae861f11da09", + "codename": "product_name" + }, + { + "name": "Price", + "guidelines": null, + "is_required": false, + "type": "number", + "id": "00380404-783c-4b62-a738-da10afa52445", + "codename": "price" + }, + { + "asset_count_limit": null, + "maximum_file_size": null, + "allowed_file_types": "any", + "image_width_limit": null, + "image_height_limit": null, + "name": "Image", + "guidelines": null, + "is_required": false, + "type": "asset", + "id": "ae677dac-3cb6-4ae6-9ed4-f3cac1ae2772", + "codename": "image" + }, + { + "guidelines": null, + "taxonomy_group": { + "id": "cfe4678d-9b94-4449-809f-f747d942f5d2" + }, + "is_required": false, + "term_count_limit": null, + "type": "taxonomy", + "id": "7def5312-eec3-475a-b4ae-5953288f915d", + "codename": "product_status" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Short description", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "b18090aa-63ef-4372-bc01-b6aec11f08f9", + "codename": "short_description" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Long description", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "f3df3ece-d57b-4a35-9df4-ee469f8e0bde", + "codename": "long_description" + }, + { + "depends_on": { + "element": { + "id": "2cab96ed-960a-4c88-acfc-ae861f11da09" + } + }, + "name": "URL pattern", + "guidelines": null, + "is_required": false, + "type": "url_slug", + "id": "5f3fa755-e81f-4b4e-978f-ccec731b9222", + "codename": "url_pattern" + }, + { + "guidelines": null, + "taxonomy_group": { + "id": "ec5fb3c4-a8ef-4ec3-af42-82d70c8f9e7c" + }, + "is_required": false, + "term_count_limit": null, + "type": "taxonomy", + "id": "9bc8f46d-db88-4e7d-88a8-60cbd8ae0932", + "codename": "manufacturer" + } + ] + }, + { + "id": "477f095a-f647-45fb-aa88-545e08c47557", + "codename": "cafe", + "last_modified": "2017-08-02T20:16:56.1561225Z", + "name": "Cafe", + "content_groups": [], + "elements": [ + { + "maximum_text_length": null, + "name": "Street", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "0b2a9fda-6117-4251-9b3d-30f188427bd2", + "codename": "street" + }, + { + "maximum_text_length": null, + "name": "City", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "08c5fa01-ca10-474f-8cff-7ea02c96f469", + "codename": "city" + }, + { + "maximum_text_length": null, + "name": "Country", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "563ff840-7928-4693-b4ef-776888b05753", + "codename": "country" + }, + { + "maximum_text_length": null, + "name": "State", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "e661329b-a9a7-4e77-aeac-4d52d70226b8", + "codename": "state" + }, + { + "maximum_text_length": null, + "name": "ZIP Code", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "63ebf6aa-af59-4293-8a62-8f06d6910a9b", + "codename": "zip_code" + }, + { + "maximum_text_length": null, + "name": "Phone", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "a1fe028e-f434-4781-b6f2-ffd128ebe8fe", + "codename": "phone" + }, + { + "maximum_text_length": null, + "name": "Email", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "b973d8c4-688c-4ed5-ad1e-fa33ef14a199", + "codename": "email" + }, + { + "asset_count_limit": null, + "maximum_file_size": null, + "allowed_file_types": "any", + "image_width_limit": null, + "image_height_limit": null, + "name": "Photo", + "guidelines": null, + "is_required": false, + "type": "asset", + "id": "7d06d400-ddd2-4b84-92b4-030a3d01abed", + "codename": "photo" + } + ] + }, + { + "id": "e5581328-32f8-403f-bd60-a7ea9cbcc020", + "codename": "grinder", + "last_modified": "2017-08-02T20:20:08.128592Z", + "name": "Grinder", + "content_groups": [], + "elements": [ + { + "maximum_text_length": null, + "name": "Product name", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "99056ccf-25cf-405d-966f-bf08f7995e7f", + "codename": "product_name" + }, + { + "name": "Price", + "guidelines": null, + "is_required": false, + "type": "number", + "id": "4c760f8c-e4e0-4b1b-8464-a1661badc767", + "codename": "price" + }, + { + "asset_count_limit": null, + "maximum_file_size": null, + "allowed_file_types": "any", + "image_width_limit": null, + "image_height_limit": null, + "name": "Image", + "guidelines": null, + "is_required": false, + "type": "asset", + "id": "590cb4d9-5ef8-47ac-a2c7-0374bed79d36", + "codename": "image" + }, + { + "maximum_text_length": null, + "name": "Manufacturer", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "62e1e3f4-d4fd-4031-a824-dda0016c0601", + "codename": "manufacturer" + }, + { + "guidelines": null, + "taxonomy_group": { + "id": "cfe4678d-9b94-4449-809f-f747d942f5d2" + }, + "is_required": false, + "term_count_limit": null, + "type": "taxonomy", + "id": "56b1a641-fe73-415d-871f-8e279ca417f7", + "codename": "product_status" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Short description", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "3df70ecc-23af-4448-9417-145c34b15c85", + "codename": "short_description" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Long description", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "ec4c23ee-e63a-40e9-916f-b8d4713af140", + "codename": "long_description" + }, + { + "depends_on": { + "element": { + "id": "62e1e3f4-d4fd-4031-a824-dda0016c0601" + } + }, + "name": "URL pattern", + "guidelines": null, + "is_required": false, + "type": "url_slug", + "id": "7796420f-0ac4-416f-91bb-45a38039bb74", + "codename": "url_pattern" + } + ] + }, + { + "id": "6189d6f0-4b1a-4c4e-b5e9-595a0c966b88", + "codename": "article", + "last_modified": "2017-08-02T20:14:48.0780904Z", + "name": "Article", + "content_groups": [], + "elements": [ + { + "guidelines": null, + "taxonomy_group": { + "id": "06b01649-311d-4fea-b5a6-18fefa5ad863" + }, + "is_required": false, + "term_count_limit": null, + "type": "taxonomy", + "id": "3ac53fb3-362b-4a6c-8d37-f801febe50a7", + "codename": "personas" + }, + { + "maximum_text_length": null, + "name": "Title", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "a6a9c19a-dbc2-434f-93f6-9ab352ab103c", + "codename": "title" + }, + { + "asset_count_limit": null, + "maximum_file_size": null, + "allowed_file_types": "any", + "image_width_limit": null, + "image_height_limit": null, + "name": "Teaser image", + "guidelines": null, + "is_required": false, + "type": "asset", + "id": "aba685d6-2d6d-4e70-8d84-20e656ab824e", + "codename": "teaser_image" + }, + { + "name": "Post date", + "guidelines": null, + "is_required": false, + "type": "date_time", + "id": "390ad225-a00b-4548-b12c-308a3fb4a0fc", + "codename": "post_date" + }, + { + "maximum_text_length": null, + "name": "Summary", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "7e984c8f-1e02-4bc3-a73e-8ae060845233", + "codename": "summary" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Bad Copy", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "e2df8af0-8cd2-435d-9972-9d60be560080", + "codename": "body_copy" + }, + { + "item_count_limit": null, + "allowed_content_types": [], + "name": "Related articles", + "guidelines": null, + "is_required": false, + "type": "modular_content", + "id": "e2e23c75-c59d-4402-ae1e-c79a65013f77", + "codename": "related_articles" + }, + { + "maximum_text_length": null, + "name": "Meta keywords", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "1731318d-4ee8-4322-a170-810cb030a2de", + "codename": "meta_keywords" + }, + { + "maximum_text_length": null, + "name": "Meta description", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "11886b21-5bdf-4e6f-affa-15c6860db2bf", + "codename": "meta_description" + }, + { + "depends_on": { + "element": { + "id": "a6a9c19a-dbc2-434f-93f6-9ab352ab103c" + } + }, + "name": "URL pattern", + "guidelines": null, + "is_required": false, + "type": "url_slug", + "id": "487d135c-852c-43da-a05d-4d8014c9a65b", + "codename": "url_pattern" + } + ] + }, + { + "id": "29e339d1-5626-4619-9406-141e8fec8ef3", + "codename": "office", + "last_modified": "2017-08-02T20:23:39.9410267Z", + "name": "Office", + "content_groups": [], + "elements": [ + { + "maximum_text_length": null, + "name": "Name", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "345aa9d3-13c9-485f-9613-cc6013a02c58", + "codename": "name" + }, + { + "maximum_text_length": null, + "name": "Street", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "474813c5-d67d-461d-b47b-9ceec660dff6", + "codename": "street" + }, + { + "maximum_text_length": null, + "name": "City", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "e4cb4d35-1f8d-405f-b5a1-333d526328d6", + "codename": "city" + }, + { + "maximum_text_length": null, + "name": "Country", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "55bc9fc0-9a10-49a4-948d-d334bf918812", + "codename": "country" + }, + { + "maximum_text_length": null, + "name": "State", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "6d45f175-0ddd-4604-b51e-363e93262d78", + "codename": "state" + }, + { + "maximum_text_length": null, + "name": "Zip code", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "e64085bf-5f5d-4c6c-9511-a180e9198ad2", + "codename": "zip_code" + }, + { + "maximum_text_length": null, + "name": "Phone", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "bdfc89da-a564-4048-9dc9-e6d347a65c7a", + "codename": "phone" + }, + { + "maximum_text_length": null, + "name": "Email", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "a1dcdd60-d464-4695-bad6-af98d7522181", + "codename": "email" + } + ] + }, + { + "id": "86cac3b1-5e79-45b4-bd87-2dc2bf442ac4", + "codename": "hero_unit", + "last_modified": "2017-08-02T20:20:37.9292451Z", + "name": "Hero Unit", + "content_groups": [], + "elements": [ + { + "maximum_text_length": null, + "name": "Title", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "6ff2724d-8c53-43af-b616-c996d6f3d901", + "codename": "title" + }, + { + "asset_count_limit": null, + "maximum_file_size": null, + "allowed_file_types": "any", + "image_width_limit": null, + "image_height_limit": null, + "name": "Image", + "guidelines": null, + "is_required": false, + "type": "asset", + "id": "eae43fc6-c5b3-4f93-8526-1a6b29815362", + "codename": "image" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Marketing message", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "de2c097d-347e-4319-a74f-2d8337221fd4", + "codename": "marketing_message" + } + ] + }, + { + "id": "e91b1486-d9cc-4c25-b111-dea7833f31ca", + "codename": "fact_about_us", + "last_modified": "2017-08-02T20:18:58.9724753Z", + "name": "Fact about us", + "content_groups": [], + "elements": [ + { + "maximum_text_length": null, + "name": "Title", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "190ca8cb-6117-4c54-957f-76693cefc691", + "codename": "title" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Description", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "d09a7879-f926-4c60-a4e5-c5de81225f4c", + "codename": "description" + }, + { + "asset_count_limit": null, + "maximum_file_size": null, + "allowed_file_types": "any", + "image_width_limit": null, + "image_height_limit": null, + "name": "Image", + "guidelines": null, + "is_required": false, + "type": "asset", + "id": "72216ea5-6be5-4824-8791-a694bee801b3", + "codename": "image" + } + ] + }, + { + "id": "a1e662da-cb4e-469e-888a-a4a568c0fe82", + "codename": "accessory", + "last_modified": "2017-08-02T20:13:06.5799005Z", + "name": "Accessory", + "content_groups": [], + "elements": [ + { + "maximum_text_length": null, + "name": "Product name", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "990ccf4d-9ab4-4a0a-b788-087434abd318", + "codename": "product_name" + }, + { + "name": "Price", + "guidelines": null, + "is_required": false, + "type": "number", + "id": "ad25deb2-97ff-42c1-a4b0-6a1b0a3fbb51", + "codename": "price" + }, + { + "asset_count_limit": null, + "maximum_file_size": null, + "allowed_file_types": "any", + "image_width_limit": null, + "image_height_limit": null, + "name": "Image", + "guidelines": null, + "is_required": false, + "type": "asset", + "id": "b6316eb1-5f86-4124-bb40-614383ba5980", + "codename": "image" + }, + { + "maximum_text_length": null, + "name": "Manufacturer", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "591bd05c-4b74-414a-bc09-01b8723c55dd", + "codename": "manufacturer" + }, + { + "guidelines": null, + "taxonomy_group": { + "id": "cfe4678d-9b94-4449-809f-f747d942f5d2" + }, + "is_required": false, + "term_count_limit": null, + "type": "taxonomy", + "id": "4040aa1b-c2d0-4277-86c5-8013ebfda842", + "codename": "product_status" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Short description", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "5057dde8-90bb-455b-ae7a-024822dc9465", + "codename": "short_description" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Long description", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "b4eff1b3-6227-4f08-bc67-162f909b13f2", + "codename": "long_description" + }, + { + "depends_on": { + "element": { + "id": "591bd05c-4b74-414a-bc09-01b8723c55dd" + } + }, + "name": "URL pattern", + "guidelines": null, + "is_required": false, + "type": "url_slug", + "id": "2a1b6915-31e2-423c-bbd8-8c1c4cd81d23", + "codename": "url_pattern" + } + ] + }, + { + "id": "ad468875-6bb5-4fb5-8a47-a13718b239b0", + "codename": "coffee", + "last_modified": "2017-08-02T20:18:27.0335579Z", + "name": "Coffee", + "content_groups": [], + "elements": [ + { + "maximum_text_length": null, + "name": "Product name", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "a75d7ce3-3a15-4127-b09f-84d8b916490d", + "codename": "product_name" + }, + { + "name": "Price", + "guidelines": null, + "is_required": false, + "type": "number", + "id": "00610cfb-d4b7-4ef6-99f6-d8520de47661", + "codename": "price" + }, + { + "asset_count_limit": null, + "maximum_file_size": null, + "allowed_file_types": "any", + "image_width_limit": null, + "image_height_limit": null, + "name": "Image", + "guidelines": null, + "is_required": false, + "type": "asset", + "id": "2205d17c-0489-45c1-911a-aeab68ff650b", + "codename": "image" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Short description", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "8ce8a0c2-acc4-4e8a-a9d6-dfa696292f41", + "codename": "short_description" + }, + { + "maximum_text_length": null, + "maximum_image_size": null, + "allowed_content_types": [], + "image_width_limit": null, + "image_height_limit": null, + "allowed_image_types": "any", + "allowed_blocks": [], + "allowed_formatting": [], + "allowed_text_blocks": [], + "allowed_table_blocks": [], + "allowed_table_formatting": [], + "allowed_table_text_blocks": [], + "name": "Long description", + "guidelines": null, + "is_required": false, + "type": "rich_text", + "id": "8f6ca38f-a76d-4fc1-9c92-89814899c3dc", + "codename": "long_description" + }, + { + "guidelines": null, + "taxonomy_group": { + "id": "cfe4678d-9b94-4449-809f-f747d942f5d2" + }, + "is_required": false, + "term_count_limit": null, + "type": "taxonomy", + "id": "1b4d1290-1f26-4874-be78-d6e751e474ba", + "codename": "product_status" + }, + { + "maximum_text_length": null, + "name": "Farm", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "ec0792ba-5c45-42f9-89ec-653717fe8fad", + "codename": "farm" + }, + { + "maximum_text_length": null, + "name": "Country", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "0dc007ab-6a07-4251-94fd-6f7d874d040c", + "codename": "country" + }, + { + "maximum_text_length": null, + "name": "Variety", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "58ccc1a8-208e-4e4b-97af-a18a851fa1df", + "codename": "variety" + }, + { + "maximum_text_length": null, + "name": "Altitude", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "514df0eb-95ba-433d-8f0e-064b3c96f17d", + "codename": "altitude" + }, + { + "depends_on": { + "element": { + "id": "58ccc1a8-208e-4e4b-97af-a18a851fa1df" + } + }, + "name": "URL pattern", + "guidelines": null, + "is_required": false, + "type": "url_slug", + "id": "16953820-5676-4223-9e44-6ae0a4772ff1", + "codename": "url_pattern" + }, + { + "guidelines": null, + "taxonomy_group": { + "id": "a71538cd-9f51-4ae0-a139-62dea6d48431" + }, + "is_required": false, + "term_count_limit": null, + "type": "taxonomy", + "id": "80f21953-4504-439a-a439-4f76b03cc7a2", + "codename": "processing" + } + ] + }, + { + "id": "bedb01fb-7bff-47ba-a13e-f065df1e97c0", + "codename": "tweet", + "last_modified": "2017-08-02T20:24:40.0288709Z", + "name": "Tweet", + "content_groups": [], + "elements": [ + { + "maximum_text_length": null, + "name": "Tweet link", + "guidelines": null, + "is_required": false, + "type": "text", + "id": "d5b29378-15ca-4bef-a3c7-ff82276faf46", + "codename": "tweet_link" + }, + { + "mode": "single", + "options": [ + { + "id": "f64cbc68-0ec4-4a31-9848-456d5444923d", + "codename": "dark", + "name": "Dark" + }, + { + "id": "a7c0fcfb-594a-4ad0-8a41-e11705a7238e", + "codename": "light", + "name": "Light" + } + ], + "name": "Theme", + "guidelines": null, + "is_required": false, + "type": "multiple_choice", + "id": "5e52923e-9537-4f65-a090-ebb8409dd6b3", + "codename": "theme" + }, + { + "mode": "single", + "options": [ + { + "id": "4c77812b-66a8-4835-b49d-24b4cc68ed69", + "codename": "hide_media", + "name": "Hide media" + }, + { + "id": "42a4cacc-0cc9-4aa5-9975-468b4a4c2325", + "codename": "hide_thread", + "name": "Hide thread" + } + ], + "name": "Display options", + "guidelines": null, + "is_required": false, + "type": "multiple_choice", + "id": "7cd27ee9-6dd5-4f63-8708-7c767df3203a", + "codename": "display_options" + } + ] + }, + { + "id": "592870f9-4fc1-4b34-92e8-42f8c316b848", + "codename": "about_us", + "last_modified": "2017-08-02T20:11:37.2303901Z", + "name": "About us", + "content_groups": [], + "elements": [ + { + "item_count_limit": null, + "allowed_content_types": [], + "name": "Facts", + "guidelines": null, + "is_required": false, + "type": "modular_content", + "id": "93492857-2f06-43f6-877a-1d9b2d01b7a5", + "codename": "facts" + }, + { + "depends_on": { + "element": { + "id": "986d18fb-2de3-47e6-8a03-e7eb9781e707" + } + }, + "name": "URL pattern", + "guidelines": null, + "is_required": false, + "type": "url_slug", + "id": "5e88c693-245f-439e-be45-8b1b27733c8c", + "codename": "url_pattern" + } + ] + } + ], + "pagination": { + "continuation_token": null, + "next_page": null + } +} \ No newline at end of file diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/Kentico.Kontent.ModelGenerator.Tests.csproj b/test/Kentico.Kontent.ModelGenerator.Tests/Kentico.Kontent.ModelGenerator.Tests.csproj index 8d36b4fa..3413b4c6 100644 --- a/test/Kentico.Kontent.ModelGenerator.Tests/Kentico.Kontent.ModelGenerator.Tests.csproj +++ b/test/Kentico.Kontent.ModelGenerator.Tests/Kentico.Kontent.ModelGenerator.Tests.csproj @@ -33,7 +33,10 @@ PreserveNewest - + + Always + + Always From ece38d0c93bc9e89d085db24f08552e3b433187e Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 22 Jul 2021 22:41:46 +0200 Subject: [PATCH 05/12] rename GetElementId to GetElementIdFromContentType --- .../ContentTypeJObjectHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs b/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs index c238ac62..d98bf408 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs @@ -6,7 +6,7 @@ namespace Kentico.Kontent.ModelGenerator.Core { public static class ContentTypeJObjectHelper { - public static string GetElementId(JObject managementContentType, string elementCodename) + public static string GetElementIdFromContentType(JObject managementContentType, string elementCodename) { return managementContentType["elements"].ToObject>() .FirstOrDefault(el => el["codename"].ToObject() == elementCodename)["id"].ToObject(); From 93d7b906a870bebd6f3653d9b1d474362e658742 Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 22 Jul 2021 22:42:52 +0200 Subject: [PATCH 06/12] usage of GetElementIdFromContentType --- src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs b/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs index be802493..c263bd27 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs @@ -133,7 +133,7 @@ internal ClassCodeGenerator GetClassCodeGenerator(IContentType contentType, bool } var elementId = _options.ContentManagementApi - ? ContentTypeJObjectHelper.GetElementId(managementContentType, element.Codename) + ? ContentTypeJObjectHelper.GetElementIdFromContentType(managementContentType, element.Codename) : null; var property = Property.FromContentType(element.Codename, elementType, _options.ContentManagementApi, elementId); From 4d588a49700688696243b1f55aeb16f20f29b01d Mon Sep 17 00:00:00 2001 From: martins2 Date: Fri, 30 Jul 2021 12:25:15 +0200 Subject: [PATCH 07/12] 112 bump kontent packages to the latest version --- .../Kentico.Kontent.ModelGenerator.Core.csproj | 2 +- .../Kentico.Kontent.ModelGenerator.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Kentico.Kontent.ModelGenerator.Core/Kentico.Kontent.ModelGenerator.Core.csproj b/src/Kentico.Kontent.ModelGenerator.Core/Kentico.Kontent.ModelGenerator.Core.csproj index 9f7e4903..2d3bf44e 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/Kentico.Kontent.ModelGenerator.Core.csproj +++ b/src/Kentico.Kontent.ModelGenerator.Core/Kentico.Kontent.ModelGenerator.Core.csproj @@ -29,7 +29,7 @@ - + diff --git a/src/Kentico.Kontent.ModelGenerator/Kentico.Kontent.ModelGenerator.csproj b/src/Kentico.Kontent.ModelGenerator/Kentico.Kontent.ModelGenerator.csproj index 84c72a38..d56c7cfa 100644 --- a/src/Kentico.Kontent.ModelGenerator/Kentico.Kontent.ModelGenerator.csproj +++ b/src/Kentico.Kontent.ModelGenerator/Kentico.Kontent.ModelGenerator.csproj @@ -33,7 +33,7 @@ - + From 2cd4fff9e1fb6bf1aadc9d0020ef9842396af9a6 Mon Sep 17 00:00:00 2001 From: martins2 Date: Fri, 30 Jul 2021 12:28:03 +0200 Subject: [PATCH 08/12] 112 introduce validation in ContentTypeJObjectHelper --- .../CodeGenerator.cs | 4 + .../ContentTypeJObjectHelper.cs | 43 ++++- .../InvalidIdException.cs | 11 ++ .../ContentTypeJObjectHelperTests.cs | 173 ++++++++++++++++++ 4 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 src/Kentico.Kontent.ModelGenerator.Core/InvalidIdException.cs create mode 100644 test/Kentico.Kontent.ModelGenerator.Tests/ContentTypeJObjectHelperTests.cs diff --git a/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs b/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs index c263bd27..f520f804 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/CodeGenerator.cs @@ -148,6 +148,10 @@ internal ClassCodeGenerator GetClassCodeGenerator(IContentType contentType, bool { Console.WriteLine($"Warning: Can't create valid C# Identifier from '{element.Codename}'. Skipping element."); } + catch (InvalidIdException) + { + Console.WriteLine($"Warning: Can't create valid Id for '{element.Codename}'. Skipping element."); + } catch (ArgumentException) { Console.WriteLine($"Warning: Skipping unknown Content Element type '{element.Type}'. (Content Type: '{classDefinition.ClassName}', Element Codename: '{element.Codename}')."); diff --git a/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs b/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs index d98bf408..adf9b416 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/ContentTypeJObjectHelper.cs @@ -8,8 +8,47 @@ public static class ContentTypeJObjectHelper { public static string GetElementIdFromContentType(JObject managementContentType, string elementCodename) { - return managementContentType["elements"].ToObject>() - .FirstOrDefault(el => el["codename"].ToObject() == elementCodename)["id"].ToObject(); + if (!managementContentType.TryGetValue("elements", out var elements)) + { + throw new InvalidIdException($"Unable to create a valid Id for '{elementCodename}', couldn't find {nameof(elements)}."); + } + + if (elements is not { Type: JTokenType.Array }) + { + throw new InvalidIdException($"Unable to create a valid Id for '{elementCodename}', {nameof(elements)} has invalid type."); + } + + var element = elements.ToObject>().FirstOrDefault(el => + { + if (!el.TryGetValue("codename", out var codename)) + { + throw new InvalidIdException($"Unable to create a valid Id for '{elementCodename}', couldn't find {nameof(codename)}."); + } + + if (codename is not { Type: JTokenType.String }) + { + throw new InvalidIdException($"Unable to create a valid Id for '{elementCodename}', {nameof(elements)} has invalid type."); + } + + return codename.ToObject() == elementCodename; + }); + + if (element == null) + { + throw new InvalidIdException($"Unable to create a valid Id for '{elementCodename}', missing element."); + } + + if (!element.TryGetValue("id", out var elementId)) + { + throw new InvalidIdException($"Unable to create a valid Id for '{elementCodename}', couldn't find {nameof(elementId)}."); + } + + if (elementId is not { Type: JTokenType.String }) + { + throw new InvalidIdException($"Unable to create a valid Id for '{elementCodename}', {nameof(elementId)} has invalid type."); + } + + return elementId.ToObject(); } } } diff --git a/src/Kentico.Kontent.ModelGenerator.Core/InvalidIdException.cs b/src/Kentico.Kontent.ModelGenerator.Core/InvalidIdException.cs new file mode 100644 index 00000000..80a85e15 --- /dev/null +++ b/src/Kentico.Kontent.ModelGenerator.Core/InvalidIdException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Kentico.Kontent.ModelGenerator.Core +{ + public class InvalidIdException : Exception + { + public InvalidIdException(string message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/ContentTypeJObjectHelperTests.cs b/test/Kentico.Kontent.ModelGenerator.Tests/ContentTypeJObjectHelperTests.cs new file mode 100644 index 00000000..a59c75a3 --- /dev/null +++ b/test/Kentico.Kontent.ModelGenerator.Tests/ContentTypeJObjectHelperTests.cs @@ -0,0 +1,173 @@ +using System; +using Kentico.Kontent.ModelGenerator.Core; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Kentico.Kontent.ModelGenerator.Tests +{ + public class ContentTypeJObjectHelperTests + { + [Fact] + public void GetElementIdFromContentType_MissingElementsObject_ThrowsException() + { + var managementType = JObject.FromObject(new + { + id = Guid.NewGuid(), + codename = "codename", + last_modified = DateTime.UtcNow, + name = "Name" + }); + + var exception = Assert.Throws(() => ContentTypeJObjectHelper.GetElementIdFromContentType(managementType, "element_codename")); + + Assert.Equal("Unable to create a valid Id for 'element_codename', couldn't find elements.", exception.Message); + } + + [Fact] + public void GetElementIdFromContentType_EmptyElements_ThrowsException() + { + var managementType = JObject.FromObject(new + { + id = Guid.NewGuid(), + codename = "codename", + last_modified = DateTime.UtcNow, + name = "Name", + elements = Array.Empty() + }); + + var exception = Assert.Throws(() => ContentTypeJObjectHelper.GetElementIdFromContentType(managementType, "element_codename")); + + Assert.Equal("Unable to create a valid Id for 'element_codename', missing element.", exception.Message); + } + + [Fact] + public void GetElementIdFromContentType_InvalidElementsType_ThrowsException() + { + var codename = "codename"; + var managementType = JObject.FromObject(new + { + id = Guid.NewGuid(), + codename = "codename", + last_modified = DateTime.UtcNow, + name = "Name", + elements = "" + }); + + var exception = Assert.Throws(() => ContentTypeJObjectHelper.GetElementIdFromContentType(managementType, "element_codename")); + + Assert.Equal("Unable to create a valid Id for 'element_codename', elements has invalid type.", exception.Message); + } + + [Fact] + public void GetElementIdFromContentType_MissingElementObject_ThrowsException() + { + var managementType = JObject.FromObject(new + { + id = Guid.NewGuid(), + codename = "codename", + last_modified = DateTime.UtcNow, + name = "Name", + elements = new object[] + { + new { + maximum_text_length= 5, + name= "element_name", + guidelines= "guidelines", + is_required= false, + type="text", + id= Guid.NewGuid(), + codename= "element_codename" + } + } + }); + + var exception = Assert.Throws(() => ContentTypeJObjectHelper.GetElementIdFromContentType(managementType, "codename")); + + Assert.Equal("Unable to create a valid Id for 'codename', missing element.", exception.Message); + } + + [Fact] + public void GetElementIdFromContentType_MissingElementIdObject_ThrowsException() + { + var managementType = JObject.FromObject(new + { + id = Guid.NewGuid(), + codename = "codename", + last_modified = DateTime.UtcNow, + name = "Name", + elements = new object[] + { + new { + maximum_text_length= 5, + name= "element_name", + guidelines= "guidelines", + is_required= false, + type="text", + codename= "element_codename" + } + } + }); + + var exception = Assert.Throws(() => ContentTypeJObjectHelper.GetElementIdFromContentType(managementType, "element_codename")); + + Assert.Equal("Unable to create a valid Id for 'element_codename', couldn't find elementId.", exception.Message); + } + + [Fact] + public void GetElementIdFromContentType_InvalidElementIdType_ThrowsException() + { + var managementType = JObject.FromObject(new + { + id = Guid.NewGuid(), + codename = "codename", + last_modified = DateTime.UtcNow, + name = "Name", + elements = new object[] + { + new { + maximum_text_length= 5, + name= "element_name", + guidelines= "guidelines", + is_required= false, + type="text", + id = 4, + codename= "element_codename" + } + } + }); + + var exception = Assert.Throws(() => ContentTypeJObjectHelper.GetElementIdFromContentType(managementType, "element_codename")); + + Assert.Equal("Unable to create a valid Id for 'element_codename', elementId has invalid type.", exception.Message); + } + + [Fact] + public void GetElementIdFromContentType_Returns() + { + var elementId = Guid.NewGuid().ToString(); + var managementType = JObject.FromObject(new + { + id = Guid.NewGuid(), + codename = "codename", + last_modified = DateTime.UtcNow, + name = "Name", + elements = new object[] + { + new { + maximum_text_length= 5, + name= "element_name", + guidelines= "guidelines", + is_required= false, + type="text", + id = elementId, + codename= "element_codename" + } + } + }); + + var result = ContentTypeJObjectHelper.GetElementIdFromContentType(managementType, "element_codename"); + + Assert.Equal(elementId, result); + } + } +} From 72425c39552a9a1ff0c671acb53212546f6beca3 Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 5 Aug 2021 12:55:52 +0200 Subject: [PATCH 09/12] introduce KontentElementId's namespace to usings --- .../ClassCodeGenerator.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs b/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs index 9b8a3ba3..3dd23977 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs @@ -35,9 +35,10 @@ public string GenerateCode(bool cmApi = false) { var cmApiUsings = new[] { - SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName($"{nameof(Newtonsoft)}.{nameof(Newtonsoft.Json)}")), SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(Management.Models.Items.ContentItemModel).Namespace!)), - SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(Management.Models.Assets.AssetModel).Namespace!)) + SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(Management.Models.Assets.AssetModel).Namespace!)), + SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(Management.Modules.ModelBuilders.IModelProvider).Namespace!)),//todo replace with KontentElementId + SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName($"{nameof(Newtonsoft)}.{nameof(Newtonsoft.Json)}")) }; var deliveryUsings = new[] From a5911d6420c4a009b09fb8456cf66148b869ffc6 Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 5 Aug 2021 13:03:52 +0200 Subject: [PATCH 10/12] introduce namespace in tests --- .../Assets/CompleteContentType_CompiledCode_CMAPI.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt b/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt index 46c9a46d..ec492ada 100644 --- a/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt +++ b/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt @@ -6,9 +6,10 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; using Kentico.Kontent.Management.Models.Items; using Kentico.Kontent.Management.Models.Assets; +using Kentico.Kontent.Management.Modules.ModelBuilders; +using Newtonsoft.Json; namespace KenticoKontentModels { From 4f3648fd094f78fdecdbf8b6a341b82954f0f829 Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 5 Aug 2021 16:15:23 +0200 Subject: [PATCH 11/12] remove unused variable --- .../ContentTypeJObjectHelperTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/ContentTypeJObjectHelperTests.cs b/test/Kentico.Kontent.ModelGenerator.Tests/ContentTypeJObjectHelperTests.cs index a59c75a3..882d1065 100644 --- a/test/Kentico.Kontent.ModelGenerator.Tests/ContentTypeJObjectHelperTests.cs +++ b/test/Kentico.Kontent.ModelGenerator.Tests/ContentTypeJObjectHelperTests.cs @@ -43,7 +43,6 @@ public void GetElementIdFromContentType_EmptyElements_ThrowsException() [Fact] public void GetElementIdFromContentType_InvalidElementsType_ThrowsException() { - var codename = "codename"; var managementType = JObject.FromObject(new { id = Guid.NewGuid(), From 3619c9ee058246c9ee4f1a8e7a46fbe2b9e4e0cc Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 26 Aug 2021 14:28:24 +0200 Subject: [PATCH 12/12] update management client nuget, use latests models --- .../ClassCodeGenerator.cs | 17 ++++---- ...Kentico.Kontent.ModelGenerator.Core.csproj | 2 +- .../Property.cs | 20 ++++----- ...CompleteContentType_CompiledCode_CMAPI.txt | 25 +++++------ .../PropertyTests.cs | 43 ++++++++++++++++--- 5 files changed, 68 insertions(+), 39 deletions(-) diff --git a/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs b/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs index 3dd23977..9dbf4615 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/ClassCodeGenerator.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Kentico.Kontent.Management.Modules.ModelBuilders; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -10,6 +11,7 @@ namespace Kentico.Kontent.ModelGenerator.Core { public class ClassCodeGenerator { + private static readonly string KontentElementIdAttributeName = new string(nameof(KontentElementIdAttribute).SkipLast(9).ToArray()); public const string DEFAULT_NAMESPACE = "KenticoKontentModels"; public ClassDefinition ClassDefinition { get; } @@ -35,22 +37,19 @@ public string GenerateCode(bool cmApi = false) { var cmApiUsings = new[] { - SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(Management.Models.Items.ContentItemModel).Namespace!)), - SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(Management.Models.Assets.AssetModel).Namespace!)), - SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(Management.Modules.ModelBuilders.IModelProvider).Namespace!)),//todo replace with KontentElementId + SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(Management.Models.LanguageVariants.Elements.BaseElement).Namespace!)), + SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(KontentElementIdAttribute).Namespace!)), SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName($"{nameof(Newtonsoft)}.{nameof(Newtonsoft.Json)}")) }; var deliveryUsings = new[] { + SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(nameof(System))), + SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(System.Collections.Generic.IEnumerable<>).Namespace!)), SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(Delivery.Abstractions.IApiResponse).Namespace!)) }; - var usings = new[] - { - SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(nameof(System))), - SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(typeof(System.Collections.Generic.IEnumerable<>).Namespace!)), - }.Concat(cmApi ? cmApiUsings : deliveryUsings).ToArray(); + var usings = (cmApi ? cmApiUsings : deliveryUsings).ToArray(); MemberDeclarationSyntax[] properties = ClassDefinition.Properties.OrderBy(p => p.Identifier).Select((element) => { @@ -79,7 +78,7 @@ public string GenerateCode(bool cmApi = false) SyntaxFactory.Literal(element.Codename)))))))), SyntaxFactory.AttributeList( SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("KontentElementId")) + SyntaxFactory.Attribute(SyntaxFactory.IdentifierName(KontentElementIdAttributeName)) .WithArgumentList( SyntaxFactory.AttributeArgumentList( SyntaxFactory.SingletonSeparatedList( diff --git a/src/Kentico.Kontent.ModelGenerator.Core/Kentico.Kontent.ModelGenerator.Core.csproj b/src/Kentico.Kontent.ModelGenerator.Core/Kentico.Kontent.ModelGenerator.Core.csproj index 2d3bf44e..87d529ef 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/Kentico.Kontent.ModelGenerator.Core.csproj +++ b/src/Kentico.Kontent.ModelGenerator.Core/Kentico.Kontent.ModelGenerator.Core.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/Kentico.Kontent.ModelGenerator.Core/Property.cs b/src/Kentico.Kontent.ModelGenerator.Core/Property.cs index be3b2ca2..8c6acea5 100644 --- a/src/Kentico.Kontent.ModelGenerator.Core/Property.cs +++ b/src/Kentico.Kontent.ModelGenerator.Core/Property.cs @@ -38,16 +38,16 @@ public class Property private static readonly Dictionary ContentManagementTypes = new Dictionary { - { "text", "string" }, - { "rich_text", "string" }, - { "number", "decimal?" }, - { "multiple_choice", "IEnumerable" }, - { "date_time", "DateTime?" }, - { "asset", "IEnumerable" }, - { "modular_content", "IEnumerable" }, - { "taxonomy", "IEnumerable" }, - { "url_slug", "string" }, - { "custom", "string" } + { "text", "TextElement" }, + { "rich_text", "RichTextElement" }, + { "number", "NumberElement" }, + { "multiple_choice", "MultipleChoiceElement" }, + { "date_time", "DateTimeElement"}, + { "asset", "AssetElement" }, + { "modular_content", "LinkedItemsElement" }, + { "taxonomy", "TaxonomyElement" }, + { "url_slug", "UrlSlugElement" }, + { "custom", "CustomElement" } }; private static Dictionary ContentTypeToTypeName(bool cmApi) diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt b/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt index ec492ada..5965424b 100644 --- a/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt +++ b/test/Kentico.Kontent.ModelGenerator.Tests/Assets/CompleteContentType_CompiledCode_CMAPI.txt @@ -4,10 +4,7 @@ // Changes to this file may cause incorrect behavior and will be lost if the code is regenerated. // For further modifications of the class, create a separate file with the partial class. -using System; -using System.Collections.Generic; -using Kentico.Kontent.Management.Models.Items; -using Kentico.Kontent.Management.Models.Assets; +using Kentico.Kontent.Management.Models.LanguageVariants.Elements; using Kentico.Kontent.Management.Modules.ModelBuilders; using Newtonsoft.Json; @@ -17,33 +14,33 @@ namespace KenticoKontentModels { [JsonProperty("asset")] [KontentElementId("asset_element_id")] - public IEnumerable Asset { get; set; } + public AssetElement Asset { get; set; } [JsonProperty("custom")] [KontentElementId("custom_element_id")] - public string Custom { get; set; } + public CustomElement Custom { get; set; } [JsonProperty("date_time")] [KontentElementId("date_time_element_id")] - public DateTime? DateTime { get; set; } + public DateTimeElement DateTime { get; set; } [JsonProperty("modular_content")] [KontentElementId("linked_items_element_id")] - public IEnumerable ModularContent { get; set; } + public LinkedItemsElement ModularContent { get; set; } [JsonProperty("multiple_choice")] [KontentElementId("multiple_choice_element_id")] - public IEnumerable MultipleChoice { get; set; } + public MultipleChoiceElement MultipleChoice { get; set; } [JsonProperty("number")] [KontentElementId("number_element_id")] - public decimal? Number { get; set; } + public NumberElement Number { get; set; } [JsonProperty("rich_text")] [KontentElementId("rich_text_element_id")] - public string RichText { get; set; } + public RichTextElement RichText { get; set; } [JsonProperty("taxonomy")] [KontentElementId("taxonomy_element_id")] - public IEnumerable Taxonomy { get; set; } + public TaxonomyElement Taxonomy { get; set; } [JsonProperty("text")] [KontentElementId("text_element_id")] - public string Text { get; set; } + public TextElement Text { get; set; } [JsonProperty("url_slug")] [KontentElementId("url_slug_element_id")] - public string UrlSlug { get; set; } + public UrlSlugElement UrlSlug { get; set; } } } \ No newline at end of file diff --git a/test/Kentico.Kontent.ModelGenerator.Tests/PropertyTests.cs b/test/Kentico.Kontent.ModelGenerator.Tests/PropertyTests.cs index 469afefc..44011fe4 100644 --- a/test/Kentico.Kontent.ModelGenerator.Tests/PropertyTests.cs +++ b/test/Kentico.Kontent.ModelGenerator.Tests/PropertyTests.cs @@ -16,12 +16,45 @@ public void Constructor_ObjectIsInitializedWithCorrectValues() } [Theory] - [InlineData("element_codename", "text", false, "ElementCodename", "string")] - [InlineData("element_codename", "text", true, "ElementCodename", "string")] - [InlineData("element_codename", "asset", true, "ElementCodename", "IEnumerable")] - public void FromContentType(string codename, string contentType, bool cmApi, string expectedCodename, string expectedTypeName) + [InlineData("text", "string")] + [InlineData("rich_text", "string")] + [InlineData("rich_text" + Property.STRUCTURED_SUFFIX, "IRichTextContent")] + [InlineData("number", "decimal?")] + [InlineData("multiple_choice", "IEnumerable")] + [InlineData("date_time", "DateTime?")] + [InlineData("asset", "IEnumerable")] + [InlineData("modular_content", "IEnumerable")] + [InlineData("taxonomy", "IEnumerable")] + [InlineData("url_slug", "string")] + [InlineData("custom", "string")] + public void DAPIModel_FromContentType(string contentType, string expectedTypeName) { - var element = Property.FromContentType(codename, contentType, cmApi); + var codename = "element_codename"; + var expectedCodename = "ElementCodename"; + + var element = Property.FromContentType(codename, contentType, false); + + Assert.Equal(expectedCodename, element.Identifier); + Assert.Equal(expectedTypeName, element.TypeName); + } + + [Theory] + [InlineData("text", "TextElement")] + [InlineData("rich_text", "RichTextElement")] + [InlineData("number", "NumberElement")] + [InlineData("multiple_choice", "MultipleChoiceElement")] + [InlineData("date_time", "DateTimeElement")] + [InlineData("asset", "AssetElement")] + [InlineData("modular_content", "LinkedItemsElement")] + [InlineData("taxonomy", "TaxonomyElement")] + [InlineData("url_slug", "UrlSlugElement")] + [InlineData("custom", "CustomElement")] + public void CMAPIModel_FromContentType(string contentType, string expectedTypeName) + { + var codename = "element_codename"; + var expectedCodename = "ElementCodename"; + + var element = Property.FromContentType(codename, contentType, true); Assert.Equal(expectedCodename, element.Identifier); Assert.Equal(expectedTypeName, element.TypeName);