diff --git a/src/Kiota.Builder/CodeDOM/CodeProperty.cs b/src/Kiota.Builder/CodeDOM/CodeProperty.cs
index c5f155828d..4beb6e0edd 100644
--- a/src/Kiota.Builder/CodeDOM/CodeProperty.cs
+++ b/src/Kiota.Builder/CodeDOM/CodeProperty.cs
@@ -122,6 +122,14 @@ public bool IsPrimaryErrorMessage
{
get; set;
}
+ ///
+ /// Indicates that this property appeared in the parent schema's required array.
+ /// Set during Code DOM construction in KiotaBuilder; should not be modified by refiners.
+ ///
+ public bool IsRequired
+ {
+ get; set;
+ }
public object Clone()
{
@@ -143,8 +151,8 @@ public object Clone()
OriginalPropertyFromBaseType = OriginalPropertyFromBaseType?.Clone() as CodeProperty,
Deprecation = Deprecation,
IsPrimaryErrorMessage = IsPrimaryErrorMessage,
+ IsRequired = IsRequired,
};
return property;
}
}
-
diff --git a/src/Kiota.Builder/Configuration/GenerationConfiguration.cs b/src/Kiota.Builder/Configuration/GenerationConfiguration.cs
index e82f1079d2..e84bf7d84d 100644
--- a/src/Kiota.Builder/Configuration/GenerationConfiguration.cs
+++ b/src/Kiota.Builder/Configuration/GenerationConfiguration.cs
@@ -65,6 +65,10 @@ public bool UsesBackingStore
{
get; set;
}
+ public bool MakeRequiredPropertiesNonNullable
+ {
+ get; set;
+ } = true;
public bool ExcludeBackwardCompatible
{
get; set;
@@ -184,6 +188,7 @@ public object Clone()
DisableSSLValidation = DisableSSLValidation,
ExportPublicApi = ExportPublicApi,
PluginAuthInformation = PluginAuthInformation,
+ MakeRequiredPropertiesNonNullable = MakeRequiredPropertiesNonNullable,
};
}
private static readonly StringIEnumerableDeepComparer comparer = new();
diff --git a/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs b/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs
index 543e40916e..5c790929ee 100644
--- a/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs
+++ b/src/Kiota.Builder/Extensions/OpenApiSchemaExtensions.cs
@@ -80,6 +80,16 @@ public static bool HasAnyProperty(this IOpenApiSchema? schema)
{
return schema?.Properties is { Count: > 0 };
}
+
+ internal static bool IsExplicitlyNullable(this IOpenApiSchema? schema)
+ {
+ if (schema is null) return false;
+ // OAS 3.0 nullable: true or OAS 3.1 type includes null
+ if ((schema.Type & JsonSchemaType.Null) is JsonSchemaType.Null) return true;
+ // OAS 3.1 anyOf [ { type: null } ] pattern
+ return schema.AnyOf?.Any(static x =>
+ (x.Type & JsonSchemaType.Null) is JsonSchemaType.Null && !x.HasAnyProperty()) ?? false;
+ }
public static bool IsInclusiveUnion(this IOpenApiSchema? schema, uint exclusiveMinimumNumberOfEntries = 1)
{
return schema?.AnyOf?.Count(static x => IsSemanticallyMeaningful(x, true)) > exclusiveMinimumNumberOfEntries;
diff --git a/src/Kiota.Builder/KiotaBuilder.cs b/src/Kiota.Builder/KiotaBuilder.cs
index 21c02e437c..4d5226ef71 100644
--- a/src/Kiota.Builder/KiotaBuilder.cs
+++ b/src/Kiota.Builder/KiotaBuilder.cs
@@ -661,7 +661,7 @@ public async Task ApplyLanguageRefinementAsync(GenerationConfiguration config, C
public async Task CreateLanguageSourceFilesAsync(GenerationLanguage language, CodeNamespace generatedCode, CancellationToken cancellationToken)
{
- var languageWriter = LanguageWriter.GetLanguageWriter(language, config.OutputPath, config.ClientNamespaceName, config.UsesBackingStore, config.ExcludeBackwardCompatible);
+ var languageWriter = LanguageWriter.GetLanguageWriter(language, config.OutputPath, config.ClientNamespaceName, config.UsesBackingStore, config.ExcludeBackwardCompatible, config.MakeRequiredPropertiesNonNullable);
var stopwatch = new Stopwatch();
stopwatch.Start();
var codeRenderer = CodeRenderer.GetCodeRender(config);
@@ -1174,7 +1174,7 @@ private CodeIndexer[] CreateIndexer(string childIdentifier, string childType, Co
}
private static readonly StructuralPropertiesReservedNameProvider structuralPropertiesReservedNameProvider = new();
- private CodeProperty? CreateProperty(string childIdentifier, string childType, IOpenApiSchema? propertySchema = null, CodeTypeBase? existingType = null, CodePropertyKind kind = CodePropertyKind.Custom)
+ private CodeProperty? CreateProperty(string childIdentifier, string childType, IOpenApiSchema? propertySchema = null, CodeTypeBase? existingType = null, CodePropertyKind kind = CodePropertyKind.Custom, bool isRequired = false)
{
var propertyName = childIdentifier.CleanupSymbolName();
if (structuralPropertiesReservedNameProvider.ReservedNames.Contains(propertyName))
@@ -1196,6 +1196,7 @@ private CodeIndexer[] CreateIndexer(string childIdentifier, string childType, Co
ReadOnly = propertySchema?.ReadOnly ?? false,
Type = resultType,
Deprecation = propertySchema?.GetDeprecationInformation(),
+ IsRequired = isRequired,
IsPrimaryErrorMessage = kind == CodePropertyKind.Custom &&
propertySchema is { Extensions: not null } &&
propertySchema.Extensions.TryGetValue(OpenApiPrimaryErrorMessageExtension.Name, out var openApiExtension) &&
@@ -1213,6 +1214,23 @@ openApiExtension is OpenApiPrimaryErrorMessageExtension primaryErrorMessageExten
!"null".Equals(stringDefaultValue, StringComparison.OrdinalIgnoreCase))
prop.DefaultValue = $"\"{stringDefaultValue}\"";
+ // If the property is required and the schema explicitly does not allow null,
+ // mark the type as non-nullable. We clone existingType to avoid mutating a shared reference.
+ // Collections are excluded: IsNullable on a collection type controls both the outer
+ // collection ? AND the enum element ? (e.g. List vs List). Setting
+ // IsNullable = false on a required collection breaks the serialization API which always
+ // expects IEnumerable?. The outer collection ? is suppressed via IsRequired in
+ // CodePropertyWriter instead.
+ var isCollection = existingType != null
+ ? existingType.CollectionKind != CodeTypeBase.CodeTypeCollectionKind.None
+ : propertySchema.IsArray();
+ if (kind == CodePropertyKind.Custom && isRequired && !propertySchema.IsExplicitlyNullable() && !isCollection)
+ {
+ if (existingType != null)
+ prop.Type = (CodeTypeBase)existingType.Clone();
+ prop.Type.IsNullable = false;
+ }
+
if (existingType == null)
{
prop.Type.CollectionKind = propertySchema.IsArray() ? CodeTypeBase.CodeTypeCollectionKind.Complex : default;
@@ -2418,7 +2436,8 @@ private void CreatePropertiesForModelClass(OpenApiUrlTreeNode currentNode, IOpen
LogOmittedPropertyInvalidSchema(x.Key, model.Name, currentNode.Path);
return null;
}
- return CreateProperty(x.Key, definition.Name, propertySchema: propertySchema, existingType: definition);
+ var isRequired = schema.Required?.Contains(x.Key) ?? false;
+ return CreateProperty(x.Key, definition.Name, propertySchema: propertySchema, existingType: definition, isRequired: isRequired);
})
.OfType()
.ToArray() ?? [];
diff --git a/src/Kiota.Builder/Refiners/CSharpRefiner.cs b/src/Kiota.Builder/Refiners/CSharpRefiner.cs
index 371f54068a..cdb017d9c8 100644
--- a/src/Kiota.Builder/Refiners/CSharpRefiner.cs
+++ b/src/Kiota.Builder/Refiners/CSharpRefiner.cs
@@ -136,6 +136,7 @@ protected static void MakeEnumPropertiesNullable(CodeElement currentElement)
if (currentElement is CodeClass currentClass && currentClass.IsOfKind(CodeClassKind.Model))
currentClass.Properties
.Where(x => x.Type is CodeType propType && propType.TypeDefinition is CodeEnum)
+ .Where(x => !x.IsRequired)
.ToList()
.ForEach(x => x.Type.IsNullable = true);
CrawlTree(currentElement, MakeEnumPropertiesNullable);
diff --git a/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs b/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs
index 9b9497f37a..f833307582 100644
--- a/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs
+++ b/src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs
@@ -244,6 +244,20 @@ _ when NullableTypes.Contains(typeName) => true,
_ => false,
};
}
+ internal bool IsValueType(CodeTypeBase type)
+ {
+ if (type is not CodeType codeType) return false;
+ // Collections are reference types regardless of element type — never use .Value on them
+ if (codeType.CollectionKind != CodeTypeBase.CodeTypeCollectionKind.None) return false;
+ if (codeType.TypeDefinition is CodeEnum) return true;
+ var typeName = TranslateType(codeType);
+ return NullableTypes.Contains(typeName);
+ }
+ ///
+ /// When true (default), required non-nullable OAS properties are generated as non-nullable C# types.
+ /// Set to false to revert to the previous all-nullable behavior.
+ ///
+ public bool MakeRequiredPropertiesNonNullable { get; set; } = true;
public override string GetParameterSignature(CodeParameter parameter, CodeElement targetElement, LanguageWriter? writer = null)
{
ArgumentNullException.ThrowIfNull(parameter);
diff --git a/src/Kiota.Builder/Writers/CSharp/CSharpWriter.cs b/src/Kiota.Builder/Writers/CSharp/CSharpWriter.cs
index 9b393a7e40..fd6a5d3e9d 100644
--- a/src/Kiota.Builder/Writers/CSharp/CSharpWriter.cs
+++ b/src/Kiota.Builder/Writers/CSharp/CSharpWriter.cs
@@ -4,10 +4,13 @@ namespace Kiota.Builder.Writers.CSharp;
public class CSharpWriter : LanguageWriter
{
- public CSharpWriter(string rootPath, string clientNamespaceName)
+ public CSharpWriter(string rootPath, string clientNamespaceName, bool makeRequiredPropertiesNonNullable = true)
{
PathSegmenter = new CSharpPathSegmenter(rootPath, clientNamespaceName);
- var conventionService = new CSharpConventionService();
+ var conventionService = new CSharpConventionService
+ {
+ MakeRequiredPropertiesNonNullable = makeRequiredPropertiesNonNullable
+ };
AddOrReplaceCodeElementWriter(new CodeClassDeclarationWriter(conventionService));
AddOrReplaceCodeElementWriter(new CodeBlockEndWriter(conventionService));
AddOrReplaceCodeElementWriter(new CodeEnumWriter(conventionService));
diff --git a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs
index 5e1d20b7c4..78e637b3e2 100644
--- a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs
+++ b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs
@@ -359,7 +359,10 @@ private void WriteDeserializerBodyForInheritedModel(bool shouldHide, CodeMethod
.Where(static x => !x.ExistsInBaseType)
.OrderBy(static x => x.Name, StringComparer.Ordinal))
{
- writer.WriteLine($"{{ \"{otherProp.WireName.SanitizeDoubleQuote()}\", n => {{ {otherProp.Name.ToFirstCharacterUpperCase()} = n.{GetDeserializationMethodName(otherProp.Type, codeElement)}; }} }},");
+ // When a property is required and non-nullable, the C# property type is T (not T?).
+ // Parse-node methods for value types and enums return T?, so we must unwrap with .Value.
+ var deserializeSuffix = !otherProp.Type.IsNullable && conventions.IsValueType(otherProp.Type) ? ".Value" : string.Empty;
+ writer.WriteLine($"{{ \"{otherProp.WireName.SanitizeDoubleQuote()}\", n => {{ {otherProp.Name.ToFirstCharacterUpperCase()} = n.{GetDeserializationMethodName(otherProp.Type, codeElement)}{deserializeSuffix}; }} }},");
}
writer.CloseBlock("};");
}
diff --git a/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs
index 7159c9d63b..33e14caf44 100644
--- a/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs
+++ b/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs
@@ -14,9 +14,11 @@ public override void WriteCodeElement(CodeProperty codeElement, LanguageWriter w
if (codeElement.ExistsInExternalBaseType) return;
var propertyType = conventions.GetTypeString(codeElement.Type, codeElement);
var isNullableReferenceType = !propertyType.EndsWith('?')
+ && codeElement.Type.IsNullable
+ && !(conventions.MakeRequiredPropertiesNonNullable && codeElement.IsRequired)
&& codeElement.IsOfKind(
CodePropertyKind.Custom,
- CodePropertyKind.QueryParameter);// Other property types are appropriately constructor initialized
+ CodePropertyKind.QueryParameter);
conventions.WriteShortDescription(codeElement, writer);
conventions.WriteDeprecationAttribute(codeElement, writer);
if (isNullableReferenceType)
@@ -26,7 +28,7 @@ public override void WriteCodeElement(CodeProperty codeElement, LanguageWriter w
CSharpConventionService.WriteNullableMiddle(writer);
}
- WritePropertyInternal(codeElement, writer, propertyType);// Always write the normal way
+ WritePropertyInternal(codeElement, writer, propertyType);
if (isNullableReferenceType)
CSharpConventionService.WriteNullableClosing(writer);
diff --git a/src/Kiota.Builder/Writers/LanguageWriter.cs b/src/Kiota.Builder/Writers/LanguageWriter.cs
index 22313267dd..7853ba22f4 100644
--- a/src/Kiota.Builder/Writers/LanguageWriter.cs
+++ b/src/Kiota.Builder/Writers/LanguageWriter.cs
@@ -178,11 +178,11 @@ protected void AddOrReplaceCodeElementWriter(ICodeElementWriter writer) wh
Writers[typeof(T)] = writer;
}
private readonly Dictionary Writers = []; // we have to type as object because dotnet doesn't have type capture i.e eq for `? extends CodeElement`
- public static LanguageWriter GetLanguageWriter(GenerationLanguage language, string outputPath, string clientNamespaceName, bool usesBackingStore = false, bool excludeBackwardCompatible = false)
+ public static LanguageWriter GetLanguageWriter(GenerationLanguage language, string outputPath, string clientNamespaceName, bool usesBackingStore = false, bool excludeBackwardCompatible = false, bool makeRequiredPropertiesNonNullable = true)
{
return language switch
{
- GenerationLanguage.CSharp => new CSharpWriter(outputPath, clientNamespaceName),
+ GenerationLanguage.CSharp => new CSharpWriter(outputPath, clientNamespaceName, makeRequiredPropertiesNonNullable),
GenerationLanguage.Java => new JavaWriter(outputPath, clientNamespaceName),
GenerationLanguage.TypeScript => new TypeScriptWriter(outputPath, clientNamespaceName),
GenerationLanguage.Ruby => new RubyWriter(outputPath, clientNamespaceName),
diff --git a/src/kiota/Handlers/KiotaGenerateCommandHandler.cs b/src/kiota/Handlers/KiotaGenerateCommandHandler.cs
index 4d38b7edf8..6bd091b2f9 100644
--- a/src/kiota/Handlers/KiotaGenerateCommandHandler.cs
+++ b/src/kiota/Handlers/KiotaGenerateCommandHandler.cs
@@ -86,6 +86,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio
bool excludeBackwardCompatible = parseResult.GetValue(ExcludeBackwardCompatibleOption);
bool clearCache = parseResult.GetValue(ClearCacheOption);
bool disableSSLValidation = parseResult.GetValue(DisableSSLValidationOption);
+ bool makeRequiredPropertiesNonNullable = parseResult.GetValue(MakeRequiredPropertiesNonNullableOption);
bool includeAdditionalData = parseResult.GetValue(AdditionalDataOption);
string? className = parseResult.GetValue(ClassOption);
AccessModifier typeAccessModifier = parseResult.GetValue(TypeAccessModifierOption);
@@ -152,6 +153,7 @@ public override async Task InvokeAsync(ParseResult parseResult, Cancellatio
Configuration.Generation.CleanOutput = cleanOutput;
Configuration.Generation.ClearCache = clearCache;
Configuration.Generation.DisableSSLValidation = disableSSLValidation;
+ Configuration.Generation.MakeRequiredPropertiesNonNullable = makeRequiredPropertiesNonNullable;
var (loggerFactory, logger) = GetLoggerAndFactory(parseResult, Configuration.Generation.OutputPath);
using (loggerFactory)
@@ -233,6 +235,10 @@ public required Option DisableSSLValidationOption
{
get; init;
}
+ public required Option MakeRequiredPropertiesNonNullableOption
+ {
+ get; init;
+ }
private static void CreateTelemetryTags(ActivitySource? activitySource, GenerationLanguage language, bool backingStore,
bool excludeBackwardCompatible, bool clearCache, bool disableSslValidation, bool cleanOutput, string? output,
diff --git a/src/kiota/KiotaHost.cs b/src/kiota/KiotaHost.cs
index b5e17e2d9f..b95a5312bb 100644
--- a/src/kiota/KiotaHost.cs
+++ b/src/kiota/KiotaHost.cs
@@ -589,6 +589,8 @@ private static Command GetGenerateCommand(IServiceProvider serviceProvider)
var disableSSLValidationOption = GetDisableSSLValidationOption(defaultConfiguration.DisableSSLValidation);
+ var makeRequiredPropertiesNonNullableOption = GetMakeRequiredPropertiesNonNullableOption(defaultConfiguration.MakeRequiredPropertiesNonNullable);
+
var command = new Command("generate", "Generates a REST HTTP API client from an OpenAPI description file.") {
descriptionOption,
manifestOption,
@@ -610,6 +612,7 @@ private static Command GetGenerateCommand(IServiceProvider serviceProvider)
dvrOption,
clearCacheOption,
disableSSLValidationOption,
+ makeRequiredPropertiesNonNullableOption,
};
command.Action = new KiotaGenerateCommandHandler
{
@@ -633,6 +636,7 @@ private static Command GetGenerateCommand(IServiceProvider serviceProvider)
DisabledValidationRulesOption = dvrOption,
ClearCacheOption = clearCacheOption,
DisableSSLValidationOption = disableSSLValidationOption,
+ MakeRequiredPropertiesNonNullableOption = makeRequiredPropertiesNonNullableOption,
ServiceProvider = serviceProvider,
};
return command;
@@ -708,6 +712,16 @@ private static Option GetClearCacheOption(bool defaultValue)
return clearCacheOption;
}
+ internal static Option GetMakeRequiredPropertiesNonNullableOption(bool defaultValue = true)
+ {
+ var option = new Option("--make-required-properties-non-nullable")
+ {
+ DefaultValueFactory = _ => defaultValue,
+ Description = "When enabled (default), properties marked as required in the OpenAPI description and not explicitly nullable are generated as non-nullable types. Set to false to revert to the previous behavior where all properties are nullable, useful for specs that incorrectly mark fields as required.",
+ };
+ option.Aliases.Add("--mrpnn");
+ return option;
+ }
private static Option GetDisableSSLValidationOption(bool defaultValue)
{
var disableSSLValidationOption = new Option("--disable-ssl-validation")
diff --git a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
index 85f2c151f9..07002cdaea 100644
--- a/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
+++ b/tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
@@ -11062,6 +11062,457 @@ public async Task GeneratesNavigationPropertiesForActionPathsSiblingToCollection
Assert.NotNull(replyWithQuoteRb);
}
+ #region Issue-3911 — required/nullable OAS properties → IsNullable / IsRequired
+
+ [Fact]
+ public async Task RequiredNonNullableStringProperty_IsNullableFalse_IsRequiredTrue()
+ {
+ var tempFilePath = Path.GetTempFileName();
+ await using var fs = await GetDocumentStreamAsync(@"openapi: 3.0.1
+info:
+ title: Test
+ version: 1.0.0
+servers:
+ - url: https://example.com/v1.0
+paths:
+ /items:
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Item'
+components:
+ schemas:
+ Item:
+ type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs, cancellationToken: TestContext.Current.CancellationToken);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+ var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models");
+ Assert.NotNull(modelsNS);
+ var item = modelsNS.FindChildByName("Item", false);
+ Assert.NotNull(item);
+ var nameProp = item.Properties.FirstOrDefault(static p => p.Name.Equals("name", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(nameProp);
+ Assert.False(nameProp.Type.IsNullable);
+ Assert.True(nameProp.IsRequired);
+ }
+
+ [Fact]
+ public async Task RequiredNullableStringProperty_IsNullableTrue_IsRequiredTrue()
+ {
+ var tempFilePath = Path.GetTempFileName();
+ await using var fs = await GetDocumentStreamAsync(@"openapi: 3.0.1
+info:
+ title: Test
+ version: 1.0.0
+servers:
+ - url: https://example.com/v1.0
+paths:
+ /items:
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Item'
+components:
+ schemas:
+ Item:
+ type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string
+ nullable: true");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs, cancellationToken: TestContext.Current.CancellationToken);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+ var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models");
+ Assert.NotNull(modelsNS);
+ var item = modelsNS.FindChildByName("Item", false);
+ Assert.NotNull(item);
+ var nameProp = item.Properties.FirstOrDefault(static p => p.Name.Equals("name", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(nameProp);
+ Assert.True(nameProp.Type.IsNullable);
+ Assert.True(nameProp.IsRequired);
+ }
+
+ [Fact]
+ public async Task OptionalNonNullableStringProperty_IsNullableTrue_IsRequiredFalse()
+ {
+ var tempFilePath = Path.GetTempFileName();
+ await using var fs = await GetDocumentStreamAsync(@"openapi: 3.0.1
+info:
+ title: Test
+ version: 1.0.0
+servers:
+ - url: https://example.com/v1.0
+paths:
+ /items:
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Item'
+components:
+ schemas:
+ Item:
+ type: object
+ properties:
+ name:
+ type: string");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs, cancellationToken: TestContext.Current.CancellationToken);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+ var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models");
+ Assert.NotNull(modelsNS);
+ var item = modelsNS.FindChildByName("Item", false);
+ Assert.NotNull(item);
+ var nameProp = item.Properties.FirstOrDefault(static p => p.Name.Equals("name", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(nameProp);
+ Assert.True(nameProp.Type.IsNullable);
+ Assert.False(nameProp.IsRequired);
+ }
+
+ [Fact]
+ public async Task OptionalNullableStringProperty_IsNullableTrue_IsRequiredFalse()
+ {
+ var tempFilePath = Path.GetTempFileName();
+ await using var fs = await GetDocumentStreamAsync(@"openapi: 3.0.1
+info:
+ title: Test
+ version: 1.0.0
+servers:
+ - url: https://example.com/v1.0
+paths:
+ /items:
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Item'
+components:
+ schemas:
+ Item:
+ type: object
+ properties:
+ name:
+ type: string
+ nullable: true");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs, cancellationToken: TestContext.Current.CancellationToken);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+ var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models");
+ Assert.NotNull(modelsNS);
+ var item = modelsNS.FindChildByName("Item", false);
+ Assert.NotNull(item);
+ var nameProp = item.Properties.FirstOrDefault(static p => p.Name.Equals("name", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(nameProp);
+ Assert.True(nameProp.Type.IsNullable);
+ Assert.False(nameProp.IsRequired);
+ }
+
+ [Fact]
+ public async Task RequiredNonNullableIntegerProperty_IsNullableFalse_IsRequiredTrue()
+ {
+ var tempFilePath = Path.GetTempFileName();
+ await using var fs = await GetDocumentStreamAsync(@"openapi: 3.0.1
+info:
+ title: Test
+ version: 1.0.0
+servers:
+ - url: https://example.com/v1.0
+paths:
+ /items:
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Item'
+components:
+ schemas:
+ Item:
+ type: object
+ required:
+ - count
+ properties:
+ count:
+ type: integer");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs, cancellationToken: TestContext.Current.CancellationToken);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+ var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models");
+ Assert.NotNull(modelsNS);
+ var item = modelsNS.FindChildByName("Item", false);
+ Assert.NotNull(item);
+ var countProp = item.Properties.FirstOrDefault(static p => p.Name.Equals("count", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(countProp);
+ Assert.False(countProp.Type.IsNullable);
+ Assert.True(countProp.IsRequired);
+ }
+
+ [Fact]
+ public async Task RequiredNonNullableObjectRefProperty_IsNullableFalse_IsRequiredTrue()
+ {
+ var tempFilePath = Path.GetTempFileName();
+ await using var fs = await GetDocumentStreamAsync(@"openapi: 3.0.1
+info:
+ title: Test
+ version: 1.0.0
+servers:
+ - url: https://example.com/v1.0
+paths:
+ /items:
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Item'
+components:
+ schemas:
+ Item:
+ type: object
+ required:
+ - owner
+ properties:
+ owner:
+ $ref: '#/components/schemas/Owner'
+ Owner:
+ type: object
+ properties:
+ id:
+ type: string");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs, cancellationToken: TestContext.Current.CancellationToken);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+ var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models");
+ Assert.NotNull(modelsNS);
+ var item = modelsNS.FindChildByName("Item", false);
+ Assert.NotNull(item);
+ var ownerProp = item.Properties.FirstOrDefault(static p => p.Name.Equals("owner", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(ownerProp);
+ Assert.False(ownerProp.Type.IsNullable);
+ Assert.True(ownerProp.IsRequired);
+ }
+
+ [Fact]
+ public async Task RequiredNonNullableEnumProperty_IsNullableFalse_IsRequiredTrue()
+ {
+ var tempFilePath = Path.GetTempFileName();
+ await using var fs = await GetDocumentStreamAsync(@"openapi: 3.0.1
+info:
+ title: Test
+ version: 1.0.0
+servers:
+ - url: https://example.com/v1.0
+paths:
+ /items:
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Item'
+components:
+ schemas:
+ Item:
+ type: object
+ required:
+ - status
+ properties:
+ status:
+ $ref: '#/components/schemas/Status'
+ Status:
+ type: string
+ enum:
+ - active
+ - inactive");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs, cancellationToken: TestContext.Current.CancellationToken);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+ var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models");
+ Assert.NotNull(modelsNS);
+ var item = modelsNS.FindChildByName("Item", false);
+ Assert.NotNull(item);
+ var statusProp = item.Properties.FirstOrDefault(static p => p.Name.Equals("status", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(statusProp);
+ Assert.False(statusProp.Type.IsNullable);
+ Assert.True(statusProp.IsRequired);
+ }
+
+ [Fact]
+ public async Task RequiredNonNullableCollectionProperty_IsNullableFalse_IsRequiredTrue()
+ {
+ var tempFilePath = Path.GetTempFileName();
+ await using var fs = await GetDocumentStreamAsync(@"openapi: 3.0.1
+info:
+ title: Test
+ version: 1.0.0
+servers:
+ - url: https://example.com/v1.0
+paths:
+ /items:
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Item'
+components:
+ schemas:
+ Item:
+ type: object
+ required:
+ - tags
+ properties:
+ tags:
+ type: array
+ items:
+ type: string");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs, cancellationToken: TestContext.Current.CancellationToken);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+ var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models");
+ Assert.NotNull(modelsNS);
+ var item = modelsNS.FindChildByName("Item", false);
+ Assert.NotNull(item);
+ var tagsProp = item.Properties.FirstOrDefault(static p => p.Name.Equals("tags", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(tagsProp);
+ // Collections keep IsNullable = true so enum element types stay T? (required by serializer API).
+ // The outer collection ? is suppressed in the C# writer via IsRequired.
+ Assert.True(tagsProp.Type.IsNullable);
+ Assert.True(tagsProp.IsRequired);
+ }
+
+ [Fact]
+ public async Task RequiredNonNullableProperty_FlagFalse_IsNullableTrue()
+ {
+ // When MakeRequiredPropertiesNonNullable = false, required non-nullable properties keep IsNullable = true
+ var tempFilePath = Path.GetTempFileName();
+ await using var fs = await GetDocumentStreamAsync(@"openapi: 3.0.1
+info:
+ title: Test
+ version: 1.0.0
+servers:
+ - url: https://example.com/v1.0
+paths:
+ /items:
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Item'
+components:
+ schemas:
+ Item:
+ type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath, MakeRequiredPropertiesNonNullable = false }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs, cancellationToken: TestContext.Current.CancellationToken);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+ var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models");
+ Assert.NotNull(modelsNS);
+ var item = modelsNS.FindChildByName("Item", false);
+ Assert.NotNull(item);
+ var nameProp = item.Properties.FirstOrDefault(static p => p.Name.Equals("name", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(nameProp);
+ // Flag off: IsNullable stays true (old behavior)
+ Assert.True(nameProp.Type.IsNullable);
+ // IsRequired is always set accurately regardless of flag
+ Assert.True(nameProp.IsRequired);
+ }
+
+ [Fact]
+ public async Task RequiredNonNullableProperty_FlagTrue_IsNullableFalse()
+ {
+ // When MakeRequiredPropertiesNonNullable = true (default), required non-nullable properties get IsNullable = false
+ var tempFilePath = Path.GetTempFileName();
+ await using var fs = await GetDocumentStreamAsync(@"openapi: 3.0.1
+info:
+ title: Test
+ version: 1.0.0
+servers:
+ - url: https://example.com/v1.0
+paths:
+ /items:
+ get:
+ responses:
+ '200':
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Item'
+components:
+ schemas:
+ Item:
+ type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string");
+ var mockLogger = new Mock>();
+ var builder = new KiotaBuilder(mockLogger.Object, new GenerationConfiguration { ClientClassName = "Graph", OpenAPIFilePath = tempFilePath, MakeRequiredPropertiesNonNullable = true }, _httpClient);
+ var document = await builder.CreateOpenApiDocumentAsync(fs, cancellationToken: TestContext.Current.CancellationToken);
+ var node = builder.CreateUriSpace(document);
+ var codeModel = builder.CreateSourceModel(node);
+ var modelsNS = codeModel.FindNamespaceByName("ApiSdk.models");
+ Assert.NotNull(modelsNS);
+ var item = modelsNS.FindChildByName("Item", false);
+ Assert.NotNull(item);
+ var nameProp = item.Properties.FirstOrDefault(static p => p.Name.Equals("name", StringComparison.OrdinalIgnoreCase));
+ Assert.NotNull(nameProp);
+ // Flag on: required non-nullable scalar gets IsNullable = false
+ Assert.False(nameProp.Type.IsNullable);
+ Assert.True(nameProp.IsRequired);
+ }
+
+ #endregion
+
///
/// Regression test for https://github.com/microsoft/kiota/issues/7292
/// Required query parameters on one operation must not leak into the path-item URL template
diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs
index f46d93933e..55eafeb834 100644
--- a/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs
+++ b/tests/Kiota.Builder.Tests/Writers/CSharp/CodePropertyWriterTests.cs
@@ -271,5 +271,72 @@ public void WritesMessageOverrideOnPrimary()
// Then
Assert.Contains("public override string Message { get => Prop1 ?? string.Empty; }", result);
}
+
+ [Fact]
+ public void WritesRequiredNonNullableCustomProperty_NoNullableBlock()
+ {
+ // A required, non-nullable reference-type property should NOT emit #nullable enable / ?
+ property.Kind = CodePropertyKind.Custom;
+ property.Type.IsNullable = false;
+ property.IsRequired = true;
+ writer.Write(property);
+ var result = tw.ToString();
+ Assert.Contains($"{TypeName} {PropertyName}", result);
+ Assert.DoesNotContain("#nullable enable", result);
+ Assert.DoesNotContain($"{TypeName}?", result);
+ }
+
+ [Fact]
+ public void WritesNullableCustomProperty_HasNullableBlock()
+ {
+ // A nullable reference-type property (the default) should still emit #nullable enable / ?
+ property.Kind = CodePropertyKind.Custom;
+ property.Type.IsNullable = true;
+ writer.Write(property);
+ var result = tw.ToString();
+ Assert.Contains("#nullable enable", result);
+ Assert.Contains($"{TypeName}?", result);
+ }
+
+ [Fact]
+ public void WritesRequiredNonNullableEnumProperty_NoNullableBlock()
+ {
+ var enumDef = rootNamespace.AddClass(new CodeClass { Name = "MyEnum" }).First();
+ property.Kind = CodePropertyKind.Custom;
+ property.Type = new CodeType
+ {
+ Name = "MyEnum",
+ TypeDefinition = new CodeEnum { Name = "MyEnum" },
+ IsNullable = false,
+ };
+ property.IsRequired = true;
+ parentClass.AddProperty(property);
+ writer.Write(property);
+ var result = tw.ToString();
+ Assert.DoesNotContain("#nullable enable", result);
+ Assert.DoesNotContain("MyEnum?", result);
+ }
+
+ [Fact]
+ public void WritesRequiredProperty_FlagFalse_HasNullableBlock()
+ {
+ // When MakeRequiredPropertiesNonNullable = false, a required property with IsNullable = true
+ // should still get the #nullable enable block (old all-nullable behavior).
+ var flagOffWriter = LanguageWriter.GetLanguageWriter(GenerationLanguage.CSharp, DefaultPath, DefaultName, makeRequiredPropertiesNonNullable: false);
+ using var sw = new StringWriter();
+ flagOffWriter.SetTextWriter(sw);
+
+ property.Kind = CodePropertyKind.Custom;
+ property.Type.IsNullable = true; // flag=false: stays nullable
+ property.IsRequired = true; // IsRequired is still set accurately
+ writer.Write(property); // control: default writer suppresses ? for required
+ var defaultResult = tw.ToString();
+ Assert.DoesNotContain("#nullable enable", defaultResult); // flag=true suppresses it
+
+ flagOffWriter.Write(property); // flag=false: should emit nullable block
+ var flagOffResult = sw.ToString();
+ Assert.Contains("#nullable enable", flagOffResult); // opt-out restores it
+ Assert.Contains($"{TypeName}?", flagOffResult);
+ }
}
diff --git a/tests/Kiota.Builder.Tests/Writers/Java/CodePropertyWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Java/CodePropertyWriterTests.cs
index 21e75ec198..c65ac53c8b 100644
--- a/tests/Kiota.Builder.Tests/Writers/Java/CodePropertyWriterTests.cs
+++ b/tests/Kiota.Builder.Tests/Writers/Java/CodePropertyWriterTests.cs
@@ -124,4 +124,17 @@ public void WritesCollectionFlagEnumsAsOneDimensionalArray()
Assert.Contains("List<", result);
Assert.DoesNotContain("EnumSet", result);
}
+
+ [Fact]
+ public void WritesRequiredNonNullable_NonnullAnnotation()
+ {
+ // A required, non-nullable custom property must emit @Nonnull (not @Nullable)
+ property.Kind = CodePropertyKind.Custom;
+ property.Type.IsNullable = false;
+ property.IsRequired = true;
+ writer.Write(property);
+ var result = tw.ToString();
+ Assert.Contains("@jakarta.annotation.Nonnull", result);
+ Assert.DoesNotContain("@jakarta.annotation.Nullable", result);
+ }
}
diff --git a/tests/Kiota.Builder.Tests/Writers/Php/CodePropertyWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/CodePropertyWriterTests.cs
index fc7ca327c2..5ccfd90f71 100644
--- a/tests/Kiota.Builder.Tests/Writers/Php/CodePropertyWriterTests.cs
+++ b/tests/Kiota.Builder.Tests/Writers/Php/CodePropertyWriterTests.cs
@@ -290,4 +290,27 @@ public void WritePropertyWithDescription()
var result = stringWriter.ToString();
Assert.DoesNotContain("/*/*", result);
}
+
+ [Fact]
+ public void WritesRequiredNonNullableProperty_NoNullPrefix_NoNullDefault()
+ {
+ // A required, non-nullable custom property should have no '?' type prefix and no '= null' default
+ var property = new CodeProperty
+ {
+ Name = "Email",
+ Access = AccessModifier.Public,
+ Kind = CodePropertyKind.Custom,
+ IsRequired = true,
+ Type = new CodeType
+ {
+ Name = "emailAddress",
+ IsNullable = false,
+ }
+ };
+ parentClass.AddProperty(property);
+ propertyWriter.WriteCodeElement(property, languageWriter);
+ var result = stringWriter.ToString();
+ Assert.DoesNotContain("?EmailAddress", result);
+ Assert.DoesNotContain("= null", result);
+ }
}
diff --git a/tests/Kiota.Builder.Tests/Writers/Python/CodePropertyWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Python/CodePropertyWriterTests.cs
index ce0163df05..45180038a5 100644
--- a/tests/Kiota.Builder.Tests/Writers/Python/CodePropertyWriterTests.cs
+++ b/tests/Kiota.Builder.Tests/Writers/Python/CodePropertyWriterTests.cs
@@ -149,4 +149,17 @@ public void WritePrimaryErrorMessagePropertyOption2()
var result = tw.ToString();
Assert.Contains("return '' if self.error.message is None else self.error.message", result);
}
+
+ [Fact]
+ public void WritesRequiredNonNullableCustomProperty_NoOptional_NoNoneDefault()
+ {
+ // A required, non-nullable custom property must not be wrapped in Optional[...] and must have no '= None' default
+ property.Kind = CodePropertyKind.Custom;
+ property.Type.IsNullable = false;
+ property.IsRequired = true;
+ writer.Write(property);
+ var result = tw.ToString();
+ Assert.DoesNotContain("Optional[", result);
+ Assert.DoesNotContain("= None", result);
+ }
}
diff --git a/vscode/packages/microsoft-kiota/package.json b/vscode/packages/microsoft-kiota/package.json
index e48d8cec0c..0a91551599 100644
--- a/vscode/packages/microsoft-kiota/package.json
+++ b/vscode/packages/microsoft-kiota/package.json
@@ -38,6 +38,11 @@
"default": true,
"description": "%kiota.generate.includeAdditionalData.description%"
},
+ "kiota.generate.makeRequiredPropertiesNonNullable.enabled": {
+ "type": "boolean",
+ "default": true,
+ "description": "%kiota.generate.makeRequiredPropertiesNonNullable.description%"
+ },
"kiota.generate.backingStore.enabled": {
"type": "boolean",
"default": false,
diff --git a/vscode/packages/microsoft-kiota/package.nls.json b/vscode/packages/microsoft-kiota/package.nls.json
index d1c0f02166..c00c6142db 100644
--- a/vscode/packages/microsoft-kiota/package.nls.json
+++ b/vscode/packages/microsoft-kiota/package.nls.json
@@ -26,10 +26,11 @@
"kiota.generate.deserializer.description": "The fully qualified class names for deserializers",
"kiota.generate.structuredMimeTypes.description": "The MIME types and preference to use for structured data model generation. As per RFC9110 Accept header notation.",
"kiota.generate.includeAdditionalData.description": "Will include the 'AdditionalData' property for models",
+ "kiota.generate.makeRequiredPropertiesNonNullable.description": "When enabled (default), properties marked as required in the OpenAPI description and not explicitly nullable are generated as non-nullable types. Disable to revert to the previous behavior where all properties are nullable.",
"kiota.openApiExplorer.openFile.title": "Open file",
"kiota.workspace.name": "My Workspace",
"kiota.openApiExplorer.regenerateButton.title": "Re-generate",
"kiota.openApiExplorer.editPaths.title": "Select",
"kiota.openApiExplorer.refresh.title": "Refresh",
"kiota.migrateClients.title": "Migrate API clients"
-}
+}
\ No newline at end of file
diff --git a/vscode/packages/microsoft-kiota/src/commands/generate/generateClientCommand.ts b/vscode/packages/microsoft-kiota/src/commands/generate/generateClientCommand.ts
index 8fdfb73e4a..d519d99ca6 100644
--- a/vscode/packages/microsoft-kiota/src/commands/generate/generateClientCommand.ts
+++ b/vscode/packages/microsoft-kiota/src/commands/generate/generateClientCommand.ts
@@ -306,6 +306,7 @@ export class GenerateClientCommand extends Command {
deserializers: settings.languagesSerializationConfiguration[language].deserializers,
structuredMimeTypes: settings.structuredMimeTypes,
includeAdditionalData: settings.includeAdditionalData,
+ makeRequiredPropertiesNonNullable: settings.makeRequiredPropertiesNonNullable,
operation: ConsumerOperation.Add,
workingDirectory: config.workingDirectory ? config.workingDirectory : getWorkspaceJsonDirectory()
});
diff --git a/vscode/packages/microsoft-kiota/src/commands/regenerate/regenerate.service.ts b/vscode/packages/microsoft-kiota/src/commands/regenerate/regenerate.service.ts
index c2d35196be..11b51a1742 100644
--- a/vscode/packages/microsoft-kiota/src/commands/regenerate/regenerate.service.ts
+++ b/vscode/packages/microsoft-kiota/src/commands/regenerate/regenerate.service.ts
@@ -47,6 +47,7 @@ export class RegenerateService {
deserializers: settings.languagesSerializationConfiguration[language].deserializers,
structuredMimeTypes: clientObjectItem.structuredMimeTypes ? clientObjectItem.structuredMimeTypes : settings.structuredMimeTypes,
includeAdditionalData: clientObjectItem.includeAdditionalData ? clientObjectItem.includeAdditionalData : settings.includeAdditionalData,
+ makeRequiredPropertiesNonNullable: clientObjectItem.makeRequiredPropertiesNonNullable !== undefined ? clientObjectItem.makeRequiredPropertiesNonNullable : settings.makeRequiredPropertiesNonNullable,
operation: ConsumerOperation.Edit,
workingDirectory: getWorkspaceJsonDirectory()
});
diff --git a/vscode/packages/microsoft-kiota/src/types/extensionSettings.ts b/vscode/packages/microsoft-kiota/src/types/extensionSettings.ts
index 218f7c3cd5..fa23e4ce2c 100644
--- a/vscode/packages/microsoft-kiota/src/types/extensionSettings.ts
+++ b/vscode/packages/microsoft-kiota/src/types/extensionSettings.ts
@@ -4,6 +4,7 @@ import * as vscode from "vscode";
export function getExtensionSettings(extensionId: string): ExtensionSettings {
return {
includeAdditionalData: getBooleanConfiguration(extensionId, "generate.includeAdditionalData.enabled"),
+ makeRequiredPropertiesNonNullable: getBooleanConfiguration(extensionId, "generate.makeRequiredPropertiesNonNullable.enabled"),
backingStore: getBooleanConfiguration(extensionId, "generate.backingStore.enabled"),
excludeBackwardCompatible: getBooleanConfiguration(extensionId, "generate.excludeBackwardCompatible.enabled"),
cleanOutput: getBooleanConfiguration(extensionId, "cleanOutput.enabled"),
@@ -44,6 +45,7 @@ export interface ExtensionSettings {
disableValidationRules: string[];
structuredMimeTypes: string[];
includeAdditionalData: boolean;
+ makeRequiredPropertiesNonNullable: boolean;
languagesSerializationConfiguration: Record;
}
diff --git a/vscode/packages/npm-package/lib/generateClient.ts b/vscode/packages/npm-package/lib/generateClient.ts
index f4f68c4f79..1e0a09ab06 100644
--- a/vscode/packages/npm-package/lib/generateClient.ts
+++ b/vscode/packages/npm-package/lib/generateClient.ts
@@ -18,6 +18,7 @@ export interface ClientGenerationOptions {
excludeBackwardCompatible?: boolean;
excludePatterns?: string[];
includeAdditionalData?: boolean;
+ makeRequiredPropertiesNonNullable?: boolean;
includePatterns?: string[];
clearCache?: boolean;
cleanOutput?: boolean;
@@ -75,6 +76,7 @@ export async function generateClient(clientGenerationOptions: ClientGenerationOp
excludeBackwardCompatible: clientGenerationOptions.excludeBackwardCompatible ?? false,
excludePatterns: clientGenerationOptions.excludePatterns ?? [],
includeAdditionalData: clientGenerationOptions.includeAdditionalData ?? false,
+ makeRequiredPropertiesNonNullable: clientGenerationOptions.makeRequiredPropertiesNonNullable ?? true,
cleanOutput: clientGenerationOptions.cleanOutput ?? false,
clearCache: clientGenerationOptions.clearCache ?? false,
includePatterns: clientGenerationOptions.includePatterns ?? [],
diff --git a/vscode/packages/npm-package/types.ts b/vscode/packages/npm-package/types.ts
index eaed5534a9..5a25984426 100644
--- a/vscode/packages/npm-package/types.ts
+++ b/vscode/packages/npm-package/types.ts
@@ -246,6 +246,7 @@ export interface GenerationConfiguration {
excludeBackwardCompatible: boolean;
excludePatterns: string[];
includeAdditionalData: boolean;
+ makeRequiredPropertiesNonNullable: boolean;
includePatterns: string[];
language: KiotaGenerationLanguage;
openAPIFilePath: string;
@@ -278,6 +279,7 @@ export interface ClientObjectProperties extends WorkspaceObjectProperties {
clientNamespaceName: string;
usesBackingStore: boolean;
includeAdditionalData: boolean;
+ makeRequiredPropertiesNonNullable: boolean;
excludeBackwardCompatible: boolean;
disabledValidationRules: string[];
}
@@ -300,7 +302,7 @@ export interface KiotaResult extends KiotaLoggedResult {
isSuccess: boolean;
}
-export interface ValidateOpenApiResult extends KiotaLoggedResult {}
+export interface ValidateOpenApiResult extends KiotaLoggedResult { }
export interface GeneratePluginResult extends KiotaResult {
aiPlugin: string;