Move .NET serialization attributes to Pulumi namespace (#3902)

Move .NET serialization attributes to Pulumi namespace, deprecate the ones in Pulumi.Serialization
This commit is contained in:
Mikhail Shilkov 2020-02-11 11:40:14 +01:00 committed by GitHub
parent d68d22a541
commit f1cdce9488
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 141 additions and 24 deletions

2
.gitignore vendored
View file

@ -8,7 +8,7 @@
coverage.cov
*.coverprofile
/.idea/
**/.idea/
*.iml
# VSCode creates this binary when running tests in the debugger

View file

@ -14,6 +14,9 @@ CHANGELOG
- Expose resource options, parent, dependencies, and provider config to policies.
[#3862](https://github.com/pulumi/pulumi/pull/3862)
- Move .NET SDK attributes to the root namespace.
[#3902](https://github.com/pulumi/pulumi/pull/3902)
## 1.10.1 (2020-02-06)
- Support stack references in the Go SDK.
[#3829](https://github.com/pulumi/pulumi/pull/3829)

View file

@ -84,6 +84,8 @@ Pulumi.IDeployment.StackName.get -> string
Pulumi.Input<T>
Pulumi.InputArgs
Pulumi.InputArgs.InputArgs() -> void
Pulumi.InputAttribute
Pulumi.InputAttribute.InputAttribute(string name, bool required = false, bool json = false) -> void
Pulumi.InputExtensions
Pulumi.InputJson
Pulumi.InputJson.InputJson() -> void
@ -116,6 +118,13 @@ Pulumi.Output<T>.Apply<U>(System.Func<T, Pulumi.Input<U>> func) -> Pulumi.Output
Pulumi.Output<T>.Apply<U>(System.Func<T, Pulumi.Output<U>> func) -> Pulumi.Output<U>
Pulumi.Output<T>.Apply<U>(System.Func<T, System.Threading.Tasks.Task<U>> func) -> Pulumi.Output<U>
Pulumi.Output<T>.Apply<U>(System.Func<T, U> func) -> Pulumi.Output<U>
Pulumi.OutputAttribute
Pulumi.OutputAttribute.Name.get -> string
Pulumi.OutputAttribute.OutputAttribute(string name = null) -> void
Pulumi.OutputConstructorAttribute
Pulumi.OutputConstructorAttribute.OutputConstructorAttribute() -> void
Pulumi.OutputTypeAttribute
Pulumi.OutputTypeAttribute.OutputTypeAttribute() -> void
Pulumi.OutputExtensions
Pulumi.ProviderResource
Pulumi.ProviderResource.ProviderResource(string package, string name, Pulumi.ResourceArgs args, Pulumi.ResourceOptions options = null) -> void

View file

@ -23,14 +23,22 @@ namespace Pulumi
{
var fieldQuery =
from field in this.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
let attr = field.GetCustomAttribute<InputAttribute>()
where attr != null
let attr1 = field.GetCustomAttribute<Pulumi.InputAttribute>()
#pragma warning disable 618
let attr2 = field.GetCustomAttribute<Pulumi.Serialization.InputAttribute>()
#pragma warning restore 618
where attr1 != null || attr2 != null
let attr = attr1 ?? new Pulumi.InputAttribute(attr2.Name, attr2.IsRequired, attr2.Json)
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
let attr1 = prop.GetCustomAttribute<Pulumi.InputAttribute>()
#pragma warning disable 618
let attr2 = prop.GetCustomAttribute<Pulumi.Serialization.InputAttribute>()
#pragma warning restore 618
where attr1 != null || attr2 != null
let attr = attr1 ?? new Pulumi.InputAttribute(attr2.Name, attr2.IsRequired, attr2.Json)
select (attr, memberName: prop.Name, memberType: prop.PropertyType, getValue: (Func<object, object?>)prop.GetValue);
var all = fieldQuery.Concat(propQuery).ToList();

View file

@ -0,0 +1,81 @@
// Copyright 2016-2019, Pulumi Corporation
using System;
using Google.Protobuf.WellKnownTypes;
namespace Pulumi.Serialization
{
/// <summary>
/// Attribute used by a Pulumi Cloud Provider Package to mark Resource output properties.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
[Obsolete("Use Pulumi.OutputAttribute instead")]
public sealed class OutputAttribute : Attribute
{
public string? Name { get; }
public OutputAttribute(string? name = null)
{
Name = name;
}
}
/// <summary>
/// Attribute used by a Pulumi Cloud Provider Package to mark Resource input fields and
/// properties.
/// <para/>
/// Note: for simple inputs (i.e. <see cref="Input{T}"/> this should just be placed on the
/// property itself. i.e. <c>[Input] Input&lt;string&gt; Acl</c>.
///
/// For collection inputs (i.e. <see cref="InputList{T}"/> this should be placed on the
/// backing field for the property. i.e.
///
/// <code>
/// [Input] private InputList&lt;string&gt; _acls;
/// public InputList&lt;string&gt; Acls
/// {
/// get => _acls ?? (_acls = new InputList&lt;string&gt;());
/// set => _acls = value;
/// }
/// </code>
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
[Obsolete("Use Pulumi.InputAttribute instead")]
public sealed class InputAttribute : Attribute
{
internal string Name { get; }
internal bool IsRequired { get; }
internal bool Json { get; }
public InputAttribute(string name, bool required = false, bool json = false)
{
Name = name;
IsRequired = required;
Json = json;
}
}
/// <summary>
/// Attribute used by a Pulumi Cloud Provider Package to mark complex types used for a Resource
/// output property. A complex type must have a single constructor in it marked with the
/// <see cref="OutputConstructorAttribute"/> attribute.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
[Obsolete("Use Pulumi.OutputTypeAttribute instead")]
public sealed class OutputTypeAttribute : Attribute
{
}
/// <summary>
/// Attribute used by a Pulumi Cloud Provider Package to marks the constructor for a complex
/// property type so that it can be instantiated by the Pulumi runtime.
///
/// The constructor should contain parameters that map to the resultant <see
/// cref="Struct.Fields"/> returned by the engine.
/// </summary>
[AttributeUsage(AttributeTargets.Constructor)]
[Obsolete("Use Pulumi.OutputConstructorAttribute instead")]
public sealed class OutputConstructorAttribute : Attribute
{
}
}

View file

@ -3,10 +3,12 @@
using System;
using Google.Protobuf.WellKnownTypes;
namespace Pulumi.Serialization
namespace Pulumi
{
/// <summary>
/// Attribute used by a Pulumi Cloud Provider Package to mark Resource output properties.
/// Attribute used by a mark <see cref="Resource"/> output properties. Use this attribute
/// in your Pulumi programs to mark outputs of <see cref="ComponentResource"/> and
/// <see cref="Stack"/> resources.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class OutputAttribute : Attribute
@ -20,8 +22,8 @@ namespace Pulumi.Serialization
}
/// <summary>
/// Attribute used by a Pulumi Cloud Provider Package to mark Resource input fields and
/// properties.
/// Attribute used by a Pulumi Cloud Provider Package to mark <see cref="Resource"/> input
/// fields and properties.
/// <para/>
/// Note: for simple inputs (i.e. <see cref="Input{T}"/> this should just be placed on the
/// property itself. i.e. <c>[Input] Input&lt;string&gt; Acl</c>.

View file

@ -113,14 +113,17 @@ namespace Pulumi.Serialization
$"Unexpected generic target type {targetType.FullName} when deserializing {context}");
}
if (targetType.GetCustomAttribute<OutputTypeAttribute>() == null)
if (targetType.GetCustomAttribute<Pulumi.OutputTypeAttribute>() == null
#pragma warning disable 618
&& targetType.GetCustomAttribute<Pulumi.Serialization.OutputTypeAttribute>() == null)
#pragma warning restore 618
return (null, new InvalidOperationException(
$"Unexpected target type {targetType.FullName} when deserializing {context}"));
var constructor = GetPropertyConstructor(targetType);
if (constructor == null)
return (null, new InvalidOperationException(
$"Expected target type {targetType.FullName} to have [{nameof(OutputConstructorAttribute)}] constructor when deserializing {context}"));
$"Expected target type {targetType.FullName} to have [{nameof(Pulumi.OutputConstructorAttribute)}] constructor when deserializing {context}"));
var (dictionary, tempException) = TryEnsureType<ImmutableDictionary<string, object>>(context, val);
if (tempException != null)
@ -365,21 +368,24 @@ $@"{context} contains invalid type {targetType.FullName}:
}
}
var propertyTypeAttribute = targetType.GetCustomAttribute<OutputTypeAttribute>();
var propertyTypeAttribute = (Attribute?)targetType.GetCustomAttribute<Pulumi.OutputTypeAttribute>()
#pragma warning disable 618
?? targetType.GetCustomAttribute<Pulumi.Serialization.OutputTypeAttribute>();
#pragma warning restore 618
if (propertyTypeAttribute == null)
{
throw new InvalidOperationException(
$@"{context} contains invalid type {targetType.FullName}. Allowed types are:
String, Boolean, Int32, Double,
Nullable<...>, ImmutableArray<...> and ImmutableDictionary<string, ...> or
a class explicitly marked with the [{nameof(OutputTypeAttribute)}].");
a class explicitly marked with the [{nameof(Pulumi.OutputTypeAttribute)}].");
}
var constructor = GetPropertyConstructor(targetType);
if (constructor == null)
{
throw new InvalidOperationException(
$@"{targetType.FullName} had [{nameof(OutputTypeAttribute)}], but did not contain constructor marked with [{nameof(OutputConstructorAttribute)}].");
$@"{targetType.FullName} had [{nameof(Pulumi.OutputTypeAttribute)}], but did not contain constructor marked with [{nameof(Pulumi.OutputConstructorAttribute)}].");
}
foreach (var param in constructor.GetParameters())
@ -390,6 +396,9 @@ $@"{targetType.FullName} had [{nameof(OutputTypeAttribute)}], but did not contai
private static ConstructorInfo GetPropertyConstructor(System.Type outputTypeArg)
=> outputTypeArg.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(
c => c.GetCustomAttributes<OutputConstructorAttribute>() != null);
c => c.GetCustomAttributes<Pulumi.OutputConstructorAttribute>() != null
#pragma warning disable 618
|| c.GetCustomAttributes<Pulumi.Serialization.OutputConstructorAttribute>() != null);
#pragma warning restore 618
}
}

