Skip to content

Commit

Permalink
Add type definitions for providers() (#1877)
Browse files Browse the repository at this point in the history
* Add type definitions for providers()

* Update test baselines

* Update AzNamespaceSymbol.cs

Co-authored-by: Bicep Automation <[email protected]>
  • Loading branch information
anthony-c-martin and Bicep Automation authored Mar 15, 2021
1 parent da67710 commit ea8526c
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 26 deletions.
28 changes: 28 additions & 0 deletions src/Bicep.Core.IntegrationTests/ScenarioTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -971,5 +971,33 @@ public void Test_Issue1817()
});
}
}

[TestMethod]
public void Test_Issue1630()
{
var (template, diags, _) = CompilationHelper.Compile(@"
var singleResource = providers('Microsoft.Insights', 'components')
var allResources = providers('Microsoft.Insights')
// singleResource is an object!
var firstApiVersion = singleResource.apiVersions[0]
// allResources is an array of objects!
var firstResourceFirstApiVersion = allResources[0].apiVersions[0]
output singleResource object = singleResource
output allResources array = allResources
");

using (new AssertionScope())
{
diags.Should().BeEmpty();

template.Should().HaveValueAtPath("$.variables['singleResource']", "[providers('Microsoft.Insights', 'components')]");
template.Should().HaveValueAtPath("$.variables['firstApiVersion']", "[variables('singleResource').apiVersions[0]]");
template.Should().HaveValueAtPath("$.variables['allResources']", "[providers('Microsoft.Insights')]");
template.Should().HaveValueAtPath("$.variables['firstResourceFirstApiVersion']", "[variables('allResources')[0].apiVersions[0]]");
}
}
}
}
27 changes: 23 additions & 4 deletions src/Bicep.Core.Samples/Files/Functions/az.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,25 @@
"[offset: int]"
]
},
{
"name": "providers",
"description": "Returns information about a resource provider and its supported resource types. If you don't provide a resource type, the function returns all the supported types for the resource provider.",
"fixedParameters": [
{
"name": "providerNamespace",
"description": "the namespace of the provider",
"type": "string",
"required": true
}
],
"minimumArgumentCount": 1,
"maximumArgumentCount": 1,
"flags": "default",
"typeSignature": "(providerNamespace: string): provider[]",
"parameterTypeSignatures": [
"providerNamespace: string"
]
},
{
"name": "providers",
"description": "Returns information about a resource provider and its supported resource types. If you don't provide a resource type, the function returns all the supported types for the resource provider.",
Expand All @@ -131,16 +150,16 @@
"name": "resourceType",
"description": "The type of resource within the specified namespace",
"type": "string",
"required": false
"required": true
}
],
"minimumArgumentCount": 1,
"minimumArgumentCount": 2,
"maximumArgumentCount": 2,
"flags": "default",
"typeSignature": "(providerNamespace: string, [resourceType: string]): array",
"typeSignature": "(providerNamespace: string, resourceType: string): provider",
"parameterTypeSignatures": [
"providerNamespace: string",
"[resourceType: string]"
"resourceType: string"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ public static AndConstraint<JTokenAssertions> HaveValueAtPath(this JTokenAsserti
Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(valueAtPath is not null)
.FailWith("Expected value at path {0} to be {1} but it was null", jtokenPath, expected.ToString());
.FailWith("Expected value at path {0} to be {1}{reason} but it was null. Original JSON: {2}", jtokenPath, expected.ToString(), instance.Subject?.ToString());

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(JToken.DeepEquals(valueAtPath, expected))
.FailWith("Expected value at path {0} to be {1} but it was {2}", jtokenPath, expected.ToString(), valueAtPath?.ToString());
.FailWith("Expected value at path {0} to be {1}{reason} but it was {2}", jtokenPath, expected.ToString(), valueAtPath?.ToString());

return new AndConstraint<JTokenAssertions>(instance);
}
Expand All @@ -86,7 +86,7 @@ public static AndConstraint<JTokenAssertions> NotHaveValueAtPath(this JTokenAsse
Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(valueAtPath is null)
.FailWith("Expected value at path {0} to be null, but it was {1}", jtokenPath, valueAtPath);
.FailWith("Expected value at path {0} to be null{reason}, but it was {1}", jtokenPath, valueAtPath);

return new AndConstraint<JTokenAssertions>(instance);
}
Expand All @@ -96,7 +96,7 @@ public static AndConstraint<JTokenAssertions> NotHaveValue(this JTokenAssertions
Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(instance.Subject is null)
.FailWith("Expected value to be null, but it was {0}", instance.Subject?.ToString());
.FailWith("Expected value to be null{reason}, but it was {0}", instance.Subject?.ToString());

return new AndConstraint<JTokenAssertions>(instance);
}
Expand Down
53 changes: 35 additions & 18 deletions src/Bicep.Core/Semantics/Namespaces/AzNamespaceSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ namespace Bicep.Core.Semantics.Namespaces
{
public class AzNamespaceSymbol : NamespaceSymbol
{
private static ObjectType GetRestrictedResourceGroupReturnValue(IEnumerable<FunctionArgumentSyntax> arguments)
private static ObjectType GetRestrictedResourceGroupReturnType(IEnumerable<FunctionArgumentSyntax> arguments)
=> new ResourceGroupScopeType(arguments, Enumerable.Empty<TypeProperty>());

private static ObjectType GetRestrictedSubscriptionReturnValue(IEnumerable<FunctionArgumentSyntax> arguments)
private static ObjectType GetRestrictedSubscriptionReturnType(IEnumerable<FunctionArgumentSyntax> arguments)
=> new SubscriptionScopeType(arguments, Enumerable.Empty<TypeProperty>());

private static ObjectType GetRestrictedManagementGroupReturnValue(IEnumerable<FunctionArgumentSyntax> arguments)
private static ObjectType GetRestrictedManagementGroupReturnType(IEnumerable<FunctionArgumentSyntax> arguments)
=> new ManagementGroupScopeType(arguments, Enumerable.Empty<TypeProperty>());

private static ObjectType GetRestrictedTenantReturnValue(IEnumerable<FunctionArgumentSyntax> arguments)
private static ObjectType GetRestrictedTenantReturnType(IEnumerable<FunctionArgumentSyntax> arguments)
=> new TenantScopeType(arguments, Enumerable.Empty<TypeProperty>());

private static ObjectType GetResourceGroupReturnValue(IEnumerable<FunctionArgumentSyntax> arguments)
private static ObjectType GetResourceGroupReturnType(IEnumerable<FunctionArgumentSyntax> arguments)
{
var properties = new NamedObjectType("properties", TypeSymbolValidationFlags.Default, new []
{
Expand All @@ -46,7 +46,7 @@ private static ObjectType GetResourceGroupReturnValue(IEnumerable<FunctionArgume
});
}

private static ObjectType GetSubscriptionReturnValue(IEnumerable<FunctionArgumentSyntax> arguments)
private static ObjectType GetSubscriptionReturnType(IEnumerable<FunctionArgumentSyntax> arguments)
{
return new SubscriptionScopeType(arguments, new []
{
Expand All @@ -56,6 +56,17 @@ private static ObjectType GetSubscriptionReturnValue(IEnumerable<FunctionArgumen
new TypeProperty("displayName", LanguageConstants.String),
});
}

private static ObjectType GetSingleProvidersReturnType()
{
// from https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions-resource?tabs=json#providers
return new NamedObjectType("provider", TypeSymbolValidationFlags.Default, new []
{
new TypeProperty("resourceType", LanguageConstants.String),
new TypeProperty("locations", new TypedArrayType(LanguageConstants.String, TypeSymbolValidationFlags.Default)),
new TypeProperty("apiVersions", new TypedArrayType(LanguageConstants.String, TypeSymbolValidationFlags.Default)),
}, null);
}

private static NamedObjectType GetEnvironmentReturnType()
{
Expand Down Expand Up @@ -130,55 +141,55 @@ private static NamedObjectType GetDeploymentReturnType(ResourceScope targetScope

yield return (
new FunctionOverloadBuilder("tenant")
.WithDynamicReturnType(GetRestrictedTenantReturnValue)
.WithDynamicReturnType(GetRestrictedTenantReturnType)
.WithDescription("Returns the current tenant scope.")
.Build(),
ResourceScope.Tenant | ResourceScope.ManagementGroup | ResourceScope.Subscription | ResourceScope.ResourceGroup);

yield return (
new FunctionOverloadBuilder("managementGroup")
.WithDynamicReturnType(GetRestrictedManagementGroupReturnValue)
.WithDynamicReturnType(GetRestrictedManagementGroupReturnType)
.WithDescription("Returns the current management group scope. **This function can only be used in managementGroup deployments.**")
.Build(),
ResourceScope.ManagementGroup);
yield return (
new FunctionOverloadBuilder("managementGroup")
.WithDynamicReturnType(GetRestrictedManagementGroupReturnValue)
.WithDynamicReturnType(GetRestrictedManagementGroupReturnType)
.WithDescription("Returns the scope for a named management group.")
.WithRequiredParameter("name", LanguageConstants.String, "The unique identifier of the management group (not the display name).")
.Build(),
ResourceScope.Tenant | ResourceScope.ManagementGroup | ResourceScope.Subscription | ResourceScope.ResourceGroup);

yield return (
new FunctionOverloadBuilder("subscription")
.WithDynamicReturnType(GetSubscriptionReturnValue)
.WithDynamicReturnType(GetSubscriptionReturnType)
.WithDescription("Returns the subscription scope for the current deployment. **This function can only be used in subscription and resourceGroup deployments.**")
.Build(),
ResourceScope.Subscription | ResourceScope.ResourceGroup);
yield return (
new FunctionOverloadBuilder("subscription")
.WithDynamicReturnType(GetRestrictedSubscriptionReturnValue)
.WithDynamicReturnType(GetRestrictedSubscriptionReturnType)
.WithDescription("Returns a named subscription scope. **This function can only be used in subscription and resourceGroup deployments.**")
.WithRequiredParameter("subscriptionId", LanguageConstants.String, "The subscription ID")
.Build(),
ResourceScope.Tenant | ResourceScope.ManagementGroup | ResourceScope.Subscription | ResourceScope.ResourceGroup);

yield return (
new FunctionOverloadBuilder("resourceGroup")
.WithDynamicReturnType(GetResourceGroupReturnValue)
.WithDynamicReturnType(GetResourceGroupReturnType)
.WithDescription("Returns the current resource group scope. **This function can only be used in resourceGroup deployments.**")
.Build(),
ResourceScope.ResourceGroup);
yield return (
new FunctionOverloadBuilder("resourceGroup")
.WithDynamicReturnType(GetRestrictedResourceGroupReturnValue)
.WithDynamicReturnType(GetRestrictedResourceGroupReturnType)
.WithDescription("Returns a named resource group scope. **This function can only be used in subscription and resourceGroup deployments.**")
.WithRequiredParameter("resourceGroupName", LanguageConstants.String, "The resource group name")
.Build(),
ResourceScope.Subscription | ResourceScope.ResourceGroup);
yield return (
new FunctionOverloadBuilder("resourceGroup")
.WithDynamicReturnType(GetRestrictedResourceGroupReturnValue)
.WithDynamicReturnType(GetRestrictedResourceGroupReturnType)
.WithDescription("Returns a named resource group scope. **This function can only be used in subscription and resourceGroup deployments.**")
.WithRequiredParameter("subscriptionId", LanguageConstants.String, "The subscription ID")
.WithRequiredParameter("resourceGroupName", LanguageConstants.String, "The resource group name")
Expand Down Expand Up @@ -279,12 +290,18 @@ private static IEnumerable<FunctionOverload> GetAzOverloads(ResourceScope resour
.WithVariableParameter("resourceName",LanguageConstants.String, minimumCount: 1, "The extension resource name segment")
.Build();

// TODO: Not sure about return type
var singleProvider = GetSingleProvidersReturnType();
yield return new FunctionOverloadBuilder("providers")
.WithReturnType(LanguageConstants.Array)
.WithReturnType(new TypedArrayType(singleProvider, TypeSymbolValidationFlags.Default))
.WithDescription("Returns information about a resource provider and its supported resource types. If you don't provide a resource type, the function returns all the supported types for the resource provider.")
.WithRequiredParameter("providerNamespace",LanguageConstants.String, "the namespace of the provider")
.Build();

yield return new FunctionOverloadBuilder("providers")
.WithReturnType(singleProvider)
.WithDescription("Returns information about a resource provider and its supported resource types. If you don't provide a resource type, the function returns all the supported types for the resource provider.")
.WithRequiredParameter("providerNamespace",LanguageConstants.String, "the namespace of the provider")
.WithOptionalParameter("resourceType",LanguageConstants.String, "The type of resource within the specified namespace")
.WithRequiredParameter("resourceType",LanguageConstants.String, "The type of resource within the specified namespace")
.Build();

// TODO: return type is string[]
Expand Down Expand Up @@ -325,4 +342,4 @@ public AzNamespaceSymbol(ResourceScope resourceScope)
{
}
}
}
}

0 comments on commit ea8526c

Please sign in to comment.