Skip to content

Commit 50a0972

Browse files
Make nullable parameters optional (#23686)
1 parent 7c45399 commit 50a0972

File tree

8 files changed

+352
-10
lines changed

8 files changed

+352
-10
lines changed

src/Resources/ResourceManager/SdkModels/Deployments/TemplateFileTypeDefinition.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public class TemplateFileTypeDefinition
2222
[JsonProperty("type")]
2323
public string Type { get; set; }
2424

25+
[JsonProperty("nullable")]
26+
public bool? Nullable { get; set; }
27+
2528
[JsonProperty("allowedValues")]
2629
public List<object> AllowedValues { get; set; }
2730

src/Resources/ResourceManager/Utilities/TemplateUtility.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,14 @@ private static TemplateFileTypeDefinition ResolveParameterType(TemplateFileParam
311311
throw new InvalidOperationException($"Invalid $ref {current.Ref}.");
312312
}
313313

314-
current = ResolveTypeFromPath(current.Ref, segments.Skip(2), definitions);
314+
var resolved = ResolveTypeFromPath(current.Ref, segments.Skip(2), definitions);
315+
// it's possible to override some of these properties: the highest-level non-null value wins
316+
resolved.Nullable = current.Nullable ?? resolved.Nullable;
317+
resolved.MinLength = current.MinLength ?? resolved.MinLength;
318+
resolved.MaxLength = current.MaxLength ?? resolved.MaxLength;
319+
resolved.AllowedValues = current.AllowedValues ?? resolved.AllowedValues;
320+
321+
current = resolved;
315322
}
316323

317324
return current;
@@ -321,28 +328,29 @@ internal static RuntimeDefinedParameter ConstructDynamicParameter(string[] stati
321328
{
322329
const string duplicatedParameterSuffix = "FromTemplate";
323330
var name = parameterKvp.Key;
324-
var parameter = ResolveParameterType(parameterKvp.Value, definitions);
325-
var defaultValue = parameterKvp.Value.DefaultValue;
331+
var paramDefinition = parameterKvp.Value;
332+
var paramType = ResolveParameterType(paramDefinition, definitions);
333+
var isRequired = paramDefinition.DefaultValue is null && paramType.Nullable != true;
326334

327335
RuntimeDefinedParameter runtimeParameter = new RuntimeDefinedParameter()
328336
{
329337
// For duplicated template parameter names, add a suffix FromTemplate to distinguish them from the cmdlet parameter.
330338
Name = staticParameters.Contains(name, StringComparer.OrdinalIgnoreCase) ? name + duplicatedParameterSuffix : name,
331-
ParameterType = GetParameterType(parameter.Type),
332-
Value = defaultValue
339+
ParameterType = GetParameterType(paramType.Type),
340+
Value = paramDefinition.DefaultValue
333341
};
334342
runtimeParameter.Attributes.Add(new ParameterAttribute()
335343
{
336-
Mandatory = defaultValue == null ? true : false,
344+
Mandatory = isRequired,
337345
ValueFromPipelineByPropertyName = true,
338346
// Rely on the HelpMessage property to detect the original name for the dynamic parameter.
339347
HelpMessage = name
340348
});
341349

342-
if (!string.IsNullOrEmpty(parameter.MinLength) &&
343-
!string.IsNullOrEmpty(parameter.MaxLength))
350+
if (!string.IsNullOrEmpty(paramType.MinLength) &&
351+
!string.IsNullOrEmpty(paramType.MaxLength))
344352
{
345-
runtimeParameter.Attributes.Add(new ValidateLengthAttribute(int.Parse(parameter.MinLength), int.Parse(parameter.MaxLength)));
353+
runtimeParameter.Attributes.Add(new ValidateLengthAttribute(int.Parse(paramType.MinLength), int.Parse(paramType.MaxLength)));
346354
}
347355

348356
return runtimeParameter;

src/Resources/Resources.Test/Resources.Test.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
</ItemGroup>
3232

3333
<ItemGroup>
34-
<None Update="Resources\*.json" CopyToOutputDirectory="PreserveNewest" />
34+
<None Update="Resources/**/*.*" CopyToOutputDirectory="PreserveNewest" />
3535
<None Update="*.json" CopyToOutputDirectory="PreserveNewest" />
3636
<None Update="*.bicep" CopyToOutputDirectory="PreserveNewest" />
3737
<None Update="*.bicepparam" CopyToOutputDirectory="PreserveNewest" />
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// sample from https://github.com/Azure/bicep/issues/12538
2+
targetScope = 'subscription'
3+
param yourName string?
4+
5+
output greeting string = !empty(yourName)
6+
? 'Hello, ${yourName}. Nice to meet you!'
7+
: 'Hey, nice to meet you!'
8+
9+
// verify this also works with nullable custom types
10+
param optionalCustomType customType?
11+
type customType = {
12+
age: int
13+
}
14+
15+
param requiredNullableCustomType nullableCustomType
16+
type nullableCustomType = {
17+
age: int
18+
}?
19+
20+
type customTypeWithRequired = customType!
21+
22+
param optionalRequiredCustomType customTypeWithRequired?

src/Resources/Resources.Test/ScenarioTests/DeploymentTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ public void TestNewDeploymentFromBicepFileAndBicepparamFile()
202202
TestRunner.RunTestScript("Test-NewDeploymentFromBicepFileAndBicepparamFile");
203203
}
204204

205+
[Fact]
206+
[Trait(Category.AcceptanceType, Category.CheckIn)]
207+
public void TestNullableParametersAreNotRequired()
208+
=> TestRunner.RunTestScript("Test-NullableParametersAreNotRequired");
209+
205210
[Fact]
206211
[Trait(Category.AcceptanceType, Category.CheckIn)]
207212
public void TestNewDeploymentFromBicepparamFileOnly()

src/Resources/Resources.Test/ScenarioTests/DeploymentTests.ps1

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,7 +897,31 @@ function Test-NewDeploymentFromBicepFileAndBicepparamFile
897897
}
898898
}
899899

900+
<#
901+
.SYNOPSIS
902+
Verifies that nullable parameters are not required to be passed
903+
#>
904+
function Test-NullableParametersAreNotRequired
905+
{
906+
# Setup
907+
$rname = Get-ResourceName
908+
$location = "West US 2"
909+
$subId = (Get-AzContext).Subscription.SubscriptionId
900910

911+
try
912+
{
913+
#Create deployment
914+
$deployment = New-AzSubscriptionDeployment -Name $rname -Location $location -TemplateFile "Resources/DeploymentTests/NullableParametersAreNotRequired/main.bicep"
915+
916+
# Assert
917+
Assert-AreEqual Succeeded $deployment.ProvisioningState
918+
}
919+
finally
920+
{
921+
# Cleanup
922+
Clean-DeploymentAtSubscription $deployment
923+
}
924+
}
901925

902926
<#
903927
.SYNOPSIS

0 commit comments

Comments
 (0)