1
0
mirror of https://github.com/bitwarden/server synced 2025-12-16 00:03:54 +00:00

PM-25413 no badRequest result because of error from Onyx (#6285)

This commit is contained in:
Vijay Oommen
2025-09-04 12:54:24 -05:00
committed by GitHub
parent e456b4ce21
commit 8b30c33eae
2 changed files with 58 additions and 8 deletions

View File

@@ -152,6 +152,12 @@ public class FreshdeskController : Controller
return new BadRequestResult(); return new BadRequestResult();
} }
// if there is no description, then we don't send anything to onyx
if (string.IsNullOrEmpty(model.TicketDescriptionText.Trim()))
{
return Ok();
}
// create the onyx `answer-with-citation` request // create the onyx `answer-with-citation` request
var onyxRequestModel = new OnyxAnswerWithCitationRequestModel(model.TicketDescriptionText, _billingSettings.Onyx.PersonaId); var onyxRequestModel = new OnyxAnswerWithCitationRequestModel(model.TicketDescriptionText, _billingSettings.Onyx.PersonaId);
var onyxRequest = new HttpRequestMessage(HttpMethod.Post, var onyxRequest = new HttpRequestMessage(HttpMethod.Post,
@@ -164,9 +170,12 @@ public class FreshdeskController : Controller
// the CallOnyxApi will return a null if we have an error response // the CallOnyxApi will return a null if we have an error response
if (onyxJsonResponse?.Answer == null || !string.IsNullOrEmpty(onyxJsonResponse?.ErrorMsg)) if (onyxJsonResponse?.Answer == null || !string.IsNullOrEmpty(onyxJsonResponse?.ErrorMsg))
{ {
return BadRequest( _logger.LogWarning("Error getting answer from Onyx AI. Freshdesk model: {model}\r\n Onyx query {query}\r\nresponse: {response}. ",
string.Format("Failed to get a valid response from Onyx API. Response: {0}", JsonSerializer.Serialize(model),
JsonSerializer.Serialize(onyxJsonResponse ?? new OnyxAnswerWithCitationResponseModel()))); JsonSerializer.Serialize(onyxRequestModel),
JsonSerializer.Serialize(onyxJsonResponse));
return Ok(); // return ok so we don't retry
} }
// add the answer as a note to the ticket // add the answer as a note to the ticket

View File

@@ -8,6 +8,7 @@ using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using NSubstitute; using NSubstitute;
using NSubstitute.ReceivedExtensions; using NSubstitute.ReceivedExtensions;
@@ -126,7 +127,7 @@ public class FreshdeskControllerTests
[Theory] [Theory]
[BitAutoData(WebhookKey)] [BitAutoData(WebhookKey)]
public async Task PostWebhookOnyxAi_invalid_onyx_response_results_in_BadRequest( public async Task PostWebhookOnyxAi_invalid_onyx_response_results_is_logged(
string freshdeskWebhookKey, FreshdeskOnyxAiWebhookModel model, string freshdeskWebhookKey, FreshdeskOnyxAiWebhookModel model,
SutProvider<FreshdeskController> sutProvider) SutProvider<FreshdeskController> sutProvider)
{ {
@@ -150,8 +151,18 @@ public class FreshdeskControllerTests
var response = await sutProvider.Sut.PostWebhookOnyxAi(freshdeskWebhookKey, model); var response = await sutProvider.Sut.PostWebhookOnyxAi(freshdeskWebhookKey, model);
var result = Assert.IsAssignableFrom<BadRequestObjectResult>(response); var statusCodeResult = Assert.IsAssignableFrom<StatusCodeResult>(response);
Assert.Equal(StatusCodes.Status400BadRequest, result.StatusCode); Assert.Equal(StatusCodes.Status200OK, statusCodeResult.StatusCode);
var _logger = sutProvider.GetDependency<ILogger<FreshdeskController>>();
// workaround because _logger.Received(1).LogWarning(...) does not work
_logger.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Log" && c.GetArguments()[1].ToString().Contains("Error getting answer from Onyx AI"));
// sent call to Onyx API - but we got an error response
_ = mockOnyxHttpMessageHandler.Received(1).Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>());
// did not call freshdesk to add a note since onyx failed
_ = mockFreshdeskHttpMessageHandler.DidNotReceive().Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>());
} }
[Theory] [Theory]
@@ -174,10 +185,9 @@ public class FreshdeskControllerTests
.Returns(mockFreshdeskAddNoteResponse); .Returns(mockFreshdeskAddNoteResponse);
var freshdeskHttpClient = new HttpClient(mockFreshdeskHttpMessageHandler); var freshdeskHttpClient = new HttpClient(mockFreshdeskHttpMessageHandler);
// mocking Onyx api response given a ticket description // mocking Onyx api response given a ticket description
var mockOnyxHttpMessageHandler = Substitute.ForPartsOf<MockHttpMessageHandler>(); var mockOnyxHttpMessageHandler = Substitute.ForPartsOf<MockHttpMessageHandler>();
onyxResponse.ErrorMsg = string.Empty; onyxResponse.ErrorMsg = "string.Empty";
var mockOnyxResponse = new HttpResponseMessage(System.Net.HttpStatusCode.OK) var mockOnyxResponse = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{ {
Content = new StringContent(JsonSerializer.Serialize(onyxResponse)) Content = new StringContent(JsonSerializer.Serialize(onyxResponse))
@@ -195,6 +205,37 @@ public class FreshdeskControllerTests
Assert.Equal(StatusCodes.Status200OK, result.StatusCode); Assert.Equal(StatusCodes.Status200OK, result.StatusCode);
} }
[Theory]
[BitAutoData(WebhookKey)]
public async Task PostWebhookOnyxAi_ticket_description_is_empty_return_success(
string freshdeskWebhookKey, FreshdeskOnyxAiWebhookModel model,
SutProvider<FreshdeskController> sutProvider)
{
var billingSettings = sutProvider.GetDependency<IOptions<BillingSettings>>().Value;
billingSettings.FreshDesk.WebhookKey.Returns(freshdeskWebhookKey);
billingSettings.Onyx.BaseUrl.Returns("http://simulate-onyx-api.com/api");
model.TicketDescriptionText = " "; // empty description
// mocking freshdesk api add note request (POST)
var mockFreshdeskHttpMessageHandler = Substitute.ForPartsOf<MockHttpMessageHandler>();
var freshdeskHttpClient = new HttpClient(mockFreshdeskHttpMessageHandler);
// mocking Onyx api response given a ticket description
var mockOnyxHttpMessageHandler = Substitute.ForPartsOf<MockHttpMessageHandler>();
var onyxHttpClient = new HttpClient(mockOnyxHttpMessageHandler);
sutProvider.GetDependency<IHttpClientFactory>().CreateClient("FreshdeskApi").Returns(freshdeskHttpClient);
sutProvider.GetDependency<IHttpClientFactory>().CreateClient("OnyxApi").Returns(onyxHttpClient);
var response = await sutProvider.Sut.PostWebhookOnyxAi(freshdeskWebhookKey, model);
var result = Assert.IsAssignableFrom<OkResult>(response);
Assert.Equal(StatusCodes.Status200OK, result.StatusCode);
_ = mockFreshdeskHttpMessageHandler.DidNotReceive().Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>());
_ = mockOnyxHttpMessageHandler.DidNotReceive().Send(Arg.Any<HttpRequestMessage>(), Arg.Any<CancellationToken>());
}
public class MockHttpMessageHandler : HttpMessageHandler public class MockHttpMessageHandler : HttpMessageHandler
{ {
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)