-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Backend slack integration (#14168)
- Loading branch information
Showing
11 changed files
with
318 additions
and
0 deletions.
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
backend/src/Designer/Configuration/FeedbackFormSettings.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using Altinn.Studio.Designer.Configuration.Marker; | ||
|
||
namespace Altinn.Studio.Designer.Configuration | ||
{ | ||
/// <summary> | ||
/// Class representation for basic FeedbackForm configuration | ||
/// </summary> | ||
public class FeedbackFormSettings : ISettingsMarker | ||
{ | ||
/// <summary> | ||
/// Gets or sets the Slack settings | ||
/// </summary> | ||
public SlackSettings SlackSettings { get; set; } | ||
} | ||
|
||
public class SlackSettings | ||
{ | ||
/// <summary> | ||
/// Gets or sets the WebhookUrl | ||
/// </summary> | ||
public string WebhookUrl { get; set; } | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
backend/src/Designer/Controllers/FeedbackFormController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
using System.Text.Json; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Altinn.Studio.Designer.Configuration; | ||
using Altinn.Studio.Designer.Models.Dto; | ||
using Altinn.Studio.Designer.TypedHttpClients.Slack; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Routing; | ||
|
||
namespace Altinn.Studio.Designer.Controllers; | ||
|
||
/// <summary> | ||
/// Controller containing actions related to feedback form | ||
/// </summary> | ||
[Authorize] | ||
[ApiController] | ||
[ValidateAntiForgeryToken] | ||
[Route("designer/api/{org}/{app:regex(^(?!datamodels$)[[a-z]][[a-z0-9-]]{{1,28}}[[a-z0-9]]$)}/feedbackform")] | ||
public class FeedbackFormController : ControllerBase | ||
{ | ||
private readonly ISlackClient _slackClient; | ||
private readonly GeneralSettings _generalSettings; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="FeedbackFormController"/> class. | ||
/// </summary> | ||
/// <param name="slackClient">A http client to send messages to slack</param> | ||
/// <param name="generalSettings">the general settings</param> | ||
public FeedbackFormController(ISlackClient slackClient, GeneralSettings generalSettings) | ||
{ | ||
_slackClient = slackClient; | ||
_generalSettings = generalSettings; | ||
} | ||
|
||
/// <summary> | ||
/// Endpoint for submitting feedback | ||
/// </summary> | ||
[HttpPost] | ||
[Route("submit")] | ||
public async Task<IActionResult> SubmitFeedback([FromRoute] string org, [FromRoute] string app, [FromBody] FeedbackForm feedback, CancellationToken cancellationToken) | ||
{ | ||
if (feedback == null) | ||
{ | ||
return BadRequest("Feedback object is null"); | ||
} | ||
|
||
if (feedback.Answers == null || feedback.Answers.Count == 0) | ||
{ | ||
return BadRequest("Feedback answers are null or empty"); | ||
} | ||
|
||
if (!feedback.Answers.ContainsKey("org")) | ||
{ | ||
feedback.Answers.Add("org", org); | ||
} | ||
|
||
if (!feedback.Answers.ContainsKey("app")) | ||
{ | ||
feedback.Answers.Add("app", app); | ||
} | ||
|
||
if (!feedback.Answers.ContainsKey("env")) | ||
{ | ||
feedback.Answers.Add("env", _generalSettings.HostName); | ||
} | ||
|
||
await _slackClient.SendMessage(new SlackRequest | ||
{ | ||
Text = JsonSerializer.Serialize(feedback.Answers, new JsonSerializerOptions { WriteIndented = true }) | ||
}, cancellationToken); | ||
|
||
return Ok(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System.Collections.Generic; | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Altinn.Studio.Designer.Models.Dto | ||
{ | ||
/// <summary> | ||
/// Represents a feedback form | ||
/// </summary> | ||
public class FeedbackForm | ||
{ | ||
[JsonPropertyName("answers")] | ||
public Dictionary<string, string> Answers { get; set; } | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
backend/src/Designer/TypedHttpClients/Slack/ISlackClient.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Altinn.Studio.Designer.TypedHttpClients.Slack | ||
{ | ||
public interface ISlackClient | ||
{ | ||
public Task SendMessage(SlackRequest request, CancellationToken cancellationToken = default); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
backend/src/Designer/TypedHttpClients/Slack/SlackClient.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using System.Net.Http; | ||
using System.Net.Mime; | ||
using System.Text; | ||
using System.Text.Json; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Altinn.Studio.Designer.TypedHttpClients.Slack; | ||
|
||
public class SlackClient : ISlackClient | ||
{ | ||
private readonly HttpClient _httpClient; | ||
private readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions | ||
{ | ||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase | ||
}; | ||
|
||
public SlackClient(HttpClient httpClient) | ||
{ | ||
_httpClient = httpClient; | ||
} | ||
|
||
public async Task SendMessage(SlackRequest request, CancellationToken cancellationToken = default) | ||
{ | ||
using var payloadContent = new StringContent(JsonSerializer.Serialize(request, _jsonSerializerOptions), | ||
Encoding.UTF8, | ||
MediaTypeNames.Application.Json); | ||
|
||
using var response = await _httpClient.PostAsync(string.Empty, payloadContent, cancellationToken); | ||
response.EnsureSuccessStatusCode(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Altinn.Studio.Designer.TypedHttpClients.Slack | ||
{ | ||
public class SlackRequest | ||
{ | ||
public string Text { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
.../Designer.Tests/Controllers/FeedbackFormController/Base/FeedbackFormControllerTestBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
|
||
using Designer.Tests.Controllers.ApiTests; | ||
using Designer.Tests.Fixtures; | ||
using Microsoft.AspNetCore.Mvc.Testing; | ||
|
||
namespace Designer.Tests.Controllers.FeedbackFormController.Base; | ||
|
||
public class FeedbackFormControllerTestBase<TControllerTest> : DesignerEndpointsTestsBase<TControllerTest> | ||
where TControllerTest : class | ||
{ | ||
public FeedbackFormControllerTestBase(WebApplicationFactory<Program> factory) : base(factory) | ||
{ | ||
JsonConfigOverrides.Add( | ||
$$""" | ||
{ | ||
"GeneralSettings": { | ||
"HostName": "TestHostName" | ||
} | ||
} | ||
"""); | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
backend/tests/Designer.Tests/Controllers/FeedbackFormController/SendMessageToSlackTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
|
||
using System.Collections.Generic; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Net.Http.Json; | ||
using System.Threading.Tasks; | ||
using Altinn.Studio.Designer.Models.Dto; | ||
using Designer.Tests.Controllers.FeedbackFormController.Base; | ||
using Designer.Tests.Controllers.FeedbackFormController.Utils; | ||
using Designer.Tests.Fixtures; | ||
using FluentAssertions; | ||
using Microsoft.AspNetCore.Mvc.Testing; | ||
using Xunit; | ||
|
||
namespace Designer.Tests.Controllers.FeedbackFormController; | ||
|
||
public class SendMessageToSlackTests : FeedbackFormControllerTestBase<SendMessageToSlackTests>, IClassFixture<WebApplicationFactory<Program>>, IClassFixture<MockServerFixture> | ||
{ | ||
private static string VersionPrefix(string org, string repository) => | ||
$"/designer/api/{org}/{repository}/feedbackform/submit"; | ||
|
||
private readonly MockServerFixture _mockServerFixture; | ||
|
||
public SendMessageToSlackTests(WebApplicationFactory<Program> factory, MockServerFixture mockServerFixture) : base(factory) | ||
{ | ||
_mockServerFixture = mockServerFixture; | ||
JsonConfigOverrides.Add( | ||
$$""" | ||
{ | ||
"FeedbackFormSettings" : { | ||
"SlackSettings": { | ||
"WebhookUrl": "{{mockServerFixture.MockApi.Url}}" | ||
} | ||
} | ||
} | ||
""" | ||
); | ||
} | ||
|
||
[Fact] | ||
public async Task SendMessageToSlack_Should_ReturnOk() | ||
{ | ||
_mockServerFixture.PrepareSlackResponse(_mockServerFixture.MockApi.Url); | ||
var mockAnswers = new FeedbackForm | ||
{ | ||
Answers = new Dictionary<string, string> | ||
{ | ||
{ "message", "test" } | ||
} | ||
}; | ||
using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, VersionPrefix("ttd", "non-existing-app")) | ||
{ | ||
Content = JsonContent.Create(mockAnswers) | ||
}; | ||
|
||
|
||
using var response = await HttpClient.SendAsync(httpRequestMessage); | ||
response.StatusCode.Should().Be(HttpStatusCode.OK); | ||
} | ||
|
||
[Fact] | ||
public async Task SendMessageToSlack_NullAnswers_Should_ReturnBadRequest() | ||
{ | ||
object mockAnswers = null; | ||
using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, VersionPrefix("ttd", "non-existing-app")) | ||
{ | ||
Content = JsonContent.Create(mockAnswers) | ||
}; | ||
|
||
using var response = await HttpClient.SendAsync(httpRequestMessage); | ||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest); | ||
} | ||
|
||
[Fact] | ||
public async Task SendMessageToSlack_WithMissingAnswers_Should_ReturnBadRequest() | ||
{ | ||
var mockAnswers = new FeedbackForm | ||
{ | ||
Answers = null | ||
}; | ||
using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, VersionPrefix("ttd", "non-existing-app")) | ||
{ | ||
Content = JsonContent.Create(mockAnswers) | ||
}; | ||
|
||
using var response = await HttpClient.SendAsync(httpRequestMessage); | ||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
...ests/Designer.Tests/Controllers/FeedbackFormController/Utils/SlackMockServerExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using System.Net.Mime; | ||
using Designer.Tests.Fixtures; | ||
using WireMock.RequestBuilders; | ||
using WireMock.ResponseBuilders; | ||
|
||
namespace Designer.Tests.Controllers.FeedbackFormController.Utils; | ||
|
||
public static class SlackMockServerExtensions | ||
{ | ||
public static void PrepareSlackResponse(this MockServerFixture mockServerFixture, string path) | ||
{ | ||
var request = Request.Create() | ||
.UsingPost() | ||
.WithPath("/"); | ||
|
||
var response = Response.Create() | ||
.WithStatusCode(200) | ||
.WithHeader("content-type", MediaTypeNames.Application.Json); | ||
|
||
mockServerFixture.MockApi.Given(request) | ||
.RespondWith( | ||
response | ||
); | ||
|
||
} | ||
|
||
} |