Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve nullability when loading resource-derived type from ARM JSON template #13536

Merged
merged 1 commit into from Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/Bicep.Core.IntegrationTests/UserDefinedTypeTests.cs
Expand Up @@ -1550,4 +1550,34 @@ public void Using_a_complete_resource_body_as_a_type_should_not_throw_exception(
("BCP394", DiagnosticLevel.Error, "Resource-derived type expressions must derefence a property within the resource body. Using the entire resource body type is not permitted."),
});
}

[TestMethod]
public void Resource_derived_type_nullability_should_be_preserved_when_loading_from_ARM_JSON()
{
var result = CompilationHelper.Compile(new ServiceBuilder().WithFeatureOverrides(new(TestContext, ResourceDerivedTypesEnabled: true)),
("main.bicep", """
module mod 'mod.json' = {
name: 'mod'
}
"""),
("mod.json", $$"""
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"languageVersion": "2.0",
"contentVersion": "1.0.0.0",
"parameters": {
"foo": {
"type": "string",
"metadata": {
"{{LanguageConstants.MetadataResourceDerivedTypePropertyName}}": "Microsoft.Storage/storageAccounts@2022-09-01#properties/sku/properties/name"
},
"nullable": true
}
},
"resources": []
}
"""));

result.Should().NotHaveAnyDiagnostics();
}
}
42 changes: 21 additions & 21 deletions src/Bicep.Core/TypeSystem/ArmTemplateTypeLoader.cs
Expand Up @@ -24,27 +24,7 @@ public static ITypeReference ToTypeReference(SchemaValidationContext context, IT
{
var resolved = TemplateEngine.ResolveSchemaReferences(context, armTemplateSchemaNode);

if (TryGetResourceDerivedType(context, resolved, flags) is ITypeReference resourceDerivedType)
{
return resourceDerivedType;
}

if (resolved.Type.Value == TemplateParameterType.SecureString || resolved.Type.Value == TemplateParameterType.SecureObject)
{
flags = TypeSymbolValidationFlags.IsSecure | flags;
}

var bicepType = resolved.Type.Value switch
{
TemplateParameterType.String or
TemplateParameterType.SecureString => GetStringType(resolved, flags),
TemplateParameterType.Int => GetIntegerType(resolved, flags),
TemplateParameterType.Bool => GetBooleanType(resolved, flags),
TemplateParameterType.Array => GetArrayType(context, resolved),
TemplateParameterType.Object or
TemplateParameterType.SecureObject => GetObjectType(context, resolved, flags),
_ => ErrorType.Empty(),
};
var bicepType = ToTypeReferenceIgnoringNullability(context, resolved, flags);

if (resolved.Nullable?.Value == true)
{
Expand All @@ -59,6 +39,26 @@ TemplateParameterType.Object or
}
}

private static ITypeReference ToTypeReferenceIgnoringNullability(SchemaValidationContext context, ITemplateSchemaNode withResolvedRefs, TypeSymbolValidationFlags flags)
{
if (TryGetResourceDerivedType(context, withResolvedRefs, flags) is ITypeReference resourceDerivedType)
{
return resourceDerivedType;
}

return withResolvedRefs.Type.Value switch
{
TemplateParameterType.String => GetStringType(withResolvedRefs, flags),
TemplateParameterType.SecureString => GetStringType(withResolvedRefs, flags | TypeSymbolValidationFlags.IsSecure),
TemplateParameterType.Int => GetIntegerType(withResolvedRefs, flags),
TemplateParameterType.Bool => GetBooleanType(withResolvedRefs, flags),
TemplateParameterType.Array => GetArrayType(context, withResolvedRefs),
TemplateParameterType.Object => GetObjectType(context, withResolvedRefs, flags),
TemplateParameterType.SecureObject => GetObjectType(context, withResolvedRefs, flags | TypeSymbolValidationFlags.IsSecure),
_ => ErrorType.Empty(),
};
}

private static ITypeReference? TryGetResourceDerivedType(SchemaValidationContext context, ITemplateSchemaNode schemaNode, TypeSymbolValidationFlags flags)
{
if (schemaNode.Metadata?.Value is JObject metadataObject &&
Expand Down