();
diff --git a/src/Billing/appsettings.Development.json b/src/Billing/appsettings.Development.json
index fe8e47b2f6..77057fde7f 100644
--- a/src/Billing/appsettings.Development.json
+++ b/src/Billing/appsettings.Development.json
@@ -32,10 +32,5 @@
"connectionString": "UseDevelopmentStorage=true"
}
},
- "billingSettings": {
- "onyx": {
- "personaId": 68
- }
- },
"pricingUri": "https://billingpricing.qa.bitwarden.pw"
}
diff --git a/src/Billing/appsettings.Production.json b/src/Billing/appsettings.Production.json
index 4be5d51a52..819986181f 100644
--- a/src/Billing/appsettings.Production.json
+++ b/src/Billing/appsettings.Production.json
@@ -26,10 +26,7 @@
"payPal": {
"production": true,
"businessId": "4ZDA7DLUUJGMN"
- },
- "onyx": {
- "personaId": 7
- }
+ }
},
"Logging": {
"IncludeScopes": false,
diff --git a/src/Billing/appsettings.json b/src/Billing/appsettings.json
index aa14f1d377..7093b6a923 100644
--- a/src/Billing/appsettings.json
+++ b/src/Billing/appsettings.json
@@ -61,27 +61,6 @@
"production": false,
"businessId": "AD3LAUZSNVPJY",
"webhookKey": "SECRET"
- },
- "freshdesk": {
- "apiKey": "SECRET",
- "webhookKey": "SECRET",
- "region": "US",
- "userFieldName": "cf_user",
- "orgFieldName": "cf_org",
- "removeNewlinesInReplies": true,
- "autoReplyGreeting": "Greetings,
Thank you for contacting Bitwarden. The reply below was generated by our AI agent based on your message:
",
- "autoReplySalutation": "
If this response doesn’t fully address your question, simply reply to this email and a member of our Customer Success team will be happy to assist you further.
Best Regards,
The Bitwarden Customer Success Team
"
- },
- "onyx": {
- "apiKey": "SECRET",
- "baseUrl": "https://cloud.onyx.app/api",
- "path": "/chat/send-message-simple-api",
- "useAnswerWithCitationModels": true,
- "personaId": 7,
- "searchSettings": {
- "runSearch": "always",
- "realTime": true
- }
}
}
}
diff --git a/test/Billing.Test/Controllers/FreshdeskControllerTests.cs b/test/Billing.Test/Controllers/FreshdeskControllerTests.cs
deleted file mode 100644
index 5c9199d29a..0000000000
--- a/test/Billing.Test/Controllers/FreshdeskControllerTests.cs
+++ /dev/null
@@ -1,251 +0,0 @@
-using System.Text.Json;
-using Bit.Billing.Controllers;
-using Bit.Billing.Models;
-using Bit.Core.AdminConsole.Entities;
-using Bit.Core.Entities;
-using Bit.Core.Repositories;
-using Bit.Test.Common.AutoFixture;
-using Bit.Test.Common.AutoFixture.Attributes;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using NSubstitute;
-using NSubstitute.ReceivedExtensions;
-using Xunit;
-
-namespace Bit.Billing.Test.Controllers;
-
-[ControllerCustomize(typeof(FreshdeskController))]
-[SutProviderCustomize]
-public class FreshdeskControllerTests
-{
- private const string ApiKey = "TESTFRESHDESKAPIKEY";
- private const string WebhookKey = "TESTKEY";
-
- private const string UserFieldName = "cf_user";
- private const string OrgFieldName = "cf_org";
-
- [Theory]
- [BitAutoData((string)null, null)]
- [BitAutoData((string)null)]
- [BitAutoData(WebhookKey, null)]
- public async Task PostWebhook_NullRequiredParameters_BadRequest(string freshdeskWebhookKey, FreshdeskWebhookModel model,
- BillingSettings billingSettings, SutProvider sutProvider)
- {
- sutProvider.GetDependency>().Value.FreshDesk.WebhookKey.Returns(billingSettings.FreshDesk.WebhookKey);
-
- var response = await sutProvider.Sut.PostWebhook(freshdeskWebhookKey, model);
-
- var statusCodeResult = Assert.IsAssignableFrom(response);
- Assert.Equal(StatusCodes.Status400BadRequest, statusCodeResult.StatusCode);
- }
-
- [Theory]
- [BitAutoData]
- public async Task PostWebhook_Success(User user, FreshdeskWebhookModel model,
- List organizations, SutProvider sutProvider)
- {
- model.TicketContactEmail = user.Email;
-
- sutProvider.GetDependency().GetByEmailAsync(user.Email).Returns(user);
- sutProvider.GetDependency().GetManyByUserIdAsync(user.Id).Returns(organizations);
-
- var mockHttpMessageHandler = Substitute.ForPartsOf();
- var mockResponse = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
- mockHttpMessageHandler.Send(Arg.Any(), Arg.Any())
- .Returns(mockResponse);
- var httpClient = new HttpClient(mockHttpMessageHandler);
-
- sutProvider.GetDependency().CreateClient("FreshdeskApi").Returns(httpClient);
-
- sutProvider.GetDependency>().Value.FreshDesk.WebhookKey.Returns(WebhookKey);
- sutProvider.GetDependency>().Value.FreshDesk.ApiKey.Returns(ApiKey);
- sutProvider.GetDependency>().Value.FreshDesk.UserFieldName.Returns(UserFieldName);
- sutProvider.GetDependency>().Value.FreshDesk.OrgFieldName.Returns(OrgFieldName);
-
- var response = await sutProvider.Sut.PostWebhook(WebhookKey, model);
-
- var statusCodeResult = Assert.IsAssignableFrom(response);
- Assert.Equal(StatusCodes.Status200OK, statusCodeResult.StatusCode);
-
- _ = mockHttpMessageHandler.Received(1).Send(Arg.Is(m => m.Method == HttpMethod.Put && m.RequestUri.ToString().EndsWith(model.TicketId)), Arg.Any());
- _ = mockHttpMessageHandler.Received(1).Send(Arg.Is(m => m.Method == HttpMethod.Post && m.RequestUri.ToString().EndsWith($"{model.TicketId}/notes")), Arg.Any());
- }
-
- [Theory]
- [BitAutoData(WebhookKey)]
- public async Task PostWebhook_add_note_when_user_is_invalid(
- string freshdeskWebhookKey, FreshdeskWebhookModel model,
- SutProvider sutProvider)
- {
- // Arrange - for an invalid user
- model.TicketContactEmail = "invalid@user";
- sutProvider.GetDependency().GetByEmailAsync(model.TicketContactEmail).Returns((User)null);
- sutProvider.GetDependency>().Value.FreshDesk.WebhookKey.Returns(WebhookKey);
-
- var mockHttpMessageHandler = Substitute.ForPartsOf();
- var mockResponse = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
- mockHttpMessageHandler.Send(Arg.Any(), Arg.Any())
- .Returns(mockResponse);
- var httpClient = new HttpClient(mockHttpMessageHandler);
- sutProvider.GetDependency().CreateClient("FreshdeskApi").Returns(httpClient);
-
- // Act
- var response = await sutProvider.Sut.PostWebhook(freshdeskWebhookKey, model);
-
- // Assert
- var statusCodeResult = Assert.IsAssignableFrom(response);
- Assert.Equal(StatusCodes.Status200OK, statusCodeResult.StatusCode);
-
- await mockHttpMessageHandler
- .Received(1).Send(
- Arg.Is(
- m => m.Method == HttpMethod.Post
- && m.RequestUri.ToString().EndsWith($"{model.TicketId}/notes")
- && m.Content.ReadAsStringAsync().Result.Contains("No user found")),
- Arg.Any());
- }
-
-
- [Theory]
- [BitAutoData((string)null, null)]
- [BitAutoData((string)null)]
- [BitAutoData(WebhookKey, null)]
- public async Task PostWebhookOnyxAi_InvalidWebhookKey_results_in_BadRequest(
- string freshdeskWebhookKey, FreshdeskOnyxAiWebhookModel model,
- BillingSettings billingSettings, SutProvider sutProvider)
- {
- sutProvider.GetDependency>()
- .Value.FreshDesk.WebhookKey.Returns(billingSettings.FreshDesk.WebhookKey);
-
- var response = await sutProvider.Sut.PostWebhookOnyxAi(freshdeskWebhookKey, model);
-
- var statusCodeResult = Assert.IsAssignableFrom(response);
- Assert.Equal(StatusCodes.Status400BadRequest, statusCodeResult.StatusCode);
- }
-
- [Theory]
- [BitAutoData(WebhookKey)]
- public async Task PostWebhookOnyxAi_invalid_onyx_response_results_is_logged(
- string freshdeskWebhookKey, FreshdeskOnyxAiWebhookModel model,
- SutProvider sutProvider)
- {
- var billingSettings = sutProvider.GetDependency>().Value;
- billingSettings.FreshDesk.WebhookKey.Returns(freshdeskWebhookKey);
- billingSettings.Onyx.BaseUrl.Returns("http://simulate-onyx-api.com/api");
-
- // mocking freshdesk Api request for ticket info
- var mockFreshdeskHttpMessageHandler = Substitute.ForPartsOf();
- var freshdeskHttpClient = new HttpClient(mockFreshdeskHttpMessageHandler);
-
- // mocking Onyx api response given a ticket description
- var mockOnyxHttpMessageHandler = Substitute.ForPartsOf();
- var mockOnyxResponse = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
- mockOnyxHttpMessageHandler.Send(Arg.Any(), Arg.Any())
- .Returns(mockOnyxResponse);
- var onyxHttpClient = new HttpClient(mockOnyxHttpMessageHandler);
-
- sutProvider.GetDependency().CreateClient("FreshdeskApi").Returns(freshdeskHttpClient);
- sutProvider.GetDependency().CreateClient("OnyxApi").Returns(onyxHttpClient);
-
- var response = await sutProvider.Sut.PostWebhookOnyxAi(freshdeskWebhookKey, model);
-
- var statusCodeResult = Assert.IsAssignableFrom(response);
- Assert.Equal(StatusCodes.Status200OK, statusCodeResult.StatusCode);
-
- var _logger = sutProvider.GetDependency>();
-
- // 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(), Arg.Any());
- // did not call freshdesk to add a note since onyx failed
- _ = mockFreshdeskHttpMessageHandler.DidNotReceive().Send(Arg.Any(), Arg.Any());
- }
-
- [Theory]
- [BitAutoData(WebhookKey)]
- public async Task PostWebhookOnyxAi_success(
- string freshdeskWebhookKey, FreshdeskOnyxAiWebhookModel model,
- OnyxResponseModel onyxResponse,
- SutProvider sutProvider)
- {
- var billingSettings = sutProvider.GetDependency>().Value;
- billingSettings.FreshDesk.WebhookKey.Returns(freshdeskWebhookKey);
- billingSettings.Onyx.BaseUrl.Returns("http://simulate-onyx-api.com/api");
-
- // mocking freshdesk api add note request (POST)
- var mockFreshdeskHttpMessageHandler = Substitute.ForPartsOf();
- var mockFreshdeskAddNoteResponse = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
- mockFreshdeskHttpMessageHandler.Send(
- Arg.Is(_ => _.Method == HttpMethod.Post),
- Arg.Any())
- .Returns(mockFreshdeskAddNoteResponse);
- var freshdeskHttpClient = new HttpClient(mockFreshdeskHttpMessageHandler);
-
- // mocking Onyx api response given a ticket description
- var mockOnyxHttpMessageHandler = Substitute.ForPartsOf();
- onyxResponse.ErrorMsg = "string.Empty";
- var mockOnyxResponse = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
- {
- Content = new StringContent(JsonSerializer.Serialize(onyxResponse))
- };
- mockOnyxHttpMessageHandler.Send(Arg.Any(), Arg.Any())
- .Returns(mockOnyxResponse);
- var onyxHttpClient = new HttpClient(mockOnyxHttpMessageHandler);
-
- sutProvider.GetDependency().CreateClient("FreshdeskApi").Returns(freshdeskHttpClient);
- sutProvider.GetDependency().CreateClient("OnyxApi").Returns(onyxHttpClient);
-
- var response = await sutProvider.Sut.PostWebhookOnyxAi(freshdeskWebhookKey, model);
-
- var result = Assert.IsAssignableFrom(response);
- Assert.Equal(StatusCodes.Status200OK, result.StatusCode);
- }
-
- [Theory]
- [BitAutoData(WebhookKey)]
- public async Task PostWebhookOnyxAi_ticket_description_is_empty_return_success(
- string freshdeskWebhookKey, FreshdeskOnyxAiWebhookModel model,
- SutProvider sutProvider)
- {
- var billingSettings = sutProvider.GetDependency>().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();
- var freshdeskHttpClient = new HttpClient(mockFreshdeskHttpMessageHandler);
-
- // mocking Onyx api response given a ticket description
- var mockOnyxHttpMessageHandler = Substitute.ForPartsOf();
- var onyxHttpClient = new HttpClient(mockOnyxHttpMessageHandler);
-
- sutProvider.GetDependency().CreateClient("FreshdeskApi").Returns(freshdeskHttpClient);
- sutProvider.GetDependency().CreateClient("OnyxApi").Returns(onyxHttpClient);
-
- var response = await sutProvider.Sut.PostWebhookOnyxAi(freshdeskWebhookKey, model);
-
- var result = Assert.IsAssignableFrom(response);
- Assert.Equal(StatusCodes.Status200OK, result.StatusCode);
- _ = mockFreshdeskHttpMessageHandler.DidNotReceive().Send(Arg.Any(), Arg.Any());
- _ = mockOnyxHttpMessageHandler.DidNotReceive().Send(Arg.Any(), Arg.Any());
- }
-
- public class MockHttpMessageHandler : HttpMessageHandler
- {
- protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- return Send(request, cancellationToken);
- }
-
- public new virtual Task Send(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/test/Billing.Test/Controllers/FreshsalesControllerTests.cs b/test/Billing.Test/Controllers/FreshsalesControllerTests.cs
deleted file mode 100644
index c9ae6efb1a..0000000000
--- a/test/Billing.Test/Controllers/FreshsalesControllerTests.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using Bit.Billing.Controllers;
-using Bit.Core.AdminConsole.Entities;
-using Bit.Core.Entities;
-using Bit.Core.Repositories;
-using Bit.Core.Settings;
-using Bit.Test.Common.AutoFixture.Attributes;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using NSubstitute;
-using Xunit;
-
-namespace Bit.Billing.Test.Controllers;
-
-public class FreshsalesControllerTests
-{
- private const string ApiKey = "TEST_FRESHSALES_APIKEY";
- private const string TestLead = "TEST_FRESHSALES_TESTLEAD";
-
- private static (FreshsalesController, IUserRepository, IOrganizationRepository) CreateSut(
- string freshsalesApiKey)
- {
- var userRepository = Substitute.For();
- var organizationRepository = Substitute.For();
-
- var billingSettings = Options.Create(new BillingSettings
- {
- FreshsalesApiKey = freshsalesApiKey,
- });
- var globalSettings = new GlobalSettings();
- globalSettings.BaseServiceUri.Admin = "https://test.com";
-
- var sut = new FreshsalesController(
- userRepository,
- organizationRepository,
- billingSettings,
- Substitute.For>(),
- globalSettings
- );
-
- return (sut, userRepository, organizationRepository);
- }
-
- [RequiredEnvironmentTheory(ApiKey, TestLead), EnvironmentData(ApiKey, TestLead)]
- public async Task PostWebhook_Success(string freshsalesApiKey, long leadId)
- {
- // This test is only for development to use:
- // `export TEST_FRESHSALES_APIKEY=[apikey]`
- // `export TEST_FRESHSALES_TESTLEAD=[lead id]`
- // `dotnet test --filter "FullyQualifiedName~FreshsalesControllerTests.PostWebhook_Success"`
- var (sut, userRepository, organizationRepository) = CreateSut(freshsalesApiKey);
-
- var user = new User
- {
- Id = Guid.NewGuid(),
- Email = "test@email.com",
- Premium = true,
- };
-
- userRepository.GetByEmailAsync(user.Email)
- .Returns(user);
-
- organizationRepository.GetManyByUserIdAsync(user.Id)
- .Returns(new List
- {
- new Organization
- {
- Id = Guid.NewGuid(),
- Name = "Test Org",
- }
- });
-
- var response = await sut.PostWebhook(freshsalesApiKey, new CustomWebhookRequestModel
- {
- LeadId = leadId,
- }, new CancellationToken(false));
-
- var statusCodeResult = Assert.IsAssignableFrom(response);
- Assert.Equal(StatusCodes.Status204NoContent, statusCodeResult.StatusCode);
- }
-}