V14/feature/custom generic swagger (#15025)
Co-authored-by: Sven Geusens <sge@umbraco.dk>
This commit is contained in:
@@ -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<T1, T2> : ShortGenericSchemaNameAttribute
|
||||
{
|
||||
public ShortGenericSchemaNameAttribute(string schemaName)
|
||||
: base(schemaName, new[] { typeof(T1), typeof(T2) })
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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<RelationItemViewModel>" 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<RelationItemViewModel>" 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<DocumentValueModel, DocumentVariantRequestModel>("CreateContentForDocumentRequestModel")]
|
||||
public class CreateDocumentRequestModel : CreateContentRequestModelBase<DocumentValueModel, DocumentVariantRequestModel>
|
||||
{
|
||||
public Guid ContentTypeId { get; set; }
|
||||
|
||||
@@ -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<DocumentValueModel, DocumentVariantResponseModel>("ContentForDocumentResponseModel")]
|
||||
public class DocumentResponseModel : ContentResponseModelBase<DocumentValueModel, DocumentVariantResponseModel>
|
||||
{
|
||||
public IEnumerable<ContentUrlInfo> Urls { get; set; } = Array.Empty<ContentUrlInfo>();
|
||||
|
||||
@@ -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<DocumentValueModel, DocumentVariantRequestModel>("UpdateContentForDocumentRequestModel")]
|
||||
public class UpdateDocumentRequestModel : UpdateContentRequestModelBase<DocumentValueModel, DocumentVariantRequestModel>
|
||||
{
|
||||
public Guid? TemplateId { get; set; }
|
||||
|
||||
@@ -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<CreateDocumentTypePropertyTypeRequestModel, CreateDocumentTypePropertyTypeContainerRequestModel>("CreateContentTypeForDocumentTypeRequestModel")]
|
||||
public class CreateDocumentTypeRequestModel
|
||||
: CreateContentTypeRequestModelBase<CreateDocumentTypePropertyTypeRequestModel, CreateDocumentTypePropertyTypeContainerRequestModel>
|
||||
{
|
||||
|
||||
@@ -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<DocumentTypePropertyTypeResponseModel, DocumentTypePropertyTypeContainerResponseModel>("ContentTypeForDocumentTypeResponseModel")]
|
||||
public class DocumentTypeResponseModel : ContentTypeResponseModelBase<DocumentTypePropertyTypeResponseModel, DocumentTypePropertyTypeContainerResponseModel>
|
||||
{
|
||||
public IEnumerable<Guid> AllowedTemplateIds { get; set; } = Array.Empty<Guid>();
|
||||
|
||||
@@ -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<UpdateDocumentTypePropertyTypeRequestModel, UpdateDocumentTypePropertyTypeContainerRequestModel>("UpdateContentTypeForDocumentTypeRequestModel")]
|
||||
public class UpdateDocumentTypeRequestModel
|
||||
: UpdateContentTypeRequestModelBase<UpdateDocumentTypePropertyTypeRequestModel, UpdateDocumentTypePropertyTypeContainerRequestModel>
|
||||
{
|
||||
|
||||
@@ -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<MediaValueModel, MediaVariantRequestModel>("CreateContentForMediaRequestModel")]
|
||||
public class CreateMediaRequestModel : CreateContentRequestModelBase<MediaValueModel, MediaVariantRequestModel>
|
||||
{
|
||||
public Guid ContentTypeId { get; set; }
|
||||
|
||||
@@ -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<MediaValueModel, MediaVariantRequestModel>("UpdateContentForMediaRequestModel")]
|
||||
public class UpdateMediaRequestModel : UpdateContentRequestModelBase<MediaValueModel, MediaVariantRequestModel>
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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<CreateMediaTypePropertyTypeRequestModel, CreateMediaTypePropertyTypeContainerRequestModel>("CreateContentTypeForMediaTypeRequestModel")]
|
||||
public class CreateMediaTypeRequestModel
|
||||
: CreateContentTypeRequestModelBase<CreateMediaTypePropertyTypeRequestModel, CreateMediaTypePropertyTypeContainerRequestModel>
|
||||
{
|
||||
|
||||
@@ -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<MediaTypePropertyTypeResponseModel, MediaTypePropertyTypeContainerResponseModel>("ContentTypeForMediaTypeResponseModel")]
|
||||
public class MediaTypeResponseModel : ContentTypeResponseModelBase<MediaTypePropertyTypeResponseModel, MediaTypePropertyTypeContainerResponseModel>
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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<UpdateMediaTypePropertyTypeRequestModel, UpdateMediaTypePropertyTypeContainerRequestModel>("UpdateContentTypeForMediaTypeRequestModel")]
|
||||
public class UpdateMediaTypeRequestModel
|
||||
: UpdateContentTypeRequestModelBase<UpdateMediaTypePropertyTypeRequestModel, UpdateMediaTypePropertyTypeContainerRequestModel>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user