Skip to content

Commit

Permalink
Support load*Content() functions in UDFs (#12915)
Browse files Browse the repository at this point in the history
Closes #12912
Also addresses #12698

###### Microsoft Reviewers: [Open in
CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/12915)
  • Loading branch information
anthony-c-martin authored Jan 4, 2024
1 parent 762fd85 commit 86e8f18
Show file tree
Hide file tree
Showing 14 changed files with 346 additions and 7 deletions.
54 changes: 54 additions & 0 deletions src/Bicep.Core.IntegrationTests/ScenarioTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5420,6 +5420,60 @@ public void Test_Issue12640()
});
}

// https://github.com/Azure/bicep/issues/12912
[TestMethod]
public void Test_Issue12912()
{

var result = CompilationHelper.Compile(
Services.WithFeatureOverrides(new(UserDefinedFunctionsEnabled: true)),
("main.bicep", """
func test() object => loadJsonContent('./repro-data.json')
func test2() string => loadTextContent('./repro-data.json')
func test3() object => loadYamlContent('./repro-data.json')
func test4() string => loadFileAsBase64('./repro-data.json')
"""),
("repro-data.json", """
{}
"""));

result.Should().NotHaveAnyDiagnostics();
var evaluated = TemplateEvaluator.Evaluate(result.Template);
evaluated.Should().HaveValueAtPath("$.functions[0].members['test'].output.value", new JObject());
evaluated.Should().HaveValueAtPath("$.functions[0].members['test2'].output.value", "{}");
evaluated.Should().HaveValueAtPath("$.functions[0].members['test3'].output.value", new JObject());
evaluated.Should().HaveValueAtPath("$.functions[0].members['test4'].output.value", "e30=");
}

// https://github.com/Azure/bicep/issues/12698
[TestMethod]
public void Test_Issue12698()
{

var result = CompilationHelper.Compile(
Services.WithFeatureOverrides(new(UserDefinedFunctionsEnabled: true, CompileTimeImportsEnabled: true)),
("main.bicep", """
import { MyFunction } from 'export.bicep'
output foo string = MyFunction('foo')
"""),
("export.bicep", """
@export()
func MyFunction(name string) string => '${loadJsonContent('./test-mapping.json')['${name}'].myValue}'
"""),
("test-mapping.json", """
{
"foo": {
"myValue": "bar"
}
}
"""));

result.Should().NotHaveAnyDiagnostics();
var evaluated = TemplateEvaluator.Evaluate(result.Template);
evaluated.Should().HaveValueAtPath("$.outputs['foo'].value", "bar");
}

// https://github.com/Azure/bicep/issues/12799
[TestMethod]
public void Test_Issue12799()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ func barTest() array => ['abc', 'def']
func fooTest() array => map(barTest(), a => 'Hello ${a}!')

output fooValue array = fooTest()

func test() object => loadJsonContent('./repro-data.json')
func test2() string => loadTextContent('./repro-data.json')
func test3() object => loadYamlContent('./repro-data.json')
func test4() string => loadFileAsBase64('./repro-data.json')
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ func fooTest() array => map(barTest(), a => 'Hello ${a}!')

output fooValue array = fooTest()

func test() object => loadJsonContent('./repro-data.json')
func test2() string => loadTextContent('./repro-data.json')
func test3() object => loadYamlContent('./repro-data.json')
func test4() string => loadFileAsBase64('./repro-data.json')

Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ func barTest() array => [ 'abc', 'def' ]
func fooTest() array => map(barTest(), a => 'Hello ${a}!')

output fooValue array = fooTest()

