Skip to content

Commit

Permalink
.Net: Include request info in HttpOperationException (#6309)
Browse files Browse the repository at this point in the history
### Motivation and Context

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->

### Description

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [ ] The code builds clean without any errors or warnings
- [ ] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [ ] All unit tests pass, and I have added new tests where possible
- [ ] I didn't break anyone 😄
  • Loading branch information
markwallace-microsoft committed May 17, 2024
1 parent 75ee1a9 commit 1e6c49e
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 5 deletions.
19 changes: 15 additions & 4 deletions dotnet/src/Functions/Functions.OpenApi/RestApiOperationRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,24 @@ internal sealed class RestApiOperationRunner
}
}

using var responseMessage = await this._httpClient.SendWithSuccessCheckAsync(requestMessage, cancellationToken).ConfigureAwait(false);
try
{
using var responseMessage = await this._httpClient.SendWithSuccessCheckAsync(requestMessage, cancellationToken).ConfigureAwait(false);

var response = await SerializeResponseContentAsync(requestMessage, payload, responseMessage.Content).ConfigureAwait(false);
var response = await SerializeResponseContentAsync(requestMessage, payload, responseMessage.Content).ConfigureAwait(false);

response.ExpectedSchema ??= GetExpectedSchema(expectedSchemas, responseMessage.StatusCode);
response.ExpectedSchema ??= GetExpectedSchema(expectedSchemas, responseMessage.StatusCode);

return response;
return response;
}
catch (HttpOperationException ex)
{
ex.RequestMethod = requestMessage.Method.Method;
ex.RequestUri = requestMessage.RequestUri;
ex.RequestPayload = payload;

throw;
}
}

/// <summary>
Expand Down
41 changes: 40 additions & 1 deletion dotnet/src/IntegrationTests/Plugins/RepairServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace SemanticKernel.IntegrationTests.Plugins;
public class RepairServiceTests
{
[Fact(Skip = "This test is for manual verification.")]
public async Task RepairServicePluginAsync()
public async Task ValidateInvokingRepairServicePluginAsync()
{
// Arrange
var kernel = new Kernel();
Expand Down Expand Up @@ -67,6 +67,45 @@ public async Task RepairServicePluginAsync()
Assert.Equal("Repair deleted", result.ToString());
}

[Fact(Skip = "This test is for manual verification.")]
public async Task HttpOperationExceptionIncludeRequestInfoAsync()
{
// Arrange
var kernel = new Kernel();
using var stream = System.IO.File.OpenRead("Plugins/repair-service.json");
using HttpClient httpClient = new();

var plugin = await kernel.ImportPluginFromOpenApiAsync(
"RepairService",
stream,
new OpenAIFunctionExecutionParameters(httpClient) { IgnoreNonCompliantErrors = true, EnableDynamicPayload = false });

var arguments = new KernelArguments
{
["payload"] = """{ "title": "Engine oil change", "description": "Need to drain the old engine oil and replace it with fresh oil.", "assignedTo": "", "date": "", "image": "" }"""
};

var id = 99999;

// Update Repair
arguments = new KernelArguments
{
["payload"] = $"{{ \"id\": {id}, \"assignedTo\": \"Karin Blair\", \"date\": \"2024-04-16\", \"image\": \"https://www.howmuchisit.org/wp-content/uploads/2011/01/oil-change.jpg\" }}"
};

try
{
await plugin["updateRepair"].InvokeAsync(kernel, arguments);
Assert.Fail("Expected HttpOperationException");
}
catch (HttpOperationException ex)
{
Assert.Equal("Response status code does not indicate success: 404 (Not Found).", ex.Message);
Assert.Equal("Patch", ex.RequestMethod);
Assert.Equal("https://piercerepairsapi.azurewebsites.net/repairs", ex.RequestUri!.ToString());
}
}

public class Repair
{
[JsonPropertyName("id")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,28 @@ public HttpOperationException(HttpStatusCode? statusCode, string? responseConten
/// Gets or sets the content of the HTTP response.
/// </summary>
public string? ResponseContent { get; set; }

/// <summary>
/// Gets the method used for the HTTP request.
/// </summary>
/// <remarks>
/// This information is only available in limited circumstances e.g. when using Open API plugins.
/// </remarks>
public string? RequestMethod { get; set; }

/// <summary>
/// Gets the System.Uri used for the HTTP request.
/// </summary>
/// <remarks>
/// This information is only available in limited circumstances e.g. when using Open API plugins.
/// </remarks>
public Uri? RequestUri { get; set; }

/// <summary>
/// Gets the payload sent in the request.
/// </summary>
/// <remarks>
/// This information is only available in limited circumstances e.g. when using Open API plugins.
/// </remarks>
public object? RequestPayload { get; set; }
}

0 comments on commit 1e6c49e

Please sign in to comment.