diff --git a/src/Umbraco.Cms.Api.Common/Attributes/ShortGenericSchemaNameAttribute.cs b/src/Umbraco.Cms.Api.Common/Attributes/ShortGenericSchemaNameAttribute.cs new file mode 100644 index 0000000000..2dd7e57d5d --- /dev/null +++ b/src/Umbraco.Cms.Api.Common/Attributes/ShortGenericSchemaNameAttribute.cs @@ -0,0 +1,22 @@ +namespace Umbraco.Cms.Api.Common.Attributes; + +public abstract class ShortGenericSchemaNameAttribute : Attribute +{ + public Type[] GenericTypes { get; set; } + + public string SchemaName { get; set; } + + public ShortGenericSchemaNameAttribute(string schemaName, Type[] genericTypes) + { + GenericTypes = genericTypes; + SchemaName = schemaName; + } +} + +public class ShortGenericSchemaNameAttribute : ShortGenericSchemaNameAttribute +{ + public ShortGenericSchemaNameAttribute(string schemaName) + : base(schemaName, new[] { typeof(T1), typeof(T2) }) + { + } +} diff --git a/src/Umbraco.Cms.Api.Common/OpenApi/SchemaIdSelector.cs b/src/Umbraco.Cms.Api.Common/OpenApi/SchemaIdSelector.cs index d9e40f5351..5c9b9be004 100644 --- a/src/Umbraco.Cms.Api.Common/OpenApi/SchemaIdSelector.cs +++ b/src/Umbraco.Cms.Api.Common/OpenApi/SchemaIdSelector.cs @@ -1,4 +1,5 @@ using System.Text.RegularExpressions; +using Umbraco.Cms.Api.Common.Attributes; using Umbraco.Extensions; namespace Umbraco.Cms.Api.Common.OpenApi; @@ -10,18 +11,9 @@ public class SchemaIdSelector : ISchemaIdSelector protected string UmbracoSchemaId(Type type) { - string SanitizedTypeName(Type t) => t.Name - // first grab the "non generic" part of any generic type name (i.e. "PagedViewModel`1" becomes "PagedViewModel") - .Split('`').First() - // then remove the "ViewModel" postfix from type names - .TrimEnd("ViewModel"); - var name = SanitizedTypeName(type); - if (type.IsGenericType) - { - // append the generic type names, ultimately turning i.e. "PagedViewModel" into "PagedRelationItem" - name = $"{name}{string.Join(string.Empty, type.GenericTypeArguments.Select(SanitizedTypeName))}"; - } + + name = HandleGenerics(name, type); if (name.EndsWith("Model") == false) { @@ -33,4 +25,58 @@ public class SchemaIdSelector : ISchemaIdSelector // make absolutely sure we don't pass any invalid named by removing all non-word chars return Regex.Replace(name, @"[^\w]", string.Empty); } + + private string SanitizedTypeName(Type t) => t.Name + // first grab the "non generic" part of any generic type name (i.e. "PagedViewModel`1" becomes "PagedViewModel") + .Split('`').First() + // then remove the "ViewModel" postfix from type names + .TrimEnd("ViewModel"); + + private string HandleGenerics(string name, Type type) + { + if (!type.IsGenericType) + { + return name; + } + + // find all types that implement this type and have an matching attribute + var assignableTypesWithAttributeInfo = AppDomain.CurrentDomain.GetAssemblies() + .Where(assembly => assembly.FullName?.StartsWith("Umbraco") == true) + .SelectMany(assembly => assembly.GetTypes()) + .Where(t => t.IsAssignableTo(type)) + .Select(t => + { + var attribute = System.Attribute.GetCustomAttributes(t) + .FirstOrDefault(attribute => attribute is ShortGenericSchemaNameAttribute) as + ShortGenericSchemaNameAttribute; + return attribute == null + ? new ShortSchemaNameAttributeInfo(t) + : new ShortSchemaNameAttributeInfo(t, attribute.GenericTypes, attribute.SchemaName); + }) + .Where(info => info.GenericTypes != null); + + var matchingType = assignableTypesWithAttributeInfo + .SingleOrDefault(t => t.GenericTypes!.Length == type.GenericTypeArguments.Length + && t.GenericTypes.Intersect(type.GenericTypeArguments).Count() == + type.GenericTypeArguments.Length && t.SchemaName.IsNullOrWhiteSpace() == false); + + // use attribute custom name or append the generic type names, ultimately turning i.e. "PagedViewModel" into "PagedRelationItem" + return matchingType != null + ? matchingType.SchemaName! + : $"{name}{string.Join(string.Empty, type.GenericTypeArguments.Select(SanitizedTypeName))}"; + } + + private class ShortSchemaNameAttributeInfo + { + public Type Type { get; set; } + public Type[]? GenericTypes { get; set; } + public string? SchemaName { get; set; } + + public ShortSchemaNameAttributeInfo(Type type, Type[]? genericTypes = null, string? schemaName = null) + { + Type = type; + GenericTypes = genericTypes; + SchemaName = schemaName; + } + } } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index beb4bfc5ee..21e26e9d44 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -17357,7 +17357,7 @@ }, "additionalProperties": false }, - "ContentResponseModelBaseDocumentValueModelDocumentVariantResponseModel": { + "ContentForDocumentResponseModel": { "type": "object", "properties": { "values": { @@ -17462,7 +17462,7 @@ "type": "integer", "format": "int32" }, - "ContentTypeResponseModelBaseDocumentTypePropertyTypeResponseModelDocumentTypePropertyTypeContainerResponseModel": { + "ContentTypeForDocumentTypeResponseModel": { "type": "object", "properties": { "alias": { @@ -17537,7 +17537,7 @@ }, "additionalProperties": false }, - "ContentTypeResponseModelBaseMediaTypePropertyTypeResponseModelMediaTypePropertyTypeContainerResponseModel": { + "ContentTypeForMediaTypeResponseModel": { "type": "object", "properties": { "alias": { @@ -17667,7 +17667,7 @@ }, "additionalProperties": false }, - "CreateContentRequestModelBaseDocumentValueModelDocumentVariantRequestModel": { + "CreateContentForDocumentRequestModel": { "type": "object", "properties": { "values": { @@ -17703,7 +17703,7 @@ }, "additionalProperties": false }, - "CreateContentRequestModelBaseMediaValueModelMediaVariantRequestModel": { + "CreateContentForMediaRequestModel": { "type": "object", "properties": { "values": { @@ -17739,7 +17739,7 @@ }, "additionalProperties": false }, - "CreateContentTypeRequestModelBaseCreateDocumentTypePropertyTypeRequestModelCreateDocumentTypePropertyTypeContainerRequestModel": { + "CreateContentTypeForDocumentTypeRequestModel": { "type": "object", "properties": { "alias": { @@ -17820,7 +17820,7 @@ }, "additionalProperties": false }, - "CreateContentTypeRequestModelBaseCreateMediaTypePropertyTypeRequestModelCreateMediaTypePropertyTypeContainerRequestModel": { + "CreateContentTypeForMediaTypeRequestModel": { "type": "object", "properties": { "alias": { @@ -17942,7 +17942,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/CreateContentRequestModelBaseDocumentValueModelDocumentVariantRequestModel" + "$ref": "#/components/schemas/CreateContentForDocumentRequestModel" } ], "properties": { @@ -17980,7 +17980,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/CreateContentTypeRequestModelBaseCreateDocumentTypePropertyTypeRequestModelCreateDocumentTypePropertyTypeContainerRequestModel" + "$ref": "#/components/schemas/CreateContentTypeForDocumentTypeRequestModel" } ], "properties": { @@ -18059,7 +18059,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/CreateContentRequestModelBaseMediaValueModelMediaVariantRequestModel" + "$ref": "#/components/schemas/CreateContentForMediaRequestModel" } ], "properties": { @@ -18092,7 +18092,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/CreateContentTypeRequestModelBaseCreateMediaTypePropertyTypeRequestModelCreateMediaTypePropertyTypeContainerRequestModel" + "$ref": "#/components/schemas/CreateContentTypeForMediaTypeRequestModel" } ], "additionalProperties": false @@ -18677,6 +18677,9 @@ "contentTypeId": { "type": "string", "format": "uuid" + }, + "isTrashed": { + "type": "boolean" } }, "additionalProperties": false @@ -18697,7 +18700,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/ContentResponseModelBaseDocumentValueModelDocumentVariantResponseModel" + "$ref": "#/components/schemas/ContentForDocumentResponseModel" } ], "properties": { @@ -18715,6 +18718,9 @@ "type": "string", "format": "uuid", "nullable": true + }, + "isTrashed": { + "type": "boolean" } }, "additionalProperties": false @@ -18796,7 +18802,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/ContentTypeResponseModelBaseDocumentTypePropertyTypeResponseModelDocumentTypePropertyTypeContainerResponseModel" + "$ref": "#/components/schemas/ContentTypeForDocumentTypeResponseModel" } ], "properties": { @@ -19633,6 +19639,9 @@ "icon": { "type": "string", "nullable": true + }, + "isTrashed": { + "type": "boolean" } }, "additionalProperties": false @@ -19688,7 +19697,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/ContentTypeResponseModelBaseMediaTypePropertyTypeResponseModelMediaTypePropertyTypeContainerResponseModel" + "$ref": "#/components/schemas/ContentTypeForMediaTypeResponseModel" } ], "additionalProperties": false @@ -22148,7 +22157,7 @@ }, "additionalProperties": false }, - "UpdateContentRequestModelBaseDocumentValueModelDocumentVariantRequestModel": { + "UpdateContentForDocumentRequestModel": { "type": "object", "properties": { "values": { @@ -22174,7 +22183,7 @@ }, "additionalProperties": false }, - "UpdateContentRequestModelBaseMediaValueModelMediaVariantRequestModel": { + "UpdateContentForMediaRequestModel": { "type": "object", "properties": { "values": { @@ -22200,7 +22209,7 @@ }, "additionalProperties": false }, - "UpdateContentTypeRequestModelBaseUpdateDocumentTypePropertyTypeRequestModelUpdateDocumentTypePropertyTypeContainerRequestModel": { + "UpdateContentTypeForDocumentTypeRequestModel": { "type": "object", "properties": { "alias": { @@ -22271,7 +22280,7 @@ }, "additionalProperties": false }, - "UpdateContentTypeRequestModelBaseUpdateMediaTypePropertyTypeRequestModelUpdateMediaTypePropertyTypeContainerRequestModel": { + "UpdateContentTypeForMediaTypeRequestModel": { "type": "object", "properties": { "alias": { @@ -22376,7 +22385,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/UpdateContentRequestModelBaseDocumentValueModelDocumentVariantRequestModel" + "$ref": "#/components/schemas/UpdateContentForDocumentRequestModel" } ], "properties": { @@ -22410,7 +22419,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/UpdateContentTypeRequestModelBaseUpdateDocumentTypePropertyTypeRequestModelUpdateDocumentTypePropertyTypeContainerRequestModel" + "$ref": "#/components/schemas/UpdateContentTypeForDocumentTypeRequestModel" } ], "properties": { @@ -22467,7 +22476,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/UpdateContentRequestModelBaseMediaValueModelMediaVariantRequestModel" + "$ref": "#/components/schemas/UpdateContentForMediaRequestModel" } ], "additionalProperties": false @@ -22494,7 +22503,7 @@ "type": "object", "allOf": [ { - "$ref": "#/components/schemas/UpdateContentTypeRequestModelBaseUpdateMediaTypePropertyTypeRequestModelUpdateMediaTypePropertyTypeContainerRequestModel" + "$ref": "#/components/schemas/UpdateContentTypeForMediaTypeRequestModel" } ], "additionalProperties": false diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Document/CreateDocumentRequestModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Document/CreateDocumentRequestModel.cs index 20117bfb9d..7ba1420f25 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Document/CreateDocumentRequestModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Document/CreateDocumentRequestModel.cs @@ -1,7 +1,9 @@ -using Umbraco.Cms.Api.Management.ViewModels.Content; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.ViewModels.Content; namespace Umbraco.Cms.Api.Management.ViewModels.Document; +[ShortGenericSchemaName("CreateContentForDocumentRequestModel")] public class CreateDocumentRequestModel : CreateContentRequestModelBase { public Guid ContentTypeId { get; set; } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Document/DocumentResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Document/DocumentResponseModel.cs index ebf3fa385d..a453f7f11e 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Document/DocumentResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Document/DocumentResponseModel.cs @@ -1,7 +1,9 @@ -using Umbraco.Cms.Api.Management.ViewModels.Content; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.ViewModels.Content; namespace Umbraco.Cms.Api.Management.ViewModels.Document; +[ShortGenericSchemaName("ContentForDocumentResponseModel")] public class DocumentResponseModel : ContentResponseModelBase { public IEnumerable Urls { get; set; } = Array.Empty(); diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Document/UpdateDocumentRequestModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Document/UpdateDocumentRequestModel.cs index a0a8b5f6cf..cc1f0af1b2 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Document/UpdateDocumentRequestModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Document/UpdateDocumentRequestModel.cs @@ -1,7 +1,9 @@ -using Umbraco.Cms.Api.Management.ViewModels.Content; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.ViewModels.Content; namespace Umbraco.Cms.Api.Management.ViewModels.Document; +[ShortGenericSchemaName("UpdateContentForDocumentRequestModel")] public class UpdateDocumentRequestModel : UpdateContentRequestModelBase { public Guid? TemplateId { get; set; } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/CreateDocumentTypeRequestModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/CreateDocumentTypeRequestModel.cs index c970c229cc..0a83145e25 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/CreateDocumentTypeRequestModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/CreateDocumentTypeRequestModel.cs @@ -1,7 +1,9 @@ -using Umbraco.Cms.Api.Management.ViewModels.ContentType; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.ViewModels.ContentType; namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType; +[ShortGenericSchemaName("CreateContentTypeForDocumentTypeRequestModel")] public class CreateDocumentTypeRequestModel : CreateContentTypeRequestModelBase { diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/DocumentTypeResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/DocumentTypeResponseModel.cs index d9b41e5a2d..211679337f 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/DocumentTypeResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/DocumentTypeResponseModel.cs @@ -1,7 +1,9 @@ -using Umbraco.Cms.Api.Management.ViewModels.ContentType; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.ViewModels.ContentType; namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType; +[ShortGenericSchemaName("ContentTypeForDocumentTypeResponseModel")] public class DocumentTypeResponseModel : ContentTypeResponseModelBase { public IEnumerable AllowedTemplateIds { get; set; } = Array.Empty(); diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/UpdateDocumentTypeRequestModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/UpdateDocumentTypeRequestModel.cs index a33b3988bf..27650fd6d0 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/UpdateDocumentTypeRequestModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/DocumentType/UpdateDocumentTypeRequestModel.cs @@ -1,7 +1,9 @@ +using Umbraco.Cms.Api.Common.Attributes; using Umbraco.Cms.Api.Management.ViewModels.ContentType; namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType; +[ShortGenericSchemaName("UpdateContentTypeForDocumentTypeRequestModel")] public class UpdateDocumentTypeRequestModel : UpdateContentTypeRequestModelBase { diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Media/CreateMediaRequestModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Media/CreateMediaRequestModel.cs index fda09b05ca..6fafa870cc 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Media/CreateMediaRequestModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Media/CreateMediaRequestModel.cs @@ -1,7 +1,9 @@ -using Umbraco.Cms.Api.Management.ViewModels.Content; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.ViewModels.Content; namespace Umbraco.Cms.Api.Management.ViewModels.Media; +[ShortGenericSchemaName("CreateContentForMediaRequestModel")] public class CreateMediaRequestModel : CreateContentRequestModelBase { public Guid ContentTypeId { get; set; } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/Media/UpdateMediaRequestModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/Media/UpdateMediaRequestModel.cs index 54af0e7c0e..a0f5cc1dab 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/Media/UpdateMediaRequestModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/Media/UpdateMediaRequestModel.cs @@ -1,7 +1,9 @@ -using Umbraco.Cms.Api.Management.ViewModels.Content; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.ViewModels.Content; namespace Umbraco.Cms.Api.Management.ViewModels.Media; +[ShortGenericSchemaName("UpdateContentForMediaRequestModel")] public class UpdateMediaRequestModel : UpdateContentRequestModelBase { } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/CreateMediaTypeRequestModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/CreateMediaTypeRequestModel.cs index c32c239102..1862340e59 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/CreateMediaTypeRequestModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/CreateMediaTypeRequestModel.cs @@ -1,7 +1,9 @@ -using Umbraco.Cms.Api.Management.ViewModels.ContentType; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.ViewModels.ContentType; namespace Umbraco.Cms.Api.Management.ViewModels.MediaType; +[ShortGenericSchemaName("CreateContentTypeForMediaTypeRequestModel")] public class CreateMediaTypeRequestModel : CreateContentTypeRequestModelBase { diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/MediaTypeResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/MediaTypeResponseModel.cs index 4d0aca9516..748c73ac7f 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/MediaTypeResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/MediaTypeResponseModel.cs @@ -1,7 +1,9 @@ -using Umbraco.Cms.Api.Management.ViewModels.ContentType; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.ViewModels.ContentType; namespace Umbraco.Cms.Api.Management.ViewModels.MediaType; +[ShortGenericSchemaName("ContentTypeForMediaTypeResponseModel")] public class MediaTypeResponseModel : ContentTypeResponseModelBase { } diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/UpdateMediaTypeRequestModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/UpdateMediaTypeRequestModel.cs index acbbf66b0c..47292b8711 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/UpdateMediaTypeRequestModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/MediaType/UpdateMediaTypeRequestModel.cs @@ -1,7 +1,9 @@ -using Umbraco.Cms.Api.Management.ViewModels.ContentType; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.ViewModels.ContentType; namespace Umbraco.Cms.Api.Management.ViewModels.MediaType; +[ShortGenericSchemaName("UpdateContentTypeForMediaTypeRequestModel")] public class UpdateMediaTypeRequestModel : UpdateContentTypeRequestModelBase {