Browse Source

Add nullable property support to API modeling

Introduces IsNullable detection in ReflectionHelper and propagates it to PropertyApiDescriptionModel and TypeScript models. Updates logic to consider nullable properties when determining optionality, improving API schema accuracy for nullable reference types.
pull/24335/head
Fahri Gedik 2 months ago
parent
commit
a1a60d5fbb
  1. 20
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs
  2. 3
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs
  3. 1
      npm/ng-packs/packages/schematics/src/models/api-definition.ts
  4. 1
      npm/ng-packs/packages/schematics/src/utils/model.ts

20
framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs

@ -8,6 +8,26 @@ namespace Volo.Abp.Reflection;
//TODO: Consider to make internal
public static class ReflectionHelper
{
/// <summary>
/// Checks whether the property is nullable, including nullable reference types (NRT).
/// </summary>
/// <param name="propertyInfo">Property info to check</param>
public static bool IsNullable(PropertyInfo propertyInfo)
{
if (propertyInfo.PropertyType.IsValueType)
{
return false;
}
#if NET6_0_OR_GREATER
var nullabilityInfoContext = new System.Reflection.NullabilityInfoContext();
var nullabilityInfo = nullabilityInfoContext.Create(propertyInfo);
return nullabilityInfo.ReadState == System.Reflection.NullabilityState.Nullable;
#else
return false;
#endif
}
//TODO: Ehhance summary
/// <summary>
/// Checks whether <paramref name="givenType"/> implements/inherits <paramref name="genericType"/>.

3
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs

@ -29,6 +29,8 @@ public class PropertyApiDescriptionModel
public string? Regex { get; set; }
public bool IsNullable { get; set; }
public static PropertyApiDescriptionModel Create(PropertyInfo propertyInfo)
{
var customAttributes = propertyInfo.GetCustomAttributes(true);
@ -39,6 +41,7 @@ public class PropertyApiDescriptionModel
Type = ApiTypeNameHelper.GetTypeName(propertyInfo.PropertyType),
TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(propertyInfo.PropertyType),
IsRequired = customAttributes.OfType<RequiredAttribute>().Any() || propertyInfo.GetCustomAttributesData().Any(attr => attr.AttributeType.Name == "RequiredMemberAttribute"),
IsNullable = Volo.Abp.Reflection.ReflectionHelper.IsNullable(propertyInfo),
Minimum = customAttributes.OfType<RangeAttribute>().Select(x => x.Minimum).FirstOrDefault()?.ToString(),
Maximum = customAttributes.OfType<RangeAttribute>().Select(x => x.Maximum).FirstOrDefault()?.ToString(),
MinLength = customAttributes.OfType<MinLengthAttribute>().FirstOrDefault()?.Length ?? customAttributes.OfType<StringLengthAttribute>().FirstOrDefault()?.MinimumLength,

1
npm/ng-packs/packages/schematics/src/models/api-definition.ts

@ -20,6 +20,7 @@ export interface PropertyDef {
type: string;
typeSimple: string;
isRequired: boolean;
isNullable: boolean;
}
export interface Module {

1
npm/ng-packs/packages/schematics/src/utils/model.ts

@ -187,6 +187,7 @@ export function createRefToImportReducerCreator(params: ModelGeneratorParams) {
function isOptionalProperty(prop: PropertyDef) {
return (
prop.isNullable ||
prop.typeSimple.endsWith('?') ||
((prop.typeSimple === 'string' || prop.typeSimple.includes('enum')) && !prop.isRequired)
);

Loading…
Cancel
Save