Swagger: Don't use allOf for inheritance + fix polymorphic models (#15985)

* Swagger: Don't use allOf for inheritance + fix polymorphic models

* Cannot mix $ref and nullable in OpenAPI

* Update OpenApi.json

* Regenerate OpenApi.json
This commit is contained in:
Kenn Jacobsen
2024-04-09 10:20:20 +02:00
committed by GitHub
parent 91085d9b21
commit de1fc65393
31 changed files with 10844 additions and 4742 deletions

View File

@@ -1,22 +0,0 @@
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) })
{
}
}

View File

@@ -39,45 +39,7 @@ public class SchemaIdSelector : ISchemaIdSelector
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);
// We need FirstOrDefault() because through inheritance, the attribute can be on several classes implementing a base
var matchingType = assignableTypesWithAttributeInfo
.FirstOrDefault(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;
}
return $"{name}{string.Join(string.Empty, type.GenericTypeArguments.Select(SanitizedTypeName))}";
}
}

View File

@@ -11,12 +11,18 @@ public sealed class UmbracoJsonTypeInfoResolver : DefaultJsonTypeInfoResolver, I
private readonly ConcurrentDictionary<Type, ISet<Type>> _subTypesCache = new ConcurrentDictionary<Type, ISet<Type>>();
public UmbracoJsonTypeInfoResolver(ITypeFinder typeFinder)
{
_typeFinder = typeFinder;
}
=> _typeFinder = typeFinder;
public IEnumerable<Type> FindSubTypes(Type type)
{
if (type.IsInterface is false)
{
// IMPORTANT: do NOT return an empty enumerable here. it will cause nullability to fail on reference
// properties, because "$ref" does not mix and match well with "nullable" in OpenAPI.
// see also https://github.com/OAI/OpenAPI-Specification/issues/1368
return new[] { type };
}
if (_subTypesCache.TryGetValue(type, out ISet<Type>? cachedResult))
{
return cachedResult;
@@ -30,32 +36,9 @@ public sealed class UmbracoJsonTypeInfoResolver : DefaultJsonTypeInfoResolver, I
public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
{
JsonTypeInfo result = base.GetTypeInfo(type, options);
if (type.IsInterface)
{
return GetTypeInfoForInterface(result, type, options);
}
else
{
return GetTypeInfoForClass(result, type, options);
}
}
private JsonTypeInfo GetTypeInfoForClass(JsonTypeInfo result, Type type, JsonSerializerOptions options)
{
if (result.Kind != JsonTypeInfoKind.Object || !type.GetInterfaces().Any())
{
return result;
}
JsonPolymorphismOptions jsonPolymorphismOptions = result.PolymorphismOptions ?? new JsonPolymorphismOptions();
jsonPolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(type, type.Name));
result.PolymorphismOptions = jsonPolymorphismOptions;
return result;
return type.IsInterface
? GetTypeInfoForInterface(result, type, options)
: result;
}
private JsonTypeInfo GetTypeInfoForInterface(JsonTypeInfo result, Type type, JsonSerializerOptions options)

View File

@@ -32,7 +32,6 @@ public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions<
swaggerGenOptions.OperationFilter<ResponseHeaderOperationFilter>();
swaggerGenOptions.SelectSubTypesUsing(_umbracoJsonTypeInfoResolver.FindSubTypes);
swaggerGenOptions.UseOneOfForPolymorphism();
swaggerGenOptions.UseAllOfForInheritance();
// Ensure all types that implements the IOpenApiDiscriminator have a $type property in the OpenApi schema with the default value (The class name) that is expected by the server
swaggerGenOptions.SelectDiscriminatorNameUsing(type => typeof(IOpenApiDiscriminator).IsAssignableFrom(type) ? "$type" : null);

View File

@@ -28,6 +28,7 @@ public class UpdateDomainsController : DocumentControllerBase
[MapToApiVersion("1.0")]
[HttpPut("{id:guid}/domains")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)]

View File

