diff --git a/backend/packagegroups/NuGet.props b/backend/packagegroups/NuGet.props index 1007d47fed8..4801794327b 100644 --- a/backend/packagegroups/NuGet.props +++ b/backend/packagegroups/NuGet.props @@ -21,7 +21,6 @@ - @@ -42,6 +41,15 @@ + + + + + + + + + @@ -56,13 +64,13 @@ - - + + - + diff --git a/backend/src/DataModeling/Converter/Csharp/CsharpGenerationException.cs b/backend/src/DataModeling/Converter/Csharp/CsharpGenerationException.cs index 76a606468e8..b66bbe00ca8 100644 --- a/backend/src/DataModeling/Converter/Csharp/CsharpGenerationException.cs +++ b/backend/src/DataModeling/Converter/Csharp/CsharpGenerationException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Altinn.Studio.DataModeling.Converter.Csharp { @@ -23,11 +22,5 @@ public CsharpGenerationException(string message) : base(message) public CsharpGenerationException(string message, Exception innerException) : base(message, innerException) { } - - /// - protected CsharpGenerationException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - } } diff --git a/backend/src/DataModeling/Converter/Json/JsonSchemaConvertException.cs b/backend/src/DataModeling/Converter/Json/JsonSchemaConvertException.cs index b1d300ec7c6..3619f7f8748 100644 --- a/backend/src/DataModeling/Converter/Json/JsonSchemaConvertException.cs +++ b/backend/src/DataModeling/Converter/Json/JsonSchemaConvertException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Altinn.Studio.DataModeling.Converter.Json { @@ -29,12 +28,5 @@ public JsonSchemaConvertException(string message) : base(message) public JsonSchemaConvertException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// - /// - protected JsonSchemaConvertException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/backend/src/DataModeling/Converter/Metadata/ModelMetadataConvertException.cs b/backend/src/DataModeling/Converter/Metadata/ModelMetadataConvertException.cs index e5f89193706..73846a7081f 100644 --- a/backend/src/DataModeling/Converter/Metadata/ModelMetadataConvertException.cs +++ b/backend/src/DataModeling/Converter/Metadata/ModelMetadataConvertException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Altinn.Studio.DataModeling.Converter.Metadata { @@ -23,10 +22,5 @@ public MetamodelConvertException(string message) : base(message) public MetamodelConvertException(string message, Exception innerException) : base(message, innerException) { } - - /// - protected MetamodelConvertException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/backend/src/DataModeling/Converter/Xml/XmlSchemaConvertException.cs b/backend/src/DataModeling/Converter/Xml/XmlSchemaConvertException.cs index 8111ef93a6f..f785fafdca3 100644 --- a/backend/src/DataModeling/Converter/Xml/XmlSchemaConvertException.cs +++ b/backend/src/DataModeling/Converter/Xml/XmlSchemaConvertException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Altinn.Studio.DataModeling.Converter.Xml { @@ -29,12 +28,5 @@ public XmlSchemaConvertException(string message) : base(message) public XmlSchemaConvertException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// - /// - protected XmlSchemaConvertException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/backend/src/DataModeling/DataModeling.csproj b/backend/src/DataModeling/DataModeling.csproj index dce74b39402..4f91a5245f7 100644 --- a/backend/src/DataModeling/DataModeling.csproj +++ b/backend/src/DataModeling/DataModeling.csproj @@ -4,6 +4,7 @@ Altinn.Studio.DataModeling latest Altinn.Studio.DataModeling + 1.0.0 diff --git a/backend/src/Designer/Controllers/ApplicationMetadataController.cs b/backend/src/Designer/Controllers/ApplicationMetadataController.cs index 53fb28f44c9..f5d54343721 100644 --- a/backend/src/Designer/Controllers/ApplicationMetadataController.cs +++ b/backend/src/Designer/Controllers/ApplicationMetadataController.cs @@ -23,7 +23,6 @@ public class ApplicationMetadataController : ControllerBase /// Initializes a new instance of the class. /// /// The application metadata service - /// The user requests synchronization service public ApplicationMetadataController(IApplicationMetadataService applicationMetadataService) { _applicationMetadataService = applicationMetadataService; diff --git a/backend/src/Designer/Controllers/DatamodelsController.cs b/backend/src/Designer/Controllers/DatamodelsController.cs index f7c57a88525..12e8874da6d 100644 --- a/backend/src/Designer/Controllers/DatamodelsController.cs +++ b/backend/src/Designer/Controllers/DatamodelsController.cs @@ -6,6 +6,7 @@ using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; +using Altinn.Platform.Storage.Interface.Models; using Altinn.Studio.DataModeling.Validator.Json; using Altinn.Studio.Designer.Filters; using Altinn.Studio.Designer.Helpers; @@ -37,6 +38,7 @@ public class DatamodelsController : ControllerBase /// /// Interface for working with models. /// An . + /// Interface for validating that the model name does not already belong to a data type public DatamodelsController(ISchemaModelService schemaModelService, IJsonSchemaValidator jsonSchemaValidator, IModelNameValidator modelNameValidator) { _schemaModelService = schemaModelService; @@ -248,6 +250,34 @@ public async Task UseXsdFromRepo(string org, string repository, s } } + /// + /// Gets the dataType for a given data model. + /// + [HttpGet("datamodel/{modelName}/dataType")] + [UseSystemTextJson] + public async Task> GetModelDataType(string org, string repository, string modelName) + { + DataType dataType = await _schemaModelService.GetModelDataType(org, repository, modelName); + return Ok(dataType); + } + + /// + /// Updates the dataType for a given data model. + /// + [HttpPut("datamodel/{modelName}/dataType")] + [UseSystemTextJson] + public async Task SetModelDataType(string org, string repository, string modelName, [FromBody] DataType dataType) + { + if (!Equals(modelName, dataType.Id)) + { + return BadRequest("Model name in path and request body does not match"); + } + + await _schemaModelService.SetModelDataType(org, repository, modelName, dataType); + DataType updatedDataType = await _schemaModelService.GetModelDataType(org, repository, modelName); + return Ok(updatedDataType); + } + private static string GetFileNameFromUploadedFile(IFormFile thefile) { return ContentDispositionHeaderValue.Parse(new StringSegment(thefile.ContentDisposition)).FileName.ToString(); diff --git a/backend/src/Designer/Controllers/OptionsController.cs b/backend/src/Designer/Controllers/OptionsController.cs index 97b4baf1c60..56f23452ce0 100644 --- a/backend/src/Designer/Controllers/OptionsController.cs +++ b/backend/src/Designer/Controllers/OptionsController.cs @@ -129,6 +129,25 @@ public async Task>> GetOptionsList(string org, string } } + /// + /// Gets all usages of all optionListIds in the layouts as . + /// + /// Unique identifier of the organisation responsible for the app. + /// Application identifier which is unique within an organisation. + /// A that observes if operation is cancelled. + [HttpGet] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Route("usage")] + public async Task>> GetOptionListsReferences(string org, string repo, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); + + List optionListReferences = await _optionsService.GetAllOptionListReferences(AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer), cancellationToken); + return Ok(optionListReferences); + } + /// /// Creates or overwrites an options list. /// diff --git a/backend/src/Designer/Controllers/Preview/DataController.cs b/backend/src/Designer/Controllers/Preview/DataController.cs index d4515b42cf9..dc3e03def6a 100644 --- a/backend/src/Designer/Controllers/Preview/DataController.cs +++ b/backend/src/Designer/Controllers/Preview/DataController.cs @@ -1,50 +1,101 @@ using System; using System.Collections.Generic; using System.Text.Json.Nodes; -using System.Threading; -using System.Threading.Tasks; -using System.Web; using Altinn.Platform.Storage.Interface.Models; using Altinn.Studio.Designer.Filters; using Altinn.Studio.Designer.Helpers; -using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Infrastructure.GitRepository; using Altinn.Studio.Designer.Models.Preview; -using Altinn.Studio.Designer.Services.Implementation; using Altinn.Studio.Designer.Services.Interfaces; using Altinn.Studio.Designer.Services.Interfaces.Preview; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Routing; namespace Altinn.Studio.Designer.Controllers.Preview { [Authorize] [AutoValidateAntiforgeryToken] [Route("{org:regex(^(?!designer))}/{app:regex(^(?!datamodels$)[[a-z]][[a-z0-9-]]{{1,28}}[[a-z0-9]]$)}/instances/{partyId}/{instanceGuid}/data")] - public class DataController(IHttpContextAccessor httpContextAccessor, - IPreviewService previewService, - ISchemaModelService schemaModelService, - IDataService dataService + public class DataController( + IInstanceService instanceService, + IDataService dataService, + IAltinnGitRepositoryFactory altinnGitRepositoryFactory + ) : Controller { + // + // Redirect requests from older versions of Studio to old controller + // + public override void OnActionExecuting(ActionExecutingContext context) + { + string org = context.RouteData.Values["org"] as string; + string app = context.RouteData.Values["app"] as string; + string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); + AltinnAppGitRepository altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, app, developer); + if (!altinnAppGitRepository.AppUsesLayoutSets()) + { + RouteValueDictionary routeData = context.RouteData.Values; + foreach (var queryParam in context.HttpContext.Request.Query) + { + routeData[queryParam.Key] = queryParam.Value.ToString(); + } + context.Result = base.RedirectToActionPreserveMethod(controllerName: "OldData", routeValues: routeData); + } + base.OnActionExecuting(context); + } + [HttpGet("{dataGuid}")] - public ActionResult Get([FromRoute] Guid dataGuid) + [UseSystemTextJson] + public ActionResult Get( + [FromRoute] Guid dataGuid + ) { JsonNode dataItem = dataService.GetDataElement(dataGuid); return Ok(dataItem); } [HttpPost] - public ActionResult Post( + [UseSystemTextJson] + public ActionResult Post( [FromRoute] int partyId, [FromRoute] Guid instanceGuid, [FromQuery] string dataType ) { DataElement dataElement = dataService.CreateDataElement(partyId, instanceGuid, dataType); + instanceService.AddDataElement(instanceGuid, dataElement); return Created("link-to-app-placeholder", dataElement); } + [HttpPatch] + [UseSystemTextJson] + public ActionResult PatchMultiple( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId, + [FromRoute] Guid instanceGuid, + [FromBody] DataPatchRequestMultiple dataPatch + ) + { + Instance instance = instanceService.GetInstance(instanceGuid); + + List newDataModels = []; + dataPatch.Patches.ForEach(patch => + { + JsonNode dataItem = dataService.PatchDataElement(patch.DataElementId, patch.Patch); + newDataModels.Add(new DataModelPairResponse(patch.DataElementId, dataItem)); + }); + + return Ok(new DataPatchResponseMultiple() + { + ValidationIssues = [], + NewDataModels = newDataModels, + Instance = instance, + }); + } + [HttpPatch("{dataGuid}")] [UseSystemTextJson] public ActionResult Patch( @@ -60,72 +111,30 @@ [FromBody] DataPatchRequest dataPatch }); } - [HttpDelete("{dataTypeId}")] - public ActionResult DeleteAttachment([FromRoute] Guid dataGuid) + [HttpDelete("{dataGuid}")] + public ActionResult Delete( + [FromRoute] Guid instanceGuid, + [FromRoute] Guid dataGuid + ) { + instanceService.RemoveDataElement(instanceGuid, dataGuid); return Ok(); } [HttpGet("{dataGuid}/validate")] - public ActionResult ValidateInstanceForData([FromRoute] Guid dataGuid) + public ActionResult ValidateInstanceForData( + [FromRoute] Guid dataGuid + ) { return Ok(new List()); } - [HttpPost("{dataTypeId}/tags")] - public ActionResult UpdateTagsForAttachment([FromBody] string tag) - { - return Created("link-to-app-placeholder", tag); - } - - [HttpGet(PreviewService.MockDataTaskId)] - public async Task GetDefaultFormData( - [FromRoute] string org, - [FromRoute] string app, - [FromRoute] int partyId, - CancellationToken cancellationToken + [HttpPost("{dataGuid}/tags")] + public ActionResult UpdateTagsForAttachment( + [FromBody] string tag ) { - string developer = AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext); - string refererHeader = Request.Headers.Referer; - string layoutSetName = GetSelectedLayoutSetInEditorFromRefererHeader(refererHeader); - DataType dataType = await previewService.GetDataTypeForLayoutSetName(org, app, developer, layoutSetName, cancellationToken); - // For apps that does not have a datamodel - if (dataType == null) - { - Instance mockInstance = await previewService.GetMockInstance(org, app, developer, partyId, layoutSetName, cancellationToken); - return Ok(mockInstance.Id); - } - string modelPath = $"/App/models/{dataType.Id}.schema.json"; - string decodedPath = Uri.UnescapeDataString(modelPath); - string formData = await schemaModelService.GetSchema(AltinnRepoEditingContext.FromOrgRepoDeveloper(org, app, developer), decodedPath, cancellationToken); - return Ok(formData); - } - - [HttpPut(PreviewService.MockDataTaskId)] - public async Task UpdateFormData( - [FromRoute] string org, - [FromRoute] string app, - [FromRoute] int partyId, - CancellationToken cancellationToken - ) - { - return await GetDefaultFormData(org, app, partyId, cancellationToken); - } - - [HttpPatch(PreviewService.MockDataTaskId)] - public ActionResult PatchFormData() - { - return Ok(); - } - - private static string GetSelectedLayoutSetInEditorFromRefererHeader(string refererHeader) - { - Uri refererUri = new(refererHeader); - string layoutSetName = HttpUtility.ParseQueryString(refererUri.Query)["selectedLayoutSet"]; - - return string.IsNullOrEmpty(layoutSetName) ? null : layoutSetName; + return Created("link-to-app-placeholder", tag); } } } - diff --git a/backend/src/Designer/Controllers/Preview/InstancesController.cs b/backend/src/Designer/Controllers/Preview/InstancesController.cs index 12bfaa02504..ba947d1bea7 100644 --- a/backend/src/Designer/Controllers/Preview/InstancesController.cs +++ b/backend/src/Designer/Controllers/Preview/InstancesController.cs @@ -2,203 +2,231 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using System.Web; using Altinn.App.Core.Internal.Process.Elements; using Altinn.Platform.Storage.Interface.Models; +using Altinn.Studio.Designer.Filters; using Altinn.Studio.Designer.Helpers; using Altinn.Studio.Designer.Infrastructure.GitRepository; +using Altinn.Studio.Designer.Models.App; using Altinn.Studio.Designer.Services.Interfaces; +using Altinn.Studio.Designer.Services.Interfaces.Preview; using LibGit2Sharp; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Routing; -namespace Altinn.Studio.Designer.Controllers.Preview +namespace Altinn.Studio.Designer.Controllers.Preview; + +[Authorize] +[AutoValidateAntiforgeryToken] +[Route("{org:regex(^(?!designer))}/{app:regex(^(?!datamodels$)[[a-z]][[a-z0-9-]]{{1,28}}[[a-z0-9]]$)}/instances")] +public class InstancesController(IHttpContextAccessor httpContextAccessor, + IPreviewService previewService, + IAltinnGitRepositoryFactory altinnGitRepositoryFactory, + IInstanceService instanceService, + IApplicationMetadataService applicationMetadataService +) : Controller { - [Authorize] - [AutoValidateAntiforgeryToken] - [Route("{org:regex(^(?!designer))}/{app:regex(^(?!datamodels$)[[a-z]][[a-z0-9-]]{{1,28}}[[a-z0-9]]$)}/instances")] - public class InstancesController(IHttpContextAccessor httpContextAccessor, - IPreviewService previewService, - IAltinnGitRepositoryFactory altinnGitRepositoryFactory - ) : Controller + // + // Redirect requests from older versions of Studio to old controller + // + public override void OnActionExecuting(ActionExecutingContext context) { - /// - /// Action for creating the mocked instance object - /// - /// Unique identifier of the organisation responsible for the app. - /// Application identifier which is unique within an organisation. - /// - /// A that observes if operation is cancelled. - /// The mocked instance object - [HttpPost] - public async Task> Instances(string org, string app, [FromQuery] int? instanceOwnerPartyId, CancellationToken cancellationToken) + string org = context.RouteData.Values["org"] as string; + string app = context.RouteData.Values["app"] as string; + string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); + AltinnAppGitRepository altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, app, developer); + if (!altinnAppGitRepository.AppUsesLayoutSets()) { - string developer = AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext); - string refererHeader = Request.Headers["Referer"]; - string layoutSetName = GetSelectedLayoutSetInEditorFromRefererHeader(refererHeader); - Instance mockInstance = await previewService.GetMockInstance(org, app, developer, instanceOwnerPartyId, layoutSetName, cancellationToken); - return Ok(mockInstance); + RouteValueDictionary routeData = context.RouteData.Values; + foreach (var queryParam in context.HttpContext.Request.Query) + { + routeData[queryParam.Key] = queryParam.Value.ToString(); + } + context.Result = base.RedirectToActionPreserveMethod(controllerName: "OldInstances", routeValues: routeData); } + base.OnActionExecuting(context); + } - /// - /// Action for getting a mocked response for the current task connected to the instance - /// - /// The processState - [HttpGet("{partyId}/{instanceGuId}/process")] - public async Task> Process(string org, string app, [FromRoute] int partyId, CancellationToken cancellationToken) - { - string developer = AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext); - string refererHeader = Request.Headers["Referer"]; - string layoutSetName = GetSelectedLayoutSetInEditorFromRefererHeader(refererHeader); - Instance mockInstance = await previewService.GetMockInstance(org, app, developer, partyId, layoutSetName, cancellationToken); - List tasks = await previewService.GetTasksForAllLayoutSets(org, app, developer, cancellationToken); - AppProcessState processState = new AppProcessState(mockInstance.Process) - { - ProcessTasks = tasks != null - ? new List(tasks?.ConvertAll(task => new AppProcessTaskTypeInfo { ElementId = task, AltinnTaskType = "data" })) - : null - }; + /// + /// Get instance data + /// + [HttpGet("{partyId}/{instanceGuid}")] + [UseSystemTextJson] + public ActionResult GetInstance( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId, + [FromRoute] Guid instanceGuid, + CancellationToken cancellationToken) + { + Instance instanceData = instanceService.GetInstance(instanceGuid); + return Ok(instanceData); + } - return Ok(processState); - } + /// + /// Create a new instance + /// + [HttpPost] + public async Task> Post( + [FromRoute] string org, + [FromRoute] string app, + [FromQuery] int instanceOwnerPartyId, + [FromQuery] string taskId, + [FromQuery] string language = null + ) + { + ApplicationMetadata applicationMetadata = await applicationMetadataService.GetApplicationMetadataFromRepository(org, app); + Instance instance = instanceService.CreateInstance(org, app, instanceOwnerPartyId, taskId, applicationMetadata.DataTypes); + return Ok(instance); + } - /// - /// Endpoint to get instance for next process step - /// - /// A mocked instance object - [HttpGet("{partyId}/{instanceGuId}")] - public async Task> InstanceForNextTask(string org, string app, [FromRoute] int partyId, CancellationToken cancellationToken) - { - string developer = AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext); - string refererHeader = Request.Headers["Referer"]; - string layoutSetName = GetSelectedLayoutSetInEditorFromRefererHeader(refererHeader); - Instance mockInstance = await previewService.GetMockInstance(org, app, developer, partyId, layoutSetName, cancellationToken); - return Ok(mockInstance); - } + /// + /// Endpoint to get active instances for apps with state/layout sets/multiple processes + /// + /// A list of a single mocked instance + [HttpGet("{partyId}/active")] + public ActionResult> ActiveInstances( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId + ) + { + // Simulate never having any active instances + List activeInstances = new(); + return Ok(activeInstances); + } - /// - /// Endpoint to get active instances for apps with state/layout sets/multiple processes - /// - /// A list of a single mocked instance - [HttpGet("{partyId}/active")] - public ActionResult> ActiveInstancesForAppsWithLayoutSets(string org, string app, [FromRoute] int partyId) - { - // Simulate never having any active instances - List activeInstances = new(); - return Ok(activeInstances); - } + /// + /// Endpoint to validate an instance + /// + /// Ok + [HttpGet("{partyId}/{instanceGuId}/validate")] + public ActionResult ValidateInstance() + { + return Ok(); + } - /// - /// Endpoint to validate an instance - /// - /// Ok - [HttpGet("{partyId}/{instanceGuId}/validate")] - public ActionResult ValidateInstance() + /// + /// Action for getting a mocked response for the current task connected to the instance + /// + /// The processState + [HttpGet("{partyId}/{instanceGuid}/process")] + public async Task> Process( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId, + [FromRoute] Guid instanceGuid, + CancellationToken cancellationToken) + { + string developer = AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext); + Instance instance = instanceService.GetInstance(instanceGuid); + List tasks = await previewService.GetTasksForAllLayoutSets(org, app, developer, cancellationToken); + AppProcessState processState = new(instance.Process) { - return Ok(); - } + ProcessTasks = tasks != null + ? new List(tasks?.ConvertAll(task => new AppProcessTaskTypeInfo { ElementId = task, AltinnTaskType = "data" })) + : null + }; - /// - /// Action for getting a mocked response for the next task connected to the instance - /// - /// The processState object on the global mockInstance object - [HttpGet("{partyId}/{instanceGuId}/process/next")] - public async Task ProcessNext(string org, string app, [FromRoute] int partyId, CancellationToken cancellationToken) - { - string developer = AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext); - string refererHeader = Request.Headers["Referer"]; - string layoutSetName = GetSelectedLayoutSetInEditorFromRefererHeader(refererHeader); - Instance mockInstance = await previewService.GetMockInstance(org, app, developer, partyId, layoutSetName, cancellationToken); - return Ok(mockInstance.Process); - } + return Ok(processState); + } - /// - /// Action for mocking an end to the process in order to get receipt after "send inn" is pressed - /// - /// Process object where ended is set - [HttpPut("{partyId}/{instanceGuId}/process/next")] - public async Task UpdateProcessNext(string org, string app, [FromRoute] int partyId, [FromQuery] string lang, CancellationToken cancellationToken) - { - string refererHeader = Request.Headers["Referer"]; - string layoutSetName = GetSelectedLayoutSetInEditorFromRefererHeader(refererHeader); - if (string.IsNullOrEmpty(layoutSetName)) - { - string endProcess = """{"ended": "ended"}"""; - return Ok(endProcess); - } - string developer = AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext); - Instance mockInstance = await previewService.GetMockInstance(org, app, developer, partyId, layoutSetName, cancellationToken); - return Ok(mockInstance.Process); - } + /// + /// Action for getting a mocked response for the next task connected to the instance + /// + /// The processState object on the global mockInstance object + [HttpGet("{partyId}/{instanceGuid}/process/next")] + public ActionResult ProcessNext( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId, + [FromRoute] Guid instanceGuid, + CancellationToken cancellationToken + ) + { + Instance instance = instanceService.GetInstance(instanceGuid); + return Ok(instance.Process); + } - /// - /// Action for getting options list for a given options list id for a given instance - /// - /// Unique identifier of the organisation responsible for the app. - /// Application identifier which is unique within an organisation. - /// The id of the options list - /// The language for the options list - /// The source of the options list - /// A that observes if operation is cancelled. - /// The options list if it exists, otherwise nothing - [HttpGet("{partyId}/{instanceGuid}/options/{optionListId}")] - public async Task> GetOptionsForInstance(string org, string app, string optionListId, [FromQuery] string language, [FromQuery] string source, CancellationToken cancellationToken) - { - try - { - // TODO: Need code to get dynamic options list based on language and source? - string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); - AltinnAppGitRepository altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, app, developer); - string options = await altinnAppGitRepository.GetOptionsList(optionListId, cancellationToken); - return Ok(options); - } - catch (NotFoundException) - { - // Return empty list since app-frontend don't handle a null result - return Ok(new List()); - } - } + /// + /// Action for mocking an end to the process in order to get receipt after "send inn" is pressed + /// + /// Process object where ended is set + [HttpPut("{partyId}/{instanceGuid}/process/next")] + public ActionResult UpdateProcessNext( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId, + [FromRoute] Guid instanceGuid, + [FromQuery] string lang, + CancellationToken cancellationToken + ) + { + Instance instance = instanceService.GetInstance(instanceGuid); + return Ok(instance.Process); + } - /// - /// Action for getting data list for a given data list id for a given instance - /// - /// Unique identifier of the organisation responsible for the app. - /// Application identifier which is unique within an organisation. - /// The id of the data list - /// The language for the data list - /// The number of items to return - /// A that observes if operation is cancelled. - /// The options list if it exists, otherwise nothing - [HttpGet("{partyId}/{instanceGuid}/datalists/{dataListId}")] - public ActionResult> GetDataListsForInstance(string org, string app, string dataListId, [FromQuery] string language, [FromQuery] string size, CancellationToken cancellationToken) + /// + /// Action for getting options list for a given options list id for a given instance + /// + [HttpGet("{partyId}/{instanceGuid}/options/{optionListId}")] + public async Task> GetOptionsForInstance( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] string optionListId, + [FromQuery] string language, + [FromQuery] string source, + CancellationToken cancellationToken + ) + { + try { - // TODO: Should look into whether we can get some actual data here, or if we can make an "informed" mock based on the setup. - // For now, we just return an empty list. - return Ok(new List()); + // TODO: Need code to get dynamic options list based on language and source? + string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); + AltinnAppGitRepository altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, app, developer); + string options = await altinnAppGitRepository.GetOptionsList(optionListId, cancellationToken); + return Ok(options); } - - /// - /// Action for updating data model with tag for attachment component // TODO: Figure out what actually happens here - /// - /// Unique identifier of the organisation responsible for the app. - /// Application identifier which is unique within an organisation. - /// Current page in running app - /// Current layout set in running app - /// Connected datatype for that process task - /// The options list if it exists, otherwise nothing - [HttpPost("{partyId}/{instanceGuid}/pages/order")] - public IActionResult UpdateAttachmentWithTag(string org, string app, [FromQuery] string currentPage, [FromQuery] string layoutSetId, [FromQuery] string dataTypeId) + catch (NotFoundException) { - return Ok(); + // Return empty list since app-frontend don't handle a null result + return Ok(new List()); } + } - private static string GetSelectedLayoutSetInEditorFromRefererHeader(string refererHeader) - { - Uri refererUri = new(refererHeader); - string layoutSetName = HttpUtility.ParseQueryString(refererUri.Query)["selectedLayoutSet"]; + /// + /// Action for getting data list for a given data list id for a given instance + /// + [HttpGet("{partyId}/{instanceGuid}/datalists/{dataListId}")] + public ActionResult> GetDataListsForInstance( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] string dataListId, + [FromQuery] string language, + [FromQuery] string size, + CancellationToken cancellationToken + ) + { + // TODO: Should look into whether we can get some actual data here, or if we can make an "informed" mock based on the setup. + // For now, we just return an empty list. + return Ok(new List()); + } - return string.IsNullOrEmpty(layoutSetName) ? null : layoutSetName; - } + /// + /// Action for updating data model with tag for attachment component // TODO: Figure out what actually happens here + /// + [HttpPost("{partyId}/{instanceGuid}/pages/order")] + public IActionResult UpdateAttachmentWithTag( + [FromRoute] string org, + [FromRoute] string app, + [FromQuery] string currentPage, + [FromQuery] string layoutSetId, + [FromQuery] string dataTypeId + ) + { + return Ok(); } } diff --git a/backend/src/Designer/Controllers/Preview/V3/OldDataController.cs b/backend/src/Designer/Controllers/Preview/V3/OldDataController.cs new file mode 100644 index 00000000000..2a2aa72fe8b --- /dev/null +++ b/backend/src/Designer/Controllers/Preview/V3/OldDataController.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using Altinn.Platform.Storage.Interface.Models; +using Altinn.Studio.Designer.Filters; +using Altinn.Studio.Designer.Models.Preview; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; + +namespace Altinn.Studio.Designer.Controllers.Preview.V3 +{ + [Authorize] + [AutoValidateAntiforgeryToken] + [Route("{org:regex(^(?!designer))}/{app:regex(^(?!datamodels$)[[a-z]][[a-z0-9-]]{{1,28}}[[a-z0-9]]$)}/v3/instances/{partyId}/{instanceGuid}/data")] + public class OldDataController( + ) : Controller + { + + [HttpGet("{dataGuid}")] + [UseSystemTextJson] + public ActionResult Get( + [FromRoute] string partyId, + [FromRoute] string instanceGuid + ) + { + return Ok(partyId + "/" + instanceGuid); + } + + [HttpPost] + [UseSystemTextJson] + public ActionResult Post( + [FromRoute] int partyId, + [FromRoute] Guid instanceGuid, + [FromQuery] string dataType + ) + { + return Created("link-to-app-placeholder", "{}"); + } + + [HttpDelete("{dataGuid}")] + public ActionResult Delete( + [FromRoute] Guid instanceGuid, + [FromRoute] Guid dataGuid + ) + { + return Ok(); + } + + [HttpGet("{dataGuid}/validate")] + public ActionResult ValidateInstanceForData( + [FromRoute] Guid dataGuid + ) + { + return Ok(new List()); + } + + [HttpPost("{dataGuid}/tags")] + public ActionResult UpdateTagsForAttachment( + [FromBody] string tag + ) + { + return Created("link-to-app-placeholder", tag); + } + } +} diff --git a/backend/src/Designer/Controllers/Preview/V3/OldInstancesController.cs b/backend/src/Designer/Controllers/Preview/V3/OldInstancesController.cs new file mode 100644 index 00000000000..b7240b9ec0d --- /dev/null +++ b/backend/src/Designer/Controllers/Preview/V3/OldInstancesController.cs @@ -0,0 +1,228 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using Altinn.App.Core.Internal.Process.Elements; +using Altinn.Platform.Storage.Interface.Models; +using Altinn.Studio.Designer.Filters; +using Altinn.Studio.Designer.Helpers; +using Altinn.Studio.Designer.Infrastructure.GitRepository; +using Altinn.Studio.Designer.Services.Interfaces; +using LibGit2Sharp; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Altinn.Studio.Designer.Controllers.Preview.V3; + +/// +/// Controller for preview support of older versions of Studio +/// +[Authorize] +[AutoValidateAntiforgeryToken] +[Route("{org:regex(^(?!designer))}/{app:regex(^(?!datamodels$)[[a-z]][[a-z0-9-]]{{1,28}}[[a-z0-9]]$)}/v3/instances")] +public class OldInstancesController(IHttpContextAccessor httpContextAccessor, + IPreviewService previewService, + IAltinnGitRepositoryFactory altinnGitRepositoryFactory +) : Controller +{ + /// + /// Get instance data + /// + [HttpGet("{partyId}/{instanceGuid}")] + [UseSystemTextJson] + public async Task> GetInstanceAsync( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId, + [FromRoute] Guid instanceGuid, + CancellationToken cancellationToken) + { + string refererHeader = Request.Headers["Referer"]; + Uri refererUri = new(refererHeader); + string layoutSetName = HttpUtility.ParseQueryString(refererUri.Query)["selectedLayoutSet"]; + layoutSetName = string.IsNullOrEmpty(layoutSetName) ? null : layoutSetName; + Instance instance = await previewService.GetMockInstance(org, app, AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext), partyId, layoutSetName, CancellationToken.None); + return Ok(instance); + } + + /// + /// Create a new instance + /// + [HttpPost] + public async Task> Post( + [FromRoute] string org, + [FromRoute] string app, + [FromQuery] int instanceOwnerPartyId, + [FromQuery] string taskId, + [FromQuery] string language = null + ) + { + string refererHeader = Request.Headers["Referer"]; + Uri refererUri = new(refererHeader); + string layoutSetName = HttpUtility.ParseQueryString(refererUri.Query)["selectedLayoutSet"]; + layoutSetName = string.IsNullOrEmpty(layoutSetName) ? null : layoutSetName; + Instance instance = await previewService.GetMockInstance(org, app, AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext), instanceOwnerPartyId, layoutSetName, CancellationToken.None); + return Ok(instance); + } + + /// + /// Endpoint to get active instances for apps with state/layout sets/multiple processes + /// + /// A list of a single mocked instance + [HttpGet("{partyId}/active")] + public ActionResult> ActiveInstances( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId + ) + { + // Simulate never having any active instances + List activeInstances = new(); + return Ok(activeInstances); + } + + /// + /// Endpoint to validate an instance + /// + /// Ok + [HttpGet("{partyId}/{instanceGuId}/validate")] + public ActionResult ValidateInstance() + { + return Ok(); + } + + /// + /// Action for getting a mocked response for the current task connected to the instance + /// + /// The processState + [HttpGet("{partyId}/{instanceGuid}/process")] + public async Task> Process( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId, + [FromRoute] Guid instanceGuid, + CancellationToken cancellationToken) + { + string developer = AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext); + string refererHeader = Request.Headers["Referer"]; + Uri refererUri = new(refererHeader); + string layoutSetName = HttpUtility.ParseQueryString(refererUri.Query)["selectedLayoutSet"]; + layoutSetName = string.IsNullOrEmpty(layoutSetName) ? null : layoutSetName; + Instance instance = await previewService.GetMockInstance(org, app, AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext), partyId, layoutSetName, CancellationToken.None); + List tasks = await previewService.GetTasksForAllLayoutSets(org, app, developer, cancellationToken); + AppProcessState processState = new(instance.Process) + { + ProcessTasks = tasks != null + ? new List(tasks?.ConvertAll(task => new AppProcessTaskTypeInfo { ElementId = task, AltinnTaskType = "data" })) + : null + }; + + return Ok(processState); + } + + /// + /// Action for getting a mocked response for the next task connected to the instance + /// + /// The processState object on the global mockInstance object + [HttpGet("{partyId}/{instanceGuid}/process/next")] + public async Task ProcessNextAsync( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId, + [FromRoute] Guid instanceGuid, + CancellationToken cancellationToken + ) + { + string refererHeader = Request.Headers["Referer"]; + Uri refererUri = new(refererHeader); + string layoutSetName = HttpUtility.ParseQueryString(refererUri.Query)["selectedLayoutSet"]; + layoutSetName = string.IsNullOrEmpty(layoutSetName) ? null : layoutSetName; + Instance instance = await previewService.GetMockInstance(org, app, AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext), partyId, layoutSetName, CancellationToken.None); + return Ok(instance.Process); + } + + /// + /// Action for mocking an end to the process in order to get receipt after "send inn" is pressed + /// + /// Process object where ended is set + [HttpPut("{partyId}/{instanceGuid}/process/next")] + public async Task UpdateProcessNext( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] int partyId, + [FromRoute] Guid instanceGuid, + [FromQuery] string lang, + CancellationToken cancellationToken + ) + { + string refererHeader = Request.Headers.Referer; + Uri refererUri = new(refererHeader); + string layoutSetName = HttpUtility.ParseQueryString(refererUri.Query)["selectedLayoutSet"]; + layoutSetName = string.IsNullOrEmpty(layoutSetName) ? null : layoutSetName; + Instance instance = await previewService.GetMockInstance(org, app, AuthenticationHelper.GetDeveloperUserName(httpContextAccessor.HttpContext), partyId, layoutSetName, CancellationToken.None); + return Ok(instance.Process); + } + + /// + /// Action for getting options list for a given options list id for a given instance + /// + [HttpGet("{partyId}/{instanceGuid}/options/{optionListId}")] + public async Task> GetOptionsForInstance( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] string optionListId, + [FromQuery] string language, + [FromQuery] string source, + CancellationToken cancellationToken + ) + { + try + { + // TODO: Need code to get dynamic options list based on language and source? + string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); + AltinnAppGitRepository altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, app, developer); + string options = await altinnAppGitRepository.GetOptionsList(optionListId, cancellationToken); + return Ok(options); + } + catch (NotFoundException) + { + // Return empty list since app-frontend don't handle a null result + return Ok(new List()); + } + } + + /// + /// Action for getting data list for a given data list id for a given instance + /// + [HttpGet("{partyId}/{instanceGuid}/datalists/{dataListId}")] + public ActionResult> GetDataListsForInstance( + [FromRoute] string org, + [FromRoute] string app, + [FromRoute] string dataListId, + [FromQuery] string language, + [FromQuery] string size, + CancellationToken cancellationToken + ) + { + // TODO: Should look into whether we can get some actual data here, or if we can make an "informed" mock based on the setup. + // For now, we just return an empty list. + return Ok(new List()); + } + + /// + /// Action for updating data model with tag for attachment component // TODO: Figure out what actually happens here + /// + [HttpPost("{partyId}/{instanceGuid}/pages/order")] + public IActionResult UpdateAttachmentWithTag( + [FromRoute] string org, + [FromRoute] string app, + [FromQuery] string currentPage, + [FromQuery] string layoutSetId, + [FromQuery] string dataTypeId + ) + { + return Ok(); + } +} diff --git a/backend/src/Designer/Controllers/PreviewController.cs b/backend/src/Designer/Controllers/PreviewController.cs index 5a3c74245d3..8baf107aae1 100644 --- a/backend/src/Designer/Controllers/PreviewController.cs +++ b/backend/src/Designer/Controllers/PreviewController.cs @@ -142,12 +142,6 @@ public async Task> ApplicationMetadata(string string appNugetVersionString = _appDevelopmentService.GetAppLibVersion(AltinnRepoEditingContext.FromOrgRepoDeveloper(org, app, developer)).ToString(); // This property is populated at runtime by the apps, so we need to mock it here applicationMetadata.AltinnNugetVersion = GetMockedAltinnNugetBuildFromVersion(appNugetVersionString); - if (altinnAppGitRepository.AppUsesLayoutSets()) - { - LayoutSets layoutSets = await altinnAppGitRepository.GetLayoutSetsFile(cancellationToken); - applicationMetadata = SetMockDataTypeIfMissing(applicationMetadata, layoutSets); - } - applicationMetadata = SetMockedPartyTypesAllowedAsAllFalse(applicationMetadata); return Ok(applicationMetadata); } @@ -192,8 +186,7 @@ public async Task> LayoutSets(string org, string app, C string developer = AuthenticationHelper.GetDeveloperUserName(_httpContextAccessor.HttpContext); AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, app, developer); LayoutSets layoutSets = await altinnAppGitRepository.GetLayoutSetsFile(cancellationToken); - LayoutSets layoutSetsWithMockedDataTypes = AddDataTypesToReturnedLayoutSetsIfMissing(layoutSets); - return Ok(layoutSetsWithMockedDataTypes); + return Ok(layoutSets); } catch (NotFoundException) { @@ -739,33 +732,6 @@ private static ApplicationMetadata SetMockedPartyTypesAllowedAsAllFalse(Applicat return applicationMetadata; } - private static ApplicationMetadata SetMockDataTypeIfMissing(ApplicationMetadata applicationMetadata, LayoutSets layoutSets) - { - LayoutSets layoutSetsWithMockedDataTypesIfMissing = AddDataTypesToReturnedLayoutSetsIfMissing(layoutSets); - layoutSetsWithMockedDataTypesIfMissing.Sets.ForEach(set => - { - if (set.Tasks?[0] == Constants.General.CustomReceiptId) - { - return; - } - - if (!applicationMetadata.DataTypes.Any(dataType => dataType.Id == set.DataType)) - { - applicationMetadata.DataTypes.Add(new DataType() - { - Id = set.DataType, - AppLogic = new ApplicationLogic() - { - ClassRef = $"Altinn.App.Models.model.{set.DataType}" - }, - TaskId = set.Tasks?[0] - }); - } - }); - - return applicationMetadata; - } - private bool IsValidSemVerVersion(string[] versionParts) { return versionParts.Length >= 3 && Convert.ToInt32(versionParts[0]) >= 8; @@ -780,17 +746,5 @@ private int GetPreviewVersion(string[] versionParts) { return Convert.ToInt32(versionParts[3]); } - - private static LayoutSets AddDataTypesToReturnedLayoutSetsIfMissing(LayoutSets layoutSets) - { - int counter = 0; - foreach (var set in layoutSets.Sets.Where(set => string.IsNullOrEmpty(set.DataType))) - { - string mockDataTypeId = $"{PreviewService.MockDataModelIdPrefix}-{counter++}"; - set.DataType = mockDataTypeId; - } - - return layoutSets; - } } } diff --git a/backend/src/Designer/Controllers/ResourceAdminController.cs b/backend/src/Designer/Controllers/ResourceAdminController.cs index a6ac0fc4c38..ad3f565ba20 100644 --- a/backend/src/Designer/Controllers/ResourceAdminController.cs +++ b/backend/src/Designer/Controllers/ResourceAdminController.cs @@ -610,7 +610,7 @@ private async Task GetOrgList() return orgList; } - private static bool IsServiceOwner(ServiceResource? resource, string loggedInOrg) + private static bool IsServiceOwner(ServiceResource resource, string loggedInOrg) { if (resource?.HasCompetentAuthority == null) { diff --git a/backend/src/Designer/Helpers/AppFrontendVersionHelper.cs b/backend/src/Designer/Helpers/AppFrontendVersionHelper.cs index 5e4efba4cfd..71b681054d0 100644 --- a/backend/src/Designer/Helpers/AppFrontendVersionHelper.cs +++ b/backend/src/Designer/Helpers/AppFrontendVersionHelper.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Text.RegularExpressions; +using HtmlAgilityPack; namespace Altinn.Studio.Designer.Helpers; @@ -9,13 +10,29 @@ public static class AppFrontendVersionHelper private const string SemanticVersionRegex = @"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"; private const string ExtendedVersion = @"^(\d+)(\.\d+)?$"; + // allow overwriting altinn-app-frontend version with a meta tag + // i.e. + private static string getMetaTagVersion(HtmlDocument htmlDoc) + { + HtmlNode metaTag = htmlDoc.DocumentNode.SelectSingleNode("//meta[@data-altinn-app-frontend-version]"); + return metaTag?.GetAttributeValue("data-altinn-app-frontend-version", null); + } + public static bool TryGetFrontendVersionFromIndexFile(string filePath, out string version) { version = null; string fileContent = File.ReadAllText(filePath); - var htmlDoc = new HtmlAgilityPack.HtmlDocument(); + var htmlDoc = new HtmlDocument(); htmlDoc.LoadHtml(fileContent); + + string metaTagVersion = getMetaTagVersion(htmlDoc); + if (metaTagVersion != null) + { + version = metaTagVersion; + return true; + } + var scriptTag = htmlDoc.DocumentNode.SelectSingleNode( "//script[contains(@src, 'https://altinncdn.no/toolkits/altinn-app-frontend') and contains(@src, 'altinn-app-frontend.js')]"); diff --git a/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs b/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs index b0c3c5ed527..db8ba9527dd 100644 --- a/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs +++ b/backend/src/Designer/Infrastructure/GitRepository/AltinnAppGitRepository.cs @@ -15,6 +15,7 @@ using Altinn.Studio.Designer.Helpers; using Altinn.Studio.Designer.Models; using Altinn.Studio.Designer.Models.App; +using Altinn.Studio.Designer.Models.Dto; using Altinn.Studio.Designer.TypedHttpClients.Exceptions; using LibGit2Sharp; using JsonSerializer = System.Text.Json.JsonSerializer; @@ -504,6 +505,94 @@ public async Task GetLayoutSettingsAndCreateNewIfNotFound(string layou return layoutSettings; } + /// + /// Finds all in a given layout. + /// + /// The layout. + /// A list of occurrences to append any optionListIdRefs in the layout to. + /// The layoutSetName the layout belongs to. + /// The name of the given layout. + /// A list of . + public List FindOptionListReferencesInLayout(JsonNode layout, List refToOptionListSpecifiers, string layoutSetName, string layoutName) + { + var optionListIds = GetOptionsListIds(); + var layoutArray = layout["data"]?["layout"] as JsonArray; + if (layoutArray == null) + { + return refToOptionListSpecifiers; + } + + foreach (var item in layoutArray) + { + string optionListId = item["optionsId"]?.ToString(); + + if (!optionListIds.Contains(optionListId)) + { + continue; + } + + if (!String.IsNullOrEmpty(optionListId)) + { + if (OptionListIdAlreadyOccurred(refToOptionListSpecifiers, optionListId, out var existingRef)) + { + if (OptionListIdAlreadyOccurredInLayout(existingRef, layoutSetName, layoutName, out var existingSource)) + { + existingSource.ComponentIds.Add(item["id"]?.ToString()); + } + else + { + AddNewOptionListIdSource(existingRef, layoutSetName, layoutName, item["id"]?.ToString()); + } + } + else + { + AddNewRefToOptionListSpecifier(refToOptionListSpecifiers, optionListId, layoutSetName, layoutName, item["id"]?.ToString()); + } + } + } + return refToOptionListSpecifiers; + } + + private bool OptionListIdAlreadyOccurred(List refToOptionListSpecifiers, string optionListId, out RefToOptionListSpecifier existingRef) + { + existingRef = refToOptionListSpecifiers.FirstOrDefault(refToOptionList => refToOptionList.OptionListId == optionListId); + return existingRef != null; + } + + private bool OptionListIdAlreadyOccurredInLayout(RefToOptionListSpecifier refToOptionListSpecifier, string layoutSetName, string layoutName, out OptionListIdSource existingSource) + { + existingSource = refToOptionListSpecifier.OptionListIdSources + .FirstOrDefault(optionListIdSource => optionListIdSource.LayoutSetId == layoutSetName && optionListIdSource.LayoutName == layoutName); + return existingSource != null; + } + + private void AddNewRefToOptionListSpecifier(List refToOptionListSpecifiers, string optionListId, string layoutSetName, string layoutName, string componentId) + { + refToOptionListSpecifiers.Add(new() + { + OptionListId = optionListId, + OptionListIdSources = + [ + new OptionListIdSource + { + LayoutSetId = layoutSetName, + LayoutName = layoutName, + ComponentIds = [componentId] + } + ] + }); + } + + private void AddNewOptionListIdSource(RefToOptionListSpecifier refToOptionListSpecifier, string layoutSetName, string layoutName, string componentId) + { + refToOptionListSpecifier.OptionListIdSources.Add(new OptionListIdSource + { + LayoutSetId = layoutSetName, + LayoutName = layoutName, + ComponentIds = [componentId] + }); + } + private async Task CreateLayoutSettings(string layoutSetName) { string layoutSetPath = GetPathToLayoutSet(layoutSetName); diff --git a/backend/src/Designer/Infrastructure/GitRepository/LocalGitCloneIoException.cs b/backend/src/Designer/Infrastructure/GitRepository/LocalGitCloneIoException.cs index 4018eb27933..485fff54d02 100644 --- a/backend/src/Designer/Infrastructure/GitRepository/LocalGitCloneIoException.cs +++ b/backend/src/Designer/Infrastructure/GitRepository/LocalGitCloneIoException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Altinn.Studio.Designer.Infrastructure.GitRepository; @@ -23,10 +22,5 @@ public LocalGitCloneIoException(string message) : base(message) public LocalGitCloneIoException(string message, Exception innerException) : base(message, innerException) { } - - /// - protected LocalGitCloneIoException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } diff --git a/backend/src/Designer/Infrastructure/ServiceRegistration.cs b/backend/src/Designer/Infrastructure/ServiceRegistration.cs index 8aba45e2e11..5d3b0607d8b 100644 --- a/backend/src/Designer/Infrastructure/ServiceRegistration.cs +++ b/backend/src/Designer/Infrastructure/ServiceRegistration.cs @@ -73,6 +73,7 @@ public static IServiceCollection RegisterServiceImplementations(this IServiceCol services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.RegisterDatamodeling(configuration); diff --git a/backend/src/Designer/Models/AccessList.cs b/backend/src/Designer/Models/AccessList.cs index 0718d478824..6169364507d 100644 --- a/backend/src/Designer/Models/AccessList.cs +++ b/backend/src/Designer/Models/AccessList.cs @@ -8,8 +8,8 @@ namespace Altinn.Studio.Designer.Models public class AccessList : HeaderEtag { - public string Identifier { get; set; } - public string Name { get; set; } + public required string Identifier { get; set; } + public required string Name { get; set; } public string? Description { get; set; } public IEnumerable? ResourceConnections { get; set; } } diff --git a/backend/src/Designer/Models/AccessListMember.cs b/backend/src/Designer/Models/AccessListMember.cs index 0898f1fcb8c..99169b1d372 100644 --- a/backend/src/Designer/Models/AccessListMember.cs +++ b/backend/src/Designer/Models/AccessListMember.cs @@ -1,9 +1,10 @@ -namespace Altinn.Studio.Designer.Models +#nullable enable +namespace Altinn.Studio.Designer.Models { public class AccessListMember { - public string OrgNr { get; set; } - public string OrgName { get; set; } - public bool IsSubParty { get; set; } + public required string OrgNr { get; set; } + public required string OrgName { get; set; } + public bool? IsSubParty { get; set; } } } diff --git a/backend/src/Designer/Models/BrregPartyResultSet.cs b/backend/src/Designer/Models/BrregPartyResultSet.cs index 462dff0f20d..38fcd6eae33 100644 --- a/backend/src/Designer/Models/BrregPartyResultSet.cs +++ b/backend/src/Designer/Models/BrregPartyResultSet.cs @@ -7,7 +7,7 @@ namespace Altinn.Studio.Designer.Models public class BrregParty { [JsonPropertyName("organisasjonsnummer")] - public string Organisasjonsnummer { get; set; } + public required string Organisasjonsnummer { get; set; } [JsonPropertyName("navn")] public string? Navn { get; set; } } diff --git a/backend/src/Designer/Models/CreateAccessListModel.cs b/backend/src/Designer/Models/CreateAccessListModel.cs index 23629565f21..9e16d9ec020 100644 --- a/backend/src/Designer/Models/CreateAccessListModel.cs +++ b/backend/src/Designer/Models/CreateAccessListModel.cs @@ -3,7 +3,7 @@ namespace Altinn.Studio.Designer.Models { public class CreateAccessListModel { - public string Name { get; set; } + public required string Name { get; set; } public string? Description { get; set; } } } diff --git a/backend/src/Designer/Models/Dto/AccessListInfoDtoPaginated.cs b/backend/src/Designer/Models/Dto/AccessListInfoDtoPaginated.cs index ae755d69509..574102977eb 100644 --- a/backend/src/Designer/Models/Dto/AccessListInfoDtoPaginated.cs +++ b/backend/src/Designer/Models/Dto/AccessListInfoDtoPaginated.cs @@ -5,7 +5,7 @@ namespace Altinn.Studio.Designer.Models.Dto { public class AccessListInfoDtoPaginated { - public IEnumerable Data { get; set; } + public required IEnumerable Data { get; set; } public AccessListPaging? Links { get; set; } } } diff --git a/backend/src/Designer/Models/Dto/AccessListMember.cs b/backend/src/Designer/Models/Dto/AccessListMember.cs index 3ab21eb4a52..752a4d485eb 100644 --- a/backend/src/Designer/Models/Dto/AccessListMember.cs +++ b/backend/src/Designer/Models/Dto/AccessListMember.cs @@ -1,34 +1,35 @@ -using System.Collections.Generic; +#nullable enable +using System.Collections.Generic; using System.Text.Json.Serialization; namespace Altinn.Studio.Designer.Models.Dto { public class AccessListMembersDto { - public IList Data { get; set; } + public required IList Data { get; set; } public AccessListPaging? Links { get; set; } } public class AccessListMemberDataDto { - public string Id { get; set; } - public string Since { get; set; } - public AccessListMemberDtoIdentifier Identifiers { get; set; } + public required string Id { get; set; } + public required string Since { get; set; } + public required AccessListMemberDtoIdentifier Identifiers { get; set; } } public class AccessListMemberDtoIdentifier { [JsonPropertyName("urn:altinn:party:uuid")] - public string PartyUuid { get; set; } + public required string PartyUuid { get; set; } [JsonPropertyName("urn:altinn:party:id")] - public int PartyId { get; set; } + public required int PartyId { get; set; } [JsonPropertyName("urn:altinn:organization:identifier-no")] - public string OrganizationNumber { get; set; } + public required string OrganizationNumber { get; set; } } public class UpdateAccessListMemberDto { - public IList Data { get; set; } + public required IList Data { get; set; } } diff --git a/backend/src/Designer/Models/Dto/OptionListReferences.cs b/backend/src/Designer/Models/Dto/OptionListReferences.cs new file mode 100644 index 00000000000..b0224f0ded4 --- /dev/null +++ b/backend/src/Designer/Models/Dto/OptionListReferences.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Altinn.Studio.Designer.Models.Dto; + +public class RefToOptionListSpecifier +{ + [JsonPropertyName("optionListId")] + public string OptionListId { get; set; } + [JsonPropertyName("optionListIdSources")] + public List OptionListIdSources { get; set; } +} + +public class OptionListIdSource +{ + [JsonPropertyName("layoutSetId")] + public string LayoutSetId { get; set; } + [JsonPropertyName("layoutName")] + public string LayoutName { get; set; } + [JsonPropertyName("componentIds")] + public List ComponentIds { get; set; } +} diff --git a/backend/src/Designer/Models/LayoutSets.cs b/backend/src/Designer/Models/LayoutSets.cs index 9efb83fba5a..0eb4e18019b 100644 --- a/backend/src/Designer/Models/LayoutSets.cs +++ b/backend/src/Designer/Models/LayoutSets.cs @@ -1,40 +1,36 @@ #nullable enable using System.Collections.Generic; using System.Text.Json.Serialization; -using JetBrains.Annotations; namespace Altinn.Studio.Designer.Models; public class LayoutSets : Altinn.App.Core.Models.LayoutSets { [JsonPropertyName("$schema")] - public string Schema { get; set; } + public required string Schema { get; set; } [JsonPropertyName("sets")] - public new List Sets { get; set; } + public new required List Sets { get; set; } [JsonExtensionData] - public IDictionary UnknownProperties { get; set; } + public IDictionary? UnknownProperties { get; set; } } public class LayoutSetConfig { [JsonPropertyName("id")] - public string Id { get; set; } + public required string Id { get; set; } - [JsonPropertyName("dataType")] - [CanBeNull] - public string DataType { get; set; } + [JsonPropertyName("dataType")] public string? DataType { get; set; } [JsonPropertyName("tasks")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [CanBeNull] - public List Tasks { get; set; } + public List? Tasks { get; set; } [JsonPropertyName("type")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string Type { get; set; } + public string? Type { get; set; } [JsonExtensionData] - public IDictionary UnknownProperties { get; set; } + public IDictionary? UnknownProperties { get; set; } } diff --git a/backend/src/Designer/Models/ListviewServiceResource.cs b/backend/src/Designer/Models/ListviewServiceResource.cs index 944cbde913c..5a69223f079 100644 --- a/backend/src/Designer/Models/ListviewServiceResource.cs +++ b/backend/src/Designer/Models/ListviewServiceResource.cs @@ -22,7 +22,7 @@ public class ListviewServiceResource public string? CreatedBy { get; set; } /// - /// Timestamp for when the resourcefile was last changed + /// Timestamp for when the resource file was last changed /// public DateTime? LastChanged { get; set; } @@ -34,6 +34,6 @@ public class ListviewServiceResource /// /// A list of environments the resource is deployed in /// - public IList Environments { get; set; } + public IList? Environments { get; set; } } } diff --git a/backend/src/Designer/Models/PagedAccessListResponse.cs b/backend/src/Designer/Models/PagedAccessListResponse.cs index b1fdfd708af..39b20befd77 100644 --- a/backend/src/Designer/Models/PagedAccessListResponse.cs +++ b/backend/src/Designer/Models/PagedAccessListResponse.cs @@ -5,7 +5,7 @@ namespace Altinn.Studio.Designer.Models { public class PagedAccessListResponse { - public IEnumerable Data { get; set; } + public IEnumerable? Data { get; set; } public string? NextPage { get; set; } } } diff --git a/backend/src/Designer/Models/Preview/DataPatchRequest.cs b/backend/src/Designer/Models/Preview/DataPatchRequest.cs index 033279247b1..d2472d6c277 100644 --- a/backend/src/Designer/Models/Preview/DataPatchRequest.cs +++ b/backend/src/Designer/Models/Preview/DataPatchRequest.cs @@ -1,3 +1,4 @@ +#nullable enable using System.Collections.Generic; using System.Text.Json.Serialization; using Json.Patch; diff --git a/backend/src/Designer/Models/Preview/DataPatchRequestMultiple.cs b/backend/src/Designer/Models/Preview/DataPatchRequestMultiple.cs new file mode 100644 index 00000000000..bdc0da5eb08 --- /dev/null +++ b/backend/src/Designer/Models/Preview/DataPatchRequestMultiple.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Json.Patch; + +namespace Altinn.Studio.Designer.Models.Preview; + +/// +/// Represents the request to patch data on the . +/// This version allows multiple patches to be applied by the same request. +/// +public class DataPatchRequestMultiple +{ + /// + /// List of patches to apply. + /// + [JsonPropertyName("patches")] + public required List Patches { get; init; } + + /// + /// Item class for the list of Patches + /// + /// The guid of the data element to patch + /// The patch to apply + public record PatchListItem + ( + [property: JsonPropertyName("dataElementId")] Guid DataElementId, + [property: JsonPropertyName("patch")] JsonPatch Patch + ); + + public required List? IgnoredValidators { get; init; } +} diff --git a/backend/src/Designer/Models/Preview/DataPatchResponseMultiple.cs b/backend/src/Designer/Models/Preview/DataPatchResponseMultiple.cs new file mode 100644 index 00000000000..b793b2589d8 --- /dev/null +++ b/backend/src/Designer/Models/Preview/DataPatchResponseMultiple.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Altinn.Platform.Storage.Interface.Models; + +namespace Altinn.Studio.Designer.Models.Preview; + +/// +/// Represents the response from a data patch operation on the . +/// +public class DataPatchResponseMultiple +{ + /// + /// The validation issues that were found during the patch operation. + /// + [JsonPropertyName("validationIssues")] + public required List ValidationIssues { get; init; } + + /// + /// The current data in all data models updated by the patch operation. + /// + [JsonPropertyName("newDataModels")] + public required List NewDataModels { get; init; } + + /// + /// The instance with updated dataElement list. + /// + [JsonPropertyName("instance")] + public required Instance Instance { get; init; } +} + +/// +/// Pair of Guid and data object. +/// +/// The guid of the DataElement +/// The form data of the data element +public record DataModelPairResponse( + [property: JsonPropertyName("dataElementId")] Guid DataElementId, + [property: JsonPropertyName("data")] object Data +); diff --git a/backend/src/Designer/Models/Preview/DataPostResponse.cs b/backend/src/Designer/Models/Preview/DataPostResponse.cs new file mode 100644 index 00000000000..d4886d7212b --- /dev/null +++ b/backend/src/Designer/Models/Preview/DataPostResponse.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text.Json.Serialization; +using Altinn.Platform.Storage.Interface.Models; +using Microsoft.AspNetCore.Mvc; + +namespace Altinn.Studio.Designer.Models.Preview; + +/// +/// Response object for POST and DELETE to /org/app/instances/{instanceOwnerPartyId:int}/{instanceGuid:guid}/data/{dataType} +/// +public class DataPostResponse +{ + /// + /// The Id of the created data element + /// + [JsonPropertyName("newDataElementId")] + public required Guid NewDataElementId { get; init; } + + /// + /// The instance with updated data + /// + [JsonPropertyName("instance")] + public required Instance Instance { get; init; } + + /// + /// List of validation issues that reported to have relevant changes after a new data element was added + /// + [JsonPropertyName("validationIssues")] + public required List ValidationIssues { get; init; } + + /// + /// List of updated DataModels caused by dataProcessing + /// + [JsonPropertyName("newDataModels")] + public required List NewDataModels { get; init; } +} + +/// +/// Extension of ProblemDetails to include Validation issues from the file upload. +/// +public class DataPostErrorResponse : ProblemDetails +{ + /// + /// Constructor for simple initialization from upload validation issues. + /// + public DataPostErrorResponse(string detail, List validationIssues) + { + Title = "File validation failed"; + Detail = detail; + Status = (int)HttpStatusCode.BadRequest; + UploadValidationIssues = validationIssues; + } + + /// + /// List of the validators that reported to have relevant changes after a new data element was added + /// + [JsonPropertyName("uploadValidationIssues")] + public List UploadValidationIssues { get; } +} diff --git a/backend/src/Designer/Models/Preview/ValidationIssueWithSource.cs b/backend/src/Designer/Models/Preview/ValidationIssueWithSource.cs new file mode 100644 index 00000000000..f8996115d7b --- /dev/null +++ b/backend/src/Designer/Models/Preview/ValidationIssueWithSource.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Altinn.App.Core.Models.Validation; + +namespace Altinn.Studio.Designer.Models.Preview; + +/// +/// Represents a detailed message from validation. +/// +public class ValidationIssueWithSource +{ + /// + /// Converter function to create a from a and adding a source. + /// + public static ValidationIssueWithSource FromIssue(ValidationIssue issue, string source, bool noIncrementalUpdates) + { + return new ValidationIssueWithSource + { + Severity = issue.Severity, + DataElementId = issue.DataElementId, + Field = issue.Field, + Code = issue.Code, + Description = issue.Description, + Source = source, + NoIncrementalUpdates = noIncrementalUpdates, + CustomTextKey = issue.CustomTextKey, + CustomTextParams = issue.CustomTextParams, + }; + } + + /// + /// The seriousness of the identified issue. + /// + /// + /// This property is serialized in json as a number + /// 1: Error (something needs to be fixed) + /// 2: Warning (does not prevent submission) + /// 3: Information (hint shown to the user) + /// 4: Fixed (obsolete, only used for v3 of frontend) + /// 5: Success (Inform the user that something was completed with success) + /// + [JsonPropertyName("severity")] + [JsonConverter(typeof(JsonNumberEnumConverter))] + public required ValidationIssueSeverity Severity { get; set; } + + /// + /// The unique id of the data element of a given instance with the identified issue. + /// + [JsonPropertyName("dataElementId")] + public string? DataElementId { get; set; } + + /// + /// A reference to a property the issue is about. + /// + [JsonPropertyName("field")] + public string? Field { get; set; } + + /// + /// A system readable identification of the type of issue. + /// Eg: + /// + [JsonPropertyName("code")] + public required string? Code { get; set; } + + /// + /// A human readable description of the issue. + /// + [JsonPropertyName("description")] + public required string? Description { get; set; } + + /// + /// The short name of the class that crated the message (set automatically after return of list) + /// + [JsonPropertyName("source")] + public required string Source { get; set; } + + /// + /// Weather the issue is from a validator that correctly implements . + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonPropertyName("noIncrementalUpdates")] + public bool NoIncrementalUpdates { get; set; } + + /// + /// The custom text key to use for the localized text in the frontend. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("customTextKey")] + public string? CustomTextKey { get; set; } + + /// + /// might include some parameters (typically the field value, or some derived value) + /// that should be included in error message. + /// + /// + /// The localized text for the key might be "Date must be between {0} and {1}" + /// and the param will provide the dynamical range of allowable dates (eg teh reporting period) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("customTextParams")] + public List? CustomTextParams { get; set; } +} + +/// +/// API responses that returns validation issues grouped by source, typically return a list of these. +/// +/// The for the Validator that created theese issues +/// List of issues +public record ValidationSourcePair( + [property: JsonPropertyName("source")] string Source, + [property: JsonPropertyName("issues")] List Issues +); diff --git a/backend/src/Designer/Models/ServiceResource.cs b/backend/src/Designer/Models/ServiceResource.cs index 3f76576f410..407eeee1fc6 100644 --- a/backend/src/Designer/Models/ServiceResource.cs +++ b/backend/src/Designer/Models/ServiceResource.cs @@ -6,7 +6,7 @@ namespace Altinn.Studio.Designer.Models { /// - /// Model describing a complete resource from the resrouce registry + /// Model describing a complete resource from the resource registry /// public class ServiceResource { @@ -54,7 +54,7 @@ public class ServiceResource /// /// List of possible contact points /// - public List ContactPoints { get; set; } + public List? ContactPoints { get; set; } /// /// Linkes to the outcome of a public service diff --git a/backend/src/Designer/Program.cs b/backend/src/Designer/Program.cs index bae7c0a7425..cd668a551f4 100644 --- a/backend/src/Designer/Program.cs +++ b/backend/src/Designer/Program.cs @@ -351,13 +351,4 @@ void CreateDirectory(IConfiguration configuration) } } -static string GetXmlCommentsPathForControllers() -{ - // locate the xml file being generated by .NET - string xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.XML"; - string xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); - - return xmlPath; -} - public partial class Program { } diff --git a/backend/src/Designer/RepositoryClient/Model/ContentsResponse.cs b/backend/src/Designer/RepositoryClient/Model/ContentsResponse.cs index 7117cf335d0..c696f26bf97 100644 --- a/backend/src/Designer/RepositoryClient/Model/ContentsResponse.cs +++ b/backend/src/Designer/RepositoryClient/Model/ContentsResponse.cs @@ -8,48 +8,48 @@ namespace Altinn.Studio.Designer.RepositoryClient.Model public class ContentsResponse { [JsonProperty("_links")] - public FileLinksResponse Links { get; set; } + public required FileLinksResponse Links { get; set; } [JsonProperty("content")] - public string? Content { get; set; } + public required string? Content { get; set; } [JsonProperty("download_url")] - public string DownloadUrl { get; set; } + public required string DownloadUrl { get; set; } [JsonProperty("encoding")] - public string? Encoding { get; set; } + public required string? Encoding { get; set; } [JsonProperty("git_url")] - public string GitUrl { get; set; } + public required string GitUrl { get; set; } [JsonProperty("html_url")] - public string HtmlUrl { get; set; } + public required string HtmlUrl { get; set; } [JsonProperty("last_commit_sha")] - public string LastCommitSha { get; set; } + public required string LastCommitSha { get; set; } [JsonProperty("name")] - public string Name { get; set; } + public required string Name { get; set; } [JsonProperty("path")] - public string Path { get; set; } + public required string Path { get; set; } [JsonProperty("sha")] - public string Sha { get; set; } + public required string Sha { get; set; } [JsonProperty("size")] - public int Size { get; set; } + public required int Size { get; set; } [JsonProperty("submodule_git_url")] - public string SubmoduleGitUrl { get; set; } + public required string SubmoduleGitUrl { get; set; } [JsonProperty("target")] public string? Target { get; set; } [JsonProperty("type")] - public string Type { get; set; } + public required string Type { get; set; } [JsonProperty("url")] - public string Url { get; set; } + public required string Url { get; set; } } } diff --git a/backend/src/Designer/Services/Implementation/MaskinPortenClientDefinition.cs b/backend/src/Designer/Services/Implementation/MaskinPortenClientDefinition.cs index 6431d810b79..47523483422 100644 --- a/backend/src/Designer/Services/Implementation/MaskinPortenClientDefinition.cs +++ b/backend/src/Designer/Services/Implementation/MaskinPortenClientDefinition.cs @@ -19,14 +19,14 @@ public MaskinPortenClientDefinition(IOptions clientS ClientSettings = clientSettings.Value; } - public async Task GetClientSecrets() + public Task GetClientSecrets() { ClientSecrets clientSecrets = new ClientSecrets(); byte[] base64EncodedBytes = Convert.FromBase64String(ClientSettings.EncodedJwk); string jwkjson = Encoding.UTF8.GetString(base64EncodedBytes); clientSecrets.ClientKey = new Microsoft.IdentityModel.Tokens.JsonWebKey(jwkjson); - return clientSecrets; + return Task.FromResult(clientSecrets); } } } diff --git a/backend/src/Designer/Services/Implementation/OptionsService.cs b/backend/src/Designer/Services/Implementation/OptionsService.cs index 5dc4e7a26a4..591daad2a30 100644 --- a/backend/src/Designer/Services/Implementation/OptionsService.cs +++ b/backend/src/Designer/Services/Implementation/OptionsService.cs @@ -6,6 +6,7 @@ using Altinn.Studio.Designer.Exceptions.Options; using Altinn.Studio.Designer.Infrastructure.GitRepository; using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Models.Dto; using Altinn.Studio.Designer.Services.Interfaces; using LibGit2Sharp; using Microsoft.AspNetCore.Http; @@ -62,10 +63,33 @@ public async Task> GetOptionsList(string org, string repo, string d throw new InvalidOptionsFormatException($"One or more of the options have an invalid format in option list: {optionsListId}."); } - return optionsList; } + /// + public async Task> GetAllOptionListReferences(AltinnRepoEditingContext altinnRepoEditingContext, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + AltinnAppGitRepository altinnAppGitRepository = + _altinnGitRepositoryFactory.GetAltinnAppGitRepository(altinnRepoEditingContext.Org, + altinnRepoEditingContext.Repo, altinnRepoEditingContext.Developer); + + List optionsListReferences = new List(); + + string[] layoutSetNames = altinnAppGitRepository.GetLayoutSetNames(); + foreach (string layoutSetName in layoutSetNames) + { + string[] layoutNames = altinnAppGitRepository.GetLayoutNames(layoutSetName); + foreach (var layoutName in layoutNames) + { + var layout = await altinnAppGitRepository.GetLayout(layoutSetName, layoutName, cancellationToken); + optionsListReferences = altinnAppGitRepository.FindOptionListReferencesInLayout(layout, optionsListReferences, layoutSetName, layoutName); + } + } + + return optionsListReferences; + } + private void ValidateOption(Option option) { var validationContext = new ValidationContext(option); diff --git a/backend/src/Designer/Services/Implementation/Preview/DataService.cs b/backend/src/Designer/Services/Implementation/Preview/DataService.cs index 75a00b64155..5f6ebca7fcd 100644 --- a/backend/src/Designer/Services/Implementation/Preview/DataService.cs +++ b/backend/src/Designer/Services/Implementation/Preview/DataService.cs @@ -10,7 +10,7 @@ namespace Altinn.Studio.Designer.Services.Implementation.Preview; public class DataService( IDistributedCache distributedCache - ) : IDataService +) : IDataService { readonly DistributedCacheEntryOptions _cacheOptions = new() { diff --git a/backend/src/Designer/Services/Implementation/Preview/InstanceService.cs b/backend/src/Designer/Services/Implementation/Preview/InstanceService.cs new file mode 100644 index 00000000000..adafb949868 --- /dev/null +++ b/backend/src/Designer/Services/Implementation/Preview/InstanceService.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Altinn.Platform.Storage.Interface.Models; +using Altinn.Studio.Designer.Services.Interfaces.Preview; +using Microsoft.Extensions.Caching.Distributed; + +namespace Altinn.Studio.Designer.Services.Implementation.Preview; + +public class InstanceService( + IDistributedCache distributedCache, + IDataService dataService +) : IInstanceService +{ + readonly DistributedCacheEntryOptions _cacheOptions = new() + { + SlidingExpiration = TimeSpan.FromMinutes(30), + }; + + public Instance CreateInstance(string org, string app, int partyId, string taskId, List dataTypes) + { + Guid instanceGuid = Guid.NewGuid(); + Instance instance = new() + { + InstanceOwner = new InstanceOwner { PartyId = partyId.ToString() }, + Id = $"{instanceGuid}", + AppId = app, + Data = [], + Org = org, + Process = new ProcessState + { + CurrentTask = new ProcessElementInfo + { + AltinnTaskType = "data", + ElementId = taskId + } + } + }; + dataTypes.ForEach(dataType => + { + if (dataType.AppLogic?.AutoCreate == true) + { + DataElement dataElement = dataService.CreateDataElement(partyId, instanceGuid, dataType.Id); + instance.Data.Add(dataElement); + } + }); + distributedCache.SetString(instance.Id, JsonSerializer.Serialize(instance), _cacheOptions); + return instance; + } + + public Instance GetInstance(Guid instanceGuid) + { + string instanceJson = distributedCache.GetString(instanceGuid.ToString()); + Instance instanceElement = JsonSerializer.Deserialize(instanceJson); + return instanceElement; + } + + public Instance AddDataElement(Guid instanceGuid, DataElement dataElement) + { + string instanceJson = distributedCache.GetString(instanceGuid.ToString()); + Instance instanceElement = JsonSerializer.Deserialize(instanceJson); + instanceElement.Data.Add(dataElement); + distributedCache.SetString(instanceGuid.ToString(), JsonSerializer.Serialize(instanceElement), _cacheOptions); + return instanceElement; + } + + public Instance RemoveDataElement(Guid instanceGuid, Guid dataElementGuid) + { + string instanceJson = distributedCache.GetString(instanceGuid.ToString()); + Instance instanceElement = JsonSerializer.Deserialize(instanceJson); + instanceElement.Data.RemoveAll(dataElement => dataElement.Id == dataElementGuid.ToString()); + distributedCache.SetString(instanceGuid.ToString(), JsonSerializer.Serialize(instanceElement), _cacheOptions); + return instanceElement; + } +} diff --git a/backend/src/Designer/Services/Implementation/PreviewService.cs b/backend/src/Designer/Services/Implementation/PreviewService.cs index aa88aec4e41..a8e07f8cd38 100644 --- a/backend/src/Designer/Services/Implementation/PreviewService.cs +++ b/backend/src/Designer/Services/Implementation/PreviewService.cs @@ -6,6 +6,7 @@ using Altinn.Platform.Storage.Interface.Models; using Altinn.Studio.Designer.Infrastructure.GitRepository; using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Models.App; using Altinn.Studio.Designer.Services.Interfaces; using LibGit2Sharp; @@ -14,21 +15,17 @@ namespace Altinn.Studio.Designer.Services.Implementation; /// /// Service for handling a mocked instance object for preview mode /// -public class PreviewService : IPreviewService +/// +/// Constructor +/// +/// IAltinnGitRepository +/// +public class PreviewService(IAltinnGitRepositoryFactory altinnGitRepositoryFactory) : IPreviewService { - private readonly IAltinnGitRepositoryFactory _altinnGitRepositoryFactory; + private readonly IAltinnGitRepositoryFactory _altinnGitRepositoryFactory = altinnGitRepositoryFactory; public const string MockDataModelIdPrefix = "MockDataModel"; public const string MockDataTaskId = "test-datatask-id"; - /// - /// Constructor - /// - /// IAltinnGitRepository - public PreviewService(IAltinnGitRepositoryFactory altinnGitRepositoryFactory) - { - _altinnGitRepositoryFactory = altinnGitRepositoryFactory; - } - /// public async Task GetMockInstance(string org, string app, string developer, int? instanceOwnerPartyId, string layoutSetName, CancellationToken cancellationToken = default) { @@ -133,27 +130,12 @@ private async Task> GetDataTypesForInstance(string org, string AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, app, developer); DataType dataType = await GetDataTypeForLayoutSetName(org, app, developer, layoutSetName); string dataTypeForDataElement = shouldProcessActAsReceipt ? await GetDataTypeForCustomReceipt(altinnAppGitRepository) : dataType?.Id; - if (dataTypeForDataElement is null && altinnAppGitRepository.AppUsesLayoutSets()) + ApplicationMetadata applicationMetadata = await altinnAppGitRepository.GetApplicationMetadata(); + List dataElements = applicationMetadata.DataTypes.Select(dataType => new DataElement { - int counter = 0; - LayoutSets layoutSets = await altinnAppGitRepository.GetLayoutSetsFile(); - return layoutSets.Sets - .Where(set => string.IsNullOrEmpty(set.DataType)) - .Select(_ => new DataElement - { - DataType = $"{MockDataModelIdPrefix}-{counter++}", - Id = MockDataTaskId - }) - .ToList(); - } - return [ - // All data types attached to the current task in the process model should be added here - new DataElement - { - DataType = dataTypeForDataElement, - Id = MockDataTaskId - } - ]; + Id = (dataTypeForDataElement != dataType.Id) ? dataType.Id : MockDataTaskId + }).ToList(); + return dataElements; } private async Task GetDataTypeForCustomReceipt(AltinnAppGitRepository altinnAppGitRepository) diff --git a/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs b/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs index b35f8a8ccdb..3c01e869a16 100644 --- a/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs +++ b/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs @@ -95,7 +95,7 @@ public async Task GetTaskTypeFromProcessDefinition(AltinnRepoEditingCont XmlSerializer serializer = new XmlSerializer(typeof(Definitions)); Definitions? definitions = (Definitions?)serializer.Deserialize(processDefinitionStream); LayoutSetConfig layoutSet = await _appDevelopmentService.GetLayoutSetConfig(altinnRepoEditingContext, layoutSetId); - string taskId = layoutSet.Tasks?.First(); + string? taskId = layoutSet.Tasks?.First(); ProcessTask? task = definitions?.Process.Tasks.FirstOrDefault(task => task.Id == taskId); return task?.ExtensionElements?.TaskExtension?.TaskType ?? string.Empty; } @@ -115,7 +115,12 @@ private Stream GetTemplateStream(SemanticVersion version, string templateName) throw new FileNotFoundException("Unknown template."); } string template = templates.Single(template => template.EndsWith(templateName)); - return typeof(ProcessModelingService).Assembly.GetManifestResourceStream(template); + Stream? templateStream = typeof(ProcessModelingService).Assembly.GetManifestResourceStream(template); + if (templateStream == null) + { + throw new FileNotFoundException($"Template resource '{template}' not found in the assembly."); + } + return templateStream; } } } diff --git a/backend/src/Designer/Services/Implementation/SchemaModelService.cs b/backend/src/Designer/Services/Implementation/SchemaModelService.cs index 3067a31d98d..556440a7e3c 100644 --- a/backend/src/Designer/Services/Implementation/SchemaModelService.cs +++ b/backend/src/Designer/Services/Implementation/SchemaModelService.cs @@ -40,6 +40,7 @@ public class SchemaModelService : ISchemaModelService private readonly IXmlSchemaToJsonSchemaConverter _xmlSchemaToJsonSchemaConverter; private readonly IJsonSchemaToXmlSchemaConverter _jsonSchemaToXmlSchemaConverter; private readonly IModelMetadataToCsharpConverter _modelMetadataToCsharpConverter; + private readonly IApplicationMetadataService _applicationMetadataService; /// /// Initializes a new instance of the class. @@ -59,13 +60,15 @@ public class SchemaModelService : ISchemaModelService /// /// Class for converting Json schemas to Xml schemas. /// C# model generator + /// public SchemaModelService( IAltinnGitRepositoryFactory altinnGitRepositoryFactory, ILoggerFactory loggerFactory, ServiceRepositorySettings serviceRepositorySettings, IXmlSchemaToJsonSchemaConverter xmlSchemaToJsonSchemaConverter, IJsonSchemaToXmlSchemaConverter jsonSchemaToXmlSchemaConverter, - IModelMetadataToCsharpConverter modelMetadataToCsharpConverter) + IModelMetadataToCsharpConverter modelMetadataToCsharpConverter, + IApplicationMetadataService applicationMetadataService) { _altinnGitRepositoryFactory = altinnGitRepositoryFactory; _loggerFactory = loggerFactory; @@ -73,6 +76,7 @@ public SchemaModelService( _xmlSchemaToJsonSchemaConverter = xmlSchemaToJsonSchemaConverter; _jsonSchemaToXmlSchemaConverter = jsonSchemaToXmlSchemaConverter; _modelMetadataToCsharpConverter = modelMetadataToCsharpConverter; + _applicationMetadataService = applicationMetadataService; } /// @@ -431,5 +435,24 @@ private bool NamespaceNeedsToBeSeparated(ApplicationMetadata application, { return application.DataTypes.All(d => d.AppLogic?.ClassRef != $"Altinn.App.Models.{csharpModelName}"); } + + public async Task GetModelDataType(string org, string app, string modelId) + { + ApplicationMetadata applicationMetadata = await _applicationMetadataService.GetApplicationMetadataFromRepository(org, app); + DataType dataType = applicationMetadata.DataTypes.Find((dataType) => dataType.Id == modelId); + return dataType; + } + + public async Task SetModelDataType(string org, string app, string modelId, DataType dataType) + { + if (dataType.Id != modelId) + { + throw new ArgumentException("Provided modelId does not match the DataType's Id"); + } + ApplicationMetadata applicationMetadata = await _applicationMetadataService.GetApplicationMetadataFromRepository(org, app); + applicationMetadata.DataTypes.RemoveAll((dt) => dt.Id == dataType.Id); + applicationMetadata.DataTypes.Add(dataType); + await _applicationMetadataService.UpdateApplicationMetaDataLocally(org, app, applicationMetadata); + } } } diff --git a/backend/src/Designer/Services/Interfaces/IOptionsService.cs b/backend/src/Designer/Services/Interfaces/IOptionsService.cs index 0a291a45819..74e31e12a97 100644 --- a/backend/src/Designer/Services/Interfaces/IOptionsService.cs +++ b/backend/src/Designer/Services/Interfaces/IOptionsService.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Models.Dto; using Microsoft.AspNetCore.Http; namespace Altinn.Studio.Designer.Services.Interfaces; @@ -31,6 +32,14 @@ public interface IOptionsService /// The options list public Task> GetOptionsList(string org, string repo, string developer, string optionsListId, CancellationToken cancellationToken = default); + /// + /// Gets a list of sources, , for all OptionListIds. + /// + /// An . + /// A that observes if operation is cancelled. + /// A list of + public Task> GetAllOptionListReferences(AltinnRepoEditingContext altinnRepoEditingContext, CancellationToken cancellationToken = default); + /// /// Creates a new options list in the app repository. /// If the file already exists, it will be overwritten. @@ -47,7 +56,7 @@ public interface IOptionsService /// Adds a new option to the option list. /// If the file already exists, it will be overwritten. /// - /// Orginisation + /// Organisation /// Repository /// Username of developer /// Name of the new options list diff --git a/backend/src/Designer/Services/Interfaces/ISchemaModelService.cs b/backend/src/Designer/Services/Interfaces/ISchemaModelService.cs index c043f7fa8cd..16cc0a91f34 100644 --- a/backend/src/Designer/Services/Interfaces/ISchemaModelService.cs +++ b/backend/src/Designer/Services/Interfaces/ISchemaModelService.cs @@ -84,5 +84,15 @@ public interface ISchemaModelService /// An that observes if operation is cancelled. /// Returns the model metadata Task GenerateModelMetadataFromJsonSchema(AltinnRepoEditingContext altinnRepoEditingContext, string relativeFilePath, CancellationToken cancellationToken = default); + + /// + /// Gets the dataType for a given model. + /// + Task GetModelDataType(string org, string app, string modelId); + + /// + /// Updates the dataType for a given model. + /// + Task SetModelDataType(string org, string app, string modelId, DataType dataType); } } diff --git a/backend/src/Designer/Services/Interfaces/Preview/IDataService.cs b/backend/src/Designer/Services/Interfaces/Preview/IDataService.cs index e12c80a05bf..ac60a9db279 100644 --- a/backend/src/Designer/Services/Interfaces/Preview/IDataService.cs +++ b/backend/src/Designer/Services/Interfaces/Preview/IDataService.cs @@ -11,6 +11,7 @@ namespace Altinn.Studio.Designer.Services.Interfaces.Preview; public interface IDataService { public DataElement CreateDataElement(int partyId, Guid instanceGuid, string dataTypeId); + public JsonNode GetDataElement(Guid dataGuid); public JsonNode PatchDataElement(Guid dataGuid, JsonPatch patch); } diff --git a/backend/src/Designer/Services/Interfaces/Preview/IInstanceService.cs b/backend/src/Designer/Services/Interfaces/Preview/IInstanceService.cs new file mode 100644 index 00000000000..9567f420e33 --- /dev/null +++ b/backend/src/Designer/Services/Interfaces/Preview/IInstanceService.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using Altinn.Platform.Storage.Interface.Models; + +namespace Altinn.Studio.Designer.Services.Interfaces.Preview; + +/// +/// Interface for handling a mocked datatype object for preview mode +/// +public interface IInstanceService +{ + public Instance CreateInstance(string org, string app, int partyId, string taskId, List dataTypes); + public Instance GetInstance(Guid instanceGuid); + + public Instance AddDataElement(Guid instanceGuid, DataElement dataElement); + public Instance RemoveDataElement(Guid instanceGuid, Guid dataElementGuid); +} diff --git a/backend/src/Designer/TypedHttpClients/Altinn2Metadata/Altinn2MetadataClient.cs b/backend/src/Designer/TypedHttpClients/Altinn2Metadata/Altinn2MetadataClient.cs index 29cf2b5373f..2eda4ef7783 100644 --- a/backend/src/Designer/TypedHttpClients/Altinn2Metadata/Altinn2MetadataClient.cs +++ b/backend/src/Designer/TypedHttpClients/Altinn2Metadata/Altinn2MetadataClient.cs @@ -57,7 +57,7 @@ public async Task GetXacmlPolicy(string serviceCode, int serviceEdi public async Task> AvailableServices(int languageId, string environment) { - List? availableServices = null; + List availableServices = null; string bridgeBaseUrl = GetSblBridgeUrl(environment); string availabbleServicePath = $"{bridgeBaseUrl}metadata/api/availableServices?languageID={languageId}&appTypesToInclude=0&includeExpired=false"; diff --git a/backend/src/Designer/TypedHttpClients/AzureDevOps/AzureDevOpsBuildClient.cs b/backend/src/Designer/TypedHttpClients/AzureDevOps/AzureDevOpsBuildClient.cs index 5ea2f691e15..f87b31ec4a1 100644 --- a/backend/src/Designer/TypedHttpClients/AzureDevOps/AzureDevOpsBuildClient.cs +++ b/backend/src/Designer/TypedHttpClients/AzureDevOps/AzureDevOpsBuildClient.cs @@ -30,6 +30,7 @@ public class AzureDevOpsBuildClient : IAzureDevOpsBuildClient /// GeneralSettings /// ISourceControl /// ILogger + /// The http context accessor. public AzureDevOpsBuildClient( HttpClient httpClient, GeneralSettings generalSettingsOptions, diff --git a/backend/tests/DataModeling.Tests/DataModeling.Tests.csproj b/backend/tests/DataModeling.Tests/DataModeling.Tests.csproj index 74c99c7cc08..a1dbd2fb83b 100644 --- a/backend/tests/DataModeling.Tests/DataModeling.Tests.csproj +++ b/backend/tests/DataModeling.Tests/DataModeling.Tests.csproj @@ -5,7 +5,6 @@ - diff --git a/backend/tests/Designer.Tests/Controllers/AnsattPortenController/AuthStatusTests.cs b/backend/tests/Designer.Tests/Controllers/AnsattPortenController/AuthStatusTests.cs index 59958508764..cb44ab9b2b6 100644 --- a/backend/tests/Designer.Tests/Controllers/AnsattPortenController/AuthStatusTests.cs +++ b/backend/tests/Designer.Tests/Controllers/AnsattPortenController/AuthStatusTests.cs @@ -1,3 +1,4 @@ +using System; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -67,7 +68,7 @@ public async Task AuthStatus_Should_ReturnTrue_IfAuthenticated() { services.AddAuthentication(defaultScheme: TestAuthConstants.TestAuthenticationScheme) .AddScheme( - TestAuthConstants.TestAuthenticationScheme, options => { }); + TestAuthConstants.TestAuthenticationScheme, options => { options.TimeProvider = TimeProvider.System; }); services.AddTransient(); }; diff --git a/backend/tests/Designer.Tests/Controllers/ApiTests/ApiTestsBase.cs b/backend/tests/Designer.Tests/Controllers/ApiTests/ApiTestsBase.cs index 7b03c34ac9e..0a4908f1c82 100644 --- a/backend/tests/Designer.Tests/Controllers/ApiTests/ApiTestsBase.cs +++ b/backend/tests/Designer.Tests/Controllers/ApiTests/ApiTestsBase.cs @@ -94,7 +94,8 @@ protected virtual HttpClient GetTestClient() { services.AddAuthentication(defaultScheme: TestAuthConstants.TestAuthenticationScheme) .AddScheme( - TestAuthConstants.TestAuthenticationScheme, options => { }); + TestAuthConstants.TestAuthenticationScheme, + options => { options.TimeProvider = TimeProvider.System; }); services.AddTransient(); }); builder.ConfigureServices(ConfigureTestServicesForSpecificTest); diff --git a/backend/tests/Designer.Tests/Controllers/ApiTests/TestAuthHandler.cs b/backend/tests/Designer.Tests/Controllers/ApiTests/TestAuthHandler.cs index d510b1d5af6..8139d1c4f28 100644 --- a/backend/tests/Designer.Tests/Controllers/ApiTests/TestAuthHandler.cs +++ b/backend/tests/Designer.Tests/Controllers/ApiTests/TestAuthHandler.cs @@ -1,3 +1,4 @@ +using System; using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; @@ -10,8 +11,8 @@ namespace Designer.Tests.Controllers.ApiTests; public class TestAuthHandler : AuthenticationHandler { public TestAuthHandler(IOptionsMonitor options, - ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) - : base(options, logger, encoder, clock) + ILoggerFactory logger, UrlEncoder encoder) + : base(options, logger, encoder) { } diff --git a/backend/tests/Designer.Tests/Controllers/DataModelsController/GetTests.cs b/backend/tests/Designer.Tests/Controllers/DataModelsController/GetTests.cs index bac7b5943e7..31c013aa15f 100644 --- a/backend/tests/Designer.Tests/Controllers/DataModelsController/GetTests.cs +++ b/backend/tests/Designer.Tests/Controllers/DataModelsController/GetTests.cs @@ -1,6 +1,9 @@ -using System.Net; +using System.Collections.Generic; +using System.Net; using System.Net.Http; +using System.Net.Http.Json; using System.Threading.Tasks; +using Altinn.Platform.Storage.Interface.Models; using Designer.Tests.Controllers.ApiTests; using FluentAssertions; using Microsoft.AspNetCore.Mvc.Testing; @@ -26,4 +29,42 @@ public async Task GetDatamodel_ValidPath_ShouldReturnContent(string modelPath, s using var response = await HttpClient.SendAsync(httpRequestMessage); response.StatusCode.Should().Be(HttpStatusCode.OK); } + + [Theory] + [InlineData("Kursdomene_HvemErHvem_M_2021-04-08_5742_34627_SERES", "ttd", "hvem-er-hvem")] + public async Task GetDatamodelDataType_ShouldReturnContent(string modelName, string org, string repo) + { + string url = $"{VersionPrefix(org, repo)}/datamodel/{modelName}/dataType"; + using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); + + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + response.StatusCode.Should().Be(HttpStatusCode.OK); + DataType dataTypeResponse = await response.Content.ReadFromJsonAsync(); + dataTypeResponse.Should().NotBeNull(); + dataTypeResponse.Should().BeEquivalentTo(new DataType + { + Id = "Kursdomene_HvemErHvem_M_2021-04-08_5742_34627_SERES", + AllowedContentTypes = new List { "application/xml" }, + AppLogic = new ApplicationLogic + { + AutoCreate = true, + ClassRef = "Altinn.App.Models.HvemErHvem_M" + }, + TaskId = "Task_1", + MaxCount = 1, + MinCount = 1 + }); + } + + [Theory] + [InlineData("notfound", "ttd", "hvem-er-hvem")] + public async Task GetDatamodelDataType_ShouldNullWhenNotFound(string modelName, string org, string repo) + { + string url = $"{VersionPrefix(org, repo)}/datamodel/{modelName}/dataType"; + using var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); + + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + response.StatusCode.Should().Be(HttpStatusCode.OK); + (await response.Content.ReadAsStringAsync()).Should().Be("null"); + } } diff --git a/backend/tests/Designer.Tests/Controllers/DataModelsController/PutDatamodelTests.cs b/backend/tests/Designer.Tests/Controllers/DataModelsController/PutDatamodelTests.cs index 0ee22606477..4db5c7cf72f 100644 --- a/backend/tests/Designer.Tests/Controllers/DataModelsController/PutDatamodelTests.cs +++ b/backend/tests/Designer.Tests/Controllers/DataModelsController/PutDatamodelTests.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Http.Json; using System.Net.Mime; using System.Text; using System.Text.Json; @@ -11,6 +12,7 @@ using System.Web; using System.Xml; using System.Xml.Schema; +using Altinn.Platform.Storage.Interface.Models; using Altinn.Studio.DataModeling.Converter.Json; using Altinn.Studio.DataModeling.Converter.Json.Strategy; using Altinn.Studio.DataModeling.Converter.Metadata; @@ -144,6 +146,54 @@ public async Task IncompatibleSchema_ShouldReturn422(string modelPath, string sc } } + [Theory] + [InlineData("testmodelname", "ttd", "hvem-er-hvem")] + public async Task PutDatamodelDataType_ShouldReturnWithoutErrors(string datamodelName, string org, string repo) + { + string url = $"{VersionPrefix(org, TargetTestRepository)}/datamodel/{datamodelName}/dataType"; + await CopyRepositoryForTest(org, repo, "testUser", TargetTestRepository); + + DataType dataType = new() + { + Id = datamodelName, + MaxCount = 1, + MinCount = 1, + }; + using var putRequest = new HttpRequestMessage(HttpMethod.Put, url) + { + Content = JsonContent.Create(dataType) + }; + + HttpResponseMessage response = await HttpClient.SendAsync(putRequest); + response.StatusCode.Should().Be(HttpStatusCode.OK); + DataType dataTypeResponse = await response.Content.ReadFromJsonAsync(); + dataTypeResponse.Should().NotBeNull(); + dataTypeResponse.Should().BeEquivalentTo(dataType); + } + + [Theory] + [InlineData("testmodelname", "ttd", "hvem-er-hvem")] + public async Task PutDatamodelDataType_FailsIfDatamodelNameMismatchesObjectId(string datamodelName, string org, string repo) + { + string url = $"{VersionPrefix(org, TargetTestRepository)}/datamodel/{datamodelName}/dataType"; + await CopyRepositoryForTest(org, repo, "testUser", TargetTestRepository); + + DataType dataType = new() + { + Id = "wrongId", + MaxCount = 1, + MinCount = 1, + }; + using var putRequest = new HttpRequestMessage(HttpMethod.Put, url) + { + Content = JsonContent.Create(dataType) + }; + + HttpResponseMessage response = await HttpClient.SendAsync(putRequest); + response.StatusCode.Should().Be(HttpStatusCode.BadRequest); + } + + private async Task FilesWithCorrectNameAndContentShouldBeCreated(string modelName) { var location = Path.GetFullPath(Path.Combine(TestRepoPath, "App", "models")); diff --git a/backend/tests/Designer.Tests/Controllers/OptionsController/GetOptionListsReferencesTests.cs b/backend/tests/Designer.Tests/Controllers/OptionsController/GetOptionListsReferencesTests.cs new file mode 100644 index 00000000000..d4fdd5d11db --- /dev/null +++ b/backend/tests/Designer.Tests/Controllers/OptionsController/GetOptionListsReferencesTests.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Altinn.Studio.Designer.Models.Dto; +using Designer.Tests.Controllers.ApiTests; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Testing; +using Xunit; + +namespace Designer.Tests.Controllers.OptionsController; + +public class GetOptionListsReferencesTests : DesignerEndpointsTestsBase, IClassFixture> +{ + const string RepoWithUsedOptions = "app-with-options"; + const string RepoWithUnusedOptions = "app-with-layoutsets"; + + public GetOptionListsReferencesTests(WebApplicationFactory factory) : base(factory) + { + } + + [Fact] + public async Task GetOptionListsReferences_Returns200OK_WithValidOptionsReferences() + { + string apiUrl = $"/designer/api/ttd/{RepoWithUsedOptions}/options/usage"; + using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, apiUrl); + + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + string responseBody = await response.Content.ReadAsStringAsync(); + List responseList = JsonSerializer.Deserialize>(responseBody); + + List expectedResponseList = new() + { + new RefToOptionListSpecifier + { + OptionListId = "test-options", OptionListIdSources = + [ + new OptionListIdSource + { + ComponentIds = ["component-using-same-options-id-in-same-set-and-another-layout"], + LayoutName = "layoutWithOneOptionListIdRef.json", + LayoutSetId = "layoutSet1" + }, + new OptionListIdSource + { + ComponentIds = ["component-using-test-options-id", "component-using-test-options-id-again"], + LayoutName = "layoutWithFourCheckboxComponentsAndThreeOptionListIdRefs.json", + LayoutSetId = "layoutSet1" + }, + new OptionListIdSource + { + ComponentIds = ["component-using-same-options-id-in-another-set"], + LayoutName = "layoutWithTwoOptionListIdRefs.json", + LayoutSetId = "layoutSet2" + } + ] + }, + new() + { + OptionListId = "other-options", OptionListIdSources = + [ + new OptionListIdSource + { + ComponentIds = ["component-using-other-options-id"], + LayoutName = "layoutWithFourCheckboxComponentsAndThreeOptionListIdRefs.json", + LayoutSetId = "layoutSet1" + } + ] + } + }; + + Assert.Equal(StatusCodes.Status200OK, (int)response.StatusCode); + Assert.Equivalent(expectedResponseList, responseList); + } + + [Fact] + public async Task GetOptionListsReferences_Returns200Ok_WithEmptyOptionsReferences_WhenLayoutsDoesNotReferenceAnyOptionsInApp() + { + string apiUrl = $"/designer/api/ttd/{RepoWithUnusedOptions}/options/usage"; + using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, apiUrl); + + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + string responseBody = await response.Content.ReadAsStringAsync(); + + Assert.Equal(StatusCodes.Status200OK, (int)response.StatusCode); + Assert.Equivalent("[]", responseBody); + } +} diff --git a/backend/tests/Designer.Tests/Controllers/Preview/DataControllerTests.cs b/backend/tests/Designer.Tests/Controllers/Preview/DataControllerTests.cs new file mode 100644 index 00000000000..468c73aa4d3 --- /dev/null +++ b/backend/tests/Designer.Tests/Controllers/Preview/DataControllerTests.cs @@ -0,0 +1,123 @@ +using System.Net; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using Altinn.Platform.Storage.Interface.Models; +using Altinn.Studio.Designer.Models.Preview; +using Designer.Tests.Controllers.PreviewController; +using Microsoft.AspNetCore.Mvc.Testing; +using Xunit; + +namespace Designer.Tests.Controllers.Preview; + +public class DataControllerTests(WebApplicationFactory factory) : PreviewControllerTestsBase(factory), IClassFixture> +{ + [Fact] + public async Task Post_ReturnsCreated() + { + Instance instance = await createInstance(); + string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/data"; + using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData); + + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + + string responseBody = await response.Content.ReadAsStringAsync(); + DataElement dataElement = JsonSerializer.Deserialize(responseBody, JsonSerializerOptions); + Assert.NotNull(dataElement.Id); + Assert.Equal(instance.Id, dataElement.InstanceGuid); + } + + [Fact] + public async Task Get_ReturnsOk() + { + Instance instance = await createInstance(); + DataElement dataElement = await createDataElement(instance, "datamodel"); + + string dataPath = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/data/{dataElement.Id}"; + using HttpRequestMessage httpRequestMessageGet = new(HttpMethod.Get, dataPath); + using HttpResponseMessage responseGet = await HttpClient.SendAsync(httpRequestMessageGet); + Assert.Equal(HttpStatusCode.OK, responseGet.StatusCode); + string responseBodyGet = await responseGet.Content.ReadAsStringAsync(); + JsonNode dataItem = JsonSerializer.Deserialize(responseBodyGet, JsonSerializerOptions); + Assert.NotNull(dataItem); + } + + [Fact] + public async Task Patch_ReturnsOk() + { + Instance instance = await createInstance(); + DataElement dataElement = await createDataElement(instance, "datamodel"); + + string dataPath = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/data/{dataElement.Id}"; + using HttpRequestMessage httpRequestMessagePatch = new(HttpMethod.Patch, dataPath); + + string patch = "{\"patch\":[{\"op\":\"add\",\"path\":\"/RegNo\",\"value\":\"asdf\"}],\"ignoredValidators\":[\"DataAnnotations\",\"Required\",\"Expression\"]}"; + httpRequestMessagePatch.Content = new StringContent(patch, System.Text.Encoding.UTF8, "application/json"); + using HttpResponseMessage responsePatch = await HttpClient.SendAsync(httpRequestMessagePatch); + Assert.Equal(HttpStatusCode.OK, responsePatch.StatusCode); + string responseBodyPatch = await responsePatch.Content.ReadAsStringAsync(); + JsonNode dataItem = JsonSerializer.Deserialize(responseBodyPatch, JsonSerializerOptions); + Assert.NotNull(dataItem); + Assert.Equal("asdf", dataItem["newDataModel"]["RegNo"].ToString()); + } + + [Fact] + public async Task PatchMultiple_ReturnsOk() + { + Instance instance = await createInstance(); + DataElement dataElement1 = await createDataElement(instance, "datamodel"); + DataElement dataElement2 = await createDataElement(instance, "datamodel"); + string dataPath = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/data"; + using HttpRequestMessage httpRequestMessagePatchMultiple = new(HttpMethod.Patch, dataPath); + + string patches = "{\"patches\":[{\"dataElementId\":\"" + dataElement1.Id + "\",\"patch\":[{\"op\":\"add\",\"path\":\"/RegNo\",\"value\":\"dataobj1\"}]},{\"dataElementId\":\"" + dataElement2.Id + "\",\"patch\":[{\"op\":\"add\",\"path\":\"/RegNo\",\"value\":\"dataobj2\"}]}],\"ignoredValidators\":[\"DataAnnotations\",\"Required\",\"Expression\"]}"; + httpRequestMessagePatchMultiple.Content = new StringContent(patches, System.Text.Encoding.UTF8, "application/json"); + using HttpResponseMessage responsePatchMultiple = await HttpClient.SendAsync(httpRequestMessagePatchMultiple); + Assert.Equal(HttpStatusCode.OK, responsePatchMultiple.StatusCode); + string responseBodyPatchMultiple = await responsePatchMultiple.Content.ReadAsStringAsync(); + DataPatchResponseMultiple dataItem = JsonSerializer.Deserialize(responseBodyPatchMultiple, JsonSerializerOptions); + Assert.NotNull(dataItem); + Assert.Equal(2, dataItem.NewDataModels.Count); + object dataItem1 = JsonSerializer.Serialize(dataItem.NewDataModels[0].Data); + Assert.Equal("{\"RegNo\":\"dataobj1\"}", dataItem1.ToString()); + object dataItem2 = JsonSerializer.Serialize(dataItem.NewDataModels[1].Data); + Assert.Equal("{\"RegNo\":\"dataobj2\"}", dataItem2.ToString()); + } + + [Fact] + public async Task Delete_ReturnsOk() + { + Instance instance = await createInstance(); + DataElement dataElement = await createDataElement(instance, "datamodel"); + + string dataPath = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/data/{dataElement.Id}"; + using HttpRequestMessage httpRequestMessageDelete = new(HttpMethod.Delete, dataPath); + using HttpResponseMessage responseDelete = await HttpClient.SendAsync(httpRequestMessageDelete); + Assert.Equal(HttpStatusCode.OK, responseDelete.StatusCode); + } + + [Fact] + public async Task Validate_ReturnsOk() + { + Instance instance = await createInstance(); + DataElement dataElement = await createDataElement(instance, "datamodel"); + string dataPath = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/data/{dataElement.Id}/validate"; + using HttpRequestMessage httpRequestMessageValidate = new(HttpMethod.Get, dataPath); + using HttpResponseMessage responseValidate = await HttpClient.SendAsync(httpRequestMessageValidate); + Assert.Equal(HttpStatusCode.OK, responseValidate.StatusCode); + } + + [Fact] + public async Task Tag_ReturnsCreateOkd() + { + Instance instance = await createInstance(); + DataElement dataElement = await createDataElement(instance, "datamodel"); + string dataPath = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/data/{dataElement.Id}/tags"; + using HttpRequestMessage httpRequestMessageTag = new(HttpMethod.Post, dataPath); + httpRequestMessageTag.Content = new StringContent("{\"tag\":\"test\"}", System.Text.Encoding.UTF8, "application/json"); + using HttpResponseMessage responseTag = await HttpClient.SendAsync(httpRequestMessageTag); + Assert.Equal(HttpStatusCode.Created, responseTag.StatusCode); + } +} diff --git a/backend/tests/Designer.Tests/Controllers/Preview/InstancesControllerTests.cs b/backend/tests/Designer.Tests/Controllers/Preview/InstancesControllerTests.cs new file mode 100644 index 00000000000..716770b3e7d --- /dev/null +++ b/backend/tests/Designer.Tests/Controllers/Preview/InstancesControllerTests.cs @@ -0,0 +1,71 @@ +using System.Net; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Altinn.Platform.Storage.Interface.Models; +using Designer.Tests.Controllers.PreviewController; +using Microsoft.AspNetCore.Mvc.Testing; +using Xunit; + +namespace Designer.Tests.Controllers.Preview; + +public class InstancesControllerTests( + WebApplicationFactory factory +) : PreviewControllerTestsBase(factory), IClassFixture> +{ + + [Fact] + public async Task Post_ReturnsCreated() + { + Instance instance = await createInstance(); + Assert.NotNull(instance); + Assert.NotNull(instance.Id); + } + + [Fact] + public async Task GetInstance_ReturnsOk() + { + Instance instance = await createInstance(); + Assert.NotNull(instance); + Assert.NotNull(instance.Id); + + string dataPath = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}"; + using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPath); + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + string responseBody = await response.Content.ReadAsStringAsync(); + Instance responseInstance = JsonSerializer.Deserialize(responseBody, JsonSerializerOptions); + Assert.NotNull(responseInstance); + Assert.Equal(instance.Id, responseInstance.Id); + } + + [Fact] + public async Task Validate_ReturnsOk() + { + Instance instance = await createInstance(); + string dataPath = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/validate"; + using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPath); + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task Process_ReturnsOk() + { + Instance instance = await createInstance(); + string dataPath = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/process"; + using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPath); + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task ProcessNext_ReturnsOk() + { + Instance instance = await createInstance(); + string dataPath = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/process/next"; + using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPath); + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } +} diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/AnonymousTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/AnonymousTests.cs index ae21b0c033d..f3accf4071a 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/AnonymousTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/AnonymousTests.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; @@ -17,8 +18,9 @@ public AnonymousTests(WebApplicationFactory factory) : base(factory) [Fact] public async Task Get_Anonymous_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/api/v1/data/anonymous"; + string dataPathWithData = $"{Org}/{AppV4}/api/v1/data/anonymous"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/ApplicationMetadataTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/ApplicationMetadataTests.cs index 3671a2b4747..c8ecc70b75d 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/ApplicationMetadataTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/ApplicationMetadataTests.cs @@ -78,42 +78,6 @@ public async Task Get_ApplicationMetadata_With_V8_Altinn_Nuget_Version_Ok() JsonUtils.DeepEquals(expectedJson, responseBody).Should().BeTrue(); } - [Fact] - public async Task Get_ApplicationMetadata_WithLessDataTypesThanLayoutSetsFile_OkWithMockedDataTypes() - { - string expectedApplicationMetadataString = TestDataHelper.GetFileFromRepo(Org, AppV4, Developer, "App/config/applicationmetadata.json"); - _appDevelopmentServiceMock - .Setup(rs => rs.GetAppLibVersion(It.IsAny())) - .Returns(NuGet.Versioning.NuGetVersion.Parse("8.0.0")); - - string dataPathWithData = $"{Org}/{AppV4}/api/v1/applicationmetadata"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - ApplicationMetadata expectedApplicationMetadata = JsonSerializer.Deserialize(expectedApplicationMetadataString, JsonSerializerOptions); - expectedApplicationMetadata.AltinnNugetVersion = "8.0.0.0"; - // Add the mocked data type to expected app metadata - expectedApplicationMetadata.DataTypes.Add(new DataType() - { - Id = $"{PreviewService.MockDataModelIdPrefix}-0", - AppLogic = new ApplicationLogic() - { - ClassRef = $"Altinn.App.Models.model.{PreviewService.MockDataModelIdPrefix}-0" - }, - TaskId = "Task_2" - }); - expectedApplicationMetadata.PartyTypesAllowed.Person = false; - expectedApplicationMetadata.PartyTypesAllowed.Organisation = false; - expectedApplicationMetadata.PartyTypesAllowed.SubUnit = false; - expectedApplicationMetadata.PartyTypesAllowed.BankruptcyEstate = false; - - string expectedJson = JsonSerializer.Serialize(expectedApplicationMetadata, JsonSerializerOptions); - JsonUtils.DeepEquals(expectedJson, responseBody).Should().BeTrue(); - } - [Fact] public async Task Get_ApplicationMetadata_WithAllPartyTypesAllowedSetToFalse() { diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/CurrentPartyTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/CurrentPartyTests.cs index d9d092d6da8..fae557ab685 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/CurrentPartyTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/CurrentPartyTests.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; @@ -19,8 +20,9 @@ public CurrentPartyTests(WebApplicationFactory factory) : base(factory) [Fact] public async Task Get_CurrentParty_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/api/authorization/parties/current"; + string dataPathWithData = $"{Org}/{AppV4}/api/authorization/parties/current"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/CurrentUserTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/CurrentUserTests.cs index 0836a9c485d..de858d8e7d7 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/CurrentUserTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/CurrentUserTests.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; @@ -19,8 +20,9 @@ public CurrentUserTests(WebApplicationFactory factory) : base(factory) [Fact] public async Task Get_CurrentUser_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/api/v1/profile/user"; + string dataPathWithData = $"{Org}/{AppV4}/api/v1/profile/user"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/DeleteAttachmentTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/DeleteAttachmentTests.cs index 3f0ff5e14c1..fd946cee59d 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/DeleteAttachmentTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/DeleteAttachmentTests.cs @@ -2,6 +2,7 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; +using Altinn.Platform.Storage.Interface.Models; using Microsoft.AspNetCore.Mvc.Testing; using Xunit; @@ -17,9 +18,9 @@ public DeleteAttachmentTests(WebApplicationFactory factory) : base(fact [Fact] public async Task Delete_Attachment_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/instances/{PartyId}/{InstanceGuId}/data/{AttachmentGuId}"; + string dataPathWithData = $"{Org}/{AppV3Path}/instances/{PartyId}/{V3InstanceId}/data/asdf"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Delete, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3}&selectedLayoutSet="); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3Path}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -28,9 +29,10 @@ public async Task Delete_Attachment_Ok() [Fact] public async Task Delete_AttachmentForV4App_Ok() { - string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{InstanceGuId}/data/{AttachmentGuId}"; + Instance instance = await createInstance(); + DataElement dataElement = await createDataElement(instance, "attachment"); + string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/data/{dataElement.Id}"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Delete, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet={LayoutSetName}"); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/GetFooterTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/GetFooterTests.cs index ee391e0371f..23d979706af 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/GetFooterTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/GetFooterTests.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Net; using System.Net.Http; @@ -35,18 +36,5 @@ public async Task Get_Footer_Exists_Ok() responseFooterFile.Footer.Should().BeEquivalentTo(actualFooterFile.Footer); } - - [Fact] - public async Task Get_Footer_Non_Existing_Ok() - { - string dataPathWithData = $"{Org}/{AppV3}/api/v1/footer"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseString = await response.Content.ReadAsStringAsync(); - responseString.Should().BeEmpty(); - } } } diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/GetFormDataTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/GetFormDataTests.cs deleted file mode 100644 index eae40b51eb8..00000000000 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/GetFormDataTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Designer.Tests.Utils; -using FluentAssertions; -using Microsoft.AspNetCore.Mvc.Testing; -using SharedResources.Tests; -using Xunit; - -namespace Designer.Tests.Controllers.PreviewController -{ - public class GetFormDataTests : PreviewControllerTestsBase, IClassFixture> - { - - public GetFormDataTests(WebApplicationFactory factory) : base(factory) - { - } - - [Fact] - public async Task Get_FormData_Ok() - { - string expectedFormData = TestDataHelper.GetFileFromRepo(Org, PreviewApp, Developer, "App/models/custom-dm-name.schema.json"); - - string dataPathWithData = $"{Org}/{PreviewApp}/instances/{PartyId}/{InstanceGuId}/data/test-datatask-id"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={PreviewApp}&selectedLayoutSet="); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - JsonUtils.DeepEquals(expectedFormData, responseBody).Should().BeTrue(); - } - - [Fact] - public async Task Get_FormDataForAppWithoutDatamodel_Ok() - { - string dataPathWithData = $"{Org}/empty-app/instances/{PartyId}/{InstanceGuId}/data/test-datatask-id"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3}&selectedLayoutSet="); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - responseBody.Should().Be($"{PartyId}/{InstanceGuId}"); - } - - [Fact] - public async Task Get_FormDataForV4App_Ok() - { - string expectedFormData = TestDataHelper.GetFileFromRepo(Org, AppV4, Developer, "App/models/datamodel.schema.json"); - - string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{InstanceGuId}/data/test-datatask-id"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet={LayoutSetName}"); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - JsonUtils.DeepEquals(expectedFormData, responseBody).Should().BeTrue(); - } - - [Fact] - public async Task Get_FormDataForV4AppForTaskWithoutDatamodel_Ok() - { - string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{InstanceGuId}/data/test-datatask-id"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet={LayoutSetName2}"); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - responseBody.Should().Be($"{PartyId}/{InstanceGuId}"); - } - } -} diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/GetImageTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/GetImageTests.cs index e314dfb04bb..45d1ff5e68b 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/GetImageTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/GetImageTests.cs @@ -65,7 +65,7 @@ public async Task Get_Image_From_Sub_Sub_Folder_Ok() [Fact] public async Task Get_Image_Non_Existing_Folder_Returns_NotFound() { - string dataPathWithData = $"{Org}/{AppV3}/images/subImagesFolder/SubSubImageFolder/AltinnD-logo.svg"; + string dataPathWithData = $"{Org}/{AppV3Path}/images/subImagesFolder/SubSubImageFolder/AltinnD-logo.svg"; using HttpResponseMessage response = await HttpClient.GetAsync(dataPathWithData); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); @@ -74,7 +74,7 @@ public async Task Get_Image_Non_Existing_Folder_Returns_NotFound() [Fact] public async Task Get_Image_Non_Existing_Image_Return_NotFound() { - string dataPathWithData = $"{Org}/{AppV3}/images/subImagesFolder/non-existing-image.svg"; + string dataPathWithData = $"{Org}/{AppV3Path}/images/subImagesFolder/non-existing-image.svg"; using HttpResponseMessage response = await HttpClient.GetAsync(dataPathWithData); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/GetOptionsTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/GetOptionsTests.cs index 22c3bb7d419..82c8ebc04e7 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/GetOptionsTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/GetOptionsTests.cs @@ -1,7 +1,9 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Altinn.Platform.Storage.Interface.Models; using Microsoft.AspNetCore.Mvc.Testing; using Xunit; @@ -31,7 +33,8 @@ public async Task Get_Options_when_options_exists_Ok() [Fact] public async Task Get_Options_when_options_exists_for_v4_app_Ok() { - string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{InstanceGuId}/options/test-options"; + Instance instance = await createInstance(); + string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/options/test-options"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); @@ -45,8 +48,9 @@ public async Task Get_Options_when_options_exists_for_v4_app_Ok() [Fact] public async Task Get_Options_when_no_options_exist_returns_Ok_empty_list() { - string dataPathWithData = $"{Org}/{AppV3}/api/options/non-existing-options"; + string dataPathWithData = $"{Org}/{AppV4}/api/options/non-existing-options"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/GetRuleHandlerTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/GetRuleHandlerTests.cs index 70f2f7c04b0..cfb2f4b7ac6 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/GetRuleHandlerTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/GetRuleHandlerTests.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Testing; @@ -16,8 +17,9 @@ public GetRuleHandlerTests(WebApplicationFactory factory) : base(factor [Fact] public async Task Get_RuleHandler_NoContent() { - string dataPathWithData = $"{Org}/{AppV3}/api/resource/RuleHandler.js"; + string dataPathWithData = $"{Org}/{AppV4}/api/resource/RuleHandler.js"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/InstanceForNextTaskTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/InstanceForNextTaskTests.cs index c751bfaa0c9..061c1ec8904 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/InstanceForNextTaskTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/InstanceForNextTaskTests.cs @@ -10,46 +10,41 @@ namespace Designer.Tests.Controllers.PreviewController { - public class InstanceForNextTaskTests : PreviewControllerTestsBase, IClassFixture> + public class InstanceForNextTaskTests(WebApplicationFactory factory) : PreviewControllerTestsBase(factory), IClassFixture> { - - public InstanceForNextTaskTests(WebApplicationFactory factory) : base(factory) - { - } - [Fact] public async Task Get_InstanceForNextProcess_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/instances/{PartyId}/{InstanceGuId}"; + string dataPathWithData = $"{Org}/{AppV3Path}/instances/{PartyId}/{V3InstanceId}"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3}&selectedLayoutSet="); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3Path}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); string responseBody = await response.Content.ReadAsStringAsync(); JsonDocument responseDocument = JsonDocument.Parse(responseBody); - Instance instance = JsonConvert.DeserializeObject(responseDocument.RootElement.ToString()); - Assert.Equal($"{PartyId}/{InstanceGuId}", instance.Id); - Assert.Equal("ttd", instance.Org); + Instance responseInstance = JsonConvert.DeserializeObject(responseDocument.RootElement.ToString()); + Assert.Equal(PartyId + "/" + V3InstanceId, responseInstance.Id); + Assert.Equal(Org, responseInstance.Org); } [Fact] public async Task Get_InstanceForNextTaskForV4App_Ok_TaskIsIncreased() { - string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{InstanceGuId}"; + Instance instance = await createInstance(); + string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet={LayoutSetName}"); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); string responseBody = await response.Content.ReadAsStringAsync(); JsonDocument responseDocument = JsonDocument.Parse(responseBody); - Instance instance = JsonConvert.DeserializeObject(responseDocument.RootElement.ToString()); - Assert.Equal($"{PartyId}/{InstanceGuId}", instance.Id); - Assert.Equal("ttd", instance.Org); - Assert.Equal("Task_1", instance.Process.CurrentTask.ElementId); + Instance responseInstance = JsonConvert.DeserializeObject(responseDocument.RootElement.ToString()); + Assert.Equal(instance.Id, responseInstance.Id); + Assert.Equal(Org, responseInstance.Org); + Assert.Equal(TaskId, instance.Process.CurrentTask.ElementId); } } } diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/InstancesTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/InstancesTests.cs deleted file mode 100644 index bbd1977ab98..00000000000 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/InstancesTests.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; -using Altinn.Platform.Storage.Interface.Models; -using Designer.Tests.Utils; -using FluentAssertions; -using Microsoft.AspNetCore.Mvc.Testing; -using Newtonsoft.Json; -using Xunit; - -namespace Designer.Tests.Controllers.PreviewController -{ - public class InstancesTests : PreviewControllerTestsBase, IClassFixture> - { - - public InstancesTests(WebApplicationFactory factory) : base(factory) - { - } - - [Fact] - public async Task Post_Instance_Ok() - { - string targetRepository = TestDataHelper.GenerateTestRepoName(); - await CopyRepositoryForTest(Org, AppV3, Developer, targetRepository); - - string dataPathWithData = $"{Org}/{targetRepository}/instances?instanceOwnerPartyId=51001"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3}&selectedLayoutSet="); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - JsonDocument responseDocument = JsonDocument.Parse(responseBody); - Instance instance = JsonConvert.DeserializeObject(responseDocument.RootElement.ToString()); - Assert.Equal("test-datatask-id", instance.Data[0].Id); - Assert.Equal("Task_1", instance.Process.CurrentTask.ElementId); - } - - [Fact] - public async Task Post_InstanceForV4App_Ok() - { - string targetRepository = TestDataHelper.GenerateTestRepoName(); - await CopyRepositoryForTest(Org, AppV4, Developer, targetRepository); - - string dataPathWithData = $"{Org}/{targetRepository}/instances?instanceOwnerPartyId=51001"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet={LayoutSetName}"); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - JsonDocument responseDocument = JsonDocument.Parse(responseBody); - Instance instance = JsonConvert.DeserializeObject(responseDocument.RootElement.ToString()); - Assert.Equal("test-datatask-id", instance.Data[0].Id); - Assert.Equal("Task_1", instance.Process.CurrentTask.ElementId); - } - - [Fact] - public async Task Post_InstanceForCustomReceipt_Ok() - { - string targetRepository = TestDataHelper.GenerateTestRepoName(); - await CopyRepositoryForTest(Org, AppV4, Developer, targetRepository); - - string dataPathWithData = $"{Org}/{targetRepository}/instances?instanceOwnerPartyId=51001"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet={CustomReceiptLayoutSetName}"); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - JsonDocument responseDocument = JsonDocument.Parse(responseBody); - Instance instance = JsonConvert.DeserializeObject(responseDocument.RootElement.ToString()); - Assert.Equal(CustomReceiptDataType, instance.Data[0].DataType); - Assert.Equal("EndEvent_1", instance.Process.EndEvent); - instance.Process.CurrentTask.Should().BeNull(); - } - } -} diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/KeepAliveTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/KeepAliveTests.cs index 2b70f87910c..0eb818f3827 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/KeepAliveTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/KeepAliveTests.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Testing; @@ -16,8 +17,9 @@ public KeepAliveTests(WebApplicationFactory factory) : base(factory) [Fact] public async Task Get_KeepAlive_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/api/authentication/keepAlive"; + string dataPathWithData = $"{Org}/{AppV4}/api/authentication/keepAlive"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/LayoutSetsTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/LayoutSetsTests.cs index 10d693b8af9..9f171ef582f 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/LayoutSetsTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/LayoutSetsTests.cs @@ -1,12 +1,8 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; -using System.Text.Json; using System.Threading.Tasks; -using Altinn.Studio.Designer.Models; -using Designer.Tests.Utils; -using FluentAssertions; using Microsoft.AspNetCore.Mvc.Testing; -using SharedResources.Tests; using Xunit; namespace Designer.Tests.Controllers.PreviewController @@ -17,30 +13,12 @@ public LayoutSetsTests(WebApplicationFactory factory) : base(factory) { } - [Fact] - public async Task Get_LayoutSets_ShouldReturnLayoutSetsWithoutAnyUndefinedDataTypes() - { - string actualLayoutSetsString = TestDataHelper.GetFileFromRepo(Org, AppV4, Developer, "App/ui/layout-sets.json"); - LayoutSets actualLayoutSets = JsonSerializer.Deserialize(actualLayoutSetsString); - - string dataPathWithData = $"{Org}/{AppV4}/api/layoutsets"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - LayoutSets responseLayoutSets = JsonSerializer.Deserialize(responseBody); - - actualLayoutSets.Sets.Exists(set => set.DataType is null).Should().BeTrue(); - responseLayoutSets.Sets.Exists(set => set.DataType is null).Should().BeFalse(); - } - [Fact] public async Task Get_LayoutSets_NotFound() { - string dataPathWithData = $"{Org}/{AppV3}/api/layoutsets"; + string dataPathWithData = $"{Org}/{AppV4}/api/layoutsets"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3Path}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/LayoutSettingsTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/LayoutSettingsTests.cs index 795418d82e1..16f328fbc44 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/LayoutSettingsTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/LayoutSettingsTests.cs @@ -1,4 +1,5 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using Designer.Tests.Utils; @@ -9,12 +10,10 @@ namespace Designer.Tests.Controllers.PreviewController { - public class LayoutSettingsTests : PreviewControllerTestsBase, IClassFixture> + public class LayoutSettingsTests( + WebApplicationFactory factory + ) : PreviewControllerTestsBase(factory), IClassFixture> { - public LayoutSettingsTests(WebApplicationFactory factory) : base(factory) - { - } - [Fact] public async Task Get_LayoutSettings_Ok() { @@ -22,6 +21,7 @@ public async Task Get_LayoutSettings_Ok() string dataPathWithData = $"{Org}/{AppV3}/api/layoutsettings"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/PostAttachmentTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/PostAttachmentTests.cs index c01b4ddbd3f..09f39c27ef1 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/PostAttachmentTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/PostAttachmentTests.cs @@ -2,6 +2,7 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; +using Altinn.Platform.Storage.Interface.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Testing; using Xunit; @@ -18,9 +19,9 @@ public PostAttachmentTests(WebApplicationFactory factory) : base(factor [Fact] public async Task Post_Attachment_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/instances/{PartyId}/{InstanceGuId}/data?dataType=FileUploadId"; + Instance instance = await createInstance(); + string dataPathWithData = $"{Org}/{AppV3Path}/instances/{PartyId}/{instance.Id}/data?dataType=FileUploadId"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.Created, response.StatusCode); @@ -29,9 +30,9 @@ public async Task Post_Attachment_Ok() [Fact] public async Task Post_AttachmentForV4App_Ok() { - string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{InstanceGuId}/data?dataType=FileUploadId"; + Instance instance = await createInstance(); + string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/data?dataType=FileUploadId"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet={LayoutSetName}"); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(StatusCodes.Status201Created, (int)response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/PreviewControllerTestsBase.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/PreviewControllerTestsBase.cs index b0ca17543ea..0f24aa57773 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/PreviewControllerTestsBase.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/PreviewControllerTestsBase.cs @@ -1,28 +1,31 @@ using System.Linq; -using System.Text.Encodings.Web; +using System.Net; +using System.Net.Http; using System.Text.Json; -using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Altinn.Platform.Storage.Interface.Models; using Designer.Tests.Controllers.ApiTests; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; +using Xunit; namespace Designer.Tests.Controllers.PreviewController { - public class PreviewControllerTestsBase : DesignerEndpointsTestsBase + public class PreviewControllerTestsBase(WebApplicationFactory factory) : DesignerEndpointsTestsBase(factory) where TTestClass : class { protected const string Org = "ttd"; protected const string AppV3 = "app-without-layoutsets"; + protected const string AppV3Path = "app-without-layoutsets/V3"; protected const string AppV4 = "app-with-layoutsets"; protected const string PreviewApp = "preview-app"; protected const string Developer = "testUser"; protected const string LayoutSetName = "layoutSet1"; protected const string LayoutSetName2 = "layoutSet2"; - protected const string CustomReceiptLayoutSetName = "receipt"; - protected const string CustomReceiptDataType = "datamodel"; protected const string PartyId = "51001"; - protected const string InstanceGuId = "f1e23d45-6789-1bcd-8c34-56789abcdef0"; + protected const string V3InstanceId = "f1e23d45-6789-1bcd-8c34-56789abcdef0"; + protected const string TaskId = "Task_1"; protected const string AttachmentGuId = "f47ac10b-58cc-4372-a567-0e02b2c3d479"; protected const string MockedReferrerUrl = "https://studio-mock-url.no"; @@ -39,10 +42,29 @@ protected override void ConfigureTestServices(IServiceCollection services) services.AddDistributedMemoryCache(); } - public PreviewControllerTestsBase(WebApplicationFactory factory) : base(factory) + protected async Task createInstance() { + string dataPath = $"{Org}/{AppV4}/instances?instanceOwnerPartyId={PartyId}&taskId={TaskId}"; + using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPath); + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + string responseBody = await response.Content.ReadAsStringAsync(); + Instance dataItem = JsonSerializer.Deserialize(responseBody, JsonSerializerOptions); + Assert.NotNull(dataItem); + return dataItem; } + protected async Task createDataElement(Instance instance, string dataType) + { + string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/data?dataType={dataType}"; + using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData); + using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + string responseBody = await response.Content.ReadAsStringAsync(); + DataElement dataElement = JsonSerializer.Deserialize(responseBody, JsonSerializerOptions); + Assert.NotNull(dataElement); + return dataElement; + } } } diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/ProcessNextTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/ProcessNextTests.cs index 9921c5ad3e4..43c07946b6f 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/ProcessNextTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/ProcessNextTests.cs @@ -10,19 +10,15 @@ namespace Designer.Tests.Controllers.PreviewController { - public class ProcessNextTests : PreviewControllerTestsBase, IClassFixture> + public class ProcessNextTests(WebApplicationFactory factory) + : PreviewControllerTestsBase(factory), IClassFixture> { - - public ProcessNextTests(WebApplicationFactory factory) : base(factory) - { - } - [Fact] public async Task Get_ProcessNext_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/instances/{PartyId}/{InstanceGuId}/process/next"; + string dataPathWithData = $"{Org}/{AppV3Path}/instances/{PartyId}/{V3InstanceId}/process/next"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3}&selectedLayoutSet="); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3Path}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -31,15 +27,15 @@ public async Task Get_ProcessNext_Ok() JsonDocument responseDocument = JsonDocument.Parse(responseBody); ProcessState processState = JsonConvert.DeserializeObject(responseDocument.RootElement.ToString()); Assert.Equal("data", processState.CurrentTask.AltinnTaskType); - Assert.Equal("Task_1", processState.CurrentTask.ElementId); + Assert.Equal(TaskId, processState.CurrentTask.ElementId); } [Fact] public async Task Get_ProcessNextForV4App_Ok() { - string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{InstanceGuId}/process/next"; + Instance instance = await createInstance(); + string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/process/next"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet={LayoutSetName}"); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -48,7 +44,7 @@ public async Task Get_ProcessNextForV4App_Ok() JsonDocument responseDocument = JsonDocument.Parse(responseBody); ProcessState processState = JsonConvert.DeserializeObject(responseDocument.RootElement.ToString()); Assert.Equal("data", processState.CurrentTask.AltinnTaskType); - Assert.Equal("Task_1", processState.CurrentTask.ElementId); + Assert.Equal(TaskId, processState.CurrentTask.ElementId); } } } diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/ProcessTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/ProcessTests.cs deleted file mode 100644 index d3de435bb51..00000000000 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/ProcessTests.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; -using Altinn.App.Core.Internal.Process.Elements; -using Altinn.Platform.Storage.Interface.Models; -using FluentAssertions; -using Microsoft.AspNetCore.Mvc.Testing; -using Xunit; - -namespace Designer.Tests.Controllers.PreviewController -{ - public class ProcessTests : PreviewControllerTestsBase, IClassFixture> - { - - public ProcessTests(WebApplicationFactory factory) : base(factory) - { - } - - [Fact] - public async Task Get_Process_Ok() - { - string dataPathWithData = $"{Org}/{AppV3}/instances/{PartyId}/{InstanceGuId}/process"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3}&selectedLayoutSet="); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - ProcessState processState = JsonSerializer.Deserialize(responseBody, JsonSerializerOptions); - Assert.Equal("data", processState.CurrentTask.AltinnTaskType); - Assert.Equal("Task_1", processState.CurrentTask.ElementId); - } - - [Fact] - public async Task Get_ProcessForV4App_Ok() - { - string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{InstanceGuId}/process"; - using HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet={LayoutSetName}"); - - using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - AppProcessState processState = - JsonSerializer.Deserialize(responseBody, JsonSerializerOptions); - var expectedProcessTasks = new List - { - new AppProcessTaskTypeInfo - { - ElementId = "Task_1", - AltinnTaskType = "data" - }, - new AppProcessTaskTypeInfo - { - ElementId = "Task_2", - AltinnTaskType = "data" - }, - new AppProcessTaskTypeInfo - { - ElementId = "Task_3", - AltinnTaskType = "data" - } - }; - Assert.Equal("data", processState.CurrentTask.AltinnTaskType); - Assert.Equal("Task_1", processState.CurrentTask.ElementId); - expectedProcessTasks.Should().BeEquivalentTo(processState.ProcessTasks); - } - } -} diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/UpdateFormDataTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/UpdateFormDataTests.cs index 1402307492f..75d062056a5 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/UpdateFormDataTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/UpdateFormDataTests.cs @@ -1,6 +1,7 @@ using System; using System.Net.Http; using System.Threading.Tasks; +using Altinn.Platform.Storage.Interface.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Testing; using Xunit; @@ -17,9 +18,9 @@ public UpdateFormDataTests(WebApplicationFactory factory) : base(factor [Fact] public async Task Put_UpdateFormData_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/instances/{PartyId}/{InstanceGuId}/data/test-datatask-id"; + Instance instance = await createInstance(); + string dataPathWithData = $"{Org}/{AppV3Path}/instances/{PartyId}/{instance.Id}/data/test-datatask-id"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Put, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(StatusCodes.Status200OK, (int)response.StatusCode); diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/UpdateProcessNextTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/UpdateProcessNextTests.cs index 430382d3cb6..698dfacb4e0 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/UpdateProcessNextTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/UpdateProcessNextTests.cs @@ -20,23 +20,21 @@ public UpdateProcessNextTests(WebApplicationFactory factory) : base(fac [Fact] public async Task Put_ProcessNext_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/instances/{PartyId}/{InstanceGuId}/process/next"; + Instance instance = await createInstance(); + string dataPathWithData = $"{Org}/{AppV3Path}/instances/{PartyId}/{instance.Id}/process/next"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Put, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3}&selectedLayoutSet="); + httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV3Path}&selectedLayoutSet="); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - string responseBody = await response.Content.ReadAsStringAsync(); - Assert.Equal(@"{""ended"": ""ended""}", responseBody); } [Fact] public async Task Put_ProcessNextForV4AppForNonExistingTask_Ok() { - string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{InstanceGuId}/process/next"; + Instance instance = await createInstance(); + string dataPathWithData = $"{Org}/{AppV4}/instances/{PartyId}/{instance.Id}/process/next"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Put, dataPathWithData); - httpRequestMessage.Headers.Referrer = new Uri($"{MockedReferrerUrl}?org={Org}&app={AppV4}&selectedLayoutSet={LayoutSetName}"); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -45,7 +43,7 @@ public async Task Put_ProcessNextForV4AppForNonExistingTask_Ok() JsonDocument responseDocument = JsonDocument.Parse(responseBody); ProcessState processState = JsonConvert.DeserializeObject(responseDocument.RootElement.ToString()); Assert.Equal("data", processState.CurrentTask.AltinnTaskType); - Assert.Equal("Task_1", processState.CurrentTask.ElementId); + Assert.Equal(TaskId, processState.CurrentTask.ElementId); } } } diff --git a/backend/tests/Designer.Tests/Controllers/PreviewController/ValidateInstantiationTests.cs b/backend/tests/Designer.Tests/Controllers/PreviewController/ValidateInstantiationTests.cs index 604395526d0..90ceb284e79 100644 --- a/backend/tests/Designer.Tests/Controllers/PreviewController/ValidateInstantiationTests.cs +++ b/backend/tests/Designer.Tests/Controllers/PreviewController/ValidateInstantiationTests.cs @@ -18,7 +18,7 @@ public ValidateInstantiationTests(WebApplicationFactory factory) : base [Fact] public async Task Post_ValidateInstantiation_Ok() { - string dataPathWithData = $"{Org}/{AppV3}/api/v1/parties/validateInstantiation"; + string dataPathWithData = $"{Org}/{AppV4}/api/v1/parties/validateInstantiation"; using HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, dataPathWithData); using HttpResponseMessage response = await HttpClient.SendAsync(httpRequestMessage); diff --git a/backend/tests/Designer.Tests/Controllers/ResourceAdminController/AccessListTests.cs b/backend/tests/Designer.Tests/Controllers/ResourceAdminController/AccessListTests.cs index 1ba2641b5b9..104b5e5e408 100644 --- a/backend/tests/Designer.Tests/Controllers/ResourceAdminController/AccessListTests.cs +++ b/backend/tests/Designer.Tests/Controllers/ResourceAdminController/AccessListTests.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Net; +using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; diff --git a/backend/tests/Designer.Tests/Controllers/ResourceAdminController/AddResourceTests.cs b/backend/tests/Designer.Tests/Controllers/ResourceAdminController/AddResourceTests.cs index 184b9077f23..82d2e4a4884 100644 --- a/backend/tests/Designer.Tests/Controllers/ResourceAdminController/AddResourceTests.cs +++ b/backend/tests/Designer.Tests/Controllers/ResourceAdminController/AddResourceTests.cs @@ -34,6 +34,7 @@ public async Task AddServiceResource_StatusCreated() RightDescription = new Dictionary { { "en", "Access Management" }, { "no", "Tilgangsstyring" } }, Homepage = "test.no", Status = "Active", + ContactPoints = null, IsPartOf = "Altinn", ThematicArea = "", ResourceReferences = GetTestResourceReferences(), diff --git a/backend/tests/Designer.Tests/Helpers/WebScrapingUtils.cs b/backend/tests/Designer.Tests/Helpers/WebScrapingUtils.cs index 681b22fce6f..a54b54cd4a6 100644 --- a/backend/tests/Designer.Tests/Helpers/WebScrapingUtils.cs +++ b/backend/tests/Designer.Tests/Helpers/WebScrapingUtils.cs @@ -10,7 +10,7 @@ public static class WebScrapingUtils /// The HTML content from which the substring will be extracted. /// The text that appears immediately before the desired substring. If there are multiple matches, only first will be considered. /// The text that appears immediately after the desired substring. - /// The extracted substring between and if they are found in the HTML content and are in the correct order. + /// The extracted substring between "beforeText" and "afterText" if they are found in the HTML content and are in the correct order. /// Returns null if the conditions are not met. /// public static string ExtractTextBetweenMarkers(string htmlContent, string beforeText, string afterText) diff --git a/backend/tests/Designer.Tests/Services/AppDevelopmentServiceTest.cs b/backend/tests/Designer.Tests/Services/AppDevelopmentServiceTest.cs index 03175e8fc92..629ec88fd7d 100644 --- a/backend/tests/Designer.Tests/Services/AppDevelopmentServiceTest.cs +++ b/backend/tests/Designer.Tests/Services/AppDevelopmentServiceTest.cs @@ -3,6 +3,7 @@ using System.IO; using System.Text.Json.Nodes; using System.Threading.Tasks; +using Altinn.Common.EFormidlingClient.Models.SBD; using Altinn.Studio.Designer.Exceptions.AppDevelopment; using Altinn.Studio.Designer.Factories; using Altinn.Studio.Designer.Models; @@ -203,7 +204,7 @@ public async Task AddLayoutSet_WhenAppHasNoLayoutSets_ShouldThrowFileNotFoundExc CreatedTestRepoPath = await TestDataHelper.CopyRepositoryForTest(_org, repository, _developer, targetRepository); // Act - Func act = async () => await _appDevelopmentService.AddLayoutSet(AltinnRepoEditingContext.FromOrgRepoDeveloper(_org, targetRepository, _developer), new LayoutSetConfig()); + Func act = async () => await _appDevelopmentService.AddLayoutSet(AltinnRepoEditingContext.FromOrgRepoDeveloper(_org, targetRepository, _developer), new() { Id = "layoutSet1" }); // Assert await act.Should().ThrowAsync(); diff --git a/backend/tests/Designer.Tests/Services/OptionsServiceTests.cs b/backend/tests/Designer.Tests/Services/OptionsServiceTests.cs index 3d787539035..6404416c048 100644 --- a/backend/tests/Designer.Tests/Services/OptionsServiceTests.cs +++ b/backend/tests/Designer.Tests/Services/OptionsServiceTests.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Altinn.Studio.Designer.Factories; using Altinn.Studio.Designer.Models; +using Altinn.Studio.Designer.Models.Dto; using Altinn.Studio.Designer.Services.Implementation; using Designer.Tests.Utils; using LibGit2Sharp; @@ -238,18 +239,69 @@ public async Task OptionListsExists_ShouldReturnFalse_WhenOptionsListDoesNotExis // Arrange const string repo = "empty-app"; const string optionListId = "test-options"; - string targetRepository = TestDataHelper.GenerateTestRepoName(); - await TestDataHelper.CopyRepositoryForTest(Org, repo, Developer, targetRepository); var optionsService = GetOptionsServiceForTest(); // Act - bool optionListExists = await optionsService.OptionsListExists(Org, targetRepository, Developer, optionListId); + bool optionListExists = await optionsService.OptionsListExists(Org, repo, Developer, optionListId); // Assert Assert.False(optionListExists); } + [Fact] + public async Task GetAllOptionListReferences_ShouldReturnAllReferences_WhenOptionsListExists() + { + // Arrange + const string repo = "app-with-options"; + var optionsService = GetOptionsServiceForTest(); + + // Act + List optionListsReferences = await optionsService.GetAllOptionListReferences(AltinnRepoEditingContext.FromOrgRepoDeveloper(Org, repo, Developer)); + + List expectedResponseList = new() + { + new RefToOptionListSpecifier + { + OptionListId = "test-options", OptionListIdSources = + [ + new OptionListIdSource + { + ComponentIds = ["component-using-same-options-id-in-same-set-and-another-layout"], + LayoutName = "layoutWithOneOptionListIdRef.json", + LayoutSetId = "layoutSet1" + }, + new OptionListIdSource + { + ComponentIds = ["component-using-test-options-id", "component-using-test-options-id-again"], + LayoutName = "layoutWithFourCheckboxComponentsAndThreeOptionListIdRefs.json", + LayoutSetId = "layoutSet1" + }, + new OptionListIdSource + { + ComponentIds = ["component-using-same-options-id-in-another-set"], + LayoutName = "layoutWithTwoOptionListIdRefs.json", + LayoutSetId = "layoutSet2" + } + ] + }, + new() + { + OptionListId = "other-options", OptionListIdSources = + [ + new OptionListIdSource + { + ComponentIds = ["component-using-other-options-id"], + LayoutName = "layoutWithFourCheckboxComponentsAndThreeOptionListIdRefs.json", + LayoutSetId = "layoutSet1" + } + ] + } + }; + // Assert + Assert.Equivalent(optionListsReferences, expectedResponseList); + } + private static OptionsService GetOptionsServiceForTest() { AltinnGitRepositoryFactory altinnGitRepositoryFactory = new(TestDataHelper.GetTestDataRepositoriesRootDirectory()); diff --git a/backend/tests/Designer.Tests/Services/SchemaModelServiceTests.cs b/backend/tests/Designer.Tests/Services/SchemaModelServiceTests.cs index a4febf21116..344f341d896 100644 --- a/backend/tests/Designer.Tests/Services/SchemaModelServiceTests.cs +++ b/backend/tests/Designer.Tests/Services/SchemaModelServiceTests.cs @@ -15,6 +15,7 @@ using Altinn.Studio.Designer.Services.Interfaces; using Designer.Tests.Utils; using FluentAssertions; +using Moq; using SharedResources.Tests; using Xunit; @@ -22,6 +23,17 @@ namespace Designer.Tests.Services { public class SchemaModelServiceTests { + private readonly Mock _applicationMetadataService; + private readonly AltinnGitRepositoryFactory _altinnGitRepositoryFactory; + private readonly ISchemaModelService _schemaModelService; + + public SchemaModelServiceTests() + { + _applicationMetadataService = new Mock(); + _altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); + _schemaModelService = new SchemaModelService(_altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter, _applicationMetadataService.Object); + } + [Fact] public async Task DeleteSchema_AppRepo_ShouldDelete() { @@ -35,22 +47,19 @@ public async Task DeleteSchema_AppRepo_ShouldDelete() await TestDataHelper.CopyRepositoryForTest(org, sourceRepository, developer, targetRepository); try { - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - - ISchemaModelService schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter); - var schemaFiles = schemaModelService.GetSchemaFiles(editingContext); + var schemaFiles = _schemaModelService.GetSchemaFiles(editingContext); schemaFiles.Should().HaveCount(7); - var altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, targetRepository, developer); + var altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, targetRepository, developer); var applicationMetadata = await altinnAppGitRepository.GetApplicationMetadata(); applicationMetadata.DataTypes.Should().HaveCount(2); // Act var schemaToDelete = schemaFiles.First(s => s.FileName == "Kursdomene_HvemErHvem_M_2021-04-08_5742_34627_SERES.schema.json"); - await schemaModelService.DeleteSchema(editingContext, schemaToDelete.RepositoryRelativeUrl); + await _schemaModelService.DeleteSchema(editingContext, schemaToDelete.RepositoryRelativeUrl); // Assert - schemaFiles = schemaModelService.GetSchemaFiles(editingContext); + schemaFiles = _schemaModelService.GetSchemaFiles(editingContext); schemaFiles.Should().HaveCount(6); applicationMetadata = await altinnAppGitRepository.GetApplicationMetadata(); applicationMetadata.DataTypes.Should().HaveCount(1); @@ -75,22 +84,20 @@ public async Task DeleteSchema_AppRepoWithLayoutSets_ShouldDelete() try { string dataModelName = "datamodel"; - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - ISchemaModelService schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter); - var schemaFiles = schemaModelService.GetSchemaFiles(editingContext); + var schemaFiles = _schemaModelService.GetSchemaFiles(editingContext); schemaFiles.Should().HaveCount(1); - var altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, targetRepository, developer); + var altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, targetRepository, developer); var applicationMetadataBefore = await altinnAppGitRepository.GetApplicationMetadata(); var layoutSetsBefore = await altinnAppGitRepository.GetLayoutSetsFile(); // Act var schemaToDelete = schemaFiles.First(s => s.FileName == $"{dataModelName}.schema.json"); - await schemaModelService.DeleteSchema(editingContext, schemaToDelete.RepositoryRelativeUrl); + await _schemaModelService.DeleteSchema(editingContext, schemaToDelete.RepositoryRelativeUrl); // Assert - schemaFiles = schemaModelService.GetSchemaFiles(editingContext); + schemaFiles = _schemaModelService.GetSchemaFiles(editingContext); schemaFiles.Should().HaveCount(0); var applicationMetadataAfter = await altinnAppGitRepository.GetApplicationMetadata(); applicationMetadataAfter.DataTypes.Should().HaveCount(applicationMetadataBefore.DataTypes.Count - 1); @@ -117,18 +124,16 @@ public async Task DeleteSchema_ModelsRepo_ShouldDelete() await TestDataHelper.CopyRepositoryForTest(org, sourceRepository, developer, targetRepository); try { - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - ISchemaModelService schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter); - var schemaFiles = schemaModelService.GetSchemaFiles(editingContext); + var schemaFiles = _schemaModelService.GetSchemaFiles(editingContext); schemaFiles.Should().HaveCount(6); // Act var schemaToDelete = schemaFiles.First(s => s.FileName == "Kursdomene_HvemErHvem_M_2021-04-08_5742_34627_SERES.schema.json"); - await schemaModelService.DeleteSchema(editingContext, schemaToDelete.RepositoryRelativeUrl); + await _schemaModelService.DeleteSchema(editingContext, schemaToDelete.RepositoryRelativeUrl); // Assert - schemaFiles = schemaModelService.GetSchemaFiles(editingContext); + schemaFiles = _schemaModelService.GetSchemaFiles(editingContext); schemaFiles.Should().HaveCount(5); } finally @@ -150,15 +155,13 @@ public async Task UpdateSchema_AppRepo_ShouldUpdate() await TestDataHelper.CopyRepositoryForTest(org, sourceRepository, developer, targetRepository); try { - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); // Act - ISchemaModelService schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter); var expectedSchemaUpdates = @"{""properties"":{""rootType1"":{""$ref"":""#/definitions/rootType""}},""definitions"":{""rootType"":{""properties"":{""keyword"":{""type"":""string""}}}}}"; - await schemaModelService.UpdateSchema(editingContext, "App/models/HvemErHvem_SERES.schema.json", expectedSchemaUpdates); + await _schemaModelService.UpdateSchema(editingContext, "App/models/HvemErHvem_SERES.schema.json", expectedSchemaUpdates); // Assert - var altinnGitRepository = altinnGitRepositoryFactory.GetAltinnGitRepository(org, targetRepository, developer); + var altinnGitRepository = _altinnGitRepositoryFactory.GetAltinnGitRepository(org, targetRepository, developer); var updatedSchema = await altinnGitRepository.ReadTextByRelativePathAsync("App/models/HvemErHvem_SERES.schema.json"); string serializedExpectedSchemaUpdates = FormatJsonString(updatedSchema); @@ -199,13 +202,11 @@ public async Task UpdateSchema_ModelMetadataExistForModelInRepo_ShouldDeleteMode await TestDataHelper.CopyRepositoryForTest(org, sourceRepository, developer, targetRepository); try { - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - ISchemaModelService schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter); var updatedSchema = @"{""properties"":{""rootType1"":{""$ref"":""#/definitions/rootType""}},""definitions"":{""rootType"":{""properties"":{""keyword"":{""type"":""string""}}}}}"; - var altinnGitRepository = altinnGitRepositoryFactory.GetAltinnGitRepository(org, targetRepository, developer); + var altinnGitRepository = _altinnGitRepositoryFactory.GetAltinnGitRepository(org, targetRepository, developer); Assert.True(altinnGitRepository.FileExistsByRelativePath("App/models/Kursdomene_HvemErHvem_M_2021-04-08_5742_34627_SERES.metadata.json")); - await schemaModelService.UpdateSchema(editingContext, "App/models/Kursdomene_HvemErHvem_M_2021-04-08_5742_34627_SERES.schema.json", updatedSchema); + await _schemaModelService.UpdateSchema(editingContext, "App/models/Kursdomene_HvemErHvem_M_2021-04-08_5742_34627_SERES.schema.json", updatedSchema); Assert.False(altinnGitRepository.FileExistsByRelativePath("App/models/Kursdomene_HvemErHvem_M_2021-04-08_5742_34627_SERES.metadata.json")); } finally @@ -227,13 +228,11 @@ public async Task UpdateSchema_NoModelMetadataForModelInRepo_ShouldOnlyUpdate() await TestDataHelper.CopyRepositoryForTest(org, sourceRepository, developer, targetRepository); try { - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - ISchemaModelService schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter); var expectedUpdatedSchema = @"{""properties"":{""rootType1"":{""$ref"":""#/definitions/rootType""}},""definitions"":{""rootType"":{""properties"":{""keyword"":{""type"":""string""}}}}}"; - var altinnGitRepository = altinnGitRepositoryFactory.GetAltinnGitRepository(org, targetRepository, developer); + var altinnGitRepository = _altinnGitRepositoryFactory.GetAltinnGitRepository(org, targetRepository, developer); Assert.False(altinnGitRepository.FileExistsByRelativePath("App/models/HvemErHvem_SERES.metadata.json")); - await schemaModelService.UpdateSchema(editingContext, "App/models/HvemErHvem_SERES.schema.json", expectedUpdatedSchema); + await _schemaModelService.UpdateSchema(editingContext, "App/models/HvemErHvem_SERES.schema.json", expectedUpdatedSchema); Assert.False(altinnGitRepository.FileExistsByRelativePath("App/models/HvemErHvem_SERES.metadata.json")); var updatedSchema = await altinnGitRepository.ReadTextByRelativePathAsync("App/models/HvemErHvem_SERES.schema.json"); string serializedExpectedSchemaUpdates = FormatJsonString(updatedSchema); @@ -257,37 +256,18 @@ public async Task UpdateSchema_InvalidJsonSchema_ShouldThrowException() await TestDataHelper.CopyRepositoryForTest(org, sourceRepository, developer, targetRepository); - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - - ISchemaModelService schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, - TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, - TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, - TestDataHelper.ModelMetadataToCsharpConverter); var invalidSchema = @"{""properties"":{""root"":{""$ref"":""#/definitions/rootType""}},""definitions"":{""rootType"":{""properties"":{""keyword"":{""type"":""string""}}}}}"; var exception = await Assert.ThrowsAsync(async () => { - await schemaModelService.UpdateSchema(editingContext, "App/models/HvemErHvem_SERES.schema.json", invalidSchema); + await _schemaModelService.UpdateSchema(editingContext, "App/models/HvemErHvem_SERES.schema.json", invalidSchema); }); Assert.NotNull(exception.CustomErrorMessages); exception.CustomErrorMessages.Should().ContainSingle(c => c.Contains("root': member names cannot be the same as their enclosing type")); } - [Theory] - [InlineData("ttd", "apprepo", "test", "", "http://studio.localhost/repos")] - [InlineData("ttd", "apprepo", "test", "/path/to/folder/", "http://studio.localhost/repos")] - public void GetSchemaUri_ValidNameProvided_ShouldReturnUri(string org, string repository, string schemaName, string relativePath, string repositoryBaseUrl) - { - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - var schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter); - - var schemaUri = schemaModelService.GetSchemaUri(org, repository, schemaName, relativePath); - - schemaUri.AbsoluteUri.Should().Be($"{repositoryBaseUrl}/{org}/{repository}{(string.IsNullOrEmpty(relativePath) ? "/" : relativePath)}{schemaName}.schema.json"); - } - [Fact] public async Task UploadSchemaFromXsd_InvalidXsd_ThrowsException() { @@ -301,13 +281,11 @@ public async Task UploadSchemaFromXsd_InvalidXsd_ThrowsException() await TestDataHelper.CopyRepositoryForTest(org, sourceRepository, developer, targetRepository); try { - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - ISchemaModelService schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter); var xsdStream = SharedResourcesHelper.LoadTestData("Model/XmlSchema/General/SimpleInvalidNonSeresSchema.xsd"); var schemaName = "SimpleInvalidNonSeresSchema"; var fileName = $"{schemaName}.xsd"; - Func action = () => schemaModelService.BuildSchemaFromXsd(editingContext, fileName, xsdStream); + Func action = () => _schemaModelService.BuildSchemaFromXsd(editingContext, fileName, xsdStream); // Act/assert await action.Should().ThrowAsync(); @@ -333,8 +311,6 @@ public async Task UploadSchemaFromXsd_ValidNonSeresXsd_ModelsCreated() await TestDataHelper.CopyRepositoryForTest(org, sourceRepository, developer, targetRepository); try { - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - ISchemaModelService schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter); var xsdStream = SharedResourcesHelper.LoadTestData("Model/XmlSchema/General/SimpleValidNonSeresSchema.xsd"); var schemaName = "SimpleValidNonSeresSchema"; var fileName = $"{schemaName}.xsd"; @@ -342,10 +318,10 @@ public async Task UploadSchemaFromXsd_ValidNonSeresXsd_ModelsCreated() var relativeFilePath = $"{relativeDirectory}/{fileName}"; // Act - await schemaModelService.BuildSchemaFromXsd(editingContext, fileName, xsdStream); + await _schemaModelService.BuildSchemaFromXsd(editingContext, fileName, xsdStream); // Assert - var altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, targetRepository, developer); + var altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, targetRepository, developer); altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.metadata.json").Should().BeFalse(); altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.schema.json").Should().BeTrue(); altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.cs").Should().BeTrue(); @@ -371,8 +347,6 @@ public async Task UploadSchemaFromXsd_OED_ModelsCreated() await TestDataHelper.CopyRepositoryForTest(org, sourceRepository, developer, targetRepository); try { - var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); - ISchemaModelService schemaModelService = new SchemaModelService(altinnGitRepositoryFactory, TestDataHelper.LogFactory, TestDataHelper.ServiceRepositorySettings, TestDataHelper.XmlSchemaToJsonSchemaConverter, TestDataHelper.JsonSchemaToXmlSchemaConverter, TestDataHelper.ModelMetadataToCsharpConverter); var xsdStream = SharedResourcesHelper.LoadTestData("Model/XmlSchema/Gitea/OED.xsd"); var schemaName = "OED_M"; var fileName = $"{schemaName}.xsd"; @@ -380,10 +354,10 @@ public async Task UploadSchemaFromXsd_OED_ModelsCreated() var relativeFilePath = $"{relativeDirectory}/{fileName}"; // Act - await schemaModelService.BuildSchemaFromXsd(editingContext, fileName, xsdStream); + await _schemaModelService.BuildSchemaFromXsd(editingContext, fileName, xsdStream); // Assert - var altinnAppGitRepository = altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, targetRepository, developer); + var altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(org, targetRepository, developer); altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.metadata.json").Should().BeFalse(); altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.schema.json").Should().BeTrue(); altinnAppGitRepository.FileExistsByRelativePath($"{relativeDirectory}/{schemaName}.xsd").Should().BeTrue(); diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-options/App/ui/layoutSet1/layouts/layoutWithFourCheckboxComponentsAndThreeOptionListIdRefs.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-options/App/ui/layoutSet1/layouts/layoutWithFourCheckboxComponentsAndThreeOptionListIdRefs.json new file mode 100644 index 00000000000..afc15b4d56a --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-options/App/ui/layoutSet1/layouts/layoutWithFourCheckboxComponentsAndThreeOptionListIdRefs.json @@ -0,0 +1,34 @@ +{ + "schema": "https://altinncdn.no/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "id": "component-using-manual-options", + "type": "Checkboxes", + "options": [ + { + "value": "1", + "label": "id-used-by-options", + "helpText": "help-text-used-by-options", + "description": "description-used-by-options" + } + ] + }, + { + "id": "component-using-test-options-id", + "type": "Checkboxes", + "optionsId": "test-options" + }, + { + "id": "component-using-test-options-id-again", + "type": "Checkboxes", + "optionsId": "test-options" + }, + { + "id": "component-using-other-options-id", + "type": "Checkboxes", + "optionsId": "other-options" + } + ] + } +} diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-options/App/ui/layoutSet1/layouts/layoutWithOneOptionListIdRef.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-options/App/ui/layoutSet1/layouts/layoutWithOneOptionListIdRef.json new file mode 100644 index 00000000000..65b1de4f879 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-options/App/ui/layoutSet1/layouts/layoutWithOneOptionListIdRef.json @@ -0,0 +1,12 @@ +{ + "schema": "https://altinncdn.no/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "id": "component-using-same-options-id-in-same-set-and-another-layout", + "type": "Checkboxes", + "optionsId": "test-options" + } + ] + } +} diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-options/App/ui/layoutSet2/layouts/layoutWithTwoOptionListIdRefs.json b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-options/App/ui/layoutSet2/layouts/layoutWithTwoOptionListIdRefs.json new file mode 100644 index 00000000000..3a81c463d6a --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-options/App/ui/layoutSet2/layouts/layoutWithTwoOptionListIdRefs.json @@ -0,0 +1,17 @@ +{ + "schema": "https://altinncdn.no/schemas/json/layout/layout.schema.v1.json", + "data": { + "layout": [ + { + "id": "component-using-same-options-id-in-another-set", + "type": "Checkboxes", + "optionsId": "test-options" + }, + { + "id": "component-using-a-custom-options-id", + "type": "Checkboxes", + "optionsId": "custom-options-id" + } + ] + } +} diff --git a/development/azure-devops-mock/package.json b/development/azure-devops-mock/package.json index 3594cd34bc4..56c1da5a7d8 100644 --- a/development/azure-devops-mock/package.json +++ b/development/azure-devops-mock/package.json @@ -8,7 +8,7 @@ "cors": "2.8.5", "express": "4.21.2", "morgan": "1.10.0", - "nodemon": "3.1.7", + "nodemon": "3.1.9", "p-queue": "8.0.1" }, "license": "MIT", diff --git a/eidlogger/pom.xml b/eidlogger/pom.xml index fee662753ad..dcc55126bbc 100644 --- a/eidlogger/pom.xml +++ b/eidlogger/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.4.0 + 3.4.1 no.altinn @@ -17,7 +17,7 @@ 21 2.7.0 1.2.2 - 5.18.0 + 5.19.0 diff --git a/frontend/app-preview/src/components/PreviewControlHeader/PreviewControlHeader.test.tsx b/frontend/app-preview/src/components/PreviewControlHeader/PreviewControlHeader.test.tsx index 1ffd699e473..66031450194 100644 --- a/frontend/app-preview/src/components/PreviewControlHeader/PreviewControlHeader.test.tsx +++ b/frontend/app-preview/src/components/PreviewControlHeader/PreviewControlHeader.test.tsx @@ -10,7 +10,6 @@ import { type ServicesContextProps } from 'app-shared/contexts/ServicesContext'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import { type QueryClient } from '@tanstack/react-query'; import { QueryKey } from 'app-shared/types/QueryKey'; -import { useInstanceIdQuery } from 'app-shared/hooks/queries'; // Move jest.mock('app-shared/hooks/queries'); @@ -122,7 +121,6 @@ describe('PreviewControlHeader', () => { }); it('should not render the layout sets dropdown if layoutSets is not available', () => { - (useInstanceIdQuery as jest.Mock).mockReturnValue(mockLayoutId); renderPreviewControlHeader(); expect(screen.queryByRole('combobox')).not.toBeInTheDocument(); diff --git a/frontend/app-preview/src/hooks/useBackToEditingHref/useBackToEditingHref.test.tsx b/frontend/app-preview/src/hooks/useBackToEditingHref/useBackToEditingHref.test.tsx index 8e6421493ba..adc9f0d0dad 100644 --- a/frontend/app-preview/src/hooks/useBackToEditingHref/useBackToEditingHref.test.tsx +++ b/frontend/app-preview/src/hooks/useBackToEditingHref/useBackToEditingHref.test.tsx @@ -5,7 +5,7 @@ import { app, org } from '@studio/testing/testids'; import { RoutePaths } from 'app-development/enums/RoutePaths'; const mockLayoutId: string = 'layout1'; -const mockUiEditorPath: string = `/editor/${org}/${app}/${RoutePaths.UIEditor}?layout=${mockLayoutId}`; +const mockUiEditorPath: string = `/editor/${org}/${app}/${RoutePaths.UIEditor}`; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), diff --git a/frontend/app-preview/src/hooks/useBackToEditingHref/useBackToEditingHref.tsx b/frontend/app-preview/src/hooks/useBackToEditingHref/useBackToEditingHref.tsx index fc666e20119..126eb470cbe 100644 --- a/frontend/app-preview/src/hooks/useBackToEditingHref/useBackToEditingHref.tsx +++ b/frontend/app-preview/src/hooks/useBackToEditingHref/useBackToEditingHref.tsx @@ -1,14 +1,10 @@ import { useStudioEnvironmentParams } from 'app-shared/hooks/useStudioEnvironmentParams'; import { PackagesRouter } from 'app-shared/navigation/PackagesRouter'; -import { typedLocalStorage } from '@studio/pure-functions'; -import { useInstanceIdQuery } from 'app-shared/hooks/queries'; export const useBackToEditingHref = () => { const { org, app } = useStudioEnvironmentParams(); - const { data: instanceId } = useInstanceIdQuery(org, app); const packagesRouter = new PackagesRouter({ org, app }); - const queryParams: string = `?layout=${typedLocalStorage.getItem(instanceId)}`; - return `${packagesRouter.getPackageNavigationUrl('editorUiEditor')}${queryParams}`; + return `${packagesRouter.getPackageNavigationUrl('editorUiEditor')}`; }; diff --git a/frontend/app-preview/src/views/LandingPage.test.tsx b/frontend/app-preview/src/views/LandingPage.test.tsx index dca0563d168..a883fec8749 100644 --- a/frontend/app-preview/src/views/LandingPage.test.tsx +++ b/frontend/app-preview/src/views/LandingPage.test.tsx @@ -19,6 +19,10 @@ jest.mock('react-router-dom', () => ({ }), })); +jest.mock('app-shared/api/mutations', () => ({ + createPreviewInstance: jest.fn().mockReturnValue(Promise.resolve({ id: 1 })), +})); + const mockGetItem = jest.fn(); Object.defineProperty(window, 'localStorage', { diff --git a/frontend/app-preview/src/views/LandingPage.tsx b/frontend/app-preview/src/views/LandingPage.tsx index a09a2930bff..758b4c6e892 100644 --- a/frontend/app-preview/src/views/LandingPage.tsx +++ b/frontend/app-preview/src/views/LandingPage.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import classes from './LandingPage.module.css'; import { useTranslation } from 'react-i18next'; import { usePreviewConnection } from 'app-shared/providers/PreviewConnectionContext'; @@ -16,6 +16,7 @@ import { useSelectedFormLayoutName } from 'app-shared/hooks/useSelectedFormLayou import { useSelectedFormLayoutSetName } from 'app-shared/hooks/useSelectedFormLayoutSetName'; import { useSelectedTaskId } from 'app-shared/hooks/useSelectedTaskId'; import { useLayoutSetsQuery } from 'app-shared/hooks/queries/useLayoutSetsQuery'; +import { useCreatePreviewInstanceMutation } from 'app-shared/hooks/mutations/useCreatePreviewInstanceMutation'; export type PreviewAsViewSize = 'desktop' | 'mobile'; @@ -26,17 +27,25 @@ export const LandingPage = () => { const previewConnection = usePreviewConnection(); const { data: user, isPending: isPendingUser } = useUserQuery(); const { data: repository } = useRepoMetadataQuery(org, app); - const { data: layoutSets, isPending: pendingLayoutsets } = useLayoutSetsQuery(org, app); const { selectedFormLayoutSetName, setSelectedFormLayoutSetName } = useSelectedFormLayoutSetName(layoutSets); - const { selectedFormLayoutName } = useSelectedFormLayoutName(selectedFormLayoutSetName); const [previewViewSize, setPreviewViewSize] = useLocalStorage( 'viewSize', 'desktop', ); const taskId = useSelectedTaskId(selectedFormLayoutSetName); + const { + mutate: createInstance, + data: instance, + isPending: instanceIsPending, + } = useCreatePreviewInstanceMutation(org, app); + + useEffect(() => { + if (user && taskId) createInstance({ partyId: user?.id, taskId: taskId }); + }, [createInstance, user, taskId]); + const isIFrame = (input: HTMLElement | null): input is HTMLIFrameElement => input !== null && input.tagName === 'IFRAME'; @@ -59,9 +68,17 @@ export const LandingPage = () => { }); } - if (isPendingUser || pendingLayoutsets) + if (isPendingUser || pendingLayoutsets || instanceIsPending) return ; + const previewUrl = previewPage( + org, + app, + getSelectedFormLayoutSetName(selectedFormLayoutSetName), + taskId, + selectedFormLayoutName, + instance?.id, + ); return ( <> @@ -87,13 +104,7 @@ export const LandingPage = () => {