Skip to content

Commit

Permalink
Add Output.JsonSerialize to dotnet sdk
Browse files Browse the repository at this point in the history
Plan is to add functions like this to _all_ the SDKs.
JsonSerialization is _very_ language specific, dotnet for example uses
System.Text.Json, go would use JsonMarshal, etc. So it's worth having it
built into SDKs and then exposed as a PCL intrinsic (with the caveat
that the cross-language result will be _valid_ JSON, but with no
commmitment to formatting for example).

This is just the first part of this work, to add it to the dotnet SDK
(simply because I know that best).
  • Loading branch information
Frassle authored and aq17 committed Dec 6, 2022
1 parent bb1ef96 commit a0b9c83
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 1 deletion.
@@ -0,0 +1,4 @@
changes:
- type: feat
scope: sdk/dotnet
description: Add Output.JsonSerialize using System.Text.Json.
53 changes: 53 additions & 0 deletions sdk/dotnet/Pulumi.Tests/Core/OutputTests.cs
Expand Up @@ -9,6 +9,20 @@

namespace Pulumi.Tests.Core
{
// Simple struct used for JSON tests
public struct TestStructure {
public int X { get; set;}

private int y;

public string Z => (y+1).ToString();

public TestStructure(int x, int y) {
X = x;
this.y = y;
}
}

public class OutputTests : PulumiTest
{
private static Output<T> CreateOutput<T>(T value, bool isKnown, bool isSecret = false)
Expand Down Expand Up @@ -618,6 +632,45 @@ public Task CreateSecretSetsSecret()
Assert.True(data.IsSecret);
Assert.Equal(0, data.Value);
});

[Fact]
public Task JsonSerializeBasic()
=> RunInNormal(async () =>
{
var o1 = CreateOutput(0, true);
var o2 = Output.JsonSerialize(o1);
var data = await o2.DataTask.ConfigureAwait(false);
Assert.True(data.IsKnown);
Assert.False(data.IsSecret);
Assert.Equal("0", data.Value);
});

[Fact]
public Task JsonSerializeWithOptions()
=> RunInNormal(async () =>
{
var v = new System.Collections.Generic.Dictionary<string, TestStructure>();
v.Add("a", new TestStructure(1, 2));
v.Add("b", new TestStructure(int.MinValue, int.MaxValue));
var o1 = CreateOutput(v, true);
var options = new System.Text.Json.JsonSerializerOptions();
options.WriteIndented = true;
var o2 = Output.JsonSerialize(o1, options);
var data = await o2.DataTask.ConfigureAwait(false);
Assert.True(data.IsKnown);
Assert.False(data.IsSecret);
var expected = @"{
""a"": {
""X"": 1,
""Z"": ""3""
},
""b"": {
""X"": -2147483648,
""Z"": ""-2147483648""
}
}";
Assert.Equal(expected, data.Value);
});
}
}
}
14 changes: 13 additions & 1 deletion sdk/dotnet/Pulumi/Core/Output.cs
Expand Up @@ -106,6 +106,18 @@ public static Output<string> Format(FormattableString formattableString)

internal static Output<ImmutableArray<T>> Concat<T>(Output<ImmutableArray<T>> values1, Output<ImmutableArray<T>> values2)
=> Tuple(values1, values2).Apply(tuple => tuple.Item1.AddRange(tuple.Item2));

/// <summary>
/// Uses <see cref="System.Text.Json.JsonSerializer.SerializeAsync{T}"/> to serialize the given <see
/// cref="Output{T}"/> value into a JSON string.
/// </summary>
public static Output<string> JsonSerialize<T>(Output<T> value, System.Text.Json.JsonSerializerOptions? options = null) {
return value.Apply(async v => {
var utf8 = new System.IO.MemoryStream();
await System.Text.Json.JsonSerializer.SerializeAsync<T>(utf8, v, options);
return System.Text.Encoding.UTF8.GetString(new ReadOnlySpan<byte>(utf8.GetBuffer(), 0, (int)utf8.Length));
});
}
}

/// <summary>
Expand All @@ -128,7 +140,7 @@ internal interface IOutput
/// <see cref="Output{T}"/>s are a key part of how Pulumi tracks dependencies between <see
/// cref="Resource"/>s. Because the values of outputs are not available until resources are
/// created, these are represented using the special <see cref="Output{T}"/>s type, which
/// internally represents two things: an eventually available value of the output and
/// internally represents two things: an eventually available value of the output and
/// the dependency on the source(s) of the output value.
/// In fact, <see cref="Output{T}"/>s is quite similar to <see cref="Task{TResult}"/>.
/// Additionally, they carry along dependency information.
Expand Down

0 comments on commit a0b9c83

Please sign in to comment.