@@ -35,7 +35,7 @@ public class RebuildIndexerController : IndexerControllerBase
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)]
[ProducesResponseType(typeof(OkResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> Rebuild(CancellationToken cancellationToken, string indexName)
{
if (!_examineManager.TryGetIndex(indexName, out var index))

View File

@@ -18,18 +18,15 @@ public class CreateUserController : UserControllerBase
{
private readonly IUserService _userService;
private readonly IUserPresentationFactory _presentationFactory;
private readonly IUmbracoMapper _mapper;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
public CreateUserController(
IUserService userService,
IUserPresentationFactory presentationFactory,
IUmbracoMapper mapper,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_userService = userService;
_presentationFactory = presentationFactory;
_mapper = mapper;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}

View File

@@ -11,9 +11,9 @@ public class InstallerViewModelsMapDefinition : IMapDefinition
public void DefineMaps(IUmbracoMapper mapper)
{
mapper.Define<InstallRequestModel, InstallData>((source, context) => new InstallData(), Map);
mapper.Define<UserInstallPresentationModel, UserInstallData>((source, context) => new UserInstallData(), Map);
mapper.Define<DatabaseInstallPresentationModel, DatabaseInstallData>((source, context) => new DatabaseInstallData(), Map);
mapper.Define<DatabaseInstallPresentationModel, DatabaseModel>((source, context) => new DatabaseModel(), Map);
mapper.Define<UserInstallRequestModel, UserInstallData>((source, context) => new UserInstallData(), Map);
mapper.Define<DatabaseInstallRequestModel, DatabaseInstallData>((source, context) => new DatabaseInstallData(), Map);
mapper.Define<DatabaseInstallRequestModel, DatabaseModel>((source, context) => new DatabaseModel(), Map);
mapper.Define<DatabaseInstallData, DatabaseModel>((source, context) => new DatabaseModel(), Map);
mapper.Define<InstallSettingsModel, InstallSettingsResponseModel>((source, context) => new InstallSettingsResponseModel(), Map);
mapper.Define<UserSettingsModel, UserSettingsPresentationModel>((source, context) => new UserSettingsPresentationModel(), Map);
@@ -33,7 +33,7 @@ public class InstallerViewModelsMapDefinition : IMapDefinition
}
// Umbraco.Code.MapAll
private void Map(DatabaseInstallPresentationModel source, DatabaseModel target, MapperContext context)
private void Map(DatabaseInstallRequestModel source, DatabaseModel target, MapperContext context)
{
target.ConnectionString = source.ConnectionString;
target.DatabaseName = source.Name ?? string.Empty;
@@ -55,7 +55,7 @@ public class InstallerViewModelsMapDefinition : IMapDefinition
}
// Umbraco.Code.MapAll
private static void Map(UserInstallPresentationModel source, UserInstallData target, MapperContext context)
private static void Map(UserInstallRequestModel source, UserInstallData target, MapperContext context)
{
target.Email = source.Email;
target.Name = source.Name;
@@ -64,7 +64,7 @@ public class InstallerViewModelsMapDefinition : IMapDefinition
}
// Umbraco.Code.MapAll
private static void Map(DatabaseInstallPresentationModel source, DatabaseInstallData target, MapperContext context)
private static void Map(DatabaseInstallRequestModel source, DatabaseInstallData target, MapperContext context)
{
target.Id = source.Id;
target.ProviderName = source.ProviderName;

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,8 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Core.Models.ContentEditing;
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
[ShortGenericSchemaName<DocumentValueModel, DocumentVariantRequestModel>("CreateContentForDocumentRequestModel")]
public abstract class CreateDocumentRequestModelBase<TValueModel, TVariantModel>
: CreateContentWithParentRequestModelBase<TValueModel, TVariantModel>
where TValueModel : ValueModelBase

View File

@@ -1,11 +1,9 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
using Umbraco.Cms.Core.Models.ContentEditing;
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
[ShortGenericSchemaName<DocumentValueModel, DocumentVariantResponseModel>("ContentForDocumentResponseModel")]
public abstract class DocumentResponseModelBase<TValueResponseModelBase, TVariantResponseModel>
: ContentResponseModelBase<TValueResponseModelBase, TVariantResponseModel>
where TValueResponseModelBase : ValueModelBase

View File

@@ -1,10 +1,8 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Core.Models.ContentEditing;
namespace Umbraco.Cms.Api.Management.ViewModels.Document;
[ShortGenericSchemaName<DocumentValueModel, DocumentVariantRequestModel>("UpdateContentForDocumentRequestModel")]
public abstract class UpdateDocumentRequestModelBase<TValueModel, TVariantModel> : UpdateContentRequestModelBase<TValueModel, TVariantModel>
where TValueModel : ValueModelBase
where TVariantModel : VariantModelBase

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
namespace Umbraco.Cms.Api.Management.ViewModels.DocumentType;
[ShortGenericSchemaName<CreateDocumentTypePropertyTypeRequestModel, CreateDocumentTypePropertyTypeContainerRequestModel>("CreateContentTypeForDocumentTypeRequestModel")]
public class CreateDocumentTypeRequestModel
: CreateContentTypeWithParentRequestModelBase<CreateDocumentTypePropertyTypeRequestModel, CreateDocumentTypePropertyTypeContainerRequestModel>
{

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
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<ReferenceByIdModel> AllowedTemplates { get; set; } = Enumerable.Empty<ReferenceByIdModel>();

View File

@@ -1,9 +1,7 @@
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>
{

View File

@@ -1,28 +0,0 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Umbraco.Cms.Api.Management.ViewModels.Installer;
public class DatabaseInstallPresentationModel
{
[Required]
public Guid Id { get; set; }
[Required]
public string? ProviderName { get; set; }
public string? Server { get; set; }
public string? Name { get; set; }
public string? Username { get; set; }
[PasswordPropertyText]
public string? Password { get; set; }
public bool UseIntegratedAuthentication { get; set; }
public string? ConnectionString { get; set; }
public bool TrustServerCertificate { get; set; }
}

View File

@@ -1,6 +1,28 @@
namespace Umbraco.Cms.Api.Management.ViewModels.Installer;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
public class DatabaseInstallRequestModel : DatabaseInstallPresentationModel
namespace Umbraco.Cms.Api.Management.ViewModels.Installer;
public class DatabaseInstallRequestModel
{
[Required]
public Guid Id { get; set; }
[Required]
public string? ProviderName { get; set; }
public string? Server { get; set; }
public string? Name { get; set; }
public string? Username { get; set; }
[PasswordPropertyText]
public string? Password { get; set; }
public bool UseIntegratedAuthentication { get; set; }
public string? ConnectionString { get; set; }
public bool TrustServerCertificate { get; set; }
}

View File

@@ -7,10 +7,10 @@ namespace Umbraco.Cms.Api.Management.ViewModels.Installer;
public class InstallRequestModel
{
[Required]
public UserInstallPresentationModel User { get; set; } = null!;
public UserInstallRequestModel User { get; set; } = null!;
[Required]
public DatabaseInstallPresentationModel Database { get; set; } = null!;
public DatabaseInstallRequestModel Database { get; set; } = null!;
[JsonConverter(typeof(JsonStringEnumConverter))]
public TelemetryLevel TelemetryLevel { get; set; } = TelemetryLevel.Basic;

View File

@@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations;
namespace Umbraco.Cms.Api.Management.ViewModels.Installer;
public class UserInstallPresentationModel
public class UserInstallRequestModel
{
[Required]
[StringLength(255)]

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Api.Management.ViewModels.Content;
namespace Umbraco.Cms.Api.Management.ViewModels.Media;
[ShortGenericSchemaName<MediaValueModel, MediaVariantRequestModel>("CreateContentForMediaRequestModel")]
public class CreateMediaRequestModel : CreateContentWithParentRequestModelBase<MediaValueModel, MediaVariantRequestModel>
{
public required ReferenceByIdModel MediaType { get; set; }

View File

@@ -1,10 +1,8 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Api.Management.ViewModels.MediaType;
namespace Umbraco.Cms.Api.Management.ViewModels.Media;
[ShortGenericSchemaName<MediaValueModel, MediaVariantResponseModel>("ContentForMediaResponseModel")]
public class MediaResponseModel : ContentResponseModelBase<MediaValueModel, MediaVariantResponseModel>
{
public IEnumerable<MediaUrlInfo> Urls { get; set; } = Enumerable.Empty<MediaUrlInfo>();

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Api.Management.ViewModels.Content;
namespace Umbraco.Cms.Api.Management.ViewModels.Media;
[ShortGenericSchemaName<MediaValueModel, MediaVariantRequestModel>("UpdateContentForMediaRequestModel")]
public class UpdateMediaRequestModel : UpdateContentRequestModelBase<MediaValueModel, MediaVariantRequestModel>
{
}

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
namespace Umbraco.Cms.Api.Management.ViewModels.MediaType;
[ShortGenericSchemaName<CreateMediaTypePropertyTypeRequestModel, CreateMediaTypePropertyTypeContainerRequestModel>("CreateContentTypeForMediaTypeRequestModel")]
public class CreateMediaTypeRequestModel
: CreateContentTypeWithParentRequestModelBase<CreateMediaTypePropertyTypeRequestModel, CreateMediaTypePropertyTypeContainerRequestModel>
{

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
namespace Umbraco.Cms.Api.Management.ViewModels.MediaType;
[ShortGenericSchemaName<MediaTypePropertyTypeResponseModel, MediaTypePropertyTypeContainerResponseModel>("ContentTypeForMediaTypeResponseModel")]
public class MediaTypeResponseModel : ContentTypeResponseModelBase<MediaTypePropertyTypeResponseModel, MediaTypePropertyTypeContainerResponseModel>
{
public IEnumerable<MediaTypeSort> AllowedMediaTypes { get; set; } = Enumerable.Empty<MediaTypeSort>();

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
namespace Umbraco.Cms.Api.Management.ViewModels.MediaType;
[ShortGenericSchemaName<UpdateMediaTypePropertyTypeRequestModel, UpdateMediaTypePropertyTypeContainerRequestModel>("UpdateContentTypeForMediaTypeRequestModel")]
public class UpdateMediaTypeRequestModel
: UpdateContentTypeRequestModelBase<UpdateMediaTypePropertyTypeRequestModel, UpdateMediaTypePropertyTypeContainerRequestModel>
{

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Api.Management.ViewModels.Content;
namespace Umbraco.Cms.Api.Management.ViewModels.Member;
[ShortGenericSchemaName<MemberValueModel, MemberVariantRequestModel>("CreateContentForMemberRequestModel")]
public class CreateMemberRequestModel : CreateContentRequestModelBase<MemberValueModel, MemberVariantRequestModel>
{
public string Email { get; set; } = string.Empty;

View File

@@ -1,10 +1,8 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Api.Management.ViewModels.MemberType;
namespace Umbraco.Cms.Api.Management.ViewModels.Member;
[ShortGenericSchemaName<MemberValueModel, MemberVariantResponseModel>("ContentForMemberResponseModel")]
public class MemberResponseModel : ContentResponseModelBase<MemberValueModel, MemberVariantResponseModel>
{
public string Email { get; set; } = string.Empty;

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.Content;
using Umbraco.Cms.Api.Management.ViewModels.Content;
namespace Umbraco.Cms.Api.Management.ViewModels.Member;
[ShortGenericSchemaName<MemberValueModel, MemberVariantRequestModel>("UpdateContentForMemberRequestModel")]
public class UpdateMemberRequestModel : UpdateContentRequestModelBase<MemberValueModel, MemberVariantRequestModel>
{
public string Email { get; set; } = string.Empty;

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
namespace Umbraco.Cms.Api.Management.ViewModels.MemberType;
[ShortGenericSchemaName<CreateMemberTypePropertyTypeRequestModel, CreateMemberTypePropertyTypeContainerRequestModel>("CreateContentTypeForMemberTypeRequestModel")]
public class CreateMemberTypeRequestModel
: CreateContentTypeRequestModelBase<CreateMemberTypePropertyTypeRequestModel, CreateMemberTypePropertyTypeContainerRequestModel>
{

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
namespace Umbraco.Cms.Api.Management.ViewModels.MemberType;
[ShortGenericSchemaName<MemberTypePropertyTypeResponseModel, MemberTypePropertyTypeContainerResponseModel>("ContentTypeForMemberTypeResponseModel")]
public class MemberTypeResponseModel : ContentTypeResponseModelBase<MemberTypePropertyTypeResponseModel, MemberTypePropertyTypeContainerResponseModel>
{
public IEnumerable<MemberTypeComposition> Compositions { get; set; } = Enumerable.Empty<MemberTypeComposition>();

View File

@@ -1,9 +1,7 @@
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
using Umbraco.Cms.Api.Management.ViewModels.ContentType;
namespace Umbraco.Cms.Api.Management.ViewModels.MemberType;
[ShortGenericSchemaName<UpdateMemberTypePropertyTypeRequestModel, UpdateMemberTypePropertyTypeContainerRequestModel>("UpdateContentTypeForMemberTypeRequestModel")]
public class UpdateMemberTypeRequestModel
: UpdateContentTypeRequestModelBase<UpdateMemberTypePropertyTypeRequestModel, UpdateMemberTypePropertyTypeContainerRequestModel>
{