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

Make output API match on type and add lambda support #41988

Merged
merged 3 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ protected Resource(Azure.Provisioning.IConstruct scope, Azure.Provisioning.Resou
public Azure.Provisioning.Resource? Parent { get { throw null; } }
public Azure.Provisioning.IConstruct Scope { get { throw null; } }
public string Version { get { throw null; } }
public Azure.Provisioning.Output AddOutput(string name, string propertyName, bool isLiteral = false, bool isSecure = false) { throw null; }
public Azure.Provisioning.Output AddOutput(string name, object instance, string propertyName, bool isLiteral = false, bool isSecure = false) { throw null; }
public void AssignParameter(object instance, string propertyName, Azure.Provisioning.Parameter parameter) { }
protected virtual Azure.Provisioning.Resource? FindParentInScope(Azure.Provisioning.IConstruct scope) { throw null; }
Azure.Provisioning.Resource System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Resource>.Create(System.BinaryData data, System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
Expand All @@ -97,7 +97,8 @@ public abstract partial class Resource<T> : Azure.Provisioning.Resource where T
{
protected Resource(Azure.Provisioning.IConstruct scope, Azure.Provisioning.Resource? parent, string resourceName, Azure.Core.ResourceType resourceType, string version, T properties) : base (default(Azure.Provisioning.IConstruct), default(Azure.Provisioning.Resource), default(string), default(Azure.Core.ResourceType), default(string), default(object)) { }
public T Properties { get { throw null; } }
public void AssignParameter(System.Linq.Expressions.Expression<System.Func<T, string>> selector, Azure.Provisioning.Parameter parameter) { }
public Azure.Provisioning.Output AddOutput(System.Linq.Expressions.Expression<System.Func<T, object?>> propertySelector, string outputName, bool isLiteral = false, bool isSecure = false) { throw null; }
public void AssignParameter(System.Linq.Expressions.Expression<System.Func<T, object?>> propertySelector, Azure.Provisioning.Parameter parameter) { }
}
}
namespace Azure.Provisioning.AppService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ protected Resource(Azure.Provisioning.IConstruct scope, Azure.Provisioning.Resou
public Azure.Provisioning.Resource? Parent { get { throw null; } }
public Azure.Provisioning.IConstruct Scope { get { throw null; } }
public string Version { get { throw null; } }
public Azure.Provisioning.Output AddOutput(string name, string propertyName, bool isLiteral = false, bool isSecure = false) { throw null; }
public Azure.Provisioning.Output AddOutput(string name, object instance, string propertyName, bool isLiteral = false, bool isSecure = false) { throw null; }
public void AssignParameter(object instance, string propertyName, Azure.Provisioning.Parameter parameter) { }
protected virtual Azure.Provisioning.Resource? FindParentInScope(Azure.Provisioning.IConstruct scope) { throw null; }
Azure.Provisioning.Resource System.ClientModel.Primitives.IPersistableModel<Azure.Provisioning.Resource>.Create(System.BinaryData data, System.ClientModel.Primitives.ModelReaderWriterOptions options) { throw null; }
Expand All @@ -97,7 +97,8 @@ public abstract partial class Resource<T> : Azure.Provisioning.Resource where T
{
protected Resource(Azure.Provisioning.IConstruct scope, Azure.Provisioning.Resource? parent, string resourceName, Azure.Core.ResourceType resourceType, string version, T properties) : base (default(Azure.Provisioning.IConstruct), default(Azure.Provisioning.Resource), default(string), default(Azure.Core.ResourceType), default(string), default(object)) { }
public T Properties { get { throw null; } }
public void AssignParameter(System.Linq.Expressions.Expression<System.Func<T, string>> selector, Azure.Provisioning.Parameter parameter) { }
public Azure.Provisioning.Output AddOutput(System.Linq.Expressions.Expression<System.Func<T, object?>> propertySelector, string outputName, bool isLiteral = false, bool isSecure = false) { throw null; }
public void AssignParameter(System.Linq.Expressions.Expression<System.Func<T, object?>> propertySelector, Azure.Provisioning.Parameter parameter) { }
}
}
namespace Azure.Provisioning.AppService
Expand Down
21 changes: 13 additions & 8 deletions sdk/provisioning/Azure.Provisioning/src/Resource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,36 +119,41 @@ public void AssignParameter(object instance, string propertyName, Parameter para
/// Adds an output to the resource.
/// </summary>
/// <param name="name">The name of the output.</param>
/// <param name="instance">The instance which contains the property for the output.</param>
/// <param name="propertyName">The property name to output.</param>
/// <param name="isLiteral">Is the output literal.</param>
/// <param name="isSecure">Is the output secure.</param>
/// <returns>The <see cref="Output"/>.</returns>
/// <exception cref="ArgumentException">If the <paramref name="propertyName"/> is not found on the resources properties.</exception>
public Output AddOutput(string name, string propertyName, bool isLiteral = false, bool isSecure = false)
public Output AddOutput(string name, object instance, string propertyName, bool isLiteral = false, bool isSecure = false)
JoshLove-msft marked this conversation as resolved.
Show resolved Hide resolved
{
string? reference = GetReference(Properties.GetType(), propertyName, Name.ToCamelCase());
string? reference = GetReference(instance.GetType(), Properties.GetType(), propertyName, Name.ToCamelCase());

if (reference is null)
throw new ArgumentException(nameof(propertyName), $"{propertyName} was not found in the property tree for {Properties.GetType().Name}");
var result = new Output(name, reference, Scope, isLiteral, isSecure);
Scope.AddOutput(result);
return result;
}

private static string? GetReference(Type type, string propertyName, string str)
private static string? GetReference(Type targetType, Type currentType, string propertyName, string str)
{
var properties = type.GetProperties();
foreach (var property in properties)
var properties = currentType.GetProperties();
if (currentType == targetType)
{
if (property.Name.Equals(propertyName, StringComparison.Ordinal))
foreach (var property in properties)
{
return $"{str}.{property.Name.ToCamelCase()}";
if (property.Name.Equals(propertyName, StringComparison.Ordinal))
{
return $"{str}.{property.Name.ToCamelCase()}";
}
}
}

//need to check next level
foreach (var property in properties)
{
var result = GetReference(property.PropertyType, propertyName, $"{str}.{property.Name.ToCamelCase()}");
var result = GetReference(targetType, property.PropertyType, propertyName, $"{str}.{property.Name.ToCamelCase()}");
if (result is not null)
return result;
}
Expand Down
59 changes: 53 additions & 6 deletions sdk/provisioning/Azure.Provisioning/src/ResourceOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,66 @@ protected Resource(IConstruct scope, Resource? parent, string resourceName, Reso
/// <summary>
///
/// </summary>
/// <param name="selector"></param>
/// <param name="propertySelector"></param>
/// <param name="parameter"></param>
/// <exception cref="NotSupportedException"></exception>
public void AssignParameter(Expression<Func<T, string>> selector, Parameter parameter)
public void AssignParameter(Expression<Func<T, object?>> propertySelector, Parameter parameter)
{
if (selector is not LambdaExpression lambda ||
lambda.Body is not MemberExpression member)
(object instance, string name) = EvaluateLambda(propertySelector);
AssignParameter(instance, name, parameter);
}

/// <summary>
/// Adds an output to the resource.
/// </summary>
/// <param name="propertySelector"></param>
/// <param name="outputName">The name of the output.</param>
/// <param name="isLiteral">Is the output literal.</param>
/// <param name="isSecure">Is the output secure.</param>
/// <returns>The <see cref="Output"/>.</returns>
public Output AddOutput(Expression<Func<T, object?>> propertySelector, string outputName, bool isLiteral = false, bool isSecure = false)
{
(object instance, string name) = EvaluateLambda(propertySelector);
return AddOutput(outputName, instance, name, isLiteral, isSecure);
}

private (object Instance, string PropertyName) EvaluateLambda(Expression<Func<T, object?>> propertySelector)
{
ParameterExpression? root = null;
Expression? body = null;
string? name = null;
if (propertySelector is LambdaExpression lambda)
{
root = lambda.Parameters[0];
if (lambda.Body is MemberExpression member)
{
body = member.Expression;
name = member.Member.Name;
}
else if (lambda.Body is UnaryExpression { NodeType: ExpressionType.Convert, Operand: MemberExpression member2 })
{
body = member2.Expression;
name = member2.Member.Name;
}
else if (lambda.Body is IndexExpression { Arguments.Count: 1 } indexer)
{
body = indexer.Object;
name = Expression.Lambda(indexer.Arguments[0], root).Compile().DynamicInvoke(Properties) as string;
}
else if (lambda.Body is MethodCallExpression { Method.Name: "get_Item", Arguments.Count: 1 } call)
{
body = call.Object;
name = Expression.Lambda(call.Arguments[0], root).Compile().DynamicInvoke(Properties) as string;
}
}

if (body is null || name is null || root is null)
{
throw new NotSupportedException();
}

object instance = Expression.Lambda(member.Expression!, lambda.Parameters[0]).Compile().DynamicInvoke(Properties)!;
AssignParameter(instance, member.Member.Name, parameter);
object instance = Expression.Lambda(body, root).Compile().DynamicInvoke(Properties)!;
return (instance, name);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ resource resourceGroup_I6QNkoPsb 'Microsoft.Resources/resourceGroups@2023-07-01'
}
}

resource storageAccount_64QAbICpM 'Microsoft.Storage/storageAccounts@2022-09-01' = {
resource storageAccount_wGMcLf7GV 'Microsoft.Storage/storageAccounts@2022-09-01' = {
scope: resourceGroup_I6QNkoPsb
name: 'photoAcct-47cc88f8-4ec0-'
name: 'photoAcct-7d33c4f9e2094f'
location: 'westus'
sku: {
name: 'Premium_LRS'
Expand All @@ -21,8 +21,8 @@ resource storageAccount_64QAbICpM 'Microsoft.Storage/storageAccounts@2022-09-01'
}
}

resource blobService_hzBGHAAO1 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
parent: storageAccount_64QAbICpM
resource blobService_AKyY0JFhn 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
parent: storageAccount_wGMcLf7GV
name: 'photos-TEST'
properties: {
cors: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ resource resourceGroup_I6QNkoPsb 'Microsoft.Resources/resourceGroups@2023-07-01'
}
}

resource storageAccount_3AMx4MnVN 'Microsoft.Storage/storageAccounts@2022-09-01' = {
resource storageAccount_SAbR7FdSr 'Microsoft.Storage/storageAccounts@2022-09-01' = {
scope: resourceGroup_I6QNkoPsb
name: 'photoAcct-e9ffc066-811e-'
name: 'photoAcct-6996463351dc4d'
location: 'westus'
sku: {
name: 'Premium_LRS'
Expand All @@ -21,8 +21,8 @@ resource storageAccount_3AMx4MnVN 'Microsoft.Storage/storageAccounts@2022-09-01'
}
}

resource blobService_JVW2TmYYM 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
parent: storageAccount_3AMx4MnVN
resource blobService_cTyYyGbZK 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
parent: storageAccount_SAbR7FdSr
name: 'photos-TEST'
properties: {
cors: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ public void WebSiteUsingL1()
AppServicePlan appServicePlan = infra.AddAppServicePlan();

WebSite frontEnd = new WebSite(infra, "frontEnd", appServicePlan, WebSiteRuntime.Node, "18-lts");
var frontEndPrincipalId = frontEnd.AddOutput("SERVICE_API_IDENTITY_PRINCIPAL_ID", nameof(frontEnd.Properties.Identity.PrincipalId), isSecure: true);
var frontEndPrincipalId = frontEnd.AddOutput(
website => website.Identity.PrincipalId,
"SERVICE_API_IDENTITY_PRINCIPAL_ID",
isSecure: true);

infra.AddKeyVault()
.AddAccessPolicy(frontEndPrincipalId); // frontEnd.properties.identity.principalId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ public TestFrontEndWebSite(IConstruct scope, KeyVault? keyVault = null, AppServi
keyVault = UseExistingResource(keyVault, () => scope.AddKeyVault(ResourceGroup));

WebSite frontEnd = new WebSite(this, "frontEnd", appServicePlan, WebSiteRuntime.Node, "18-lts");
var frontEndPrincipalId = frontEnd.AddOutput("SERVICE_API_IDENTITY_PRINCIPAL_ID", nameof(frontEnd.Properties.Identity.PrincipalId), isSecure: true);
var frontEndPrincipalId = frontEnd.AddOutput(
website => website.Identity.PrincipalId,
"SERVICE_API_IDENTITY_PRINCIPAL_ID",
isSecure: true);

keyVault.AddAccessPolicy(frontEndPrincipalId);

Expand Down