Skip to content

Commit

Permalink
Merge #11335
Browse files Browse the repository at this point in the history
11335: [sdk/dotnet] Add DictionaryInvokeArgs for dynamically constructing invoke inputs r=Zaid-Ajaj a=Zaid-Ajaj

<!--- Please provide details if the checkbox below is to be left unchecked. -->
- [x] I have added tests that prove my fix is effective or that my feature works
<!--- 
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the `changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the Pulumi Service,
then the service should honor older versions of the CLI where this change would not exist.
You must then bump the API version in /pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi Service API version
  <!-- `@Pulumi` employees: If yes, you must submit corresponding changes in the service repo. -->


Co-authored-by: Zaid Ajaj <zaid.naom@gmail.com>
  • Loading branch information
bors[bot] and Zaid-Ajaj committed Nov 11, 2022
2 parents 5c1c462 + 134d821 commit ef927a7
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 0 deletions.
@@ -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);
}
}

0 comments on commit ef927a7

Please sign in to comment.