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

[sdk/dotnet] Add DictionaryInvokeArgs for dynamically constructing invoke inputs #11335

Merged
merged 1 commit into from Nov 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,4 @@
changes:
- type: feat
scope: sdk/dotnet
description: Add DictionaryInvokeArgs for dynamically constructing invoke input bag of properties.
47 changes: 47 additions & 0 deletions sdk/dotnet/Pulumi.Tests/Resources/DictionaryInvokeArgsTests.cs
@@ -0,0 +1,47 @@
// Copyright 2016-2021, Pulumi Corporation

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Xunit;

namespace Pulumi.Tests.Resources
{
public class DictionaryInvokeArgsTests
{
[Fact]
public void ConstructorValidValues()
{
try
{
var dict = new Dictionary<string, object?>
{
{ "null", null },
{ "string", "foo" },
{ "int", 123 },
{ "dictionary", new Dictionary<string, object?> { {"foo", "bar"} }.ToImmutableDictionary() },
{ "output", Output.CreateSecret("secret") },
};
var dictionary = new DictionaryInvokeArgs(dict.ToImmutableDictionary());
}
catch
{
Assert.True(false, "DictionaryInvokeArgs constructor should not throw an exception");
}
}

[Fact]
public void ConstructorInvalidValue()
{
var dict = new Dictionary<string, object?>
{
{ "arbitrary", new { Foo = "bar" } },
};

Assert.Throws<InvalidOperationException>(() =>
{
new DictionaryInvokeArgs(dict.ToImmutableDictionary());
});
}
}
}
2 changes: 2 additions & 0 deletions sdk/dotnet/Pulumi/PublicAPI.Shipped.txt
Expand Up @@ -83,6 +83,8 @@ Pulumi.CustomTimeouts.Update.get -> System.TimeSpan?
Pulumi.CustomTimeouts.Update.set -> void
Pulumi.DictionaryResourceArgs
Pulumi.DictionaryResourceArgs.DictionaryResourceArgs(System.Collections.Immutable.ImmutableDictionary<string, object> dictionary) -> void
Pulumi.DictionaryInvokeArgs
Pulumi.DictionaryInvokeArgs.DictionaryInvokeArgs(System.Collections.Immutable.ImmutableDictionary<string, object> dictionary) -> void
Pulumi.Deployment
Pulumi.DeploymentInstance
Pulumi.DeploymentInstance.Call(string token, Pulumi.CallArgs args, Pulumi.Resource self = null, Pulumi.CallOptions options = null) -> void
Expand Down
68 changes: 68 additions & 0 deletions sdk/dotnet/Pulumi/Resources/DictionaryInvokeArgs.cs
@@ -0,0 +1,68 @@
// Copyright 2016-2022, Pulumi Corporation

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Pulumi.Serialization;

namespace Pulumi
{
/// <summary>
/// A special type of <see cref="InvokeArgs"/> with resource inputs represented
/// as a loosely-typed dictionary of objects. Normally,
/// <see cref="DictionaryInvokeArgs "/> should not be used by resource providers
/// since it's too low-level and provides low safety. Its target scenario are
/// resources with a very dynamic shape of inputs.
/// The input dictionary may only contain objects that are serializable by
/// Pulumi, i.e only the following types (or pulumi.Output of those types) are allowed:
/// <list type="bullet">
/// <item><description>Primitive types: <see cref="string"/>, <see cref="double"/>,
/// <see cref="int"/>, <see cref="bool"/></description></item>
/// <item><description><see cref="Asset"/>, <see cref="Archive"/>, or
/// <see cref="AssetArchive"/></description></item>
/// <item><description><see cref="System.Text.Json.JsonElement"/>
/// </description></item>
/// <item><description>Generic collections of the above:
/// <see cref="ImmutableArray{T}"/>, <see cref="ImmutableDictionary{TKey,TValue}"/>
/// with <see cref="string"/> keys, <see cref="Union{T0,T1}"/></description></item>
/// </list>
/// </summary>
public sealed class DictionaryInvokeArgs : InvokeArgs
{
private readonly ImmutableDictionary<string, object?> _dictionary;

/// <summary>
/// Constructs an instance of <see cref="DictionaryInvokeArgs"/> from
/// a dictionary of input objects.
/// </summary>
/// <param name="dictionary">The input dictionary. It may only contain objects
/// that are serializable by Pulumi.</param>
public DictionaryInvokeArgs(ImmutableDictionary<string, object?> dictionary)
{
// Run a basic validation of types of values in the dictionary
var seenTypes = new HashSet<Type>();
foreach (var value in dictionary.Values)
{
if (value == null) continue;

var targetType = value.GetType();
if (value is IOutput)
{
var type = value.GetType();
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Output<>))
{
targetType = type.GenericTypeArguments[0];
}
}

Converter.CheckTargetType(nameof(dictionary), targetType, seenTypes);
}

_dictionary = dictionary;
}

internal override Task<ImmutableDictionary<string, object?>> ToDictionaryAsync()
=> Task.FromResult(_dictionary);
}
}