Skip to content

Commit cd05ec3

Browse files
authored
sdk/StackReference: Add GetOutputDetailsAsync (#103)
This is the C# equivalent of StackReference.GetOutputDetails that was added to: - Go SDK in pulumi/pulumi#12034 - Python SDK in pulumi/pulumi#12071 - Node SDK in pulumi/pulumi#12072 It introduces a GetOutputDetailsAsync method that returns a StackReferenceOutputDetails promise, allowing looking at the results without going through an Output type. **Testing**: As with Node (pulumi/pulumi#12072), C# appears to promote the entire dictionary to secret if one of the values is secret. Therefore, this needed separate tests for each case. Refs pulumi/pulumi#10839, pulumi/pulumi#5035
1 parent b55094b commit cd05ec3

File tree

5 files changed

+191
-0
lines changed

5 files changed

+191
-0
lines changed

CHANGELOG_PENDING.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@
44
- [sdk/providers] Updated names of "Olds" and "News" to make it clear if they are old/new inputs or state. Also removed the GetPluginInfo overload, version should now be passed into the main Serve method (defaults to the assembly version).
55
[#99](https://github.com/pulumi/pulumi-dotnet/pull/99)
66

7+
- [sdk] Added `StackReference.GetOutputDetailsAsync` to retrieve output values from stack references directly.
8+
[#103](https://github.com/pulumi/pulumi-dotnet/pull/103)
9+
710
### Bug Fixes
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright 2016-2023, Pulumi Corporation
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Threading.Tasks;
6+
using Pulumi.Testing;
7+
using Xunit;
8+
9+
namespace Pulumi.Tests.Resources
10+
{
11+
public class StackReferenceOutputDetailsTests
12+
{
13+
[Fact]
14+
public async void SupportsPlainText()
15+
{
16+
var mocks = new FakeStackOutputMocks("bucket", "my-bucket");
17+
var options = new TestOptions();
18+
19+
var (resources, outputs) = await Deployment.TestAsync(mocks, options, () =>
20+
{
21+
var stackReference = new StackReference("my-stack");
22+
return new Dictionary<string, object?>()
23+
{
24+
["bucket"] = stackReference.GetOutputDetailsAsync("bucket"),
25+
};
26+
});
27+
28+
var bucket = await (outputs["bucket"] as Task<StackReferenceOutputDetails>);
29+
Assert.Equal("my-bucket", bucket.Value);
30+
Assert.Null(bucket.SecretValue);
31+
}
32+
33+
[Fact]
34+
public async void SupportsSecrets()
35+
{
36+
var mocks = new FakeStackOutputMocks("secret", Output.CreateSecret("my-bucket"));
37+
var options = new TestOptions();
38+
39+
var (resouces, outputs) = await Deployment.TestAsync(mocks, options, () =>
40+
{
41+
var stackReference = new StackReference("my-stack");
42+
return new Dictionary<string, object?>()
43+
{
44+
["secret"] = stackReference.GetOutputDetailsAsync("secret"),
45+
};
46+
});
47+
48+
var secret = await (outputs["secret"] as Task<StackReferenceOutputDetails>);
49+
Assert.Null(secret.Value);
50+
Assert.Equal("my-bucket", secret.SecretValue);
51+
}
52+
53+
[Fact]
54+
public async void Unknowns()
55+
{
56+
var mocks = new FakeStackOutputMocks("something", "foo");
57+
var options = new TestOptions();
58+
59+
var (resources, outputs) = await Deployment.TestAsync(mocks, options, () =>
60+
{
61+
var stackReference = new StackReference("my-stack");
62+
return new Dictionary<string, object?>()
63+
{
64+
["unknown"] = stackReference.GetOutputDetailsAsync("unknown"),
65+
};
66+
});
67+
68+
var unknown = await (outputs["unknown"] as Task<StackReferenceOutputDetails>);
69+
Assert.Null(unknown.Value);
70+
Assert.Null(unknown.SecretValue);
71+
}
72+
}
73+
74+
class FakeStackOutputMocks : IMocks
75+
{
76+
private string key;
77+
private object val;
78+
public FakeStackOutputMocks(string key, object val)
79+
{
80+
this.key = key;
81+
this.val = val;
82+
}
83+
84+
public Task<object> CallAsync(MockCallArgs args)
85+
{
86+
return Task.FromResult<object>(args);
87+
}
88+
89+
public async Task<(string? id, object state)> NewResourceAsync(
90+
MockResourceArgs args)
91+
{
92+
if (args.Type == "pulumi:pulumi:StackReference")
93+
{
94+
var outputs = new Dictionary<string, object>();
95+
outputs[this.key] = this.val;
96+
97+
var props = new Dictionary<string, object>();
98+
props["outputs"] = outputs;
99+
return (args.Name + "-id", props);
100+
}
101+
else
102+
{
103+
throw new Exception($"Unknown resource {args.Type}");
104+
}
105+
}
106+
}
107+
}

sdk/Pulumi/Pulumi.xml

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/Pulumi/Resources/StackReference.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,32 @@ public Output<object> RequireOutput(Input<string> name)
8181
return value.WithIsSecret(IsSecretOutputName(name));
8282
}
8383

84+
/// <summary>
85+
/// Fetches the value of the named stack output
86+
/// and builds a <see ref="StackReferenceOutputDetails"/> object from it.
87+
/// <para />
88+
/// The returned object has its Value or SecretValue field set
89+
/// depending on whether the output is a secret.
90+
/// Neither field is set if the output is not found.
91+
/// </summary>
92+
/// <param name="name">The name of the stack output to fetch.</param>
93+
/// <returns>StackReferenceOutputDetails object containing the output.</returns>
94+
public async Task<StackReferenceOutputDetails> GetOutputDetailsAsync(string name)
95+
{
96+
var output = this.GetOutput(name);
97+
var data = await output.DataTask.ConfigureAwait(false);
98+
var details = new StackReferenceOutputDetails { };
99+
if (data.IsSecret)
100+
{
101+
details.SecretValue = data.Value;
102+
}
103+
else
104+
{
105+
details.Value = data.Value;
106+
}
107+
return details;
108+
}
109+
84110
/// <summary>
85111
/// Fetches the value of the named stack output. May return null if the value is
86112
/// not known for some reason.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2016-2023, Pulumi Corporation
2+
3+
using System.Collections.Generic;
4+
5+
namespace Pulumi
6+
{
7+
/// <summary>
8+
/// Holds a StackReference's output value.
9+
/// At most one of Value and SecretValue will be set.
10+
/// </summary>
11+
public sealed class StackReferenceOutputDetails
12+
{
13+
/// <summary>
14+
/// Output value returned by the <see cref="StackReference"/>.
15+
/// This field is only set if the output is not a secret.
16+
/// </summary>
17+
public object? Value { get; set; }
18+
19+
/// <summary>
20+
/// Secret output value returned by the <see cref="StackReference"/>.
21+
/// This field is only set if the output is a secret.
22+
/// </summary>
23+
public object? SecretValue { get; set; }
24+
}
25+
}

0 commit comments

Comments
 (0)