Skip to content

Commit 6e72faa

Browse files
committed
#1397 Add polymorphism support to generated OpenAPI json and UI:
- Moved polymorhic classes declaration to module manifest, e.g.: <openAPIPolymorphicTypes> <assemblyQualifiedTypeName>VirtoCommerce.Domain.Cart.Model.LineItem, VirtoCommerce.Domain</assemblyQualifiedTypeName> </openAPIPolymorphicTypes> Need to use assembly qualified names here; - Removed attribute that marks class polymorphic for swagger; - All polymorhic classes should have "Type" property to store type information - Type.Name (or Type.FullName, if UseFullTypeNameInSwagger is set to true for module);
1 parent 9cbdf1a commit 6e72faa

File tree

7 files changed

+58
-38
lines changed

7 files changed

+58
-38
lines changed

VirtoCommerce.Platform.Core/Common/PolymorphicBaseClassAttribute.cs

-16
This file was deleted.

VirtoCommerce.Platform.Core/Modularity/ManifestModuleInfo.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using System.Linq;
33
using VirtoCommerce.Platform.Core.Common;
44

@@ -62,11 +62,13 @@ public ManifestModuleInfo(ModuleManifest manifest)
6262
Groups.AddRange(manifest.Groups);
6363
}
6464
Incompatibilities = new List<ModuleIdentity>();
65-
if(manifest.Incompatibilities != null)
65+
if (manifest.Incompatibilities != null)
6666
{
6767
Incompatibilities.AddRange(manifest.Incompatibilities.Select(x => new ModuleIdentity(x.Id, x.Version)));
6868
}
6969

70+
OpenAPIPolymorphicTypes = manifest.OpenAPIPolymorphicTypes?.Select(x => x.Trim()) ?? Enumerable.Empty<string>();
71+
7072
InitializationMode = InitializationMode.OnDemand;
7173
}
7274

@@ -101,6 +103,7 @@ public ManifestModuleInfo(ModuleManifest manifest)
101103
public ICollection<ModuleSettingsGroup> Settings { get; private set; }
102104
public ICollection<string> Errors { get; set; }
103105
public bool UseFullTypeNameInSwagger { get; set; }
106+
public IEnumerable<string> OpenAPIPolymorphicTypes { get; private set; }
104107

105108
public override string ToString()
106109
{

VirtoCommerce.Platform.Core/Modularity/ModuleManifest.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Xml.Serialization;
1+
using System.Xml.Serialization;
22

33
namespace VirtoCommerce.Platform.Core.Modularity
44
{
@@ -88,5 +88,9 @@ public class ModuleManifest
8888

8989
[XmlElement("useFullTypeNameInSwagger")]
9090
public bool UseFullTypeNameInSwagger { get; set; }
91+
92+
[XmlArray("openAPIPolymorphicTypes")]
93+
[XmlArrayItem("assemblyQualifiedTypeName")]
94+
public string[] OpenAPIPolymorphicTypes { get; set; }
9195
}
9296
}

VirtoCommerce.Platform.Core/VirtoCommerce.Platform.Core.csproj

-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
<Compile Include="Common\IValueObject.cs" />
7979
<Compile Include="Common\ObservableChangeTracker.cs" />
8080
<Compile Include="Common\PlatformVersion.cs" />
81-
<Compile Include="Common\PolymorphicBaseClassAttribute.cs" />
8281
<Compile Include="Common\PredicateBuilder.cs" />
8382
<Compile Include="Common\PrimaryKeyResolvingMap.cs" />
8483
<Compile Include="Common\SemanticVersion.cs" />

VirtoCommerce.Platform.Web/Swagger/PolymorphismDocumentFilter.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using Swashbuckle.Swagger;
5-
using VirtoCommerce.Platform.Core.Common;
65