func test() object => loadJsonContent('./repro-data.json')
func test2() string => loadTextContent('./repro-data.json')
func test3() object => loadYamlContent('./repro-data.json')
func test4() string => loadFileAsBase64('./repro-data.json')
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
func buildUrl(https bool, hostname string, path string) string => '${https ? 'https' : 'http'}://${hostname}${empty(path) ? '' : '/${path}'}'
//@[000:734) ProgramExpression
//@[000:975) ProgramExpression
//@[000:141) ├─DeclaredFunctionExpression { Name = buildUrl }
//@[013:141) | └─LambdaExpression
//@[020:024) | ├─AmbientTypeReferenceExpression { Name = bool }
Expand All @@ -18,6 +18,8 @@ func buildUrl(https bool, hostname string, path string) string => '${https ? 'ht
//@[129:139) | | └─InterpolatedStringExpression
//@[133:137) | | └─LambdaVariableReferenceExpression { Variable = path }
//@[056:062) | └─AmbientTypeReferenceExpression { Name = string }
//@[000:000) | ├─ObjectExpression [UNPARENTED]
//@[000:000) | ├─ObjectExpression [UNPARENTED]

output foo string = buildUrl(true, 'google.com', 'search')
//@[000:058) ├─DeclaredOutputExpression { Name = foo }
Expand Down Expand Up @@ -118,3 +120,22 @@ output fooValue array = fooTest()
//@[016:021) ├─AmbientTypeReferenceExpression { Name = array }
//@[024:033) └─UserDefinedFunctionCallExpression { Name = fooTest }

func test() object => loadJsonContent('./repro-data.json')
//@[000:058) ├─DeclaredFunctionExpression { Name = test }
//@[009:058) | └─LambdaExpression
//@[012:018) | └─AmbientTypeReferenceExpression { Name = object }
func test2() string => loadTextContent('./repro-data.json')
//@[000:059) ├─DeclaredFunctionExpression { Name = test2 }
//@[010:059) | └─LambdaExpression
//@[023:059) | ├─StringLiteralExpression { Value = {} }
//@[013:019) | └─AmbientTypeReferenceExpression { Name = string }
func test3() object => loadYamlContent('./repro-data.json')
//@[000:059) ├─DeclaredFunctionExpression { Name = test3 }
//@[010:059) | └─LambdaExpression
//@[013:019) | └─AmbientTypeReferenceExpression { Name = object }
func test4() string => loadFileAsBase64('./repro-data.json')
//@[000:060) ├─DeclaredFunctionExpression { Name = test4 }
//@[010:060) | └─LambdaExpression
//@[023:060) | ├─StringLiteralExpression { Value = e30= }
//@[013:019) | └─AmbientTypeReferenceExpression { Name = string }

