Skip to content

Commit

Permalink
Bug/datamodel metadata response casing (#12457)
Browse files Browse the repository at this point in the history
* allow both uppercase and lowercase in model metadata response

* generate model metadata on demand

* invalidate modelsMetadata query on datamodel change

* some updates and bugfixes

* updated test data

* more test data updates

* update test

* one more test
  • Loading branch information
nkylstad authored Mar 8, 2024
1 parent abcca17 commit 1163fa7
Show file tree
Hide file tree
Showing 20 changed files with 380 additions and 34 deletions.
3 changes: 2 additions & 1 deletion backend/src/Designer/Controllers/AppDevelopmentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Altinn.Studio.DataModeling.Metamodel;
using Altinn.Studio.Designer.Configuration;
using Altinn.Studio.Designer.Filters;
using Altinn.Studio.Designer.Helpers;
Expand Down Expand Up @@ -274,7 +275,7 @@ public async Task<IActionResult> GetLayoutSettings(string org, string app, [From
public async Task<IActionResult> GetModelMetadata(string org, string app, [FromQuery] string layoutSetName, CancellationToken cancellationToken)
{
string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext);
JsonNode modelMetadata = await _appDevelopmentService.GetModelMetadata(AltinnRepoEditingContext.FromOrgRepoDeveloper(org, app, developer), layoutSetName, cancellationToken);
ModelMetadata modelMetadata = await _appDevelopmentService.GetModelMetadata(AltinnRepoEditingContext.FromOrgRepoDeveloper(org, app, developer), layoutSetName, cancellationToken);
return Ok(modelMetadata);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,7 @@ public async Task<string> GetAppFrontendCshtml(CancellationToken cancellationTok
/// <summary>
/// Gets the options list with the provided id.
/// <param name="optionsListId">The id of the options list to fetch.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that observes if operation is cancelled.</param>
/// <returns>The options list as a string.</returns>
/// </summary>
public async Task<string> GetOptions(string optionsListId, CancellationToken cancellationToken = default)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading;
using System.Threading.Tasks;
using Altinn.App.Core.Models;
using Altinn.Studio.DataModeling.Metamodel;
using Altinn.Studio.Designer.Helpers;
using Altinn.Studio.Designer.Infrastructure.GitRepository;
using Altinn.Studio.Designer.Models;
Expand All @@ -23,14 +24,17 @@ namespace Altinn.Studio.Designer.Services.Implementation
public class AppDevelopmentService : IAppDevelopmentService
{
private readonly IAltinnGitRepositoryFactory _altinnGitRepositoryFactory;
private readonly ISchemaModelService _schemaModelService;

/// <summary>
/// Constructor
/// </summary>
/// <param name="altinnGitRepositoryFactory">IAltinnGitRepository</param>
public AppDevelopmentService(IAltinnGitRepositoryFactory altinnGitRepositoryFactory)
/// <param name="schemaModelService">ISchemaModelService</param>
public AppDevelopmentService(IAltinnGitRepositoryFactory altinnGitRepositoryFactory, ISchemaModelService schemaModelService)
{
_altinnGitRepositoryFactory = altinnGitRepositoryFactory;
_schemaModelService = schemaModelService;
}

/// <inheritdoc />
Expand Down Expand Up @@ -147,7 +151,7 @@ public async Task SaveLayoutSettings(AltinnRepoEditingContext altinnRepoEditingC
}

/// <inheritdoc />
public async Task<JsonNode> GetModelMetadata(AltinnRepoEditingContext altinnRepoEditingContext,
public async Task<ModelMetadata> GetModelMetadata(AltinnRepoEditingContext altinnRepoEditingContext,
string layoutSetName, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
Expand All @@ -159,12 +163,23 @@ public async Task<JsonNode> GetModelMetadata(AltinnRepoEditingContext altinnRepo
// get task_id since we might not maintain dataType ref in layout-sets-file
string taskId = await GetTaskIdBasedOnLayoutSet(altinnRepoEditingContext, layoutSetName, cancellationToken);
string modelName = GetModelName(applicationMetadata, taskId);
string fileContent = await altinnAppGitRepository.GetModelMetadata(modelName);
return JsonNode.Parse(fileContent);
if (string.IsNullOrEmpty(modelName))
{
return new ModelMetadata();
}
string modelPath = $"App/models/{modelName}.schema.json";
ModelMetadata modelMetadata = await _schemaModelService.GenerateModelMetadataFromJsonSchema(altinnRepoEditingContext, modelPath, cancellationToken);
return modelMetadata;
}

private string GetModelName(ApplicationMetadata applicationMetadata, [CanBeNull] string taskId)
{
// fallback to first model if no task_id is provided (no layoutsets)
if (taskId == null)
{
return applicationMetadata.DataTypes.FirstOrDefault(data => data.AppLogic != null && !string.IsNullOrEmpty(data.AppLogic.ClassRef) && !string.IsNullOrEmpty(data.TaskId))?.Id ?? string.Empty;
}

PlatformStorageModels.DataType data = applicationMetadata.DataTypes
.FirstOrDefault(data => data.AppLogic != null && DoesDataTaskMatchTaskId(data, taskId) && !string.IsNullOrEmpty(data.AppLogic.ClassRef));

Expand Down
16 changes: 11 additions & 5 deletions backend/src/Designer/Services/Implementation/SchemaModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@ public async Task<string> UpdateModelFilesFromJsonSchema(AltinnRepoEditingContex

var metamodelConverter = new JsonSchemaToMetamodelConverter(jsonSchemaConverterStrategy.GetAnalyzer());
ModelMetadata modelMetadata = metamodelConverter.Convert(jsonContent);
string serializedModelMetadata = SerializeModelMetadata(modelMetadata);
await altinnAppGitRepository.SaveModelMetadata(serializedModelMetadata, schemaName);

string fullTypeName = await UpdateCSharpClasses(altinnAppGitRepository, modelMetadata, schemaName);

Expand All @@ -148,6 +146,17 @@ public async Task<string> UpdateModelFilesFromJsonSchema(AltinnRepoEditingContex
return jsonContent;
}

public async Task<ModelMetadata> GenerateModelMetadataFromJsonSchema(AltinnRepoEditingContext altinnRepoEditingContext, string relativeFilePath, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(altinnRepoEditingContext.Org, altinnRepoEditingContext.Repo, altinnRepoEditingContext.Developer);
var jsonContent = await altinnAppGitRepository.ReadTextByRelativePathAsync(relativeFilePath, cancellationToken);
var jsonSchema = Json.Schema.JsonSchema.FromText(jsonContent);
var jsonSchemaConverterStrategy = JsonSchemaConverterStrategyFactory.SelectStrategy(jsonSchema);
var metamodelConverter = new JsonSchemaToMetamodelConverter(jsonSchemaConverterStrategy.GetAnalyzer());
return metamodelConverter.Convert(jsonContent);
}

/// <summary>
/// Builds a JSON schema based on the uploaded XSD.
/// </summary>
Expand Down Expand Up @@ -416,9 +425,6 @@ private async Task<string> ProcessNewXsd(AltinnAppGitRepository altinnAppGitRepo
var jsonSchemaConverterStrategy = JsonSchemaConverterStrategyFactory.SelectStrategy(jsonSchema);
var metamodelConverter = new JsonSchemaToMetamodelConverter(jsonSchemaConverterStrategy.GetAnalyzer());
var modelMetadata = metamodelConverter.Convert(jsonContent);
var serializedModelMetadata = SerializeModelMetadata(modelMetadata);

await altinnAppGitRepository.SaveModelMetadata(serializedModelMetadata, schemaName);

string fullTypeName = await UpdateCSharpClasses(altinnAppGitRepository, modelMetadata, schemaName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public interface IAppDevelopmentService
/// <param name="layoutSetName">Name of layoutSet to fetch corresponding model metadata for</param>
/// <param name="cancellationToken">An <see cref="CancellationToken"/> that observes if operation is cancelled.</param>
/// <returns>The model metadata for a given layout set.</returns>
public Task<JsonNode> GetModelMetadata(
public Task<ModelMetadata> GetModelMetadata(
AltinnRepoEditingContext altinnRepoEditingContext, [CanBeNull] string layoutSetName,
CancellationToken cancellationToken = default);

Expand Down
10 changes: 10 additions & 0 deletions backend/src/Designer/Services/Interfaces/ISchemaModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading;
using System.Threading.Tasks;
using Altinn.Platform.Storage.Interface.Models;
using Altinn.Studio.DataModeling.Metamodel;
using Altinn.Studio.Designer.Models;

namespace Altinn.Studio.Designer.Services.Interfaces
Expand Down Expand Up @@ -85,5 +86,14 @@ public interface ISchemaModelService
/// <param name="relativeFilePath">Relative path to the file.</param>
/// <param name="cancellationToken">An <see cref="CancellationToken"/> that observes if operation is cancelled.</param>
Task DeleteSchema(AltinnRepoEditingContext altinnRepoEditingContext, string relativeFilePath, CancellationToken cancellationToken = default);

/// <summary>
/// Generates a model metadata from a JSON schema.
/// </summary>
/// <param name="altinnRepoEditingContext">An <see cref="AltinnRepoEditingContext"/>.</param>
/// <param name="relativeFilePath">Relative path to the file.</param>
/// <param name="cancellationToken">An <see cref="CancellationToken"/> that observes if operation is cancelled.</param>
/// <returns>Returns the model metadata</returns>
Task<ModelMetadata> GenerateModelMetadataFromJsonSchema(AltinnRepoEditingContext altinnRepoEditingContext, string relativeFilePath, CancellationToken cancellationToken = default);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public GetModelMetadataTests(WebApplicationFactory<Program> factory) : base(fact
}

[Theory]
[InlineData("ttd", "app-with-layoutsets", "testUser", "layoutSet3", "TestData/Model/Metadata/HvemErHvem.json")]
[InlineData("ttd", "app-without-layoutsets", "testUser", null, "TestData/Model/Metadata/HvemErHvem.json")]
[InlineData("ttd", "app-with-layoutsets", "testUser", "layoutSet1", "TestData/Model/Metadata/datamodel.json")]
[InlineData("ttd", "app-without-layoutsets", "testUser", null, "TestData/Model/Metadata/datamodel.json")]
public async Task GetModelMetadata_Should_Return_ModelMetadata(string org, string app, string developer, string layoutSetName, string expectedModelMetadataPath)
{
string targetRepository = TestDataHelper.GenerateTestRepoName();
Expand All @@ -45,8 +45,8 @@ public async Task GetModelMetadata_Should_Return_ModelMetadata(string org, strin

[Theory]
[InlineData("ttd", "app-with-layoutsets", "testUser", "layoutSet3")]
[InlineData("ttd", "app-without-layoutsets", "testUser", null)]
public async Task GetModelMetadata_Should_Return_Empty_Model_When_No_ModelMetadata_Exists(string org, string app, string developer, string layoutSetName)
[InlineData("ttd", "app-without-layoutsets-mismatch-modelname", "testUser", null)]
public async Task GetModelMetadata_Should_Return_404_When_No_Corresponding_Datamodel_Exists(string org, string app, string developer, string layoutSetName)
{
string targetRepository = TestDataHelper.GenerateTestRepoName();
await CopyRepositoryForTest(org, app, developer, targetRepository);
Expand All @@ -55,12 +55,7 @@ public async Task GetModelMetadata_Should_Return_Empty_Model_When_No_ModelMetada
using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url);

using var response = await HttpClient.SendAsync(httpRequestMessage);
response.StatusCode.Should().Be(HttpStatusCode.OK);

string responseContent = await response.Content.ReadAsStringAsync();
string expectedResponse = JsonConvert
.SerializeObject(JsonConvert.DeserializeObject<ModelMetadata>("{}"));
responseContent.Should().Be(expectedResponse);
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
}

private async Task<string> AddModelMetadataToRepo(string createdFolderPath, string expectedModelMetadataPath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,10 @@ private async Task FilesWithCorrectNameAndContentShouldBeCreated(string modelNam
var metamodelLocation = Path.Combine(location, $"{modelName}.metadata.json");

Assert.True(File.Exists(xsdSchemaLocation));
Assert.True(File.Exists(metamodelLocation));
Assert.True(File.Exists(jsonSchemaLocation));

await VerifyXsdFileContent(xsdSchemaLocation);
FileContentVerifier.VerifyJsonFileContent(jsonSchemaLocation, MinimumValidJsonSchema);
VerifyMetadataContent(metamodelLocation);
}

private static async Task VerifyXsdFileContent(string path)
Expand Down
16 changes: 12 additions & 4 deletions backend/tests/Designer.Tests/Services/AppDevelopmentServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@
using Altinn.Studio.Designer.Services.Interfaces;
using Designer.Tests.Utils;
using FluentAssertions;
using Moq;
using Xunit;

namespace Designer.Tests.Services;

public class AppDevelopmentServiceTest : IDisposable
{

private readonly Mock<ISchemaModelService> _schemaModelServiceMock;

public AppDevelopmentServiceTest()
{
_schemaModelServiceMock = new Mock<ISchemaModelService>();
}

public string CreatedTestRepoPath { get; set; }

[Fact]
Expand All @@ -25,7 +33,7 @@ public async Task GetLayoutSettings_FromAppWithOutLayoutSet_ShouldReturnSettings
string targetRepository = TestDataHelper.GenerateTestRepoName();

AltinnGitRepositoryFactory altinnGitRepositoryFactory = new(TestDataHelper.GetTestDataRepositoriesRootDirectory());
IAppDevelopmentService appDevelopmentService = new AppDevelopmentService(altinnGitRepositoryFactory);
IAppDevelopmentService appDevelopmentService = new AppDevelopmentService(altinnGitRepositoryFactory, _schemaModelServiceMock.Object);
CreatedTestRepoPath = await TestDataHelper.CopyRepositoryForTest(org, repository, developer, targetRepository);
var layoutSettings = await appDevelopmentService.GetLayoutSettings(AltinnRepoEditingContext.FromOrgRepoDeveloper(org, targetRepository, developer), null);

Expand Down Expand Up @@ -53,7 +61,7 @@ public async Task SaveLayoutSettingsWithAdditionalPage_ToAppWithOutLayoutSet_Sho
}}";

AltinnGitRepositoryFactory altinnGitRepositoryFactory = new(TestDataHelper.GetTestDataRepositoriesRootDirectory());
IAppDevelopmentService appDevelopmentService = new AppDevelopmentService(altinnGitRepositoryFactory);
IAppDevelopmentService appDevelopmentService = new AppDevelopmentService(altinnGitRepositoryFactory, _schemaModelServiceMock.Object);
CreatedTestRepoPath = await TestDataHelper.CopyRepositoryForTest(org, repository, developer, targetRepository);

var layoutSettingsUpdated = JsonNode.Parse(jsonSettingsUpdatedString);
Expand All @@ -75,7 +83,7 @@ public async Task GetLayoutSettings_FromAppWithLayoutSet_ShouldReturnSettings()
string targetRepository = TestDataHelper.GenerateTestRepoName();

AltinnGitRepositoryFactory altinnGitRepositoryFactory = new(TestDataHelper.GetTestDataRepositoriesRootDirectory());
IAppDevelopmentService appDevelopmentService = new AppDevelopmentService(altinnGitRepositoryFactory);
IAppDevelopmentService appDevelopmentService = new AppDevelopmentService(altinnGitRepositoryFactory, _schemaModelServiceMock.Object);
CreatedTestRepoPath = await TestDataHelper.CopyRepositoryForTest(org, repository, developer, targetRepository);
var layoutSettings = await appDevelopmentService.GetLayoutSettings(AltinnRepoEditingContext.FromOrgRepoDeveloper(org, targetRepository, developer), layoutSetName);

Expand All @@ -93,7 +101,7 @@ public async Task GetLayoutSettings_FromAppWithLayoutSetButNoSettingsExist_Shoul
string targetRepository = TestDataHelper.GenerateTestRepoName();

AltinnGitRepositoryFactory altinnGitRepositoryFactory = new(TestDataHelper.GetTestDataRepositoriesRootDirectory());
IAppDevelopmentService appDevelopmentService = new AppDevelopmentService(altinnGitRepositoryFactory);
IAppDevelopmentService appDevelopmentService = new AppDevelopmentService(altinnGitRepositoryFactory, _schemaModelServiceMock.Object);
CreatedTestRepoPath = await TestDataHelper.CopyRepositoryForTest(org, repository, developer, targetRepository);
var layoutSettings = await appDevelopmentService.GetLayoutSettings(AltinnRepoEditingContext.FromOrgRepoDeveloper(org, targetRepository, developer), layoutSetName);

Expand Down
3 changes: 2 additions & 1 deletion backend/tests/Designer.Tests/Services/RepositorySITests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,8 @@ private static RepositorySI GetServiceForTest(string developer, ISourceControl s

ApplicationMetadataService applicationInformationService = new(new Mock<ILogger<ApplicationMetadataService>>().Object, altinnStorageAppMetadataClient, altinnGitRepositoryFactory, httpContextAccessorMock.Object, new IGiteaMock());

AppDevelopmentService appDevelopmentService = new(altinnGitRepositoryFactory);
ISchemaModelService schemaModelService = new Mock<ISchemaModelService>().Object;
AppDevelopmentService appDevelopmentService = new(altinnGitRepositoryFactory, schemaModelService);

TextsService textsService = new(altinnGitRepositoryFactory, applicationInformationService);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,6 @@ public async Task UpdateSchema_AppRepo_ShouldUpdate()
var xsdSchema = XDocument.Parse(xsd);
xsdSchema.Root.Should().NotBeNull();
xsdSchema.Root.Elements().First().Attributes().First(a => a.Name.LocalName == "name").Should().HaveValue("root");

var metadataModelJson = await altinnGitRepository.ReadTextByRelativePathAsync("App/models/HvemErHvem_SERES.metadata.json");
metadataModelJson.Should().NotBeNullOrEmpty();
}
finally
{
Expand Down Expand Up @@ -247,7 +244,7 @@ public async Task UploadSchemaFromXsd_ValidNonSeresXsd_ModelsCreated()

// Assert
var altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, targetRepository, developer);
altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.metadata.json").Should().BeTrue();
altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.metadata.json").Should().BeFalse();
altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.schema.json").Should().BeTrue();
altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.cs").Should().BeTrue();
}
Expand Down Expand Up @@ -285,7 +282,7 @@ public async Task UploadSchemaFromXsd_OED_ModelsCreated()

// Assert
var altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, targetRepository, developer);
altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.metadata.json").Should().BeTrue();
altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.metadata.json").Should().BeFalse();
altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.schema.json").Should().BeTrue();
altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.xsd").Should().BeTrue();
altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.cs").Should().BeTrue();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"id": "ttd/app-with-layoutsets",
"org": "ttd",
"title": {
"nb": "app-with-layoutsets"
},
"dataTypes": [
{
"id": "HvemErHvem_M",
"allowedContentTypes": [
"application/xml"
],
"appLogic": {
"autoCreate": true,
"classRef": "Altinn.App.Models.datamodel"
},
"taskId": "Task_1",
"maxCount": 1,
"minCount": 1
}
],
"partyTypesAllowed": {
"bankruptcyEstate": false,
"organisation": false,
"person": false,
"subUnit": false
},
"autoDeleteOnProcessEnd": false,
"onEntry": {
"show": "select-instance"
},
"created": "2021-02-06T15:18:12.2060944Z",
"createdBy": "jeeva",
"lastChanged": "2021-02-06T15:18:12.2062288Z",
"lastChangedBy": "jeeva"
}
Loading

0 comments on commit 1163fa7

Please sign in to comment.