Skip to content

Use ConvertFromJson/ConvertToJson APIs directly #141

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

Merged
merged 3 commits into from
Feb 2, 2019
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions src/PowerShell/PowerShellManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,6 @@ internal Hashtable InvokeFunction(
}
}

/// <summary>
/// Helper method to convert the result returned from a function to JSON.
/// </summary>
internal string ConvertToJson(object fromObj)
{
return _pwsh.AddCommand("Microsoft.PowerShell.Utility\\ConvertTo-Json")
.AddParameter("InputObject", fromObj)
.AddParameter("Depth", 3)
.AddParameter("Compress", true)
.InvokeAndClearCommands<string>()[0];
}

private void ResetRunspace(string moduleName)
{
// Reset the runspace to the Initial Session State
Expand Down
8 changes: 4 additions & 4 deletions src/RequestProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ private void ProcessInvocationRequestImpl(StreamingMessage request, AzFunctionIn
? InvokeOrchestrationFunction(psManager, functionInfo, invocationRequest)
: InvokeSingleActivityFunction(psManager, functionInfo, invocationRequest);

BindOutputFromResult(psManager, response.InvocationResponse, functionInfo, results);
BindOutputFromResult(response.InvocationResponse, functionInfo, results);
}
catch (Exception e)
{
Expand Down Expand Up @@ -239,7 +239,7 @@ private Hashtable InvokeSingleActivityFunction(PowerShellManager psManager, AzFu
/// <summary>
/// Set the 'ReturnValue' and 'OutputData' based on the invocation results appropriately.
/// </summary>
private void BindOutputFromResult(PowerShellManager psManager, InvocationResponse response, AzFunctionInfo functionInfo, Hashtable results)
private void BindOutputFromResult(InvocationResponse response, AzFunctionInfo functionInfo, Hashtable results)
{
switch (functionInfo.Type)
{
Expand All @@ -252,7 +252,7 @@ private void BindOutputFromResult(PowerShellManager psManager, InvocationRespons

object outValue = results[outBindingName];
object transformedValue = Utils.TransformOutBindingValueAsNeeded(outBindingName, bindingInfo, outValue);
TypedData dataToUse = transformedValue.ToTypedData(psManager);
TypedData dataToUse = transformedValue.ToTypedData();

// if one of the bindings is '$return' we need to set the ReturnValue
if(string.Equals(outBindingName, AzFunctionInfo.DollarReturn, StringComparison.OrdinalIgnoreCase))
Expand All @@ -273,7 +273,7 @@ private void BindOutputFromResult(PowerShellManager psManager, InvocationRespons

case AzFunctionType.OrchestrationFunction:
case AzFunctionType.ActivityFunction:
response.ReturnValue = results[AzFunctionInfo.DollarReturn].ToTypedData(psManager);
response.ReturnValue = results[AzFunctionInfo.DollarReturn].ToTypedData();
break;

default:
Expand Down
41 changes: 17 additions & 24 deletions src/Utility/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Reflection;

using Google.Protobuf;
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
using Microsoft.Azure.Functions.PowerShellWorker.PowerShell;
using Microsoft.PowerShell.Commands;
using Newtonsoft.Json.Linq;

namespace Microsoft.Azure.Functions.PowerShellWorker.Utility
Expand Down Expand Up @@ -94,26 +94,10 @@ internal static object ToObject(this TypedData data)
}
}

// PowerShell NuGet packages only have 'System.Management.Automation.dll' as the ref assembly, and thus types from other powershell assemblies
// cannot be used directly in an application that reference the PowerShell NuGet packages. This is tracked by PowerShell#8121.
// Here we need to use 'Microsoft.PowerShell.Commands.JsonObject' from 'Microsoft.PowerShell.Commands.Utility'. Due the above issue, we have to
// use reflection to call 'JsonObject.ConvertFromJson(...)'.
private static MethodInfo s_ConvertFromJson = null;
private static object ConvertFromJson(string json)
{
const string UtilityAssemblyFullName = "Microsoft.PowerShell.Commands.Utility, Version=6.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35";
object retObj = JsonObject.ConvertFromJson(json, returnHashtable: true, error: out _);

if (s_ConvertFromJson == null)
{
Assembly utilityAssembly = AppDomain.CurrentDomain.GetAssemblies().First(asm => asm.FullName == UtilityAssemblyFullName);
Type jsonObjectType = utilityAssembly.GetType("Microsoft.PowerShell.Commands.JsonObject");
s_ConvertFromJson = jsonObjectType.GetMethod(
name: "ConvertFromJson",
types: new Type[] { typeof(string), typeof(bool), typeof(ErrorRecord).MakeByRefType() },
modifiers: null);
}

object retObj = s_ConvertFromJson.Invoke(null, new object[] { json, true, null });
if (retObj is PSObject psObj)
{
retObj = psObj.BaseObject;
Expand All @@ -136,6 +120,16 @@ private static object ConvertFromJson(string json)
return retObj;
}

private static string ConvertToJson(object fromObj)
{
var context = new JsonObject.ConvertToJsonContext(
maxDepth: 3,
enumsAsStrings: false,
compressOutput: true);

return JsonObject.ConvertToJson(fromObj, in context);
}

internal static RpcException ToRpcException(this Exception exception)
{
return new RpcException
Expand All @@ -146,7 +140,7 @@ internal static RpcException ToRpcException(this Exception exception)
};
}

private static RpcHttp ToRpcHttp(this HttpResponseContext httpResponseContext, PowerShellManager psHelper)
private static RpcHttp ToRpcHttp(this HttpResponseContext httpResponseContext)
{
var rpcHttp = new RpcHttp
{
Expand All @@ -155,7 +149,7 @@ private static RpcHttp ToRpcHttp(this HttpResponseContext httpResponseContext, P

if (httpResponseContext.Body != null)
{
rpcHttp.Body = httpResponseContext.Body.ToTypedData(psHelper);
rpcHttp.Body = httpResponseContext.Body.ToTypedData();
}

rpcHttp.EnableContentNegotiation = httpResponseContext.EnableContentNegotiation;
Expand All @@ -178,7 +172,7 @@ private static RpcHttp ToRpcHttp(this HttpResponseContext httpResponseContext, P
return rpcHttp;
}

internal static TypedData ToTypedData(this object value, PowerShellManager psHelper)
internal static TypedData ToTypedData(this object value)
{
if (value is TypedData self)
{
Expand Down Expand Up @@ -218,14 +212,13 @@ internal static TypedData ToTypedData(this object value, PowerShellManager psHel
typedData.Stream = ByteString.FromStream(s);
break;
case HttpResponseContext http:
typedData.Http = http.ToRpcHttp(psHelper);
typedData.Http = http.ToRpcHttp();
break;
case string str:
if (IsValidJson(str)) { typedData.Json = str; } else { typedData.String = str; }
break;
default:
if (psHelper == null) { throw new ArgumentNullException(nameof(psHelper)); }
typedData.Json = psHelper.ConvertToJson(originalValue);
typedData.Json = ConvertToJson(originalValue);
break;
}
return typedData;
Expand Down
42 changes: 17 additions & 25 deletions test/Unit/Utility/TypeExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,8 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Test
{
public class TypeExtensionsTests
{
private readonly ConsoleLogger _testLogger;
private readonly PowerShellManager _testManager;

public TypeExtensionsTests()
{
_testLogger = new ConsoleLogger();
_testManager = TestUtils.NewTestPowerShellManager(_testLogger);
}

#region TypedDataToObject

[Fact]
public void TestTypedDataToObjectHttpRequestContextBasic()
{
Expand Down Expand Up @@ -343,7 +335,7 @@ public void TestObjectToTypedDataRpcHttpBasic()
}
};

Assert.Equal(expected, input.ToTypedData(null));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -366,7 +358,7 @@ public void TestObjectToTypedDataRpcHttpContentTypeSet()
}
};

Assert.Equal(expected, input.ToTypedData(null));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -389,7 +381,7 @@ public void TestObjectToTypedDataRpcHttpContentTypeInHeader()
}
};

Assert.Equal(expected, input.ToTypedData(null));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -412,7 +404,7 @@ public void TestObjectToTypedDataRpcHttpStatusCodeString()
}
};

Assert.Equal(expected, input.ToTypedData(null));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -426,7 +418,7 @@ public void TestObjectToTypedDataInt()
Int = data
};

Assert.Equal(expected, input.ToTypedData(null));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -440,7 +432,7 @@ public void TestObjectToTypedDataDouble()
Double = data
};

Assert.Equal(expected, input.ToTypedData(null));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -454,7 +446,7 @@ public void TestObjectToTypedDataString()
String = data
};

Assert.Equal(expected, input.ToTypedData(null));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -468,7 +460,7 @@ public void TestObjectToTypedDataBytes()
Bytes = ByteString.CopyFrom(data)
};

Assert.Equal(expected, input.ToTypedData(null));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -483,7 +475,7 @@ public void TestObjectToTypedDataStream()
Stream = ByteString.FromStream(data)
};