30 changes: 29 additions & 1 deletion src/Bicep.Core.Samples/Files/baselines/Functions_LF/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"_generator": {
"name": "bicep",
"version": "dev",
"templateHash": "10244606579588812152"
"templateHash": "12432202576544985285"
}
},
"definitions": {
Expand Down Expand Up @@ -125,6 +125,34 @@
"type": "array",
"value": "[map(__bicep.barTest(), lambda('a', format('Hello {0}!', lambdaVariables('a'))))]"
}
},
"test": {
"parameters": [],
"output": {
"type": "object",
"value": {}
}
},
"test2": {
"parameters": [],
"output": {
"type": "string",
"value": "{}"
}
},
"test3": {
"parameters": [],
"output": {
"type": "object",
"value": {}
}
},
"test4": {
"parameters": [],
"output": {
"type": "string",
"value": "e30="
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ func barTest() array => ['abc', 'def']
func fooTest() array => map(barTest(), a => 'Hello ${a}!')

output fooValue array = fooTest()

func test() object => loadJsonContent('./repro-data.json')
func test2() string => loadTextContent('./repro-data.json')
func test3() object => loadYamlContent('./repro-data.json')
func test4() string => loadFileAsBase64('./repro-data.json')
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,44 @@ func fooTest() array => map(barTest(), a => 'Hello ${a}!')
//@ "type": "array",
//@ "value": "[map(__bicep.barTest(), lambda('a', format('Hello {0}!', lambdaVariables('a'))))]"
//@ }
//@ }
//@ },

output fooValue array = fooTest()
//@ "fooValue": {
//@ "type": "array",
//@ "value": "[__bicep.fooTest()]"
//@ }

func test() object => loadJsonContent('./repro-data.json')
//@ "test": {
//@ "parameters": [],
//@ "output": {
//@ "type": "object",
//@ "value": {}
//@ }
//@ },
func test2() string => loadTextContent('./repro-data.json')
//@ "test2": {
//@ "parameters": [],
//@ "output": {
//@ "type": "string",
//@ "value": "{}"
//@ }
//@ },
func test3() object => loadYamlContent('./repro-data.json')
//@ "test3": {
//@ "parameters": [],
//@ "output": {
//@ "type": "object",
//@ "value": {}
//@ }
//@ },
func test4() string => loadFileAsBase64('./repro-data.json')
//@ "test4": {
//@ "parameters": [],
//@ "output": {
//@ "type": "string",
//@ "value": "e30="
//@ }
//@ }

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"_generator": {
"name": "bicep",
"version": "dev",
"templateHash": "10244606579588812152"
"templateHash": "12432202576544985285"
}
},
"definitions": {
Expand Down Expand Up @@ -125,6 +125,34 @@
"type": "array",
"value": "[map(__bicep.barTest(), lambda('a', format('Hello {0}!', lambdaVariables('a'))))]"
}
},
"test": {
"parameters": [],
"output": {
"type": "object",
"value": {}
}
},
"test2": {
"parameters": [],
"output": {
"type": "string",
"value": "{}"
}
},
"test3": {
"parameters": [],
"output": {
"type": "object",
"value": {}
}
},
"test4": {
"parameters": [],
"output": {
"type": "string",
"value": "e30="
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,12 @@ func fooTest() array => map(barTest(), a => 'Hello ${a}!')
output fooValue array = fooTest()
//@[07:15) Output fooValue. Type: array. Declaration start char: 0, length: 33

func test() object => loadJsonContent('./repro-data.json')
//@[05:09) Function test. Type: () => object. Declaration start char: 0, length: 58
func test2() string => loadTextContent('./repro-data.json')
//@[05:10) Function test2. Type: () => '{}'. Declaration start char: 0, length: 59
func test3() object => loadYamlContent('./repro-data.json')
//@[05:10) Function test3. Type: () => object. Declaration start char: 0, length: 59
func test4() string => loadFileAsBase64('./repro-data.json')
//@[05:10) Function test4. Type: () => repro-data.json. Declaration start char: 0, length: 60

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
func buildUrl(https bool, hostname string, path string) string => '${https ? 'https' : 'http'}://${hostname}${empty(path) ? '' : '/${path}'}'
//@[000:734) ProgramSyntax
//@[000:975) ProgramSyntax
//@[000:141) ├─FunctionDeclarationSyntax
//@[000:004) | ├─Token(Identifier) |func|
//@[005:013) | ├─IdentifierSyntax
Expand Down Expand Up @@ -410,6 +410,95 @@ output fooValue array = fooTest()
//@[024:031) | | └─Token(Identifier) |fooTest|
//@[031:032) | ├─Token(LeftParen) |(|
//@[032:033) | └─Token(RightParen) |)|
//@[033:034) ├─Token(NewLine) |\n|
//@[033:035) ├─Token(NewLine) |\n\n|

func test() object => loadJsonContent('./repro-data.json')
//@[000:058) ├─FunctionDeclarationSyntax
//@[000:004) | ├─Token(Identifier) |func|
//@[005:009) | ├─IdentifierSyntax
//@[005:009) | | └─Token(Identifier) |test|
//@[009:058) | └─TypedLambdaSyntax
//@[009:011) | ├─TypedVariableBlockSyntax
//@[009:010) | | ├─Token(LeftParen) |(|
//@[010:011) | | └─Token(RightParen) |)|
//@[012:018) | ├─VariableAccessSyntax
//@[012:018) | | └─IdentifierSyntax
//@[012:018) | | └─Token(Identifier) |object|
//@[019:021) | ├─Token(Arrow) |=>|
//@[022:058) | └─FunctionCallSyntax
//@[022:037) | ├─IdentifierSyntax
//@[022:037) | | └─Token(Identifier) |loadJsonContent|
//@[037:038) | ├─Token(LeftParen) |(|
//@[038:057) | ├─FunctionArgumentSyntax
//@[038:057) | | └─StringSyntax
//@[038:057) | | └─Token(StringComplete) |'./repro-data.json'|
//@[057:058) | └─Token(RightParen) |)|
//@[058:059) ├─Token(NewLine) |\n|
func test2() string => loadTextContent('./repro-data.json')
//@[000:059) ├─FunctionDeclarationSyntax
//@[000:004) | ├─Token(Identifier) |func|
//@[005:010) | ├─IdentifierSyntax
//@[005:010) | | └─Token(Identifier) |test2|
//@[010:059) | └─TypedLambdaSyntax
//@[010:012) | ├─TypedVariableBlockSyntax
//@[010:011) | | ├─Token(LeftParen) |(|
//@[011:012) | | └─Token(RightParen) |)|
//@[013:019) | ├─VariableAccessSyntax
//@[013:019) | | └─IdentifierSyntax
//@[013:019) | | └─Token(Identifier) |string|
//@[020:022) | ├─Token(Arrow) |=>|
//@[023:059) | └─FunctionCallSyntax
//@[023:038) | ├─IdentifierSyntax
//@[023:038) | | └─Token(Identifier) |loadTextContent|
//@[038:039) | ├─Token(LeftParen) |(|
//@[039:058) | ├─FunctionArgumentSyntax
//@[039:058) | | └─StringSyntax
//@[039:058) | | └─Token(StringComplete) |'./repro-data.json'|
//@[058:059) | └─Token(RightParen) |)|
//@[059:060) ├─Token(NewLine) |\n|
func test3() object => loadYamlContent('./repro-data.json')
//@[000:059) ├─FunctionDeclarationSyntax
//@[000:004) | ├─Token(Identifier) |func|
//@[005:010) | ├─IdentifierSyntax
//@[005:010) | | └─Token(Identifier) |test3|
//@[010:059) | └─TypedLambdaSyntax
//@[010:012) | ├─TypedVariableBlockSyntax
//@[010:011) | | ├─Token(LeftParen) |(|
//@[011:012) | | └─Token(RightParen) |)|
//@[013:019) | ├─VariableAccessSyntax
//@[013:019) | | └─IdentifierSyntax
//@[013:019) | | └─Token(Identifier) |object|
//@[020:022) | ├─Token(Arrow) |=>|
//@[023:059) | └─FunctionCallSyntax
//@[023:038) | ├─IdentifierSyntax
//@[023:038) | | └─Token(Identifier) |loadYamlContent|
//@[038:039) | ├─Token(LeftParen) |(|
//@[039:058) | ├─FunctionArgumentSyntax
//@[039:058) | | └─StringSyntax
//@[039:058) | | └─Token(StringComplete) |'./repro-data.json'|
//@[058:059) | └─Token(RightParen) |)|
//@[059:060) ├─Token(NewLine) |\n|
func test4() string => loadFileAsBase64('./repro-data.json')
//@[000:060) ├─FunctionDeclarationSyntax
//@[000:004) | ├─Token(Identifier) |func|
//@[005:010) | ├─IdentifierSyntax
//@[005:010) | | └─Token(Identifier) |test4|
//@[010:060) | └─TypedLambdaSyntax
//@[010:012) | ├─TypedVariableBlockSyntax
//@[010:011) | | ├─Token(LeftParen) |(|
//@[011:012) | | └─Token(RightParen) |)|
//@[013:019) | ├─VariableAccessSyntax
//@[013:019) | | └─IdentifierSyntax
//@[013:019) | | └─Token(Identifier) |string|
//@[020:022) | ├─Token(Arrow) |=>|
//@[023:060) | └─FunctionCallSyntax
//@[023:039) | ├─IdentifierSyntax
//@[023:039) | | └─Token(Identifier) |loadFileAsBase64|
//@[039:040) | ├─Token(LeftParen) |(|
//@[040:059) | ├─FunctionArgumentSyntax
//@[040:059) | | └─StringSyntax
//@[040:059) | | └─Token(StringComplete) |'./repro-data.json'|
//@[059:060) | └─Token(RightParen) |)|
//@[060:061) ├─Token(NewLine) |\n|

//@[000:000) └─Token(EndOfFile) ||
Loading

0 comments on commit 86e8f18

Please sign in to comment.