From e019bcf28573ce2220c77ad056f593be7440cbba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:13:48 +0000 Subject: [PATCH 1/3] Initial plan From 9a2e10e151ffea25485445ef9b96c14f00edc107 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:22:38 +0000 Subject: [PATCH 2/3] fix: Only mark properties with 'required' modifier as required in OpenAPI schema Properties with default initializers (like ExtraData = new()) are now correctly treated as optional unless they have the explicit 'required' modifier. Co-authored-by: niemyjski <1020579+niemyjski@users.noreply.github.com> --- .../RequiredPropertySchemaTransformer.cs | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs b/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs index 13fa0bd7e7..3c4b163ae9 100644 --- a/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs +++ b/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs @@ -5,23 +5,24 @@ namespace Exceptionless.Web.Utility.OpenApi; /// -/// Schema transformer that marks non-nullable properties as required in OpenAPI schemas. +/// Schema transformer that marks required properties in OpenAPI schemas based on C# required modifiers and value types. /// /// /// /// Why this exists: Microsoft.AspNetCore.OpenApi doesn't consistently detect -/// required properties from C# nullability annotations. This causes all properties to -/// become optional in generated schemas, even when they're non-nullable in the C# model. +/// required properties from C# modifiers. This transformer ensures properties are marked as required +/// when appropriate for the OpenAPI schema. /// /// -/// This transformer inspects C# nullability context and marks properties as required when: +/// This transformer marks properties as required when: /// +/// The property has the required modifier (C# 11+) /// The property type is a non-nullable value type (e.g., int, bool, DateTime) -/// The property type is a non-nullable reference type in a nullable-enabled context /// /// /// -/// The [Required] attribute is also respected for explicit marking. +/// Non-nullable reference types are NOT marked as required unless they have the explicit required modifier. +/// This correctly handles properties with default initializers (e.g., public MyClass Prop { get; init; } = new();). /// /// /// This transformer resolves property names using the effective JSON property name @@ -82,6 +83,12 @@ private static bool IsPropertyRequired(PropertyInfo property, NullabilityInfoCon { var propertyType = property.PropertyType; + // Check if the property is marked with the 'required' modifier (C# 11+) + // This takes precedence over other heuristics + var requiredMemberAttribute = property.GetCustomAttribute(); + if (requiredMemberAttribute is not null) + return true; + // Non-nullable value types are always required (except when wrapped in Nullable) if (propertyType.IsValueType) { @@ -89,16 +96,13 @@ private static bool IsPropertyRequired(PropertyInfo property, NullabilityInfoCon return Nullable.GetUnderlyingType(propertyType) is null; } - // For reference types, check nullability annotations - try - { - var nullabilityInfo = nullabilityContext.Create(property); - return nullabilityInfo.WriteState == NullabilityState.NotNull; - } - catch - { - // If we can't determine nullability, default to not required - return false; - } + // For reference types with default initializers (e.g., "= new()"), we should NOT mark them as required + // since they can be omitted during construction. However, we can't reliably detect initializers via reflection. + // Instead, we only mark reference types as required if they have the 'required' modifier (checked above). + // This means non-nullable reference types without 'required' are treated as optional. + + // For backwards compatibility and to match expected behavior, we do NOT mark non-nullable + // reference types as required unless they have the explicit 'required' modifier. + return false; } } From 7e7411f90254b40bbb5c373237e9833b50791cb0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:24:22 +0000 Subject: [PATCH 3/3] chore: Address code review feedback - remove blank line Co-authored-by: niemyjski <1020579+niemyjski@users.noreply.github.com> --- .../Utility/OpenApi/RequiredPropertySchemaTransformer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs b/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs index 3c4b163ae9..2bf798f302 100644 --- a/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs +++ b/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs @@ -100,7 +100,7 @@ private static bool IsPropertyRequired(PropertyInfo property, NullabilityInfoCon // since they can be omitted during construction. However, we can't reliably detect initializers via reflection. // Instead, we only mark reference types as required if they have the 'required' modifier (checked above). // This means non-nullable reference types without 'required' are treated as optional. - + // For backwards compatibility and to match expected behavior, we do NOT mark non-nullable // reference types as required unless they have the explicit 'required' modifier. return false;