Skip to content

Commit

Permalink
Allow use of type properties as type expressions (#13047)
Browse files Browse the repository at this point in the history
Resolves #12920
Resolves #9229

This PR allows elements or properties of types to be used as standalone
type expressions. This feature is compatible with both resource-derived
types and compile-time imports, as shown in the following example:

types.bicep
```bicep
@export()
type myObject = {
  quux: int
  saSku: resource<'Microsoft.Storage/storageAccounts@2022-09-01'>.sku
}
```

main.bicep
```bicep
import * as types from 'types.bicep'

type test = {
  baz: types.myObject
}

type test2 = {
  foo: {
    bar: test
  }
}

type test3 = test2.foo.bar.baz.quux
// ^^ compiles to {"$ref": "#/definitions/_1.myObject/properties/quux"}

type test4 = test2.foo.bar.baz.saSku.name
// ^^ compiles to:
// {
//   "type": "string",
//   "metadata": {
//     "__bicep_resource_derived_type!": "Microsoft.Storage/storageAccounts@2022-09-01#properties/sku/properties/name"
//   }
// }
```

Accessing the following element kinds is supported:
* Properties may be dereferenced via dot notation
* Tuple items may be dereferenced via array index
* An object's additional properties type declaration may be dereferenced
via a special `.*` syntax
* A typed array's element type declaration may be dereferenced via a
special `[*]` syntax

For example:

```bicep
type anObject = {
  property: int
  *: string
}

type strings = string[]

type tuple = [int, string]

param propertyDeref anObject.property = 10 // type compiles to {"$ref": "#/definitions/anObject/properties/property"}
param additionalPropertiesDeref anObject.* = 'foo' // type compiles to {"$ref": "#/definitions/anObject/additionalProperties"}
param elementDeref strings[*] = 'bar' // type compiles to {"$ref": "#/definitions/strings/items"}
param itemDeref tuple[1] = 'baz' // type compiles to {"$ref": "#/definitions/tuple/prefixItems/1"}
```
CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/13047)
  • Loading branch information
jeskew authored and asilverman committed Jan 26, 2024
1 parent 95bbaff commit 7675ab7
Show file tree
Hide file tree
Showing 103 changed files with 4,687 additions and 1,466 deletions.
15 changes: 13 additions & 2 deletions docs/grammar.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,17 +178,28 @@ singularTypeExpression ->
parenthesizedTypeExpression
primaryTypeExpression ->
ambientTypeReference |
IDENTIFIER(type) |
typeReference |
literalValue |
unaryOperator literalValue |
stringComplete |
multilineString |
objectType |
tupleType
typeReference ->
ambientTypeReference |
fullyQualifiedAmbientTypeReference |
IDENTIFIER(type) |
IDENTIFIER(importedType) |
IDENTIFIER(wildcardImport) "." IDENTIFIER(type) |
typeReference "." IDENTIFIER(property) |
typeReference "[" (NUMBER | "*") "]" |
typeReference ".*"
ambientTypeReference -> "string" | "int" | "bool" | "array" | "object"
fullyQualifiedAmbientTypeReference -> IDENTIFIER(sysNamespace) "." ambientTypeReference
objectType -> "{" (NL+ ((objectTypeProperty | objectTypeAdditionalPropertiesMatcher) NL+ )* )? "}"
objectTypeProperty -> decorator* ( IDENTIFIER(name) | stringComplete | multilineString ) ":" typeExpression
objectTypeAdditionalPropertiesMatcher -> decorator* "*:" typeExpression
Expand Down
54 changes: 38 additions & 16 deletions src/Bicep.Cli.IntegrationTests/BuildCommandTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Bicep.Cli.UnitTests;
using Bicep.Core;
using Bicep.Core.Configuration;
using Bicep.Core.FileSystem;
Expand All @@ -14,6 +21,7 @@
using Bicep.Core.UnitTests.Utils;
using FluentAssertions;
using FluentAssertions.Execution;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -128,30 +136,30 @@ public async Task Build_Valid_SingleFile_WithProviderDeclarationStatement(
}

// 3. create a main.bicep and save it to a output directory
var bicepFile = $@"
import '{providerDeclarationSyntax}@2.0.0'
";
var bicepFile = $"""
provider '{providerDeclarationSyntax}@2.0.0'
""";
var tempDirectory = FileHelper.GetUniqueTestOutputPath(TestContext);
Directory.CreateDirectory(tempDirectory);
var bicepFilePath = Path.Combine(tempDirectory, "main.bicep");
File.WriteAllText(bicepFilePath, bicepFile);