View file

@ -63,12 +63,15 @@ namespace Pulumi.Serialization
var type = resource.GetResourceType();
var query = from property in resource.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
let attr = property.GetCustomAttribute<OutputAttribute>()
where attr != null
select (property, attr);
let attr1 = property.GetCustomAttribute<Pulumi.OutputAttribute>()
#pragma warning disable 618
let attr2 = property.GetCustomAttribute<Pulumi.Serialization.OutputAttribute>()
#pragma warning restore 618
where attr1 != null || attr2 != null
select (property, attrName: attr1?.Name ?? attr2?.Name);
var result = ImmutableDictionary.CreateBuilder<string, IOutputCompletionSource>();
foreach (var (prop, attr) in query.ToList())
foreach (var (prop, attrName) in query.ToList())
{
var propType = prop.PropertyType;
var propFullName = $"[Output] {resource.GetType().FullName}.{prop.Name}";
@ -93,7 +96,7 @@ namespace Pulumi.Serialization
setMethod.Invoke(resource, new[] { completionSource.Output });
var outputName = attr.Name ?? prop.Name;
var outputName = attrName ?? prop.Name;
result.Add(outputName, completionSource);
}

View file

@ -79,9 +79,12 @@ namespace Pulumi
internal void RegisterPropertyOutputs()
{
var outputs = (from property in this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
let attr = property.GetCustomAttribute<OutputAttribute>()
where attr != null
let name = attr.Name ?? property.Name
let attr1 = property.GetCustomAttribute<Pulumi.OutputAttribute>()
#pragma warning disable 618
let attr2 = property.GetCustomAttribute<Pulumi.Serialization.OutputAttribute>()
#pragma warning restore 618
where attr1 != null || attr2 != null
let name = attr1?.Name ?? attr2?.Name ?? property.Name
select new KeyValuePair<string, object?>(name, property.GetValue(this))).ToList();
// Check that none of the values are null: catch unassigned outputs

View file

@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Pulumi;
using Pulumi.Serialization;
class MyStack : Stack
{