Skip to content

Commit

Permalink
Json fix when server returns a malformed Json payload (microsoft#2302)
Browse files Browse the repository at this point in the history
  • Loading branch information
LucGenetier authored and Jack Kelliher (from Dev Box) committed Apr 12, 2024
1 parent 6905324 commit 6c857b1
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 5 deletions.
21 changes: 16 additions & 5 deletions src/libraries/Microsoft.PowerFx.Json/FormulaValueJSON.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text.Json;
using Microsoft.PowerFx.Core.Functions;
using Microsoft.PowerFx.Core.IR;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Functions;
Expand All @@ -32,10 +31,22 @@ public static FormulaValue FromJson(string jsonString, FormulaType formulaType =

public static FormulaValue FromJson(string jsonString, FormulaValueJsonSerializerSettings settings, FormulaType formulaType = null)
{
using JsonDocument document = JsonDocument.Parse(jsonString);
JsonElement propBag = document.RootElement;

return FromJson(propBag, settings, formulaType);
try
{
using JsonDocument document = JsonDocument.Parse(jsonString);
JsonElement propBag = document.RootElement;

return FromJson(propBag, settings, formulaType);
}
catch (JsonException je)
{
return new ErrorValue(IRContext.NotInSource(formulaType), new ExpressionError()
{
Message = $"{je.GetType().Name} {je.Message}",
Span = new Syntax.Span(0, 0),
Kind = ErrorKind.Network
});
}
}

/// <summary>
Expand Down
33 changes: 33 additions & 0 deletions src/tests/Microsoft.PowerFx.Connectors.Tests/IntellisenseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,39 @@ public void ConnectorIntellisenseTest_ServerError()
Assert.Equal(string.Empty, list);
}

[Fact]
public void ConnectorIntellisenseTest_EmptyResponse()
{
string expression = @"SQL.ExecuteProcedureV2(""default"", ""default"", ""sp_2"", { ";

using LoggingTestServer testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
OpenApiDocument apiDoc = testConnector._apiDocument;
PowerFxConfig config = new PowerFxConfig(Features.PowerFxV1);

using HttpClient httpClient = new HttpClient(testConnector);
using PowerPlatformConnectorClient client = new PowerPlatformConnectorClient("tip1-shared-002.azure-apim.net", "a2df3fb8-e4a4-e5e6-905c-e3dff9f93b46", "5f57ec83acef477b8ccc769e52fa22cc", () => "eyJ0eXA...", httpClient)
{
SessionId = "8e67ebdc-d402-455a-b33a-304820832383"
};

config.AddActionConnector("SQL", apiDoc, new ConsoleLogger(_output));

// The response is empty, this is to ensure we manage properly the exception coming from ExtractFromJson
testConnector.SetResponseFromFile(@"Responses\EmptyResponse.json");

RecalcEngine engine = new RecalcEngine(config);
BasicServiceProvider serviceProvider = new BasicServiceProvider().AddRuntimeContext(new TestConnectorRuntimeContext("SQL", client, console: _output));

CheckResult checkResult = engine.Check(expression, symbolTable: null);

// This call should not throw an exception
IIntellisenseResult suggestions = engine.Suggest(checkResult, expression.Length, serviceProvider);

// We don't get any result as the response is invalid
string list = string.Join("|", suggestions.Suggestions.Select(s => s.DisplayText.Text).OrderBy(x => x));
Assert.Equal(string.Empty, list);
}

[Theory]
[InlineData(1, 1, @"SQL.ExecuteProcedureV2(""default"", ""default"", ""sp_1"", ", @"p1")] // stored proc with 1 param, out of record
public void ConnectorIntellisenseTestLSP(int responseIndex, int networkCall, string expression, string expectedSuggestions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.OpenApi.Models;
Expand Down Expand Up @@ -1422,6 +1423,52 @@ public async Task SQL_ExecuteStoredProc_WithUserAgent()
Assert.Equal(expected, actual);
}

[Fact]
public async Task SQL_ExecuteStoredProc_WithEmptyServerResponse()
{
using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig(Features.PowerFxV1);
using var httpClient = new HttpClient(testConnector);
using var client = new PowerPlatformConnectorClient("tip1-shared-002.azure-apim.net", "a2df3fb8-e4a4-e5e6-905c-e3dff9f93b46", "5f57ec83acef477b8ccc769e52fa22cc", () => "eyJ0eX...", "MyProduct/v1.2", httpClient)
{
SessionId = "8e67ebdc-d402-455a-b33a-304820832383"
};

config.AddActionConnector("SQL", apiDoc, new ConsoleLogger(_output));
var engine = new RecalcEngine(config);
RuntimeConfig rc = new RuntimeConfig().AddRuntimeContext(new TestConnectorRuntimeContext("SQL", client, console: _output));

testConnector.SetResponseFromFile(@"Responses\EmptyResponse.json");
FormulaValue result = await engine.EvalAsync(@"SQL.ExecuteProcedureV2(""pfxdev-sql.database.windows.net"", ""connectortest"", ""sp_1"", { p1: 50 })", CancellationToken.None, new ParserOptions() { AllowsSideEffects = true }, runtimeConfig: rc).ConfigureAwait(false);
Assert.True(result is BlankValue);
}

[Fact]
public async Task SQL_ExecuteStoredProc_WithInvalidResponse()
{
using var testConnector = new LoggingTestServer(@"Swagger\SQL Server.json", _output);
var apiDoc = testConnector._apiDocument;
var config = new PowerFxConfig(Features.PowerFxV1);
using var httpClient = new HttpClient(testConnector);
using var client = new PowerPlatformConnectorClient("tip1-shared-002.azure-apim.net", "a2df3fb8-e4a4-e5e6-905c-e3dff9f93b46", "5f57ec83acef477b8ccc769e52fa22cc", () => "eyJ0eX...", "MyProduct/v1.2", httpClient)
{
SessionId = "8e67ebdc-d402-455a-b33a-304820832383"
};

config.AddActionConnector("SQL", apiDoc, new ConsoleLogger(_output));
var engine = new RecalcEngine(config);
RuntimeConfig rc = new RuntimeConfig().AddRuntimeContext(new TestConnectorRuntimeContext("SQL", client, console: _output));

testConnector.SetResponseFromFile(@"Responses\Invalid.txt");
FormulaValue result = await engine.EvalAsync(@"SQL.ExecuteProcedureV2(""pfxdev-sql.database.windows.net"", ""connectortest"", ""sp_1"", { p1: 50 })", CancellationToken.None, new ParserOptions() { AllowsSideEffects = true }, runtimeConfig: rc).ConfigureAwait(false);

ErrorValue ev = Assert.IsType<ErrorValue>(result);
string message = ev.Errors[0].Message;

Assert.Equal(@$"SQL.ExecuteProcedureV2 failed: JsonReaderException '+' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.", message);
}

[Fact]
public async Task SharePointOnlineTest()
{
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
+

0 comments on commit 6c857b1

Please sign in to comment.