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

Correct providers() return type (again...) #2018

Merged
merged 3 commits into from
Mar 26, 2021
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 105 additions & 3 deletions src/Bicep.Core.IntegrationTests/ScenarioTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -915,17 +915,17 @@ public void Test_Issue1630()
var firstApiVersion = singleResource.apiVersions[0]

// allResources is an array of objects!
var firstResourceFirstApiVersion = allResources[0].apiVersions[0]
var firstResourceFirstApiVersion = allResources.resourceTypes[0].apiVersions[0]

output singleResource object = singleResource
output allResources array = allResources
output allResources array = allResources.resourceTypes
");

result.Should().NotHaveDiagnostics();
result.Template.Should().HaveValueAtPath("$.variables['singleResource']", "[providers('Microsoft.Insights', 'components')]");
result.Template.Should().HaveValueAtPath("$.variables['firstApiVersion']", "[variables('singleResource').apiVersions[0]]");
result.Template.Should().HaveValueAtPath("$.variables['allResources']", "[providers('Microsoft.Insights')]");
result.Template.Should().HaveValueAtPath("$.variables['firstResourceFirstApiVersion']", "[variables('allResources')[0].apiVersions[0]]");
result.Template.Should().HaveValueAtPath("$.variables['firstResourceFirstApiVersion']", "[variables('allResources').resourceTypes[0].apiVersions[0]]");
}

[TestMethod]
Expand Down Expand Up @@ -1422,5 +1422,107 @@ public void Test_Issue1993()
evaluated.Should().HaveValueAtPath("$.outputs['goodArray'].value", expectedOutput);
evaluated.Should().HaveValueAtPath("$.outputs['badArray'].value", expectedOutput);
}

