Content and media type CRUD controllers and services (#14665)

* Add GetAsync method

* Fix up delete document type controller

* Add scope to delete async

* Add some scaffolding

* Add create model

* Start working on validation

* Move validation to its own service

* Use GetAllAsync instead of GetAsync

* Add initial composition support

Still need to figure out some kinks

* Validate compositions when creating

* Add initial folder support

* Initial handling of generic properties

* Add operation status responses

* Move create operation into service

* Add first test

* Fix issued shown by test

* Ensure a specific key can be specified when creating

* Rename container id to container key

Let's try and be consistent

* Create basic composition test

* Ensure new property groups are created with the correct key

* Add test showing property type issue

* Fix property types not using the expected key.

* Validate against model fetched from content type service

Just to make sure nothing explodes on the round trip

* Make helper for creating create models

* Add helper for creating container

* Make helper methods simpler to use

* Add test for compositions using compositions

* Add more composition tests

* Fix bug allowing element types to be composed by non element types

* Remove validators

This can just be a part of the editing service

* Minor cleanup

* Ensure that multiple levels of inheritance is possible

* Ensure doctype cannot be used as both composition and inheritance on the same doctype

* Ensure no duplicate aliases from composition and that compositions exists

* Minor cleanup

* Address todos

* Add SaveAsync method

* Renamed some models

* Rename from DocumentType to ContentType

* Clarify ParentKey as being container only + untangle things a tiny bit

* Clean out another TODO (less duplicate code) + more tests

* Refactor for reuse across different content types + add media type editing service + unit tests

* Refactor in preparation for update handling

* More tests + fixed bugs found while testing

* Simplify things a bit

* Content type update + a lot of unit tests + some refactor + fix bugs found while testing

* Begin building presentation factories for mapping view models to editing models

* Use async save

* Mapping factories and some clean-up

* Rename Key to Id (ParentKey to ParentId)

* Fix slight typo

* Use editing service in document type controllers and introduce media type controllers

* Validate containers and align container aliases with the current backoffice

* Remove ParentId from response

* Fix scope handling in DeleteAsync

* Refactor ContentTypeSort

* A little renaming for clarity + safeguard against changes to inheritance

* Persist allowed content types

* Fix bad merge + update controller response annotations

* Update OpenAPI JSON

* Update src/Umbraco.Cms.Api.Management/Controllers/DocumentType/DocumentTypeControllerBase.cs

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>

* Fix review comments

* Update usage of MapCreateAsync to ValidateAndMapForCreationAsync

---------

Co-authored-by: Nikolaj <nikolajlauridsen@protonmail.ch>
This commit is contained in:
Kenn Jacobsen
2023-08-17 12:28:16 +02:00
committed by GitHub
parent a1f6531453
commit 0096addcb9
100 changed files with 5074 additions and 560 deletions

View File

@@ -0,0 +1,251 @@
using NUnit.Framework;
using Umbraco.Extensions;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Extensions;
[TestFixture]
public class TypeExtensionsTests
{
private string[] PublicObjectMethodNames = { nameof(GetType), nameof(ToString), nameof(Equals), nameof(GetHashCode) };
private string[] NonPublicObjectMethodNames = { nameof(MemberwiseClone), "Finalize" };
[Test]
public void Can_Get_Public_Properties_Of_Interface()
{
var properties = typeof(ITheBaseThing).GetPublicProperties();
Assert.AreEqual(1, properties.Length);
var propertyNames = properties.Select(p => p.Name).ToArray();
Assert.Contains(nameof(ITheBaseThing.TheBaseThingProperty), propertyNames);
}
[Test]
public void Get_Public_Properties_Of_Interface_Contains_Inherited_Properties()
{
var properties = typeof(ITheThing).GetPublicProperties();
Assert.AreEqual(2, properties.Length);
var propertyNames = properties.Select(p => p.Name).ToArray();
Assert.Contains(nameof(ITheBaseThing.TheBaseThingProperty), propertyNames);
Assert.Contains(nameof(ITheThing.TheThingProperty), propertyNames);
}
[Test]
public void Can_Get_Public_Properties_Of_Class()
{
var properties = typeof(TheBaseThing).GetPublicProperties();
Assert.AreEqual(1, properties.Length);
var propertyNames = properties.Select(p => p.Name).ToArray();
Assert.Contains(nameof(TheBaseThing.TheBaseThingProperty), propertyNames);
}
[Test]
public void Get_Public_Properties_Of_Class_Contains_Inherited_Properties()
{
var properties = typeof(TheThing).GetPublicProperties();
Assert.AreEqual(3, properties.Length);
var propertyNames = properties.Select(p => p.Name).ToArray();
Assert.Contains(nameof(TheBaseThing.TheBaseThingProperty), propertyNames);
Assert.Contains(nameof(TheThing.TheThingProperty), propertyNames);
Assert.Contains(nameof(TheThing.TheExtraProperty), propertyNames);
}
[Test]
public void Get_All_Properties_Of_Class_Contains_Internal_Properties()
{
var properties = typeof(TheThing).GetAllProperties();
Assert.AreEqual(4, properties.Length);
var propertyNames = properties.Select(p => p.Name).ToArray();
Assert.Contains(nameof(TheBaseThing.TheBaseThingProperty), propertyNames);
Assert.Contains(nameof(TheThing.TheThingProperty), propertyNames);
Assert.Contains(nameof(TheThing.TheExtraProperty), propertyNames);
Assert.Contains(nameof(TheThing.TheInternalProperty), propertyNames);
}
[Test]
public void Can_Get_Public_Methods_Of_Interface()
{
var methods = typeof(ITheBaseThing).GetPublicMethods();
Assert.AreEqual(2, methods.Length);
var methodNames = methods.Select(p => p.Name).ToArray();
Assert.Contains( $"get_{nameof(ITheBaseThing.TheBaseThingProperty)}", methodNames);
Assert.Contains( nameof(ITheBaseThing.TheBaseThingMethod), methodNames);
}
[Test]
public void Get_Public_Methods_Of_Interface_Contains_Inherited_Methods()
{
var methods = typeof(ITheThing).GetPublicMethods();
Assert.AreEqual(5, methods.Length);
var methodNames = methods.Select(p => p.Name).ToArray();
Assert.Contains( $"get_{nameof(ITheBaseThing.TheBaseThingProperty)}", methodNames);
Assert.Contains( nameof(ITheBaseThing.TheBaseThingMethod), methodNames);
Assert.Contains( $"get_{nameof(ITheThing.TheThingProperty)}", methodNames);
Assert.Contains( $"set_{nameof(ITheThing.TheThingProperty)}", methodNames);
Assert.Contains( nameof(ITheThing.TheThingMethod), methodNames);
}
[Test]
public void Can_Get_Public_Methods_Of_Class()
{
var methods = typeof(TheBaseThing).GetPublicMethods();
Assert.AreEqual(3 + PublicObjectMethodNames.Length, methods.Length);
var methodNames = methods.Select(p => p.Name).ToArray();
Assert.Contains( $"get_{nameof(TheBaseThing.TheBaseThingProperty)}", methodNames);
Assert.Contains( nameof(TheBaseThing.TheBaseThingMethod), methodNames);
Assert.Contains( nameof(TheBaseThing.TheExtraMethod), methodNames);
Assert.IsTrue(methodNames.ContainsAll(PublicObjectMethodNames));
}
[Test]
public void Get_Public_Methods_Of_Class_Contains_Inherited_Methods()
{
var methods = typeof(TheThing).GetPublicMethods();
Assert.AreEqual(7 + PublicObjectMethodNames.Length, methods.Length);
var methodNames = methods.Select(p => p.Name).ToArray();
Assert.Contains( $"get_{nameof(TheBaseThing.TheBaseThingProperty)}", methodNames);
Assert.Contains( nameof(TheBaseThing.TheBaseThingMethod), methodNames);
Assert.Contains( nameof(TheBaseThing.TheExtraMethod), methodNames);
Assert.Contains( $"get_{nameof(TheThing.TheThingProperty)}", methodNames);
Assert.Contains( $"set_{nameof(TheThing.TheThingProperty)}", methodNames);
Assert.Contains( $"get_{nameof(TheThing.TheExtraProperty)}", methodNames);
Assert.Contains( nameof(TheThing.TheThingMethod), methodNames);
Assert.IsTrue(methodNames.ContainsAll(PublicObjectMethodNames));
}
[Test]
public void Can_Get_All_Methods_Of_Class()
{
var methods = typeof(TheBaseThing).GetAllMethods();
Assert.AreEqual(4 + PublicObjectMethodNames.Length + NonPublicObjectMethodNames.Length, methods.Length);
var methodNames = methods.Select(p => p.Name).ToArray();
Assert.Contains( $"get_{nameof(TheBaseThing.TheBaseThingProperty)}", methodNames);
Assert.Contains( nameof(TheBaseThing.TheBaseThingMethod), methodNames);
Assert.Contains( nameof(TheBaseThing.TheExtraMethod), methodNames);
Assert.Contains( nameof(TheBaseThing.TheInternalMethod), methodNames);
Assert.IsTrue(methodNames.ContainsAll(PublicObjectMethodNames));
Assert.IsTrue(methodNames.ContainsAll(NonPublicObjectMethodNames));
}
[Test]
public void Get_All_Methods_Of_Class_Contains_Inherited_Methods()
{
var methods = typeof(TheThing).GetAllMethods();
Assert.AreEqual(9 + PublicObjectMethodNames.Length + NonPublicObjectMethodNames.Length, methods.Length);
var methodNames = methods.Select(p => p.Name).ToArray();
Assert.Contains( $"get_{nameof(TheBaseThing.TheBaseThingProperty)}", methodNames);
Assert.Contains( nameof(TheBaseThing.TheBaseThingMethod), methodNames);
Assert.Contains( nameof(TheBaseThing.TheExtraMethod), methodNames);
Assert.Contains( nameof(TheBaseThing.TheInternalMethod), methodNames);
Assert.Contains( $"get_{nameof(TheThing.TheThingProperty)}", methodNames);
Assert.Contains( $"set_{nameof(TheThing.TheThingProperty)}", methodNames);
Assert.Contains( $"get_{nameof(TheThing.TheExtraProperty)}", methodNames);
Assert.Contains( $"get_{nameof(TheThing.TheInternalProperty)}", methodNames);
Assert.Contains( nameof(TheThing.TheThingMethod), methodNames);
Assert.IsTrue(methodNames.ContainsAll(PublicObjectMethodNames));
Assert.IsTrue(methodNames.ContainsAll(NonPublicObjectMethodNames));
}
[Test]
public void Can_Get_Public_Properties_Of_Interface_With_Internal_Declarations()
{
var properties = typeof(ITheInterfaceWithInternalDeclarations).GetPublicProperties();
Assert.AreEqual(1, properties.Length);
var propertyNames = properties.Select(p => p.Name).ToArray();
Assert.Contains(nameof(ITheInterfaceWithInternalDeclarations.ThePublicProperty), propertyNames);
}
[Test]
public void Can_Get_All_Properties_Of_Interface_With_Internal_Declarations()
{
var properties = typeof(ITheInterfaceWithInternalDeclarations).GetAllProperties();
Assert.AreEqual(2, properties.Length);
var propertyNames = properties.Select(p => p.Name).ToArray();
Assert.Contains(nameof(ITheInterfaceWithInternalDeclarations.ThePublicProperty), propertyNames);
Assert.Contains(nameof(ITheInterfaceWithInternalDeclarations.TheInternalProperty), propertyNames);
}
[Test]
public void Can_Get_Public_Methods_Of_Interface_With_Internal_Declarations()
{
var properties = typeof(ITheInterfaceWithInternalDeclarations).GetPublicMethods();
Assert.AreEqual(2, properties.Length);
var propertyNames = properties.Select(p => p.Name).ToArray();
Assert.Contains($"get_{nameof(ITheInterfaceWithInternalDeclarations.ThePublicProperty)}", propertyNames);
Assert.Contains(nameof(ITheInterfaceWithInternalDeclarations.ThePublicMethod), propertyNames);
}
[Test]
public void Can_Get_All_Methods_Of_Interface_With_Internal_Declarations()
{
var properties = typeof(ITheInterfaceWithInternalDeclarations).GetAllMethods();
Assert.AreEqual(4, properties.Length);
var propertyNames = properties.Select(p => p.Name).ToArray();
Assert.Contains($"get_{nameof(ITheInterfaceWithInternalDeclarations.ThePublicProperty)}", propertyNames);
Assert.Contains($"get_{nameof(ITheInterfaceWithInternalDeclarations.TheInternalProperty)}", propertyNames);
Assert.Contains(nameof(ITheInterfaceWithInternalDeclarations.ThePublicMethod), propertyNames);
Assert.Contains(nameof(ITheInterfaceWithInternalDeclarations.TheInternalMethod), propertyNames);
}
public interface ITheThing : ITheBaseThing
{
string TheThingProperty { get; set; }
int TheThingMethod(int input);
}
public interface ITheBaseThing
{
string TheBaseThingProperty { get; }
int TheBaseThingMethod();
}
public class TheThing : TheBaseThing, ITheThing
{
public string TheThingProperty { get; set; }
public int TheThingMethod(int input) => throw new NotImplementedException();
public bool TheExtraProperty { get; }
internal decimal TheInternalProperty { get; }
}
public class TheBaseThing : ITheBaseThing
{
public string TheBaseThingProperty { get; }
public int TheBaseThingMethod() => throw new NotImplementedException();
public void TheExtraMethod() => throw new NotImplementedException();
internal void TheInternalMethod() => throw new NotImplementedException();
}
// it's not pretty, but it is possible to declare internal properties and methods in a public interface... we need to test those as well :/
public interface ITheInterfaceWithInternalDeclarations
{
public int ThePublicProperty { get; }
internal int TheInternalProperty { get; }
public string ThePublicMethod();
internal string TheInternalMethod();
}
}