Skip to content

Commit

Permalink
Add validation when fetching options
Browse files Browse the repository at this point in the history
  • Loading branch information
standeren committed Nov 13, 2024
1 parent 4c50a5c commit 971097d
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 10 deletions.
19 changes: 19 additions & 0 deletions backend/src/Designer/Services/Implementation/OptionsService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Altinn.Studio.Designer.Exceptions.Options;
using Altinn.Studio.Designer.Models;
using Altinn.Studio.Designer.Services.Interfaces;
using LibGit2Sharp;
Expand Down Expand Up @@ -49,9 +51,26 @@ public async Task<List<Option>> GetOptionsList(string org, string repo, string d

string optionsListString = await altinnAppGitRepository.GetOptionsList(optionsListId, cancellationToken);
var optionsList = JsonSerializer.Deserialize<List<Option>>(optionsListString);

try
{
optionsList.ForEach(ValidateOption);
}
catch (ValidationException)
{
throw new InvalidOptionsFormatException($"One or more of the options have an invalid format in file: {optionsListId}.");
}


return optionsList;
}

private void ValidateOption(Option option)
{
var validationContext = new ValidationContext(option);
Validator.ValidateObject(option, validationContext, validateAllProperties: true);
}

/// <inheritdoc />
public async Task<List<Option>> CreateOrOverwriteOptionsList(string org, string repo, string developer, string optionsListId, List<Option> payload, CancellationToken cancellationToken = default)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public async Task GetOptionsListIds_ShouldReturnOk(string org, string app, strin
{
{ "other-options" },
{ "test-options" },
{ "options-with-null-fields" },
};

string url = $"{VersionPrefix(org, targetRepository)}/option-list-ids";
Expand All @@ -39,7 +40,7 @@ public async Task GetOptionsListIds_ShouldReturnOk(string org, string app, strin

string responseContent = await response.Content.ReadAsStringAsync();
List<string> responseList = JsonSerializer.Deserialize<List<string>>(responseContent);
responseList.Count.Should().Be(2);
responseList.Count.Should().Be(3);
foreach (string id in expectedOptionsListIds)
{
responseList.Should().Contain(id);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Altinn.Studio.Designer.Filters;
using Altinn.Studio.Designer.Models;
using Designer.Tests.Controllers.ApiTests;
using FluentAssertions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;

Expand Down Expand Up @@ -96,4 +98,26 @@ public async Task GetSingleOptionsList_Returns404NotFound_WhenOptionsListDoesNot
// Assert
Assert.Equal(StatusCodes.Status404NotFound, (int)response.StatusCode);
}

[Fact]
public async Task GetSingleOptionsList_Returns400BadRequest_WhenOptionsListIsInvalid()
{
// Arrange
const string repo = "app-with-options";
const string optionsListId = "options-with-null-fields";

string apiUrl = $"/designer/api/ttd/{repo}/options/{optionsListId}";
using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, apiUrl);

// Act
using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage);

// Assert
Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode);

var problemDetails = JsonSerializer.Deserialize<ProblemDetails>(await response.Content.ReadAsStringAsync());
problemDetails.Should().NotBeNull();
JsonElement errorCode = (JsonElement)problemDetails.Extensions[ProblemDetailsExtensionsCodes.ErrorCode];
errorCode.ToString().Should().Be("InvalidOptionsFormat");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,6 @@ public async Task Put_Returns_400BadRequest_When_Option_Value_Is_Invalid()
problemDetails.Should().NotBeNull();
JsonElement errorCode = (JsonElement)problemDetails.Extensions[ProblemDetailsExtensionsCodes.ErrorCode];
errorCode.ToString().Should().Be("InvalidOptionsFormat");
JsonElement detail = (JsonElement)problemDetails.Extensions[ProblemDetailsExtensionsCodes.Detail];
detail.ToString().Should().Be("{} is an unsupported type for Value field.");
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Altinn.Studio.Designer.Filters;
using Altinn.Studio.Designer.Models;
using Designer.Tests.Controllers.ApiTests;
using Designer.Tests.Utils;
using FluentAssertions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;

Expand Down Expand Up @@ -135,9 +138,7 @@ public async Task Post_Returns_400BadRequest_When_Uploading_New_OptionsList_With
await CopyRepositoryForTest(Org, repo, Developer, targetRepository);

string optionsFileName = "invalid-value-options.json";
string jsonOptions = @"[
{""value"": {}, ""label"": """" }
]";
string jsonOptions = @"[{""value"": {}, ""label"": """"}]";

byte[] optionsBytes = Encoding.UTF8.GetBytes(jsonOptions);
string apiUrl = $"{VersionPrefix}/{Org}/{targetRepository}/options/upload";
Expand All @@ -155,5 +156,10 @@ public async Task Post_Returns_400BadRequest_When_Uploading_New_OptionsList_With

// Assert
Assert.Equal(StatusCodes.Status400BadRequest, (int)response.StatusCode);

var problemDetails = JsonSerializer.Deserialize<ProblemDetails>(await response.Content.ReadAsStringAsync());
problemDetails.Should().NotBeNull();
JsonElement errorCode = (JsonElement)problemDetails.Extensions[ProblemDetailsExtensionsCodes.ErrorCode];
errorCode.ToString().Should().Be("InvalidOptionsFormat");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ public Task GetOptionListIds_WithAppThatHasOptionLists_ShouldReturnOptionListPat
string[] optionListIds = altinnAppGitRepository.GetOptionsListIds();

optionListIds.Should().NotBeNull();
optionListIds.Should().HaveCount(2);
optionListIds.Should().HaveCount(3);
return Task.CompletedTask;
}

Expand Down
4 changes: 2 additions & 2 deletions backend/tests/Designer.Tests/Services/OptionsServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class OptionsServiceTests
public async Task GetOptionsListIds_ShouldReturnOptionsListIds_WhenOptionsListsExist()
{
// Arrange
const string repo = "app-with-options"; // Has 2 options lists
const string repo = "app-with-options"; // Has 3 options lists
string targetRepository = TestDataHelper.GenerateTestRepoName();
await TestDataHelper.CopyRepositoryForTest(Org, repo, Developer, targetRepository);

Expand All @@ -27,7 +27,7 @@ public async Task GetOptionsListIds_ShouldReturnOptionsListIds_WhenOptionsListsE
string[] optionListIds = optionsService.GetOptionsListIds(Org, targetRepository, Developer);

// Assert
Assert.Equal(2, optionListIds.Length);
Assert.Equal(3, optionListIds.Length);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"label": null,
"value": null
}
]

0 comments on commit 971097d

Please sign in to comment.