diff --git a/src/Altinn.App.Api/Controllers/ResourceController.cs b/src/Altinn.App.Api/Controllers/ResourceController.cs
index e38ad0cc8e..1d3142f6f1 100644
--- a/src/Altinn.App.Api/Controllers/ResourceController.cs
+++ b/src/Altinn.App.Api/Controllers/ResourceController.cs
@@ -35,6 +35,23 @@ public ActionResult GetModelJsonSchema([FromRoute] string id)
return Ok(schema);
}
+ ///
+ /// Get the xsd schema for the model
+ ///
+ [HttpGet]
+ [ProducesResponseType(typeof(string), StatusCodes.Status200OK, "application/xml")]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [Route("{org}/{app}/api/xsdschema/{id}")]
+ public ActionResult GetModelXsdSchema([FromRoute] string id)
+ {
+ string? schema = _appResourceService.GetXsdSchema(id);
+ if (schema == null)
+ {
+ return NotFound();
+ }
+ return Content(schema, "application/xml", System.Text.Encoding.UTF8);
+ }
+
///
/// Get the form layout
///
@@ -80,6 +97,9 @@ public ActionResult GetLayouts(string org, string app, string id)
[ProducesResponseType(typeof(string), StatusCodes.Status200OK, "application/json")]
[HttpGet]
[Route("{org}/{app}/api/layoutsettings")]
+ [Obsolete(
+ "This endpoint is no longer available. Use /{org}/{app}/api/layoutsettings/{id} to get layout settings for a specific layout set."
+ )]
public ActionResult GetLayoutSettings(string org, string app)
{
string? settings = _appResourceService.GetLayoutSettingsString();
diff --git a/src/Altinn.App.Core/Implementation/AppResourcesSI.cs b/src/Altinn.App.Core/Implementation/AppResourcesSI.cs
index 95be38aa60..b445cfa906 100644
--- a/src/Altinn.App.Core/Implementation/AppResourcesSI.cs
+++ b/src/Altinn.App.Core/Implementation/AppResourcesSI.cs
@@ -523,4 +523,20 @@ private static byte[] ReadFileContentsFromLegalPath(string legalPath, string fil
return filedata;
}
+
+ ///
+ public string? GetXsdSchema(string dataTypeId)
+ {
+ string legalPath = Path.Join(_settings.AppBasePath, _settings.ModelsFolder);
+ string filename = Path.Join(legalPath, $"{dataTypeId}.xsd");
+ PathHelper.EnsureLegalPath(legalPath, filename);
+
+ string? filedata = null;
+ if (File.Exists(filename))
+ {
+ filedata = File.ReadAllText(filename, Encoding.UTF8);
+ }
+
+ return filedata;
+ }
}
diff --git a/src/Altinn.App.Core/Internal/App/IAppResources.cs b/src/Altinn.App.Core/Internal/App/IAppResources.cs
index 6a08382f28..956d8ade61 100644
--- a/src/Altinn.App.Core/Internal/App/IAppResources.cs
+++ b/src/Altinn.App.Core/Internal/App/IAppResources.cs
@@ -166,4 +166,9 @@ public interface IAppResources
/// Gets the validation configuration for a given data type
///
string? GetValidationConfiguration(string dataTypeId);
+
+ ///
+ /// Gets the xsd schema for the provided dataTypeId.
+ ///
+ string? GetXsdSchema(string dataTypeId);
}
diff --git a/test/Altinn.App.Api.Tests/Controllers/ResourceControllerTests.cs b/test/Altinn.App.Api.Tests/Controllers/ResourceControllerTests.cs
new file mode 100644
index 0000000000..d523547cf3
--- /dev/null
+++ b/test/Altinn.App.Api.Tests/Controllers/ResourceControllerTests.cs
@@ -0,0 +1,57 @@
+using Microsoft.AspNetCore.Mvc.Testing;
+using Xunit.Abstractions;
+
+namespace Altinn.App.Api.Tests.Controllers;
+
+public class ResourceControllerTests : ApiTestBase, IClassFixture>
+{
+ public ResourceControllerTests(WebApplicationFactory factory, ITestOutputHelper outputHelper)
+ : base(factory, outputHelper) { }
+
+ private const string Org = "tdd";
+ private const string App = "contributer-restriction";
+ private const string DefaultDataTypeId = "Skjema";
+
+ [Fact]
+ public async Task GetModelJsonSchema_ReturnsOk()
+ {
+ var client = GetRootedClient(Org, App);
+ using var response = await client.GetAsync($"/{Org}/{App}/api/jsonschema/{DefaultDataTypeId}");
+ response.EnsureSuccessStatusCode();
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.NotNull(content);
+ Assert.Contains("\"$comment\"", content); // TODO: Update when the schema in the test project has more content.
+ }
+
+ [Fact]
+ public async Task GetModelJsonSchema_InvalidDataTypeId_ReturnsNotFound()
+ {
+ var client = GetRootedClient(Org, App);
+ await Assert.ThrowsAsync(async () =>
+ {
+ using var response = await client.GetAsync($"/{Org}/{App}/api/jsonschema/InvalidDataTypeId");
+ // TODO: Should probably return 404 NotFound instead of throwing FileNotFoundException,
+ // but adding test for current behaviour.
+ // Assert.Equal(System.Net.HttpStatusCode.NotFound, response.StatusCode);
+ });
+ }
+
+ [Fact]
+ public async Task GetXmlSchema_ReturnsOk()
+ {
+ var client = GetRootedClient(Org, App);
+ using var response = await client.GetAsync($"/{Org}/{App}/api/xsdschema/{DefaultDataTypeId}");
+ response.EnsureSuccessStatusCode();
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.NotNull(content);
+ Assert.Contains("
+
+
+
diff --git a/test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveJsonSwagger.verified.json b/test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveJsonSwagger.verified.json
index 3b981634ca..6e922fe919 100644
--- a/test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveJsonSwagger.verified.json
+++ b/test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveJsonSwagger.verified.json
@@ -5085,6 +5085,82 @@
}
}
},
+ "/{org}/{app}/api/xsdschema/{id}": {
+ "get": {
+ "tags": [
+ "Resource"
+ ],
+ "summary": "Get the xsd schema for the model",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "org",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "app",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/xml": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Not Found",
+ "content": {
+ "text/plain": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemDetails"
+ }
+ },
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemDetails"
+ }
+ },
+ "text/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemDetails"
+ }
+ },
+ "application/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemDetails"
+ }
+ },
+ "text/xml": {
+ "schema": {
+ "$ref": "#/components/schemas/ProblemDetails"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/{org}/{app}/api/layouts": {
"get": {
"tags": [
@@ -5213,7 +5289,8 @@
}
}
}
- }
+ },
+ "deprecated": true
}
},
"/{org}/{app}/api/layoutsettings/{id}": {
diff --git a/test/Altinn.App.Api.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt b/test/Altinn.App.Api.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
index 23a2cc3d7c..2b623302a1 100644
--- a/test/Altinn.App.Api.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
+++ b/test/Altinn.App.Api.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
@@ -528,6 +528,8 @@ namespace Altinn.App.Api.Controllers
[Microsoft.AspNetCore.Mvc.HttpGet]
[Microsoft.AspNetCore.Mvc.ProducesResponseType(typeof(string), 200, "application/json", new string[0])]
[Microsoft.AspNetCore.Mvc.Route("{org}/{app}/api/layoutsettings")]
+ [System.Obsolete("This endpoint is no longer available. Use /{org}/{app}/api/layoutsettings/{id} to" +
+ " get layout settings for a specific layout set.")]
public Microsoft.AspNetCore.Mvc.ActionResult GetLayoutSettings(string org, string app) { }
[Microsoft.AspNetCore.Mvc.HttpGet]
[Microsoft.AspNetCore.Mvc.ProducesResponseType(typeof(string), 200, "application/json", new string[0])]
@@ -549,6 +551,11 @@ namespace Altinn.App.Api.Controllers
[Microsoft.AspNetCore.Mvc.Route("{org}/{app}/api/jsonschema/{id}")]
public Microsoft.AspNetCore.Mvc.ActionResult GetModelJsonSchema([Microsoft.AspNetCore.Mvc.FromRoute] string id) { }
[Microsoft.AspNetCore.Mvc.HttpGet]
+ [Microsoft.AspNetCore.Mvc.ProducesResponseType(404)]
+ [Microsoft.AspNetCore.Mvc.ProducesResponseType(typeof(string), 200, "application/xml", new string[0])]
+ [Microsoft.AspNetCore.Mvc.Route("{org}/{app}/api/xsdschema/{id}")]
+ public Microsoft.AspNetCore.Mvc.ActionResult GetModelXsdSchema([Microsoft.AspNetCore.Mvc.FromRoute] string id) { }
+ [Microsoft.AspNetCore.Mvc.HttpGet]
[Microsoft.AspNetCore.Mvc.ProducesResponseType(204)]
[Microsoft.AspNetCore.Mvc.ProducesResponseType(typeof(Microsoft.AspNetCore.Mvc.FileContentResult), 200, "application/json", new string[0])]
[Microsoft.AspNetCore.Mvc.Route("{org}/{app}/api/ruleconfiguration/{id}")]
diff --git a/test/Altinn.App.Core.Tests/Implementation/AppResourcesSITests.cs b/test/Altinn.App.Core.Tests/Implementation/AppResourcesSITests.cs
index 14d172bcc9..0de10240ff 100644
--- a/test/Altinn.App.Core.Tests/Implementation/AppResourcesSITests.cs
+++ b/test/Altinn.App.Core.Tests/Implementation/AppResourcesSITests.cs
@@ -1,4 +1,3 @@
-#nullable disable
using Altinn.App.Core.Configuration;
using Altinn.App.Core.Features.ExternalApi;
using Altinn.App.Core.Implementation;
@@ -30,7 +29,7 @@ public void GetApplication_desrializes_file_from_disk()
AppResourcesSI appResources = new(
settings,
appMetadata,
- null,
+ null!,
new NullLogger(),
_telemetry.Object
);
@@ -81,7 +80,7 @@ public void GetApplication_handles_onEntry_null()
AppResourcesSI appResources = new(
settings,
appMetadata,
- null,
+ null!,
new NullLogger(),
_telemetry.Object
);
@@ -135,7 +134,7 @@ public void GetApplication_second_read_from_cache()
AppResourcesSI appResources = new(
settings,
appMetadata,
- null,
+ null!,
new NullLogger(),
_telemetry.Object
);
@@ -190,7 +189,7 @@ public void GetApplicationMetadata_throws_ApplicationConfigException_if_file_not
IAppResources appResources = new AppResourcesSI(
settings,
appMetadata,
- null,
+ null!,
new NullLogger(),
_telemetry.Object
);
@@ -206,7 +205,7 @@ public void GetApplicationMetadata_throws_ApplicationConfigException_if_deserial
IAppResources appResources = new AppResourcesSI(
settings,
appMetadata,
- null,
+ null!,
new NullLogger(),
_telemetry.Object
);
@@ -222,7 +221,7 @@ public void GetApplicationXACMLPolicy_return_policyfile_as_string()
IAppResources appResources = new AppResourcesSI(
settings,
appMetadata,
- null,
+ null!,
new NullLogger(),
_telemetry.Object
);
@@ -240,7 +239,7 @@ public void GetApplicationXACMLPolicy_return_null_if_file_not_found()
IAppResources appResources = new AppResourcesSI(
settings,
appMetadata,
- null,
+ null!,
new NullLogger(),
_telemetry.Object
);
@@ -257,7 +256,7 @@ public void GetApplicationBPMNProcess_return_process_as_string()
IAppResources appResources = new AppResourcesSI(
settings,
appMetadata,
- null,
+ null!,
new NullLogger(),
_telemetry.Object
);
@@ -275,7 +274,7 @@ public void GetApplicationBPMNProcess_return_null_if_file_not_found()
IAppResources appResources = new AppResourcesSI(
settings,
appMetadata,
- null,
+ null!,
new NullLogger(),
_telemetry.Object
);
@@ -305,7 +304,7 @@ private AppSettings GetAppSettings(
private static IAppMetadata SetupAppMetadata(
IOptions appsettings,
- IFrontendFeatures frontendFeatures = null
+ IFrontendFeatures? frontendFeatures = null
)
{
var featureManagerMock = new Mock();
diff --git a/test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt b/test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
index 5e4161778e..23b2e1718c 100644
--- a/test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
+++ b/test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
@@ -2353,6 +2353,7 @@ namespace Altinn.App.Core.Implementation
public byte[] GetText(string org, string app, string textResource) { }
public System.Threading.Tasks.Task GetTexts(string org, string app, string language) { }
public string? GetValidationConfiguration(string dataTypeId) { }
+ public string? GetXsdSchema(string dataTypeId) { }
}
public class DefaultAppEvents : Altinn.App.Core.Internal.App.IAppEvents
{
@@ -2928,6 +2929,7 @@ namespace Altinn.App.Core.Internal.App
byte[] GetText(string org, string app, string textResource);
System.Threading.Tasks.Task GetTexts(string org, string app, string language);
string? GetValidationConfiguration(string dataTypeId);
+ string? GetXsdSchema(string dataTypeId);
}
public interface IApplicationClient
{
diff --git a/test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Custom_0_Metadata.verified.txt b/test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Custom_0_Metadata.verified.txt
index d4ba4f3935..f521e96afc 100644
--- a/test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Custom_0_Metadata.verified.txt
+++ b/test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Custom_0_Metadata.verified.txt
@@ -407,6 +407,15 @@
requiredScopesServiceOwners: null
}
},
+ {
+ endpoint: GET {org}/{app}/api/xsdschema/{id},
+ metadata: {
+ errorMessageTextResourceKeyUser: null,
+ errorMessageTextResourceKeyServiceOwner: null,
+ requiredScopesUsers: null,
+ requiredScopesServiceOwners: null
+ }
+ },
{
endpoint: GET {org}/{app}/instances/{instanceOwnerId:int}/{instanceId:guid}/data/{dataGuid:guid}/validate,
metadata: {
diff --git a/test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt b/test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt
index 189d2581d4..7445415c8a 100644
--- a/test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt
+++ b/test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt
@@ -382,6 +382,15 @@
requiredScopesServiceOwners: null
}
},
+ {
+ endpoint: GET {org}/{app}/api/xsdschema/{id},
+ metadata: {
+ errorMessageTextResourceKeyUser: null,
+ errorMessageTextResourceKeyServiceOwner: null,
+ requiredScopesUsers: null,
+ requiredScopesServiceOwners: null
+ }
+ },
{
endpoint: GET {org}/{app}/instances/{instanceOwnerId:int}/{instanceId:guid}/data/{dataGuid:guid}/validate,
metadata: {