Assert.Equal(expected, input.ToTypedData(null));
Assert.Equal(expected, input.ToTypedData());
}
}

Expand All @@ -498,7 +490,7 @@ public void TestObjectToTypedDataJsonString()
Json = data
};

Assert.Equal(expected, input.ToTypedData(null));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -512,7 +504,7 @@ public void TestObjectToTypedDataJsonHashtable()
Json = "{\"foo\":\"bar\"}"
};

Assert.Equal(expected, input.ToTypedData(_testManager));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -526,7 +518,7 @@ public void TestObjectToTypedData_PSObjectToJson_1()
Json = "{\"foo\":\"bar\"}"
};

Assert.Equal(expected, input.ToTypedData(_testManager));
Assert.Equal(expected, input.ToTypedData());
}

[Fact]
Expand All @@ -541,7 +533,7 @@ public void TestObjectToTypedData_PSObjectToJson_2()
Json = "{\"foo\":\"bar\"}"
};

Assert.Equal(expected, input.ToTypedData(_testManager));
Assert.Equal(expected, input.ToTypedData());
}
}

Expand All @@ -551,7 +543,7 @@ public void TestObjectToTypedData_PSObjectToBytes()
var data = new byte[] { 12,23,34 };
object input = PSObject.AsPSObject(data);

TypedData output = input.ToTypedData(_testManager);
TypedData output = input.ToTypedData();

Assert.Equal(TypedData.DataOneofCase.Bytes, output.DataCase);
Assert.Equal(3, output.Bytes.Length);
Expand All @@ -563,7 +555,7 @@ public void TestObjectToTypedData_PSObjectToStream()
using (var data = new MemoryStream(new byte[] { 12,23,34 }))
{
object input = PSObject.AsPSObject(data);
TypedData output = input.ToTypedData(_testManager);
TypedData output = input.ToTypedData();

Assert.Equal(TypedData.DataOneofCase.Stream, output.DataCase);
Assert.Equal(3, output.Stream.Length);
Expand All @@ -574,7 +566,7 @@ public void TestObjectToTypedData_PSObjectToStream()
public void TestObjectToTypedData_PSObjectToString()
{
object input = PSObject.AsPSObject("Hello World");
TypedData output = input.ToTypedData(_testManager);
TypedData output = input.ToTypedData();

Assert.Equal(TypedData.DataOneofCase.String, output.DataCase);
Assert.Equal("Hello World", output.String);
Expand Down