diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web.sln b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web.sln index 0666534..d700da6 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web.sln +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web.sln @@ -2,6 +2,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Headless.Client.Samples.Web", "Umbraco.Headless.Client.Samples.Web\Umbraco.Headless.Client.Samples.Web.csproj", "{7E351693-56E2-47B9-94FE-F7481C9674AB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Headless.Client.Net", "..\..\src\Umbraco.Headless.Client.Net\Umbraco.Headless.Client.Net.csproj", "{71515FBA-2746-4AAE-B9BF-99ED4AFF60CE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +14,9 @@ Global {7E351693-56E2-47B9-94FE-F7481C9674AB}.Debug|Any CPU.Build.0 = Debug|Any CPU {7E351693-56E2-47B9-94FE-F7481C9674AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {7E351693-56E2-47B9-94FE-F7481C9674AB}.Release|Any CPU.Build.0 = Release|Any CPU + {71515FBA-2746-4AAE-B9BF-99ED4AFF60CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71515FBA-2746-4AAE-B9BF-99ED4AFF60CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71515FBA-2746-4AAE-B9BF-99ED4AFF60CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71515FBA-2746-4AAE-B9BF-99ED4AFF60CE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/ContentExtensions.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/ContentExtensions.cs index e366d99..9e5468c 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/ContentExtensions.cs +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/ContentExtensions.cs @@ -32,9 +32,15 @@ public static T Value(this Content content, string alias) return (T) Convert.ChangeType(value, typeof(T)); } - public static bool IsVisible(this Content content) + public static bool IsVisible(this IContent content) { - return content.Value("umbracoNaviHide") == false; + if (content is IHideInNavigation hideInNavigation) + return hideInNavigation.HideInNavigation == false; + + if (content is Content c) + return c.Value("umbracoNaviHide") == false; + + return true; } } public static class ElementExtensions diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/Frontpage.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/Frontpage.cs new file mode 100644 index 0000000..4d45f8a --- /dev/null +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/Frontpage.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Umbraco.Headless.Client.Net.Delivery.Models; + +namespace Umbraco.Headless.Client.Samples.Web.Models +{ + public class Frontpage : ContentBase, IContent, IHideInNavigation + { + public string ContentTypeAlias { get; set; } + + public string HeroTitle { get; set; } + public string HeroSubtitle { get; set; } + public Image HeroImage { get; set; } + + public string UniqueSellingPointsTitle { get; set; } + public IEnumerable UniqueSellingPoints { get; set; } + + public IEnumerable Elements { get; set; } + + public string FooterTitle { get; set; } + public IEnumerable FooterLinks { get; set; } + + [JsonProperty("umbracoNaviHide")] + public bool HideInNavigation { get; set; } + } +} diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/IHideInNavigation.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/IHideInNavigation.cs new file mode 100644 index 0000000..690e196 --- /dev/null +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/IHideInNavigation.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Headless.Client.Samples.Web.Models +{ + public interface IHideInNavigation + { + bool HideInNavigation { get; set; } + } +} diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/TextAndImage.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/TextAndImage.cs index 6576772..1161a7c 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/TextAndImage.cs +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/TextAndImage.cs @@ -1,10 +1,14 @@ using Microsoft.AspNetCore.Html; +using Newtonsoft.Json; +using Umbraco.Headless.Client.Samples.Web.Serialization; namespace Umbraco.Headless.Client.Samples.Web.Models { public class TextAndImage { public string Title { get; set; } + + [JsonConverter(typeof(HtmlContentConverter))] public IHtmlContent Text { get; set; } public string ImageUrl { get; set; } public bool ShowLargeImage { get; set; } diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/Textpage.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/Textpage.cs new file mode 100644 index 0000000..0fe3881 --- /dev/null +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/Textpage.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Umbraco.Headless.Client.Net.Delivery.Models; + +namespace Umbraco.Headless.Client.Samples.Web.Models +{ + public class Textpage : ContentBase, IContent, IHideInNavigation + { + public string ContentTypeAlias { get; set; } + + public string HeroTitle { get; set; } + public string HeroSubtitle { get; set; } + public Image HeroImage { get; set; } + + public IEnumerable Elements { get; set; } + + [JsonProperty("umbracoNaviHide")] + public bool HideInNavigation { get; set; } + } +} diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/UniqueSellingPoint.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/UniqueSellingPoint.cs index 581381a..8d77660 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/UniqueSellingPoint.cs +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/UniqueSellingPoint.cs @@ -1,13 +1,17 @@ using Microsoft.AspNetCore.Html; +using Newtonsoft.Json; using Umbraco.Headless.Client.Net.Delivery.Models; +using Umbraco.Headless.Client.Samples.Web.Serialization; namespace Umbraco.Headless.Client.Samples.Web.Models { public class UniqueSellingPoint { public string Title { get; set; } + + [JsonConverter(typeof(HtmlContentConverter))] public IHtmlContent Text { get; set; } public MultiUrlPickerLink Link { get; set; } - public string ImageUrl { get; set; } + public Image Image { get; set; } } } diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Serialization/HtmlContentConverter.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Serialization/HtmlContentConverter.cs new file mode 100644 index 0000000..45c9983 --- /dev/null +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Serialization/HtmlContentConverter.cs @@ -0,0 +1,25 @@ +using System; +using Microsoft.AspNetCore.Html; +using Newtonsoft.Json; + +namespace Umbraco.Headless.Client.Samples.Web.Serialization +{ + public class HtmlContentConverter : JsonConverter + { + public override bool CanWrite { get; } = false; + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var value = serializer.Deserialize(reader); + return new HtmlString(value); + } + + public override bool CanConvert(Type objectType) => typeof(IHtmlContent).IsAssignableFrom(objectType); + + } +} diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Startup.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Startup.cs index f730c7b..cd3a6d9 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Startup.cs +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Startup.cs @@ -4,6 +4,8 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Umbraco.Headless.Client.Net.Configuration; +using Umbraco.Headless.Client.Samples.Web.Models; using Umbraco.Headless.Client.Samples.Web.Mvc; namespace Umbraco.Headless.Client.Samples.Web @@ -36,7 +38,11 @@ public void ConfigureServices(IServiceCollection services) var projectAlias = umbracoConfig.GetValue("projectAlias"); var apiKey = umbracoConfig.GetValue("apiKey"); - services.AddUmbracoHeadlessContentDelivery(projectAlias, apiKey); + var configuration = new ApiKeyBasedConfiguration(projectAlias, apiKey); + configuration.ContentModelTypes.Add(); + configuration.ContentModelTypes.Add(); + + services.AddUmbracoHeadlessContentDelivery(configuration); services.AddUmbracoHeadlessWebEngine(); } diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web.csproj b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web.csproj index 1d3d8a8..a6d52d0 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web.csproj +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web.csproj @@ -8,7 +8,9 @@ - + + + diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/FooterViewComponent.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/FooterViewComponent.cs index 74eb8d0..d95c130 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/FooterViewComponent.cs +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/FooterViewComponent.cs @@ -19,12 +19,12 @@ public FooterViewComponent(UmbracoContext umbracoContext) public async Task InvokeAsync() { - var content = (Content) await _umbracoContext.Cache.GetContentByUrl("/"); + var content = (Frontpage) await _umbracoContext.Cache.GetContentByUrl("/"); return View(new FooterViewModel { - Title = content.Value("footerTitle"), - Links = content.Value>("footerLinks") + Title = content.FooterTitle, + Links = content.FooterLinks }); } } diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/MainNavigationViewComponent.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/MainNavigationViewComponent.cs index 8822643..580c3ed 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/MainNavigationViewComponent.cs +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/MainNavigationViewComponent.cs @@ -24,7 +24,7 @@ public async Task InvokeAsync() var rootContent = await _umbracoCache.GetContentByUrl("/"); var children = await _contentDeliveryService.Content.GetChildren(rootContent.Id); - return View(from item in children.Content.Items.Where(x => x.IsVisible()) + return View(from item in children.Content.Items select new NavigationItem { Title = item.Name, diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/UniqueSellingPointsViewComponent.cs b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/UniqueSellingPointsViewComponent.cs index f32047c..5f39509 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/UniqueSellingPointsViewComponent.cs +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/ViewComponents/UniqueSellingPointsViewComponent.cs @@ -9,19 +9,12 @@ namespace Umbraco.Headless.Client.Samples.Web.ViewComponents { public class UniqueSellingPointsViewComponent : ViewComponent { - public IViewComponentResult Invoke(string title, IEnumerable contents) + public IViewComponentResult Invoke(string title, IEnumerable contents) { return View(new UniqueSellingPointsViewModel { Title = title, - UniqueSellingPoints = from c in contents - select new UniqueSellingPoint - { - Link = c.Value("link"), - Text = c.Value("text"), - Title = c.Value("title"), - ImageUrl = c.Value("image")?.Url - } + UniqueSellingPoints = contents }); } } diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/DefaultUmbraco/Frontpage.cshtml b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/DefaultUmbraco/Frontpage.cshtml index 629821d..1ebf71a 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/DefaultUmbraco/Frontpage.cshtml +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/DefaultUmbraco/Frontpage.cshtml @@ -1,17 +1,16 @@ -@using Umbraco.Headless.Client.Net.Delivery.Models -@model Umbraco.Headless.Client.Net.Delivery.Models.Content +@model Frontpage @await Component.InvokeAsync("Hero", new { - title = Model.Value("heroTitle"), - subTitle = Model.Value("heroSubtitle"), - image = Model.Value("heroImage") + title = Model.HeroTitle, + subTitle = Model.HeroSubtitle, + image = Model.HeroImage }) @await Component.InvokeAsync("UniqueSellingPoints", new { - title = Model.Value("uniqueSellingPointsTitle"), - contents = Model.Value>("uniqueSellingPoints") + title = Model.UniqueSellingPointsTitle, + contents = Model.UniqueSellingPoints }) -@await Html.PartialAsync("_Elements", Model.Value>("elements")) +@await Html.PartialAsync("_Elements", Model.Elements) diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/DefaultUmbraco/Textpage.cshtml b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/DefaultUmbraco/Textpage.cshtml index ec78df1..d60944e 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/DefaultUmbraco/Textpage.cshtml +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/DefaultUmbraco/Textpage.cshtml @@ -1,10 +1,9 @@ -@using Umbraco.Headless.Client.Net.Delivery.Models -@model Umbraco.Headless.Client.Net.Delivery.Models.Content +@model Textpage @await Component.InvokeAsync("Hero", new { - title = Model.Value("heroTitle"), - subTitle = Model.Value("heroSubtitle"), - image = Model.Value("heroImage") + title = Model.HeroTitle, + subTitle = Model.HeroSubtitle, + image = Model.HeroImage, }) -@await Html.PartialAsync("_Elements", Model.Value>("elements")) +@await Html.PartialAsync("_Elements", Model.Elements) diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/Shared/Components/UniqueSellingPoints/Default.cshtml b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/Shared/Components/UniqueSellingPoints/Default.cshtml index bbc17da..1a96c37 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/Shared/Components/UniqueSellingPoints/Default.cshtml +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/Shared/Components/UniqueSellingPoints/Default.cshtml @@ -7,7 +7,10 @@ {

@usp.Title

- + @if (usp.Image != null) + { + + }

@usp.Text

@if (usp.Link != null) { diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/Shared/_Elements.cshtml b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/Shared/_Elements.cshtml index 147a63c..86605c7 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/Shared/_Elements.cshtml +++ b/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Views/Shared/_Elements.cshtml @@ -1,4 +1,4 @@ -@model IEnumerable +@model IEnumerable @foreach (var element in Model) { diff --git a/src/Umbraco.Headless.Client.Net/Configuration/ApiKeyBasedConfiguration.cs b/src/Umbraco.Headless.Client.Net/Configuration/ApiKeyBasedConfiguration.cs index 21d288b..8bbcc45 100644 --- a/src/Umbraco.Headless.Client.Net/Configuration/ApiKeyBasedConfiguration.cs +++ b/src/Umbraco.Headless.Client.Net/Configuration/ApiKeyBasedConfiguration.cs @@ -1,13 +1,12 @@ namespace Umbraco.Headless.Client.Net.Configuration { - public class ApiKeyBasedConfiguration : IApiKeyBasedConfiguration + public class ApiKeyBasedConfiguration : HeadlessConfiguration, IApiKeyBasedConfiguration { - public ApiKeyBasedConfiguration(string projectAlias, string token) + public ApiKeyBasedConfiguration(string projectAlias, string token) : base(projectAlias) { - ProjectAlias = projectAlias; Token = token; } - public string ProjectAlias { get; } + public string Token { get; } } } diff --git a/src/Umbraco.Headless.Client.Net/Configuration/BasicHeadlessConfiguration.cs b/src/Umbraco.Headless.Client.Net/Configuration/BasicHeadlessConfiguration.cs deleted file mode 100644 index 95f6b02..0000000 --- a/src/Umbraco.Headless.Client.Net/Configuration/BasicHeadlessConfiguration.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Umbraco.Headless.Client.Net.Configuration -{ - internal class BasicHeadlessConfiguration : IHeadlessConfiguration - { - public BasicHeadlessConfiguration(string projectAlias) - { - ProjectAlias = projectAlias; - } - - public string ProjectAlias { get; } - } -} diff --git a/src/Umbraco.Headless.Client.Net/Configuration/HeadlessConfiguration.cs b/src/Umbraco.Headless.Client.Net/Configuration/HeadlessConfiguration.cs new file mode 100644 index 0000000..8a6fd01 --- /dev/null +++ b/src/Umbraco.Headless.Client.Net/Configuration/HeadlessConfiguration.cs @@ -0,0 +1,17 @@ +using System; +using Umbraco.Headless.Client.Net.Delivery.Models; + +namespace Umbraco.Headless.Client.Net.Configuration +{ + public class HeadlessConfiguration : IHeadlessConfiguration + { + public HeadlessConfiguration(string projectAlias) + { + ProjectAlias = projectAlias ?? throw new ArgumentNullException(nameof(projectAlias)); + ContentModelTypes = new TypeList(); + } + + public string ProjectAlias { get; } + public ITypeList ContentModelTypes { get; } + } +} diff --git a/src/Umbraco.Headless.Client.Net/Configuration/IHeadlessConfiguration.cs b/src/Umbraco.Headless.Client.Net/Configuration/IHeadlessConfiguration.cs index 112d42f..e554227 100644 --- a/src/Umbraco.Headless.Client.Net/Configuration/IHeadlessConfiguration.cs +++ b/src/Umbraco.Headless.Client.Net/Configuration/IHeadlessConfiguration.cs @@ -1,7 +1,48 @@ -namespace Umbraco.Headless.Client.Net.Configuration +using System; +using System.Collections; +using System.Collections.Generic; +using Umbraco.Headless.Client.Net.Delivery.Models; + +namespace Umbraco.Headless.Client.Net.Configuration { public interface IHeadlessConfiguration { string ProjectAlias { get; } + ITypeList ContentModelTypes { get; } + } + + public interface ITypeList : IEnumerable + { + void Add() where TImplementation : TBaseType; + void Add(Type type); + void Remove() where TImplementation : TBaseType; + void Remove(Type type); + void Clear(); + } + + internal class TypeList : ITypeList + { + private readonly List _types = new List(); + + public IEnumerator GetEnumerator() => _types.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Add() where TImplementation : TBaseType => _types.Add(typeof(TImplementation)); + + public void Add(Type type) + { + if (typeof(TBaseType).IsAssignableFrom(type) == false) + throw new Exception(); + + _types.Add(type); + } + + public void Remove() where TImplementation : TBaseType => + _types.Remove(typeof(TImplementation)); + + public void Remove(Type type) => _types.Remove(type); + + public void Clear() => _types.Clear(); } } diff --git a/src/Umbraco.Headless.Client.Net/Configuration/PasswordBasedConfiguration.cs b/src/Umbraco.Headless.Client.Net/Configuration/PasswordBasedConfiguration.cs index e3ff32d..1549e55 100644 --- a/src/Umbraco.Headless.Client.Net/Configuration/PasswordBasedConfiguration.cs +++ b/src/Umbraco.Headless.Client.Net/Configuration/PasswordBasedConfiguration.cs @@ -1,16 +1,13 @@ namespace Umbraco.Headless.Client.Net.Configuration { - public class PasswordBasedConfiguration : IPasswordBasedConfiguration + public class PasswordBasedConfiguration : HeadlessConfiguration, IPasswordBasedConfiguration { - public PasswordBasedConfiguration(string projectAlias, string username, string password) + public PasswordBasedConfiguration(string projectAlias, string username, string password) : base(projectAlias) { - ProjectAlias = projectAlias; Username = username; Password = password; } - public string ProjectAlias { get; } - public string Username { get; } public string Password { get; } diff --git a/src/Umbraco.Headless.Client.Net/Delivery/ContentDelivery.cs b/src/Umbraco.Headless.Client.Net/Delivery/ContentDelivery.cs index 7634f46..773fa9f 100644 --- a/src/Umbraco.Headless.Client.Net/Delivery/ContentDelivery.cs +++ b/src/Umbraco.Headless.Client.Net/Delivery/ContentDelivery.cs @@ -1,10 +1,14 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net.Http; +using System.Reflection; using System.Threading.Tasks; +using Newtonsoft.Json; using Refit; using Umbraco.Headless.Client.Net.Configuration; using Umbraco.Headless.Client.Net.Delivery.Models; +using Umbraco.Headless.Client.Net.Serialization; namespace Umbraco.Headless.Client.Net.Delivery { @@ -12,6 +16,7 @@ internal class ContentDelivery : IContentDelivery { private readonly IHeadlessConfiguration _configuration; private readonly HttpClient _httpClient; + private ContentDeliveryEndpoints _service; public ContentDelivery(IHeadlessConfiguration configuration, HttpClient httpClient) { @@ -19,10 +24,24 @@ public ContentDelivery(IHeadlessConfiguration configuration, HttpClient httpClie _httpClient = httpClient; } - public async Task> GetRoot(string culture) - { - var service = RestService.For(_httpClient); - var root = await service.GetRoot(_configuration.ProjectAlias, culture); + private ContentDeliveryEndpoints Service => + _service ?? (_service = RestService.For(_httpClient, + new RefitSettings + { + ContentSerializer = new JsonContentSerializer(new JsonSerializerSettings() + { + Converters = + { + new ContentConverter( + _configuration.ContentModelTypes.ToDictionary(GetAliasFromClassName) + ) + } + }) + })); + + public async Task> GetRoot(string culture) + { + var root = await Service.GetRoot(_configuration.ProjectAlias, culture); return root.Content.Items; } @@ -35,10 +54,9 @@ public async Task> GetRoot(string culture) where T : IContent return root.Content.Items; } - public async Task GetById(Guid id, string culture, int depth) + public async Task GetById(Guid id, string culture, int depth) { - var service = RestService.For(_httpClient); - var content = await service.GetById(_configuration.ProjectAlias, culture, id, depth); + var content = await Service.GetById(_configuration.ProjectAlias, culture, id, depth); return content; } @@ -51,10 +69,9 @@ public async Task GetById(Guid id, string culture, int depth) where T : IC return content; } - public async Task GetByUrl(string url, string culture, int depth) + public async Task GetByUrl(string url, string culture, int depth) { - var service = RestService.For(_httpClient); - var content = await service.GetByUrl(_configuration.ProjectAlias, culture, url, depth); + var content = await Service.GetByUrl(_configuration.ProjectAlias, culture, url, depth); return content; } @@ -69,8 +86,7 @@ public async Task GetByUrl(string url, string culture, int depth) where T public async Task GetChildren(Guid id, string culture, int page, int pageSize) { - var service = RestService.For(_httpClient); - var content = await service.GetChildren(_configuration.ProjectAlias, culture, id, page, pageSize); + var content = await Service.GetChildren(_configuration.ProjectAlias, culture, id, page, pageSize); return content; } @@ -85,8 +101,7 @@ public async Task> GetChildren(Guid id, string culture, int p public async Task GetDescendants(Guid id, string culture, int page, int pageSize) { - var service = RestService.For(_httpClient); - var content = await service.GetDescendants(_configuration.ProjectAlias, culture, id, page, pageSize); + var content = await Service.GetDescendants(_configuration.ProjectAlias, culture, id, page, pageSize); return content; } @@ -99,10 +114,9 @@ public async Task> GetDescendants(Guid id, string culture, in return content; } - public async Task> GetAncestors(Guid id, string culture) + public async Task> GetAncestors(Guid id, string culture) { - var service = RestService.For(_httpClient); - var root = await service.GetAncestors(_configuration.ProjectAlias, culture, id); + var root = await Service.GetAncestors(_configuration.ProjectAlias, culture, id); return root.Content.Items; } @@ -117,8 +131,7 @@ public async Task> GetAncestors(Guid id, string culture) where public async Task GetByType(string contentType, string culture = null, int page = 1, int pageSize = 10) { - var service = RestService.For(_httpClient); - var content = await service.GetByType(_configuration.ProjectAlias, culture, contentType, page, pageSize); + var content = await Service.GetByType(_configuration.ProjectAlias, culture, contentType, page, pageSize); return content; } @@ -133,14 +146,18 @@ public async Task> GetByType(string culture = null, int page public async Task Search(string term, string culture = null, int page = 1, int pageSize = 10) { - var service = RestService.For(_httpClient); - var content = await service.Search(_configuration.ProjectAlias, culture, term, page, pageSize); + var content = await Service.Search(_configuration.ProjectAlias, culture, term, page, pageSize); return content; } - private static string GetAliasFromClassName() + private static string GetAliasFromClassName() => GetAliasFromClassName(typeof(T)); + + private static string GetAliasFromClassName(Type type) { - var className = typeof(T).Name; + if (type.GetCustomAttribute(typeof(ContentModelAttribute)) is ContentModelAttribute attr) + return attr.ContentTypeAlias; + + var className = type.Name; if (className.IndexOf("Model", StringComparison.Ordinal) > -1) { className = className.Substring(0, className.IndexOf("Model", StringComparison.Ordinal)); diff --git a/src/Umbraco.Headless.Client.Net/Delivery/ContentDeliveryEndpoints.cs b/src/Umbraco.Headless.Client.Net/Delivery/ContentDeliveryEndpoints.cs index d0436ab..d778297 100644 --- a/src/Umbraco.Headless.Client.Net/Delivery/ContentDeliveryEndpoints.cs +++ b/src/Umbraco.Headless.Client.Net/Delivery/ContentDeliveryEndpoints.cs @@ -8,16 +8,16 @@ namespace Umbraco.Headless.Client.Net.Delivery interface ContentDeliveryEndpoints { [Get("/content?hyperlinks=false")] - Task> GetRoot([Header(Constants.Headers.ProjectAlias)] string projectAlias, [Header(Constants.Headers.AcceptLanguage)] string culture); + Task> GetRoot([Header(Constants.Headers.ProjectAlias)] string projectAlias, [Header(Constants.Headers.AcceptLanguage)] string culture); [Get("/content/{id}?depth={depth}")] - Task GetById([Header(Constants.Headers.ProjectAlias)] string projectAlias, [Header(Constants.Headers.AcceptLanguage)] string culture, Guid id, int depth); + Task GetById([Header(Constants.Headers.ProjectAlias)] string projectAlias, [Header(Constants.Headers.AcceptLanguage)] string culture, Guid id, int depth); [Get("/content/url?url={url}&depth={depth}&hyperlinks=false")] - Task GetByUrl([Header(Constants.Headers.ProjectAlias)] string projectAlias, [Header(Constants.Headers.AcceptLanguage)] string culture, string url, int depth); + Task GetByUrl([Header(Constants.Headers.ProjectAlias)] string projectAlias, [Header(Constants.Headers.AcceptLanguage)] string culture, string url, int depth); [Get("/content/{id}/ancestors?hyperlinks=false")] - Task> GetAncestors([Header(Constants.Headers.ProjectAlias)] string projectAlias, [Header(Constants.Headers.AcceptLanguage)] string culture, Guid id); + Task> GetAncestors([Header(Constants.Headers.ProjectAlias)] string projectAlias, [Header(Constants.Headers.AcceptLanguage)] string culture, Guid id); [Get("/content/{id}/children?page={page}&pageSize={pageSize}&hyperlinks=false")] Task GetChildren([Header(Constants.Headers.ProjectAlias)] string projectAlias, [Header(Constants.Headers.AcceptLanguage)] string culture, Guid id, int page, int pageSize); diff --git a/src/Umbraco.Headless.Client.Net/Delivery/ContentDeliveryService.cs b/src/Umbraco.Headless.Client.Net/Delivery/ContentDeliveryService.cs index bdee089..bbcfbcc 100644 --- a/src/Umbraco.Headless.Client.Net/Delivery/ContentDeliveryService.cs +++ b/src/Umbraco.Headless.Client.Net/Delivery/ContentDeliveryService.cs @@ -14,7 +14,7 @@ public class ContentDeliveryService /// Initializes a new instance of the ContentDeliveryService class /// /// Alias of the Project - public ContentDeliveryService(string projectAlias) : this(new BasicHeadlessConfiguration(projectAlias)) + public ContentDeliveryService(string projectAlias) : this(new HeadlessConfiguration(projectAlias)) { } /// diff --git a/src/Umbraco.Headless.Client.Net/Delivery/IContentDelivery.cs b/src/Umbraco.Headless.Client.Net/Delivery/IContentDelivery.cs index 0d1d111..252e38a 100644 --- a/src/Umbraco.Headless.Client.Net/Delivery/IContentDelivery.cs +++ b/src/Umbraco.Headless.Client.Net/Delivery/IContentDelivery.cs @@ -16,7 +16,7 @@ public interface IContentDelivery /// /// Content Culture (Optional) /// - Task> GetRoot(string culture = null); + Task> GetRoot(string culture = null); /// /// Gets the root Content items as the specified type @@ -33,7 +33,7 @@ public interface IContentDelivery /// Content Culture (Optional) /// Integer value specifying the number of levels to retrieve /// - Task GetById(Guid id, string culture = null, int depth = 1); + Task GetById(Guid id, string culture = null, int depth = 1); /// /// Gets a single Content item by its id as the specified type @@ -52,7 +52,7 @@ public interface IContentDelivery /// Content Culture (Optional) /// Integer value specifying the number of levels to retrieve /// - Task GetByUrl(string url, string culture = null, int depth = 1); + Task GetByUrl(string url, string culture = null, int depth = 1); /// /// Gets a single Content item by its Url as the specified type @@ -112,7 +112,7 @@ public interface IContentDelivery /// id of the Content to retrieve ancestors for /// Content Culture (Optional) /// - Task> GetAncestors(Guid id, string culture = null); + Task> GetAncestors(Guid id, string culture = null); /// /// Gets the ancestors of a Content item by its Id as the specified type diff --git a/src/Umbraco.Headless.Client.Net/Delivery/Models/ContentModelAttribute.cs b/src/Umbraco.Headless.Client.Net/Delivery/Models/ContentModelAttribute.cs new file mode 100644 index 0000000..0ebe1ef --- /dev/null +++ b/src/Umbraco.Headless.Client.Net/Delivery/Models/ContentModelAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Umbraco.Headless.Client.Net.Delivery.Models +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + public class ContentModelAttribute : Attribute + { + public ContentModelAttribute(string contentTypeAlias) + { + ContentTypeAlias = contentTypeAlias; + } + + public string ContentTypeAlias { get; } + } +} diff --git a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/Element.cs b/src/Umbraco.Headless.Client.Net/Delivery/Models/Element.cs similarity index 83% rename from samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/Element.cs rename to src/Umbraco.Headless.Client.Net/Delivery/Models/Element.cs index 14429fe..1c7d137 100644 --- a/samples/Umbraco.Headless.Client.Samples.Web/Umbraco.Headless.Client.Samples.Web/Models/Element.cs +++ b/src/Umbraco.Headless.Client.Net/Delivery/Models/Element.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using Newtonsoft.Json; -namespace Umbraco.Headless.Client.Samples.Web.Models +namespace Umbraco.Headless.Client.Net.Delivery.Models { - public class Element + public class Element : IElement { public Element() { diff --git a/src/Umbraco.Headless.Client.Net/Delivery/Models/IElement.cs b/src/Umbraco.Headless.Client.Net/Delivery/Models/IElement.cs new file mode 100644 index 0000000..f39dd16 --- /dev/null +++ b/src/Umbraco.Headless.Client.Net/Delivery/Models/IElement.cs @@ -0,0 +1,10 @@ +using System; + +namespace Umbraco.Headless.Client.Net.Delivery.Models +{ + public interface IElement + { + string ContentTypeAlias { get; set; } + Guid Id { get; set; } + } +} diff --git a/src/Umbraco.Headless.Client.Net/Delivery/Models/PagedContent.cs b/src/Umbraco.Headless.Client.Net/Delivery/Models/PagedContent.cs index 603cc45..67f9e92 100644 --- a/src/Umbraco.Headless.Client.Net/Delivery/Models/PagedContent.cs +++ b/src/Umbraco.Headless.Client.Net/Delivery/Models/PagedContent.cs @@ -11,7 +11,7 @@ public class PagedContent : PagedCollection where T : IContent } [JsonObject] - public class PagedContent : PagedContent + public class PagedContent : PagedContent { } } diff --git a/src/Umbraco.Headless.Client.Net/Serialization/ContentConverter.cs b/src/Umbraco.Headless.Client.Net/Serialization/ContentConverter.cs new file mode 100644 index 0000000..35cfab8 --- /dev/null +++ b/src/Umbraco.Headless.Client.Net/Serialization/ContentConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Headless.Client.Net.Delivery.Models; + +namespace Umbraco.Headless.Client.Net.Serialization +{ + internal class ContentConverter : JsonConverter + { + private readonly IDictionary _types; + + public ContentConverter(IDictionary types) + { + _types = types ?? throw new ArgumentNullException(nameof(types)); + } + + public override bool CanWrite { get; } = false; + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => + throw new NotImplementedException(); + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var obj = serializer.Deserialize(reader); + var contentTypeAlias = obj.GetValue("contentTypeAlias").Value(); + + return _types.TryGetValue(contentTypeAlias, out var type) ? obj.ToObject(type) : obj.ToObject(); + } + + public override bool CanConvert(Type objectType) => typeof(IContent).IsAssignableFrom(objectType) && + typeof(Content).IsAssignableFrom(objectType) == false; + } +} diff --git a/test/Umbraco.Headless.Client.Net.Tests/ContentDeliveryFixture.cs b/test/Umbraco.Headless.Client.Net.Tests/ContentDeliveryFixture.cs index f0c5258..5c81c2e 100644 --- a/test/Umbraco.Headless.Client.Net.Tests/ContentDeliveryFixture.cs +++ b/test/Umbraco.Headless.Client.Net.Tests/ContentDeliveryFixture.cs @@ -5,6 +5,7 @@ using RichardSzalay.MockHttp; using Umbraco.Headless.Client.Net.Configuration; using Umbraco.Headless.Client.Net.Delivery; +using Umbraco.Headless.Client.Net.Delivery.Models; using Xunit; namespace Umbraco.Headless.Client.Net.Tests @@ -38,7 +39,7 @@ public async Task Can_Retrieve_Content_By_Id(string id) var contentId = Guid.Parse(id); var service = new ContentDeliveryService(_configuration, GetMockedHttpClient($"{_contentBaseUrl}/*", ContentDeliveryJson.GetContentById)); - var content = await service.Content.GetById(contentId); + var content = (Content) await service.Content.GetById(contentId); Assert.NotNull(content); Assert.NotEmpty(content.Properties); Assert.Equal(16, content.Properties.Count); @@ -50,7 +51,7 @@ public async Task Can_Retrieve_Content_By_Url(string url) { var service = new ContentDeliveryService(_configuration, GetMockedHttpClient($"{_contentBaseUrl}/url", ContentDeliveryJson.GetByUrlHomeProductsUnicorn)); - var content = await service.Content.GetByUrl(url); + var content = (Content) await service.Content.GetByUrl(url); Assert.NotNull(content); Assert.NotEmpty(content.Properties); } diff --git a/test/Umbraco.Headless.Client.Net.Tests/FakeHeadlessConfiguration.cs b/test/Umbraco.Headless.Client.Net.Tests/FakeHeadlessConfiguration.cs index e72eb48..e599390 100644 --- a/test/Umbraco.Headless.Client.Net.Tests/FakeHeadlessConfiguration.cs +++ b/test/Umbraco.Headless.Client.Net.Tests/FakeHeadlessConfiguration.cs @@ -2,8 +2,10 @@ namespace Umbraco.Headless.Client.Net.Tests { - public class FakeHeadlessConfiguration : IHeadlessConfiguration + public class FakeHeadlessConfiguration : HeadlessConfiguration { - public string ProjectAlias => "headless-with-cdn"; + public FakeHeadlessConfiguration() : base("headless-with-cdn") + { + } } }