var bicepConfigFile = $$"""
{
"providerAliases" : {
"br": {
"contoso": {
"registry": "contoso.azurecr.io",
"providerPath": "bicep/providers"
},
"mcr": {
"registry": "mcr.microsoft.com",
"providerPath": "bicep/providers"
{
"providerAliases" : {
"br": {
"contoso": {
"registry": "contoso.azurecr.io",
"providerPath": "bicep/providers"
},
"mcr": {
"registry": "mcr.microsoft.com",
"providerPath": "bicep/providers"
}
}
}
}
}
""";
""";
var bicepConfigPath = Path.Combine(tempDirectory, "bicepconfig.json");
File.WriteAllText(bicepConfigPath, bicepConfigFile);

Expand All @@ -160,7 +168,21 @@ public async Task Build_Valid_SingleFile_WithProviderDeclarationStatement(

// TEST
// 5. run bicep build
var (output, error, result) = await Bicep(settings, "build", bicepFilePath);
var (output, error, result) = await TextWriterHelper.InvokeWriterAction((@out, err)
=> new Program(new(Output: @out, Error: err), services
=> {
if (settings.FeatureOverrides is {})
{
services.WithFeatureOverrides(settings.FeatureOverrides);
}

services
.WithEmptyAzResources()
.AddSingleton(settings.Environment ?? BicepTestConstants.EmptyEnvironment)
.AddSingleton(settings.ClientFactory)
.AddSingleton(settings.TemplateSpecRepositoryFactory);
})
.RunAsync(new[] { "build", bicepFilePath }, CancellationToken.None));

// ASSERT
// 6. assert 'bicep build' completed successfully
Expand Down
5 changes: 2 additions & 3 deletions src/Bicep.Cli.IntegrationTests/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public abstract class TestBase : Bicep.Core.UnitTests.TestBase
{
private static BicepCompiler CreateCompiler(IContainerRegistryClientFactory clientFactory, ITemplateSpecRepositoryFactory templateSpecRepositoryFactory)
=> ServiceBuilder.Create(
x => x.WithEmptyAzResources().AddSingleton(clientFactory).AddSingleton(templateSpecRepositoryFactory)).GetCompiler();
x => x.AddSingleton(clientFactory).AddSingleton(templateSpecRepositoryFactory)).GetCompiler();

protected const string BuildSummaryFailedRegex = @"Build failed: (\d*) Warning\(s\), ([1-9][0-9]*) Error\(s\)";
protected const string BuildSummarySucceededRegex = @"Build succeeded: (\d*) Warning\(s\), 0 Error\(s\)";
Expand Down Expand Up @@ -58,7 +58,6 @@ protected static Task<CliResult> Bicep(InvocationSettings settings, params strin
}

services
.WithEmptyAzResources()
.AddSingleton(settings.Environment ?? BicepTestConstants.EmptyEnvironment)
.AddSingleton(settings.ClientFactory)
.AddSingleton(settings.TemplateSpecRepositoryFactory);
Expand Down Expand Up @@ -92,7 +91,7 @@ protected static async Task<IEnumerable<string>> GetAllDiagnostics(string bicepF

protected static async Task<IEnumerable<string>> GetAllParamDiagnostics(string paramFilePath)
{
var compiler = new ServiceBuilder().WithEmptyAzResources().Build().GetCompiler();
var compiler = new ServiceBuilder().Build().GetCompiler();

var compilation = await compiler.CreateCompilation(PathHelper.FilePathToFileUrl(paramFilePath));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ public void DirectResourceCollectionAccess_NotAllowedWithinUnsupportedResourcePr
public void DirectResourceCollectionAccess_NotAllowedWithinUnsupportedResourceProperties_InlinedVariable()
{
const string additionalContent = """
var containerWorkerIps = map(containerWorkers, (w) => w.properties.ipAddress.ip)
var containerWorkerIps = join(map(containerWorkers, (w) => w.properties.ipAddress.ip), ',')
resource propertyLoop 'Microsoft.ContainerInstance/containerGroups@2022-09-01' = {
name: 'gh9440-inlined'
location: 'westus'
Expand All @@ -234,7 +234,7 @@ public void DirectResourceCollectionAccess_NotAllowedWithinUnsupportedResourcePr
{
const string additionalContent = """
var containerWorkersAliased = containerWorkers
var containerWorkerIps = map(containerWorkersAliased, (w) => w.properties.ipAddress.ip)
var containerWorkerIps = join(map(containerWorkersAliased, (w) => w.properties.ipAddress.ip), ',')
resource propertyLoop 'Microsoft.ContainerInstance/containerGroups@2022-09-01' = {
name: 'gh9440-inlined'
location: 'westus'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ namespace Bicep.Core.IntegrationTests.Emit
public class TemplateEmitterTests
{
private static ServiceBuilder Services => new ServiceBuilder()
.WithEmptyAzResources()
.WithEnvironmentVariables(
("stringEnvVariableName", "test"),
("intEnvVariableName", "100"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void Type_validation_runs_on_compilation_successfully(TypeSymbolValidatio

[DataTestMethod]
[DataRow(TypeSymbolValidationFlags.Default, DiagnosticLevel.Error)]
[DataRow(TypeSymbolValidationFlags.WarnOnTypeMismatch, DiagnosticLevel.Warning)]
[DataRow(TypeSymbolValidationFlags.WarnOnTypeMismatch | TypeSymbolValidationFlags.WarnOnPropertyTypeMismatch, DiagnosticLevel.Warning)]
public void Type_validation_runs_on_compilation_common_failures(TypeSymbolValidationFlags validationFlags, DiagnosticLevel expectedDiagnosticLevel)
{
var customTypes = new[] {
Expand Down Expand Up @@ -176,7 +176,7 @@ public void Type_validation_narrowing_on_union_types(TypeSymbolValidationFlags v

[DataTestMethod]
[DataRow(TypeSymbolValidationFlags.Default, DiagnosticLevel.Error)]
[DataRow(TypeSymbolValidationFlags.WarnOnTypeMismatch, DiagnosticLevel.Warning)]
[DataRow(TypeSymbolValidationFlags.WarnOnTypeMismatch | TypeSymbolValidationFlags.WarnOnPropertyTypeMismatch, DiagnosticLevel.Warning)]
public void Type_validation_narrowing_on_discriminated_object_types(TypeSymbolValidationFlags validationFlags, DiagnosticLevel expectedDiagnosticLevel)
{
var customTypes = new[] {
Expand Down
Loading

0 comments on commit 7675ab7

Please sign in to comment.