Skip to content

Commit

Permalink
Add scope to module/resource type defs
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-c-martin committed Jan 28, 2021
1 parent 5d1c1d5 commit d7370cc
Show file tree
Hide file tree
Showing 15 changed files with 86 additions and 69 deletions.
6 changes: 2 additions & 4 deletions src/Bicep.Core.IntegrationTests/ScopeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ public void Errors_are_raised_for_existing_resources_at_invalid_scopes()
"));

diags.Should().HaveDiagnostics(new[] {
("BCP124", DiagnosticLevel.Error, "The supplied scope \"subscription\" is not valid for this resource type. Permitted scopes: \"resourceGroup\"."),
("BCP124", DiagnosticLevel.Error, "Scope \"subscription\" is not valid for this resource type. Permitted scopes: \"resourceGroup\"."),
});

// use an invalid targetScope without setting the scope property
Expand All @@ -260,11 +260,9 @@ public void Errors_are_raised_for_existing_resources_at_invalid_scopes()
}
"));

/* TODO make this test pass
diags.Should().HaveDiagnostics(new[] {
("BCP124", DiagnosticLevel.Error, "The supplied scope \"subscription\" is not valid for this resource type. Permitted scopes: \"resourceGroup\"."),
("BCP124", DiagnosticLevel.Error, "Scope \"subscription\" is not valid for this resource type. Permitted scopes: \"resourceGroup\"."),
});
*/
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ module moduleWithInvalidScope './empty.bicep' = {

module moduleWithMissingRequiredScope './subscription_empty.bicep' = {
//@[7:37) [BCP035 (Error)] The specified "module" declaration is missing the following required properties: "scope". |moduleWithMissingRequiredScope|
//@[69:113) [BCP123 (Error)] Scope "resourceGroup" is not valid for this module. Permitted scopes: "subscription". |{\n name: 'moduleWithMissingRequiredScope'\n}|
name: 'moduleWithMissingRequiredScope'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -700,13 +700,11 @@ resource invalidScope 'My.Rp/mockResource@2020-12-01' = {
resource invalidScope2 'My.Rp/mockResource@2020-12-01' = {
name: 'invalidScope2'
scope: resourceGroup()
//@[9:24) [BCP124 (Error)] The supplied scope "resourceGroup" is not valid for this resource type. Permitted scopes: "resource". |resourceGroup()|
}

resource invalidScope3 'My.Rp/mockResource@2020-12-01' = {
name: 'invalidScope3'
scope: subscription()
//@[9:23) [BCP124 (Error)] The supplied scope "subscription" is not valid for this resource type. Permitted scopes: "resource". |subscription()|
}

resource invalidDuplicateName1 'Mock.Rp/mockResource@2020-01-01' = {
Expand Down
3 changes: 2 additions & 1 deletion src/Bicep.Core.UnitTests/Utils/ResourceTypeProviderHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public static ResourceType CreateCustomResourceType(string fullyQualifiedType, s
var resourceProperties = LanguageConstants.GetCommonResourceProperties(reference)
.Concat(new TypeProperty("properties", new NamedObjectType("properties", validationFlags, customProperties, null), TypePropertyFlags.Required));

return new ResourceType(reference, new NamedObjectType(reference.FormatName(), validationFlags, resourceProperties, null));
var bodyType = new NamedObjectType(reference.FormatName(), validationFlags, resourceProperties, null);
return new ResourceType(reference, ResourceScope.Tenant | ResourceScope.ManagementGroup | ResourceScope.Subscription | ResourceScope.ResourceGroup | ResourceScope.Resource, bodyType);
}

public static AzResourceTypeProvider CreateAzResourceTypeProvider(Action<Azure.Bicep.Types.Concrete.TypeFactory> typeFactoryFunc)
Expand Down
5 changes: 3 additions & 2 deletions src/Bicep.Core.UnitTests/Utils/TestResourceTypeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ public class TestResourceTypeProvider : IResourceTypeProvider
{
public ResourceType GetType(ResourceTypeReference reference, bool isExistingResource)
{
var resourceType = new ResourceType(reference, new NamedObjectType(reference.FormatName(), TypeSymbolValidationFlags.Default, LanguageConstants.CreateResourceProperties(reference), null));
var bodyType = new NamedObjectType(reference.FormatName(), TypeSymbolValidationFlags.Default, LanguageConstants.CreateResourceProperties(reference), null);
var resourceType = new ResourceType(reference, ResourceScope.Tenant | ResourceScope.ManagementGroup | ResourceScope.Subscription | ResourceScope.ResourceGroup | ResourceScope.Resource, bodyType);

return AzResourceTypeProvider.SetBicepResourceProperties(resourceType, Azure.Bicep.Types.Concrete.ScopeType.Unknown, isExistingResource);
return AzResourceTypeProvider.SetBicepResourceProperties(resourceType, isExistingResource);
}

public bool HasType(ResourceTypeReference typeReference)
Expand Down
4 changes: 2 additions & 2 deletions src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -745,13 +745,13 @@ public Diagnostic RuntimePropertyNotAllowed(string property, IEnumerable<string>
TextSpan,
DiagnosticLevel.Error,
"BCP123",
$"The supplied scope {ToQuotedString(GetResourceScopeDescriptions(suppliedScope))} is not valid for this module. Permitted scopes: {ToQuotedString(GetResourceScopeDescriptions(supportedScopes))}.");
$"Scope {ToQuotedString(GetResourceScopeDescriptions(suppliedScope))} is not valid for this module. Permitted scopes: {ToQuotedString(GetResourceScopeDescriptions(supportedScopes))}.");

public Diagnostic UnsupportedResourceScope(ResourceScope suppliedScope, ResourceScope supportedScopes) => new(
TextSpan,
DiagnosticLevel.Error,
"BCP124",
$"The supplied scope {ToQuotedString(GetResourceScopeDescriptions(suppliedScope))} is not valid for this resource type. Permitted scopes: {ToQuotedString(GetResourceScopeDescriptions(supportedScopes))}.");
$"Scope {ToQuotedString(GetResourceScopeDescriptions(suppliedScope))} is not valid for this resource type. Permitted scopes: {ToQuotedString(GetResourceScopeDescriptions(supportedScopes))}.");
}

public static DiagnosticBuilderInternal ForPosition(TextSpan span)
Expand Down
16 changes: 14 additions & 2 deletions src/Bicep.Core/Emit/EmitLimitationCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ void logInvalidScopeDiagnostic(IPositionable positionable, ResourceScope supplie

foreach (var resourceSymbol in semanticModel.Root.ResourceDeclarations)
{
if (resourceSymbol.Type is not ResourceType resourceType)
{
// missing type should be caught during type validation
continue;
}

var scopeProperty = resourceSymbol.SafeGetBodyProperty(LanguageConstants.ResourceScopePropertyName);
var scopeData = ScopeHelper.ValidateScope(semanticModel, logInvalidScopeDiagnostic, scopeProperty);
var scopeData = ScopeHelper.ValidateScope(semanticModel, logInvalidScopeDiagnostic, resourceType.ValidParentScopes, resourceSymbol.DeclaringResource.Body, scopeProperty);

if (scopeData is null)
{
Expand All @@ -60,8 +66,14 @@ void logInvalidScopeDiagnostic(IPositionable positionable, ResourceScope supplie

foreach (var moduleSymbol in semanticModel.Root.ModuleDeclarations)
{
if (moduleSymbol.Type is not ModuleType moduleType)
{
// missing type should be caught during type validation
continue;
}

var scopeProperty = moduleSymbol.SafeGetBodyProperty(LanguageConstants.ResourceScopePropertyName);
var scopeData = ScopeHelper.ValidateScope(semanticModel, logInvalidScopeDiagnostic, scopeProperty);
var scopeData = ScopeHelper.ValidateScope(semanticModel, logInvalidScopeDiagnostic, moduleType.ValidParentScopes, moduleSymbol.DeclaringModule.Body, scopeProperty);

if (scopeData is null)
{
Expand Down
29 changes: 17 additions & 12 deletions src/Bicep.Core/Emit/ScopeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,16 @@ public class ScopeData

public delegate void LogInvalidScopeDiagnostic(IPositionable positionable, ResourceScope suppliedScope, ResourceScope supportedScopes);

public static ScopeData? ValidateScope(SemanticModel semanticModel, LogInvalidScopeDiagnostic logInvalidScopeFunc, ObjectPropertySyntax? scopeProperty)
public static ScopeData? ValidateScope(SemanticModel semanticModel, LogInvalidScopeDiagnostic logInvalidScopeFunc, ResourceScope supportedScopes, SyntaxBase bodySyntax, ObjectPropertySyntax? scopeProperty)
{
if (scopeProperty is null || semanticModel.GetDeclaredType(scopeProperty) is not IScopeReference declaredScope)
if (scopeProperty is null)
{
// no scope provided - use the target scope for the file
if (!supportedScopes.HasFlag(semanticModel.TargetScope))
{
logInvalidScopeFunc(bodySyntax, semanticModel.TargetScope, supportedScopes);
}

return null;
}

Expand All @@ -44,17 +49,17 @@ public class ScopeData
switch (scopeType)
{
case TenantScopeType type:
if (!declaredScope.Scope.HasFlag(ResourceScope.Tenant))
if (!supportedScopes.HasFlag(ResourceScope.Tenant))
{
logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Tenant, declaredScope.Scope);
logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Tenant, supportedScopes);
return null;
}

return new ScopeData { RequestedScope = ResourceScope.Tenant };
case ManagementGroupScopeType type:
if (!declaredScope.Scope.HasFlag(ResourceScope.ManagementGroup))
if (!supportedScopes.HasFlag(ResourceScope.ManagementGroup))
{
logInvalidScopeFunc(scopeProperty.Value, ResourceScope.ManagementGroup, declaredScope.Scope);
logInvalidScopeFunc(scopeProperty.Value, ResourceScope.ManagementGroup, supportedScopes);
return null;
}

Expand All @@ -63,9 +68,9 @@ public class ScopeData
_ => new ScopeData { RequestedScope = ResourceScope.ManagementGroup, ManagementGroupNameProperty = type.Arguments[0].Expression },
};
case SubscriptionScopeType type:
if (!declaredScope.Scope.HasFlag(ResourceScope.Subscription))
if (!supportedScopes.HasFlag(ResourceScope.Subscription))
{
logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Subscription, declaredScope.Scope);
logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Subscription, supportedScopes);
return null;
}

Expand All @@ -74,9 +79,9 @@ public class ScopeData
_ => new ScopeData { RequestedScope = ResourceScope.Subscription, SubscriptionIdProperty = type.Arguments[0].Expression },
};
case ResourceGroupScopeType type:
if (!declaredScope.Scope.HasFlag(ResourceScope.ResourceGroup))
if (!supportedScopes.HasFlag(ResourceScope.ResourceGroup))
{
logInvalidScopeFunc(scopeProperty.Value, ResourceScope.ResourceGroup, declaredScope.Scope);
logInvalidScopeFunc(scopeProperty.Value, ResourceScope.ResourceGroup, supportedScopes);
return null;
}

Expand All @@ -86,9 +91,9 @@ public class ScopeData
_ => new ScopeData { RequestedScope = ResourceScope.ResourceGroup, SubscriptionIdProperty = type.Arguments[0].Expression, ResourceGroupProperty = type.Arguments[1].Expression },
};
case {} when scopeSymbol is ResourceSymbol targetResourceSymbol:
if (!declaredScope.Scope.HasFlag(ResourceScope.Resource))
if (!supportedScopes.HasFlag(ResourceScope.Resource))
{
logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Resource, declaredScope.Scope);
logInvalidScopeFunc(scopeProperty.Value, ResourceScope.Resource, supportedScopes);
return null;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Bicep.Core/LanguageConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public static TypeSymbol CreateModuleType(IEnumerable<TypeProperty> paramsProper
},
null);

return new ModuleType(typeName, moduleBody);
return new ModuleType(typeName, moduleScope, moduleBody);
}

public static IEnumerable<TypeProperty> CreateResourceProperties(ResourceTypeReference resourceTypeReference)
Expand Down
19 changes: 18 additions & 1 deletion src/Bicep.Core/TypeSystem/Az/AzResourceTypeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private TypeSymbol ToTypeSymbol(Azure.Bicep.Types.Concrete.TypeBase typeBase, bo
var resourceTypeReference = ResourceTypeReference.Parse(resourceType.Name);
var bodyType = GetTypeSymbol(resourceType.Body.Type, true);

return new ResourceType(resourceTypeReference, bodyType);
return new ResourceType(resourceTypeReference, ToResourceScope(resourceType.ScopeType), bodyType);
}
case Azure.Bicep.Types.Concrete.UnionType unionType:
{
Expand Down Expand Up @@ -146,5 +146,22 @@ private static TypeSymbolValidationFlags GetValidationFlags(bool isResourceBodyT
// in all other places, we should allow some wiggle room so that we don't block compilation if there are any swagger inaccuracies
return TypeSymbolValidationFlags.WarnOnTypeMismatch;
}

private static ResourceScope ToResourceScope(Azure.Bicep.Types.Concrete.ScopeType input)
{
if (input == Azure.Bicep.Types.Concrete.ScopeType.Unknown)
{
return ResourceScope.Tenant | ResourceScope.ManagementGroup | ResourceScope.Subscription | ResourceScope.ResourceGroup | ResourceScope.Resource;
}

var output = ResourceScope.None;
output |= input.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.Extension) ? ResourceScope.Resource : ResourceScope.None;
output |= input.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.Tenant) ? ResourceScope.Tenant : ResourceScope.None;
output |= input.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.ManagementGroup) ? ResourceScope.ManagementGroup : ResourceScope.None;
output |= input.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.Subscription) ? ResourceScope.Subscription : ResourceScope.None;
output |= input.HasFlag(Azure.Bicep.Types.Concrete.ScopeType.ResourceGroup) ? ResourceScope.ResourceGroup : ResourceScope.None;

return output;
}
}
}
Loading

0 comments on commit d7370cc

Please sign in to comment.