pulumi/sdk/dotnet/Pulumi/Resources/InputArgs.cs

122 lines
4.5 KiB
C#

// Copyright 2016-2019, Pulumi Corporation
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Google.Protobuf;
using Pulumi.Serialization;
namespace Pulumi
{
/// <summary>
/// Base type for all input argument classes.
/// </summary>
public abstract class InputArgs
{
private readonly ImmutableArray<InputInfo> _inputInfos;
private string? _provider;
private protected abstract void ValidateMember(Type memberType, string fullName);
protected InputArgs()
{
var fieldQuery =
from field in this.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
let attr = field.GetCustomAttribute<InputAttribute>()
where attr != null
select (attr, memberName: field.Name, memberType: field.FieldType, getValue: (Func<object, object?>)field.GetValue);
var propQuery =
from prop in this.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
let attr = prop.GetCustomAttribute<InputAttribute>()
where attr != null
select (attr, memberName: prop.Name, memberType: prop.PropertyType, getValue: (Func<object, object?>)prop.GetValue);
var all = fieldQuery.Concat(propQuery).ToList();
foreach (var (attr, memberName, memberType, getValue) in all)
{
var fullName = $"[Input] {this.GetType().FullName}.{memberName}";
ValidateMember(memberType, fullName);
}
_inputInfos = all.Select(t =>
new InputInfo(t.attr, t.memberName, t.memberType, t.getValue)).ToImmutableArray();
}
/// <summary>
/// Creates a shally copy of args with dynamic provider key set.
/// </summary>
internal InputArgs WithProvider(string provider)
{
if (_inputInfos.Any(info => info.Attribute.Name == Constants.ProviderPropertyName))
{
throw new ArgumentException($"A dynamic resource must not define the {Constants.ProviderPropertyName} key");
}
var clone = (InputArgs)MemberwiseClone();
clone._provider = provider;
return clone;
}
internal virtual async Task<ImmutableDictionary<string, object?>> ToDictionaryAsync()
{
var builder = ImmutableDictionary.CreateBuilder<string, object?>();
foreach (var info in _inputInfos)
{
var fullName = $"[Input] {this.GetType().FullName}.{info.MemberName}";
var value = info.GetValue(this);
if (info.Attribute.IsRequired && value == null)
{
throw new ArgumentNullException(info.MemberName, $"{fullName} is required but was not given a value");
}
if (info.Attribute.Json)
{
value = await ConvertToJsonAsync(fullName, value).ConfigureAwait(false);
}
builder.Add(info.Attribute.Name, value);
}
// Set the __provider property for dynamic providers if it's present.
if (!string.IsNullOrEmpty(_provider))
{
builder.Add(Constants.ProviderPropertyName, _provider);
}
return builder.ToImmutable();
}
private async Task<object?> ConvertToJsonAsync(string context, object? input)
{
if (input == null)
return null;
var serializer = new Serializer(excessiveDebugOutput: false);
var obj = await serializer.SerializeAsync(context, input).ConfigureAwait(false);
var value = Serializer.CreateValue(obj);
return JsonFormatter.Default.Format(value);
}
private struct InputInfo
{
public readonly InputAttribute Attribute;
public readonly Type MemberType;
public readonly string MemberName;
public Func<object, object?> GetValue;
public InputInfo(InputAttribute attribute, string memberName, Type memberType, Func<object, object> getValue) : this()
{
Attribute = attribute;
MemberName = memberName;
MemberType = memberType;
GetValue = getValue;
}
}
}
}