Skip to content

Commit

Permalink
Tidy-up
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-c-martin committed Mar 1, 2021
1 parent 3a6fb5a commit 0fda714
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 92 deletions.
33 changes: 21 additions & 12 deletions src/Bicep.Core/Syntax/SyntaxFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,32 @@ public static ObjectSyntax CreateObject(IEnumerable<ObjectPropertySyntax> proper
public static ArrayItemSyntax CreateArrayItem(SyntaxBase value)
=> new ArrayItemSyntax(value);

public static ForSyntax CreateForSyntax(string indexIdentifier, SyntaxBase count, SyntaxBase body)
public static ForSyntax CreateRangedForSyntax(string indexIdentifier, SyntaxBase count, SyntaxBase body)
{
return new ForSyntax(
// generates "range(0, <count>)"
var rangeSyntax = new FunctionCallSyntax(
new IdentifierSyntax(CreateToken(TokenType.Identifier, "range")),
CreateToken(TokenType.LeftParen, "("),
new FunctionArgumentSyntax[] {
new FunctionArgumentSyntax(
new IntegerLiteralSyntax(CreateToken(TokenType.Integer, "0"), 0),
CreateToken(TokenType.Comma, ",")),
new FunctionArgumentSyntax(count, null),
},
CreateToken(TokenType.RightParen, ")"));

return CreateForSyntax(indexIdentifier, rangeSyntax, body);
}

public static ForSyntax CreateForSyntax(string indexIdentifier, SyntaxBase inSyntax, SyntaxBase body)
{
// generates "[for <identifier> in <inSyntax>: <body>]"
return new(
CreateToken(TokenType.LeftSquare, "["),
CreateToken(TokenType.Identifier, "for"),
new LocalVariableSyntax(new IdentifierSyntax(CreateToken(TokenType.Identifier, indexIdentifier))),
CreateToken(TokenType.Identifier, "in"),
new FunctionCallSyntax(
new IdentifierSyntax(CreateToken(TokenType.Identifier, "range")),
CreateToken(TokenType.LeftParen, "("),
new FunctionArgumentSyntax[] {
new FunctionArgumentSyntax(
new IntegerLiteralSyntax(CreateToken(TokenType.Integer, "0"), 0),
CreateToken(TokenType.Comma, ",")),
new FunctionArgumentSyntax(count, null),
},
CreateToken(TokenType.RightParen, ")")),
inSyntax,
CreateToken(TokenType.Colon, ":"),
body,
CreateToken(TokenType.RightSquare, "]"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ [new Uri($"file:///{resourcePath}")] = jsonContents,
}

[DataTestMethod]
[DataRow("NonWorking/copyloop.json", "[11:9]: The 'copy' property is not supported")]
[DataRow("NonWorking/unknownprops.json", "[15:29]: Unrecognized top-level resource property 'madeUpProperty'")]
[DataRow("NonWorking/nested-outer.json", "[11:23]: Nested template decompilation requires 'inner' expression evaluation scope. See 'https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/linked-templates#expression-evaluation-scope-in-nested-templates' for more information Microsoft.Resources/deployments pid-00000000-0000-0000-0000-000000000000")]
public void Decompiler_raises_errors_for_unsupported_features(string resourcePath, string expectedMessage)
Expand Down
11 changes: 11 additions & 0 deletions src/Bicep.Decompiler.IntegrationTests/Working/copyloop/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
param storageCount int = 2

resource storage_id 'Microsoft.Storage/storageAccounts@2019-04-01' = [for i in range(0, storageCount): {
name: '${i}storage${uniqueString(resourceGroup().id)}'
location: resourceGroup().location
sku: {
name: 'Standard_LRS'
}
kind: 'Storage'
properties: {}
}]
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using Bicep.Decompiler.ArmHelpers;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json.Linq;
using Bicep.Core.UnitTests.Assertions;

namespace Bicep.Core.IntegrationTests.ArmHelpers
{
Expand Down Expand Up @@ -91,5 +93,21 @@ public void TryGetLocalFilePathForTemplateLink_fails_to_find_path_for_undecidabl

output.Should().BeNull();
}

[DataTestMethod]
[DataRow("{\"val\": \"[replaceMe()]\"}", "{\"val\": \"[replaced()]\"}")]
[DataRow("{\"val\": [\"[replaceMe()]\"]}", "{\"val\": [\"[replaced()]\"]}")]
[DataRow("\"[replaceMe()]\"", "\"[replaced()]\"")]
public void ReplaceFunctionExpressions_replaces_function_expressions(string jsonInput, string expectedJsonOutput)
{
var input = JToken.Parse(jsonInput);
var output = ExpressionHelpers.ReplaceFunctionExpressions(input, function => {
if (function.Function == "replaceMe") {
function.Function = "replaced";
}
});

output.Should().DeepEqual(JToken.Parse(expectedJsonOutput));
}
}
}
11 changes: 8 additions & 3 deletions src/Bicep.Decompiler/ArmHelpers/ExpressionHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ public static (string typeString, LanguageExpression nameExpression)? TryGetReso
return jTokenExpression.Value.ToString().Trim('/');
}

public static JToken ReplaceFunctionExpressions(JToken token, Action<FunctionExpression> onFunctionExpression)
public static TToken ReplaceFunctionExpressions<TToken>(TToken token, Action<FunctionExpression> onFunctionExpression)
where TToken : JToken
{
var expressionRewriter = new LanguageExpressionVisitor
{
Expand All @@ -266,12 +267,16 @@ string RewriteStringValue(string value)
return ExpressionsEngine.SerializeExpression(expression);
}

if (token.Type == JTokenType.String)
if (token is JValue && token.Type == JTokenType.String)
{
return RewriteStringValue(token.Value<string>());
var rewritten = RewriteStringValue(token.Value<string>());

return (new JValue(rewritten) as TToken)!;
}

// transform in-place
JTokenHelper.TransformJsonStringValues(token, (key, value) => RewriteStringValue(value));

return token;
}
}
Expand Down
16 changes: 13 additions & 3 deletions src/Bicep.Decompiler/ArmHelpers/TemplateHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,24 @@ public static class TemplateHelpers
public static bool HasProperty(JObject parent, string name)
=> GetProperty(parent, name) != null;