76
namespace VirtoCommerce.Platform.Web.Swagger
87
{
@@ -26,7 +25,7 @@ public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, Sys
2625

2726
private static void RegisterSubClasses(SchemaRegistry schemaRegistry, Type abstractType)
2827
{
29-
var discriminatorName = abstractType.GetAttributeValue<PolymorphicBaseClassAttribute, string>(x => x.DiscriminatorPropertyName) ?? "type";
28+
var discriminatorName = "type";
3029

3130
// Need to make first property character lower to avoid properties duplication because of case, as all properties in OpenApi spec are in camelCase
3231
discriminatorName = char.ToLowerInvariant(discriminatorName[0]) + discriminatorName.Substring(1);

VirtoCommerce.Platform.Web/Swagger/PolymorphismSchemaFilter.cs

+8-6
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ private HashSet<Type> Init()
2121
{
2222
var result = new HashSet<Type>();
2323

24-
var derivedTypes = _types.SelectMany(x =>
25-
x.Assembly
26-
.GetTypes()
27-
.Where(y => x != y && y.IsAssignableFrom(x)));
24+
var derivedTypes = _types.SelectMany(baseType =>
25+
baseType.Assembly
26+
.GetTypes()
27+
.Where(derivedType => baseType != derivedType && baseType.IsAssignableFrom(derivedType)));
2828

2929
foreach (var item in derivedTypes)
3030
{
@@ -46,8 +46,10 @@ public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
4646
required = schema.required
4747
};
4848

49-
//schemaRegistry.Definitions[typeof(T).Name]; does not work correctly in SwashBuckle
50-
var parentSchema = new Schema { @ref = "#/definitions/" + type.BaseType.Name };
49+
var baseType = type.BaseType;
50+
var baseTypeName = schemaRegistry.Definitions.ContainsKey(baseType.FullName) ? baseType.FullName : baseType.FriendlyId();
51+
52+
var parentSchema = new Schema { @ref = "#/definitions/" + baseTypeName };
5153

5254
schema.allOf = new List<Schema> { parentSchema, clonedSchema };
5355

VirtoCommerce.Platform.Web/Swagger/SwaggerConfig.cs

+39-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
using VirtoCommerce.Platform.Core.Common;
1515
using VirtoCommerce.Platform.Core.Modularity;
1616
using VirtoCommerce.Platform.Core.Settings;
17-
using VirtoCommerce.Platform.Data.Common;
1817

1918
namespace VirtoCommerce.Platform.Web.Swagger
2019
{
@@ -34,16 +33,22 @@ public static void RegisterRoutes(IUnityContainer container)
3433
var xmlCommentsFilePaths = xmlCommentsDirectoryPaths.SelectMany(GetXmlFilesPaths).ToArray();
3534

3635
// Add separate swagger generator for platform
37-
EnableSwagger("VirtoCommerce.Platform", httpConfiguration, container, routePrefix, xmlCommentsFilePaths, false, Assembly.GetExecutingAssembly());
36+
EnableSwagger("VirtoCommerce.Platform", httpConfiguration, container, routePrefix, xmlCommentsFilePaths, false, Assembly.GetExecutingAssembly(), null);
3837

3938
// Add separate swagger generator for each installed module
4039
var allmodules = container.Resolve<IModuleCatalog>().Modules.OfType<ManifestModuleInfo>().Where(m => m.ModuleInstance != null);
4140

41+
ICollection<Type> allPolymorphicTypes = new List<Type>();
42+
43+
// Add separate swagger generator for each installed module
4244
foreach (var module in allmodules)
4345
{
44-
EnableSwagger(module.ModuleName, httpConfiguration, container, routePrefix, xmlCommentsFilePaths, module.UseFullTypeNameInSwagger, module.ModuleInstance.GetType().Assembly);
46+
var polimorphicTypes = GetPolymorphicTypes(module);
47+
48+
allPolymorphicTypes.AddRange(polimorphicTypes);
49+
50+
EnableSwagger(module.ModuleName, httpConfiguration, container, routePrefix, xmlCommentsFilePaths, module.UseFullTypeNameInSwagger, module.ModuleInstance.GetType().Assembly, polimorphicTypes);
4551
}
46-
var allModuleAssemblies = allmodules.Select(x => x.ModuleInstance.GetType().Assembly).Concat(new[] { Assembly.GetExecutingAssembly() });
4752

4853
// Add full swagger generator
4954
httpConfiguration.EnableSwagger(routePrefix + "docs/{apiVersion}", c =>
@@ -67,7 +72,7 @@ public static void RegisterRoutes(IUnityContainer container)
6772
.Replace(',', '-')
6873
);
6974

70-
AddProlymorphicFilters(c, allModuleAssemblies);
75+
AddProlymorphicFilters(c, allPolymorphicTypes.ToArray());
7176

7277
ApplyCommonSwaggerConfiguration(c, container, string.Empty, xmlCommentsFilePaths);
7378
})
@@ -86,7 +91,32 @@ public static void RegisterRoutes(IUnityContainer container)
8691
});
8792
}
8893

89-
private static void EnableSwagger(string moduleName, HttpConfiguration httpConfiguration, IUnityContainer container, string routePrefix, string[] xmlCommentsFilePaths, bool useFullTypeNameInSchemaIds, Assembly apiAssembly)
94+
private static Type[] GetPolymorphicTypes(ManifestModuleInfo module)
95+
{
96+
//TODO: Could move type loading to module loading mechanism with its error handling
97+
Type[] polimorphicTypes = null;
98+
try
99+
{
100+
polimorphicTypes = module.OpenAPIPolymorphicTypes
101+
.Select(x => Type.GetType(x, false))
102+
.Where(x => x != null)
103+
.ToArray();
104+
}
105+
// Need to add catch as even with GetType throwOnError = false it could throw
106+
catch { }
107+
108+
return polimorphicTypes;
109+
}
110+
111+
private static void EnableSwagger(
112+
string moduleName,
113+
HttpConfiguration httpConfiguration,
114+
IUnityContainer container,
115+
string routePrefix,
116+
string[] xmlCommentsFilePaths,
117+
bool useFullTypeNameInSchemaIds,
118+
Assembly apiAssembly,
119+
Type[] polymorphicTypes)
90120
{
91121
var routeName = string.Concat("swagger_", moduleName);
92122
var routeTemplate = string.Concat(routePrefix, "docs/", moduleName, "/{apiVersion}");
@@ -106,14 +136,13 @@ private static void EnableSwagger(string moduleName, HttpConfiguration httpConfi
106136
ApplyCommonSwaggerConfiguration(c, container, moduleName, xmlCommentsFilePaths);
107137
c.OperationFilter(() => new ModuleTagsFilter(moduleName));
108138

109-
AddProlymorphicFilters(c, new[] { apiAssembly });
139+
AddProlymorphicFilters(c, polymorphicTypes);
110140
});
111141
}
112142

113-
private static void AddProlymorphicFilters(SwaggerDocsConfig swaggerDocsConfig, IEnumerable<Assembly> assemblies)
143+
private static void AddProlymorphicFilters(SwaggerDocsConfig swaggerDocsConfig, Type[] polymorphicTypes)
114144
{
115-
var polymorphicTypes = assemblies.SelectMany(x => x.GetTypesWithAttribute(typeof(PolymorphicBaseClassAttribute), false)).ToArray();
116-
if (polymorphicTypes.Any())
145+
if (polymorphicTypes?.Any() == true)
117146
{
118147
swaggerDocsConfig.DocumentFilter(() => new PolymorphismDocumentFilter(polymorphicTypes));
119148
swaggerDocsConfig.SchemaFilter(() => new PolymorphismSchemaFilter(polymorphicTypes));

0 commit comments

Comments
 (0)