bored-oyster-3147
01/06/2021, 7:57 PMstack output --json
in order to get the latest outputs. Specifically this function:
async outputs(): Promise<OutputMap> {
await this.workspace.selectStack(this.name);
// TODO: do this in parallel after this is fixed <https://github.com/pulumi/pulumi/issues/6050>
const maskedResult = await this.runPulumiCmd(["stack", "output", "--json"]);
const plaintextResult = await this.runPulumiCmd(["stack", "output", "--json", "--show-secrets"]);
const maskedOuts = JSON.parse(maskedResult.stdout);
const plaintextOuts = JSON.parse(plaintextResult.stdout);
const outputs: OutputMap = {};
for (const [key, value] of Object.entries(plaintextOuts)) {
const secret = maskedOuts[key] === secretSentinel;
outputs[key] = { value, secret };
}
return outputs;
}
I'm willing to take any suggestions on how you'd like to accomplish this same result in .NET, since we can't simply assign arbitrary JSON to an anonymous object and have it typed such that we can iterate over the properties to build the resulting dictionary. I would consider just returning the JSON and letting the consumer deal with it and whatever they want to do with it. But that means you're losing the IsSecret
boolean on your output map as well, because we would only be able to return either plaintext or masked.stack output
a only ever 1 layer deep, or can it return nested objects? If it's only ever 1 layer deep and I can return all the values as strings than that may be fine.lemon-agent-27707
01/06/2021, 8:25 PMtall-librarian-49374
01/06/2021, 8:30 PMbored-oyster-3147
01/06/2021, 8:40 PMstack output
can return nested objects?
Up until this point I have been using System.Text.Json
not newtonsoft so I would have to switch.tall-librarian-49374
01/06/2021, 8:45 PMyescan return nested objects?stack output
I have to migrate all the existing serialization codeIsn’t a similar trick doable with System.Text.Json? It’s just an old question that I picked.
bored-oyster-3147
01/06/2021, 8:47 PMDictionary<string, object>
in System.Text.Json what you get back is Dictionary<string, JsonElement>
tall-librarian-49374
01/06/2021, 9:03 PMDictionary<string, string>
for now and leave a todo to write a recursive conversionbored-oyster-3147
01/06/2021, 9:07 PMSystem.Text.Json
is throwing me an exception if I try to deserialize JSON that has nested objects to Dictionary<string, string>
. It doesn't just place the raw string in the value it just failssecretSentinel
comparison that is happening in the code above?tall-librarian-49374
01/07/2021, 4:41 PMbored-oyster-3147
01/07/2021, 4:51 PMlemon-agent-27707
01/07/2021, 6:05 PMtall-needle-56640
01/07/2021, 6:34 PMDictionary<string, JsonElement>
not enough? From JsonElement, it should be possible to inspect it's type (i.e. JObject, JArray, JValue).bored-oyster-3147
01/07/2021, 6:41 PMJObject
, JArray
, & JValue
are all Newtonsoft types, not System.Text.Json. System.Text.Json uses JsonElement
to universally describe all JSON tokens.
If it is a nested object, than the value will be JsonElement
with a TokenType == Object
. And I would need to figure out how to get some kind of instance out of that so that I can return an OutputValue
. Our OutputValue
looks like this:
{
object Value,
bool IsSecret
}
And we don't want to expose JsonElement
. meaning we shouldn't be returning an OutputValue
where OutputValue.Value
at runtime is JsonElement
.
With newtonsoft, If I deserialize to Dictionary<string, object>
than if it is a nested object it will just be another Dictionary<string, object>
, recursively, until completion. Meaning OutputValue.Value
at runtime will be Dictionary<string, object>
rather than exposing a serialization typetall-needle-56640
01/07/2021, 7:18 PMIsSecret
property, wouldn't we want to return Dictionary<string, OutputMap>
?bored-oyster-3147
01/07/2021, 7:22 PMDictionary<string, OutputValue>
. checkout the function here
The converter you linked is interesting, but it is also not a public converter - it is only used for some tests temporarily. It also isn't recursively returning dictionaries if the value is a complex object. It also falls back to JsonElement
if it cant parse the type... which means we would possibly be returning JsonElement
tall-needle-56640
01/07/2021, 7:24 PMbored-oyster-3147
01/07/2021, 7:25 PMtall-needle-56640
01/07/2021, 7:31 PMSystem.Text.Json
now.bored-oyster-3147
01/07/2021, 7:36 PMtall-librarian-49374
01/07/2021, 7:49 PMbored-oyster-3147
01/07/2021, 7:57 PMprivate class SystemObjectJsonConverter : JsonConverter<object>
{
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.True)
{
return true;
}
if (reader.TokenType == JsonTokenType.False)
{
return false;
}
if (reader.TokenType == JsonTokenType.Number)
{
if (reader.TryGetInt64(out long l))
{
return l;
}
return reader.GetDouble();
}
if (reader.TokenType == JsonTokenType.String)
{
if (reader.TryGetDateTime(out DateTime datetime))
{
return datetime;
}
return reader.GetString();
}
if (reader.TokenType == JsonTokenType.StartArray)
{
return JsonSerializer.Deserialize<object[]>(ref reader, options);
}
if (reader.TokenType == JsonTokenType.StartObject)
{
var dictionary = new Dictionary<string, object>();
reader.Read();
while (reader.TokenType != JsonTokenType.EndObject)
{
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException("Expecting property name.");
var propertyName = reader.GetString();
reader.Read();
var value = JsonSerializer.Deserialize<object>(ref reader, options);
dictionary[propertyName] = value;
reader.Read();
}
return dictionary;
}
throw new JsonException("Invalid JSON element.");
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
throw new NotSupportedException($"Writing as [{typeof(object).FullName}] is not supported.");
}
}