public static void AssertUnsupportedProperty(JObject resource, string propertyName, string message)
public static void AssertUnsupportedProperty(JObject parent, string propertyName, string message)
{
if (HasProperty(resource, propertyName))
if (HasProperty(parent, propertyName))
{
throw new ConversionFailedException(message, resource);
throw new ConversionFailedException(message, parent);
}
}

public static JToken AssertRequiredProperty(JObject parent, string propertyName, string message)
{
if (GetProperty(parent, propertyName) is not {} value)
{
throw new ConversionFailedException(message, parent);
}

return value.Value;
}

public static (string type, string name, string apiVersion) ParseResource(JObject resource)
{
var type = GetProperty(resource, "type")?.Value.Value<string>() ?? throw new ConversionFailedException($"Unable to parse 'type' for resource", resource);
Expand Down
40 changes: 40 additions & 0 deletions src/Bicep.Decompiler/ScopedNamingResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using Azure.Deployments.Expression.Expressions;

namespace Bicep.Decompiler
{
public class ScopedNamingResolver : INamingResolver
{
private readonly INamingResolver parent;
private readonly ISet<string> scopedVariables;

public ScopedNamingResolver(INamingResolver parent, IEnumerable<string> scopedVariables)
{
this.parent = parent;
this.scopedVariables = scopedVariables.ToHashSet();
}

public string? TryLookupName(NameType nameType, string desiredName)
{
if (nameType == NameType.Variable && scopedVariables.Contains(desiredName))
{
return desiredName;
}

return parent.TryLookupName(nameType, desiredName);
}

public string? TryLookupResourceName(string? typeString, LanguageExpression nameExpression)
=> parent.TryLookupResourceName(typeString, nameExpression);

public string? TryRequestName(NameType nameType, string desiredName)
=> throw new NotImplementedException();

public string? TryRequestResourceName(string typeString, LanguageExpression nameExpression)
=> throw new NotImplementedException();
}
}
Loading

0 comments on commit 0fda714

Please sign in to comment.