[TestMethod]
// https://github.com/azure/bicep/issues/2009
public void Test_Issue2009()
{
var result = CompilationHelper.Compile(@"
param providerNamespace string = 'Microsoft.Web'

output providerOutput object = {
thing: providers(providerNamespace)

otherThing: providers(providerNamespace, 'sites')
}
");

result.Should().NotHaveDiagnostics();

var providersMetadata = new[] {
new {
@namespace = "Microsoft.Web",
resourceTypes = new[] {
new {
resourceType = "sites",
locations = new[] { "West US", "East US", },
apiVersions = new[] { "2019-01-01", "2020-01-01", },
}
}
}
};

var evaluated = TemplateEvaluator.Evaluate(result.Template, config => config with {
Metadata = new() {
["providers"] = JToken.FromObject(providersMetadata),
}
});

evaluated.Should().HaveValueAtPath("$.outputs['providerOutput'].value.thing", new JObject {
["namespace"] = "Microsoft.Web",
["resourceTypes"] = new JArray {
new JObject {
["resourceType"] = "sites",
["locations"] = new JArray { "West US", "East US" },
["apiVersions"] = new JArray { "2019-01-01", "2020-01-01" },
}
}
});

evaluated.Should().HaveValueAtPath("$.outputs['providerOutput'].value.otherThing", new JObject {
["resourceType"] = "sites",
["locations"] = new JArray { "West US", "East US" },
["apiVersions"] = new JArray { "2019-01-01", "2020-01-01" },
});
}

[TestMethod]
// https://github.com/azure/bicep/issues/2009
public void Test_Issue2009_expanded()
{
var result = CompilationHelper.Compile(@"
output providersNamespace string = providers('Test.Rp').namespace
output providersResources array = providers('Test.Rp').resourceTypes

output providersResourceType string = providers('Test.Rp', 'fakeResource').resourceType
output providersApiVersionFirst string = providers('Test.Rp', 'fakeResource').apiVersions[0]
output providersLocationFirst string = providers('Test.Rp', 'fakeResource').locations[0]
");

result.Should().NotHaveDiagnostics();

var providersMetadata = new[] {
new {
@namespace = "Test.Rp",
resourceTypes = new[] {
new {
resourceType = "fakeResource",
locations = new[] { "Earth", "Mars" },
apiVersions = new[] { "3024-01-01", "4100-01-01", },
}
}
}
};

var evaluated = TemplateEvaluator.Evaluate(result.Template, config => config with {
Metadata = new() {
["providers"] = JToken.FromObject(providersMetadata),
}
});

evaluated.Should().HaveValueAtPath("$.outputs['providersNamespace'].value", "Test.Rp");
evaluated.Should().HaveValueAtPath("$.outputs['providersResources'].value", new JArray {
new JObject {
["resourceType"] = "fakeResource",
["locations"] = new JArray { "Earth", "Mars" },
["apiVersions"] = new JArray { "3024-01-01", "4100-01-01" },
}
});


evaluated.Should().HaveValueAtPath("$.outputs['providersResourceType'].value", "fakeResource");
evaluated.Should().HaveValueAtPath("$.outputs['providersApiVersionFirst'].value", "3024-01-01");
evaluated.Should().HaveValueAtPath("$.outputs['providersLocationFirst'].value", "Earth");
}
}
}
4 changes: 3 additions & 1 deletion src/Bicep.Core.IntegrationTests/TemplateEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public record EvaluationConfiguration(
string ResourceGroup,
string RgLocation,
Dictionary<string, JToken> Parameters,
Dictionary<string, JToken> Metadata,
OnListDelegate? OnListFunc,
OnReferenceDelegate? OnReferenceFunc)
{
Expand All @@ -46,6 +47,7 @@ public record EvaluationConfiguration(
TestResourceGroupName,
TestLocation,
new(),
new(),
null,
null
);
Expand Down Expand Up @@ -158,7 +160,7 @@ private static JToken EvaluateTemplate(JToken? templateJtoken, EvaluationConfigu
_ => throw new InvalidOperationException(),
};

var metadata = new InsensitiveDictionary<JToken>();
var metadata = new InsensitiveDictionary<JToken>(config.Metadata);
if (deploymentScope == TemplateDeploymentScope.Subscription || deploymentScope == TemplateDeploymentScope.ResourceGroup)
{
metadata["subscription"] = new JObject {
Expand Down
4 changes: 2 additions & 2 deletions src/Bicep.Core.Samples/Files/Functions/az.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
"minimumArgumentCount": 1,
"maximumArgumentCount": 1,
"flags": "default",
"typeSignature": "(providerNamespace: string): provider[]",
"typeSignature": "(providerNamespace: string): Provider",
"parameterTypeSignatures": [
"providerNamespace: string"
]
Expand All @@ -156,7 +156,7 @@
"minimumArgumentCount": 2,
"maximumArgumentCount": 2,
"flags": "default",
"typeSignature": "(providerNamespace: string, resourceType: string): provider",
"typeSignature": "(providerNamespace: string, resourceType: string): ProviderResource",
"parameterTypeSignatures": [
"providerNamespace: string",
"resourceType: string"
Expand Down
19 changes: 14 additions & 5 deletions src/Bicep.Core/Semantics/Namespaces/AzNamespaceSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,26 @@ private static ObjectType GetSubscriptionReturnType(IEnumerable<FunctionArgument
});
}

private static ObjectType GetSingleProvidersReturnType()
private static ObjectType GetProvidersSingleResourceReturnType()
{
// from https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions-resource?tabs=json#providers
return new ObjectType("provider", TypeSymbolValidationFlags.Default, new []
return new ObjectType("ProviderResource", 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 ObjectType GetProvidersSingleProviderReturnType()
{
// from https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions-resource?tabs=json#providers
return new ObjectType("Provider", TypeSymbolValidationFlags.Default, new []
{
new TypeProperty("namespace", LanguageConstants.String),
new TypeProperty("resourceTypes", new TypedArrayType(GetProvidersSingleResourceReturnType(), TypeSymbolValidationFlags.Default)),
}, null);
}

private static ObjectType GetEnvironmentReturnType()
{
Expand Down Expand Up @@ -290,15 +300,14 @@ private static IEnumerable<FunctionOverload> GetAzOverloads(ResourceScope resour
.WithVariableParameter("resourceName",LanguageConstants.String, minimumCount: 1, "The extension resource name segment")
.Build();

var singleProvider = GetSingleProvidersReturnType();
yield return new FunctionOverloadBuilder("providers")
.WithReturnType(new TypedArrayType(singleProvider, TypeSymbolValidationFlags.Default))
.WithReturnType(GetProvidersSingleProviderReturnType())
.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)
.WithReturnType(GetProvidersSingleResourceReturnType())
.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")
.WithRequiredParameter("resourceType",LanguageConstants.String, "The type of resource within the specified namespace")
Expand Down