pulumi/sdk/dotnet/Pulumi/Resources/InputArgs.cs
Pat Gavlin 3d2e31289a
Add support for serialized resource references. (#5041)
Resources are serialized as their URN, ID, and package version. Each
Pulumi package is expected to register itself with the SDK. The package
will be invoked to construct appropriate instances of rehydrated
resources. Packages are distinguished by their name and their version.

This is the foundation of cross-process resources.

Related to #2430.

Co-authored-by: Mikhail Shilkov <github@mikhail.io>
Co-authored-by: Luke Hoban <luke@pulumi.com>
Co-authored-by: Levi Blackstone <levi@pulumi.com>
2020-10-27 10:12:12 -07:00

99 lines
3.6 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 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();
}
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);
}
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, false).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;
}
}
}
}