From 4f5de4e6ac848aff0e3089bf51dec28e63553889 Mon Sep 17 00:00:00 2001 From: heuvea <66989902+heuvea@users.noreply.github.com> Date: Sat, 19 Sep 2026 19:13:40 +0200 Subject: [PATCH 1/4] FUND-2747 DRC: phase out API version 1.0 (keep 1.1/1.5/1.7) Removes DRC (Documenten) API version 1.0 so the service supports the latest three versions only (1.1, 1.5, 1.7). - Drop 1.0.0/1.0.1 from ApiMetaData.SupportedVersions and the LatestVersion_1_0 constant. - Remove the 1.0-only EnkelvoudigInformatieObjecten actions and strip [ZgwApiVersion(1.0)] from the shared base-v1 actions (Delete + audit-trail reads, GebruiksRechten/ObjectInformatieObjecten CRUD) that keep serving 1.1/1.5/1.7. - Remove the now-dead 1.0-only command/query handlers, request/response contract DTOs, validators and Mapster mappings. - Keep everything still referenced by 1.1/1.5/1.7 or the internal ServiceAgent (shared query-parameter DTOs + mappings, Create/Response DTOs). - Migrate the affected unit tests to 1.7 and drop tests for removed mappings. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...lvoudiginformatieobjectUpdateRequestDto.cs | 9 - ...kelvoudiginformatieobjectGetResponseDto.cs | 3 - ...voudiginformatieobjectUpdateResponseDto.cs | 9 - .../Controllers/Api.cs | 3 - ...EnkelvoudigInformatieObjectenController.cs | 538 +----------------- .../v1/GebruiksRechtenController.cs | 50 -- .../v1/ObjectInformatieObjectenController.cs | 45 -- ...kelvoudigInformatieObjectCommandHandler.cs | 452 --------------- ...EnkelvoudigInformatieObjectQueryHandler.cs | 86 --- ...kelvoudigInformatieObjectenQueryHandler.cs | 209 ------- ...EnkelvoudigInformatieObjectQueryHandler.cs | 125 ---- ...kelvoudigInformatieObjectCommandHandler.cs | 174 ------ .../v1/DomainToResponseRegister.cs | 143 ----- .../v1/RequestToDomainRegister.cs | 147 +---- .../v1/RequestToPaginationRegister.cs | 1 + ...gInformatieObjectCreateRequestValidator.cs | 12 - ...lvoudigInformatieObjectRequestValidator.cs | 63 -- ...gInformatieObjectUpdateRequestValidator.cs | 12 - ...nkelvoudigInformatieObjectVersionsTests.cs | 106 ++-- .../DomainToResponseProfileTests.cs | 242 -------- .../MappingTests/DrcMapsterWiringTests.cs | 9 +- .../RequestToDomainProfileTests.cs | 118 ---- 22 files changed, 75 insertions(+), 2481 deletions(-) delete mode 100644 src/OneGround.ZGW.Documenten.Contracts/v1/Requests/EnkelvoudiginformatieobjectUpdateRequestDto.cs delete mode 100644 src/OneGround.ZGW.Documenten.Contracts/v1/Responses/EnkelvoudiginformatieobjectGetResponseDto.cs delete mode 100644 src/OneGround.ZGW.Documenten.Contracts/v1/Responses/EnkelvoudiginformatieobjectUpdateResponseDto.cs delete mode 100644 src/OneGround.ZGW.Documenten.Web/Handlers/v1/CreateEnkelvoudigInformatieObjectCommandHandler.cs delete mode 100644 src/OneGround.ZGW.Documenten.Web/Handlers/v1/DownloadEnkelvoudigInformatieObjectQueryHandler.cs delete mode 100644 src/OneGround.ZGW.Documenten.Web/Handlers/v1/GetAllEnkelvoudigInformatieObjectenQueryHandler.cs delete mode 100644 src/OneGround.ZGW.Documenten.Web/Handlers/v1/GetEnkelvoudigInformatieObjectQueryHandler.cs delete mode 100644 src/OneGround.ZGW.Documenten.Web/Handlers/v1/LockEnkelvoudigInformatieObjectCommandHandler.cs delete mode 100644 src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectCreateRequestValidator.cs delete mode 100644 src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectRequestValidator.cs delete mode 100644 src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectUpdateRequestValidator.cs diff --git a/src/OneGround.ZGW.Documenten.Contracts/v1/Requests/EnkelvoudiginformatieobjectUpdateRequestDto.cs b/src/OneGround.ZGW.Documenten.Contracts/v1/Requests/EnkelvoudiginformatieobjectUpdateRequestDto.cs deleted file mode 100644 index 16dcff21..00000000 --- a/src/OneGround.ZGW.Documenten.Contracts/v1/Requests/EnkelvoudiginformatieobjectUpdateRequestDto.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Newtonsoft.Json; - -namespace OneGround.ZGW.Documenten.Contracts.v1.Requests; - -public class EnkelvoudigInformatieObjectUpdateRequestDto : EnkelvoudigInformatieObjectBaseRequestDto -{ - [JsonProperty(PropertyName = "lock")] - public string Lock { get; set; } -} diff --git a/src/OneGround.ZGW.Documenten.Contracts/v1/Responses/EnkelvoudiginformatieobjectGetResponseDto.cs b/src/OneGround.ZGW.Documenten.Contracts/v1/Responses/EnkelvoudiginformatieobjectGetResponseDto.cs deleted file mode 100644 index cdf8de27..00000000 --- a/src/OneGround.ZGW.Documenten.Contracts/v1/Responses/EnkelvoudiginformatieobjectGetResponseDto.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace OneGround.ZGW.Documenten.Contracts.v1.Responses; - -public class EnkelvoudigInformatieObjectGetResponseDto : EnkelvoudigInformatieObjectResponseDto { } diff --git a/src/OneGround.ZGW.Documenten.Contracts/v1/Responses/EnkelvoudiginformatieobjectUpdateResponseDto.cs b/src/OneGround.ZGW.Documenten.Contracts/v1/Responses/EnkelvoudiginformatieobjectUpdateResponseDto.cs deleted file mode 100644 index b7dbf257..00000000 --- a/src/OneGround.ZGW.Documenten.Contracts/v1/Responses/EnkelvoudiginformatieobjectUpdateResponseDto.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Newtonsoft.Json; - -namespace OneGround.ZGW.Documenten.Contracts.v1.Responses; - -public class EnkelvoudigInformatieObjectUpdateResponseDto : EnkelvoudigInformatieObjectResponseDto -{ - [JsonProperty(PropertyName = "lock")] - public string Lock { get; set; } -} diff --git a/src/OneGround.ZGW.Documenten.Web/Controllers/Api.cs b/src/OneGround.ZGW.Documenten.Web/Controllers/Api.cs index ef25c4b1..c123efcc 100644 --- a/src/OneGround.ZGW.Documenten.Web/Controllers/Api.cs +++ b/src/OneGround.ZGW.Documenten.Web/Controllers/Api.cs @@ -5,7 +5,6 @@ namespace OneGround.ZGW.Documenten.Web.Controllers; public static class Api { - public const string LatestVersion_1_0 = "1.0.1"; public const string LatestVersion_1_1 = "1.1.0"; public const string LatestVersion_1_5 = "1.5.0"; public const string LatestVersion_1_7 = "1.7.0"; @@ -17,8 +16,6 @@ public IEnumerable SupportedVersions { get { - yield return "1.0.0"; - yield return "1.0.1"; yield return "1.1.0"; yield return "1.5.0"; yield return "1.7.0"; diff --git a/src/OneGround.ZGW.Documenten.Web/Controllers/v1/EnkelvoudigInformatieObjectenController.cs b/src/OneGround.ZGW.Documenten.Web/Controllers/v1/EnkelvoudigInformatieObjectenController.cs index ec56a080..1ebfff79 100644 --- a/src/OneGround.ZGW.Documenten.Web/Controllers/v1/EnkelvoudigInformatieObjectenController.cs +++ b/src/OneGround.ZGW.Documenten.Web/Controllers/v1/EnkelvoudigInformatieObjectenController.cs @@ -1,40 +1,22 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Net.Mime; using System.Threading; using System.Threading.Tasks; -using System.Web; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using OneGround.ZGW.Common.Constants; using OneGround.ZGW.Common.Contracts.v1; using OneGround.ZGW.Common.Contracts.v1.AuditTrail; using OneGround.ZGW.Common.Handlers; -using OneGround.ZGW.Common.MimeTypes; using OneGround.ZGW.Common.Web.Authorization; using OneGround.ZGW.Common.Web.Controllers; -using OneGround.ZGW.Common.Web.Handlers; -using OneGround.ZGW.Common.Web.Middleware; -using OneGround.ZGW.Common.Web.Models; using OneGround.ZGW.Common.Web.Services; -using OneGround.ZGW.Common.Web.Services.AuditTrail; -using OneGround.ZGW.Common.Web.Validations; using OneGround.ZGW.Common.Web.Versioning; -using OneGround.ZGW.Documenten.Contracts.v1.Queries; -using OneGround.ZGW.Documenten.Contracts.v1.Requests; -using OneGround.ZGW.Documenten.Contracts.v1.Responses; -using OneGround.ZGW.Documenten.DataModel; -using OneGround.ZGW.Documenten.Services; using OneGround.ZGW.Documenten.Web.Authorization; -using OneGround.ZGW.Documenten.Web.Configuration; using OneGround.ZGW.Documenten.Web.Contracts.v1; using OneGround.ZGW.Documenten.Web.Handlers.v1; -using OneGround.ZGW.Documenten.Web.Models.v1; using Swashbuckle.AspNetCore.Annotations; // @@ -42,528 +24,23 @@ namespace OneGround.ZGW.Documenten.Web.Controllers.v1; +// Note: The versioned resource endpoints (GetAll/Get/Create/Update/Download/Lock/Unlock) live in the +// per-version controllers (v1/1, v1/5, v1/7). This base controller only hosts the endpoints that never +// changed across versions and are therefore shared by all currently supported versions: Delete (1.1/1.5, +// 1.7 has its own) and the audit-trail reads (all versions). [ApiController] [Authorize] -[ZgwApiVersion(Api.LatestVersion_1_0)] [Consumes("application/json")] [Produces("application/json")] public class EnkelvoudigInformatieObjectenController : ZGWControllerBase { - private readonly IPaginationHelper _paginationHelper; - private readonly ApplicationConfiguration _applicationConfiguration; - public EnkelvoudigInformatieObjectenController( ILogger logger, IMediator mediator, MapsterMapper.IMapper mapper, - IConfiguration configuration, - IPaginationHelper paginationHelper, - IErrorResponseBuilder errorResponseBuilder, - IValidatorService validatorService - ) - : base(logger, mediator, mapper, errorResponseBuilder) - { - _paginationHelper = paginationHelper; - _applicationConfiguration = configuration.GetSection("Application").Get(); - } - - // - // HTTP GET http://documenten.user.local:5007/api/v1/enkelvoudiginformatieobjecten - - /// - /// Alle ENKELVOUDIGEINFORMATIEOBJECTen opvragen. - /// Deze lijst kan gefilterd wordt met query-string parameters. - /// - /// Unauthorized - /// Forbidden - /// Not found - /// Too Many Requests - /// Internal Server Error - [HttpGet(ApiRoutes.EnkelvoudigInformatieObjecten.GetAll, Name = Operations.EnkelvoudigInformatieObjecten.List)] - [Scope(AuthorizationScopes.Documenten.Read)] - [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(PagedResponse))] - [ServiceFilter(typeof(ValidateQueryParametersFilter))] - public async Task GetAllAsync( - [FromQuery] GetAllEnkelvoudigInformatieObjectenQueryParameters queryParameters, - int page = 1, - CancellationToken cancellationToken = default - ) - { - _logger.LogDebug("{ControllerMethod} called with {@FromQuery}, {Page}", nameof(GetAllAsync), queryParameters, page); - - var pagination = _mapper.Map(new PaginationQuery(page, _applicationConfiguration.EnkelvoudigInformatieObjectenPageSize)); - var filter = _mapper.Map(queryParameters); - - var result = await _mediator.Send( - new GetAllEnkelvoudigInformatieObjectenQuery { GetAllEnkelvoudigInformatieObjectenFilter = filter, Pagination = pagination }, - cancellationToken - ); - - if (!_paginationHelper.ValidatePaginatedResponse(pagination, result.Result.Count)) - { - return _errorResponseBuilder.PageNotFound(); - } - - var enkelvoudigInformatieObjectenResponse = _mapper.Map>(result.Result.PageResult); - - var paginationResponse = _paginationHelper.CreatePaginatedResponse( - queryParameters, - pagination, - enkelvoudigInformatieObjectenResponse, - result.Result.Count - ); - - await _mediator.Send( - new LogAuditTrailGetObjectListCommand - { - RetrieveCatagory = RetrieveCatagory.Minimal, - Page = pagination.Page, - Count = paginationResponse.Results.Count(), - TotalCount = paginationResponse.Count, - AuditTrailOptions = new AuditTrailOptions { Bron = ServiceRoleName.DRC, Resource = "enkelvoudiginformatieobject" }, - }, - cancellationToken - ); - - return Ok(paginationResponse); - } - - // - // HTTP GET http://documenten.user.local:5007/api/v1/enkelvoudiginformatieobjecten/b24ee37c-00db-4108-b831-e3b420b35a09 - // HTTP GET http://documenten.user.local:5007/api/v1/enkelvoudiginformatieobjecten/b24ee37c-00db-4108-b831-e3b420b35a09?versie=2®istratieop=2020-11-07 - - /// - /// Een specifieke ENKELVOUDIGEINFORMATIEOBJECT opvragen. - /// - /// Unauthorized - /// Forbidden - /// Not found - /// Too Many Requests - /// Internal Server Error - [HttpGet(ApiRoutes.EnkelvoudigInformatieObjecten.Get, Name = Operations.EnkelvoudigInformatieObjecten.Read)] - [Scope(AuthorizationScopes.Documenten.Read)] - [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(EnkelvoudigInformatieObjectGetResponseDto))] - [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] - public async Task GetAsync( - Guid id, - [FromQuery] GetEnkelvoudigInformatieObjectQueryParameters queryParameters, - CancellationToken cancellationToken - ) - { - _logger.LogDebug("{ControllerMethod} called with {Uuid}, {@FromQuery}", nameof(GetAsync), id, queryParameters); - - var filter = _mapper.Map(queryParameters); - - var result = await _mediator.Send( - new GetEnkelvoudigInformatieObjectQuery { Id = id, GetEnkelvoudigInformatieObjectFilter = filter }, - cancellationToken - ); - - if (result.Status == QueryStatus.NotFound) - { - return _errorResponseBuilder.NotFound(); - } - - if (result.Status == QueryStatus.Forbidden) - { - return _errorResponseBuilder.Forbidden(); - } - - var enkelvoudigInformatieObjectResponse = _mapper.Map(result.Result); - - await _mediator.Send( - new LogAuditTrailGetObjectCommand - { - RetrieveCatagory = RetrieveCatagory.Minimal, - BaseEntity = result.Result, - SubEntity = result.Result, - AuditTrailOptions = new AuditTrailOptions { Bron = ServiceRoleName.DRC, Resource = "enkelvoudiginformatieobject" }, - LegacyAuditTrail = result.Result.LegacyAuditTrail, - }, - cancellationToken - ); - - return Ok(enkelvoudigInformatieObjectResponse); - } - - // - // HTTP POST http://documenten.user.local:5007/api/v1/enkelvoudiginformatieobjecten - - /// - /// Maak een (ENKELVOUDIG) INFORMATIEOBJECT aan. - /// - /// Unauthorized - /// Forbidden - /// Too Many Requests - /// Internal Server Error - [HttpPost(ApiRoutes.EnkelvoudigInformatieObjecten.Create, Name = Operations.EnkelvoudigInformatieObjecten.Create)] - [Scope(AuthorizationScopes.Documenten.Create)] - [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] - [SwaggerResponse(StatusCodes.Status201Created, Type = typeof(EnkelvoudigInformatieObjectCreateResponseDto))] - public async Task AddAsync( - [FromBody] EnkelvoudigInformatieObjectCreateRequestDto enkelvoudigInformatieObjectRequest, - CancellationToken cancellationToken - ) - { - _logger.LogDebug( - "{ControllerMethod} called with {@FromBody}, {Rsin}", - nameof(AddAsync), - enkelvoudigInformatieObjectRequest, - enkelvoudigInformatieObjectRequest.Bronorganisatie - ); - - var enkelvoudigInformatieObjectVersie = _mapper.Map(enkelvoudigInformatieObjectRequest); - - // Note: we should investigate who send the 2-letter language code so we log for these situations - LogInvalidTaalCode(enkelvoudigInformatieObjectRequest.Taal, enkelvoudigInformatieObjectVersie.Taal); - - var result = await _mediator.Send( - new CreateEnkelvoudigInformatieObjectCommand { EnkelvoudigInformatieObjectVersie = enkelvoudigInformatieObjectVersie }, - cancellationToken - ); - - if (result.Status == CommandStatus.Forbidden) - { - return _errorResponseBuilder.Forbidden(); - } - - if (result.Status == CommandStatus.ValidationError) - { - return _errorResponseBuilder.BadRequest(result.Errors); - } - - var enkelvoudigInformatieObjectResponse = _mapper.Map(result.Result); - - return Created(enkelvoudigInformatieObjectResponse.Url, enkelvoudigInformatieObjectResponse); - } - - // - // HTTP PUT http://documenten.user.local:5007/api/v1/enkelvoudiginformatieobjecten/59bad509-840b-4cd0-82dc-cbda74a75c2b - - /// - /// Werk een (ENKELVOUDIG) INFORMATIEOBJECT in zijn geheel bij. - /// - /// Unauthorized - /// Forbidden - /// Not found - /// EnkelvoudigInformatieObject was modified by another user - /// Too Many Requests - /// Internal Server Error - [HttpPut(ApiRoutes.EnkelvoudigInformatieObjecten.Update, Name = Operations.EnkelvoudigInformatieObjecten.Update)] - [Scope(AuthorizationScopes.Documenten.Update)] - [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] - [SwaggerResponse(StatusCodes.Status409Conflict, Type = typeof(ErrorResponse))] - [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(EnkelvoudigInformatieObjectUpdateResponseDto))] - public async Task UpdateAsync( - [FromBody] EnkelvoudigInformatieObjectUpdateRequestDto enkelvoudigInformatieObjectRequest, - Guid id, - CancellationToken cancellationToken - ) - { - _logger.LogDebug( - "{ControllerMethod} called with {@FromBody}, {Rsin}", - nameof(UpdateAsync), - enkelvoudigInformatieObjectRequest, - enkelvoudigInformatieObjectRequest.Bronorganisatie - ); - - EnkelvoudigInformatieObjectVersie enkelvoudigInformatieObjectVersie = _mapper.Map( - enkelvoudigInformatieObjectRequest - ); - - // Note: we should investigate who send the 2-letter language code so we log for these situations - LogInvalidTaalCode(enkelvoudigInformatieObjectRequest.Taal, enkelvoudigInformatieObjectVersie.Taal); - - var result = await _mediator.Send( - new CreateEnkelvoudigInformatieObjectCommand - { - ExistingEnkelvoudigInformatieObjectId = id, - EnkelvoudigInformatieObjectVersie = enkelvoudigInformatieObjectVersie, // Note: Indicates that the versie should be fully replaced in the command handler - PartialObject = null, - }, - cancellationToken - ); - - if (result.Status == CommandStatus.NotFound) - { - return _errorResponseBuilder.NotFound(); - } - - if (result.Status == CommandStatus.Forbidden) - { - return _errorResponseBuilder.Forbidden(); - } - - if (result.Status == CommandStatus.ValidationError) - { - return _errorResponseBuilder.BadRequest(result.Errors); - } - - if (result.Status == CommandStatus.Conflict) - { - return _errorResponseBuilder.Conflict(result.Errors); - } - - var enkelvoudigInformatieObjectResponse = _mapper.Map(result.Result); - - return Ok(enkelvoudigInformatieObjectResponse); - } - - /// - /// Werk een (ENKELVOUDIG) INFORMATIEOBJECT deels bij. - /// - /// Unauthorized - /// Forbidden - /// Not found - /// EnkelvoudigInformatieObject was modified by another user - /// Too Many Requests - /// Internal Server Error - [HttpPatch(ApiRoutes.EnkelvoudigInformatieObjecten.Update, Name = Operations.EnkelvoudigInformatieObjecten.PartialUpdate)] - [Scope(AuthorizationScopes.Documenten.Update)] - [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] - [SwaggerResponse(StatusCodes.Status409Conflict, Type = typeof(ErrorResponse))] - [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(EnkelvoudigInformatieObjectUpdateResponseDto))] - public async Task PartialUpdateAsync( - [FromBody] dynamic partialEnkelvoudigInformatieObjectRequest, - Guid id, - CancellationToken cancellationToken + IErrorResponseBuilder errorResponseBuilder ) - { - _logger.LogDebug("{ControllerMethod} called with {Uuid}", nameof(PartialUpdateAsync), id); - - var result = await _mediator.Send( - new CreateEnkelvoudigInformatieObjectCommand - { - ExistingEnkelvoudigInformatieObjectId = id, - EnkelvoudigInformatieObjectVersie = null, - PartialObject = partialEnkelvoudigInformatieObjectRequest, // Note: Indicates that the versie should be merged in the command handler - }, - cancellationToken - ); - - if (result.Status == CommandStatus.NotFound) - { - return _errorResponseBuilder.NotFound(); - } - - if (result.Status == CommandStatus.Forbidden) - { - return _errorResponseBuilder.Forbidden(); - } - - if (result.Status == CommandStatus.ValidationError) - { - return _errorResponseBuilder.BadRequest(result.Errors); - } - - if (result.Status == CommandStatus.Conflict) - { - return _errorResponseBuilder.Conflict(result.Errors); - } - - var enkelvoudigInformatieObjectResponse = _mapper.Map(result.Result); - - return Ok(enkelvoudigInformatieObjectResponse); - } - - // - // HTTP GET http://documenten.user.local:5007/api/v1/enkelvoudiginformatieobjecten/b24ee37c-00db-4108-b831-e3b420b35a09/download - // HTTP GET http://documenten.user.local:5007/api/v1/enkelvoudiginformatieobjecten/b24ee37c-00db-4108-b831-e3b420b35a09/download?versie=2 - - /// - /// Download de binaire data van het (ENKELVOUDIG) INFORMATIEOBJECT.. - /// - /// OK - /// Unauthorized - /// Forbidden - /// Not found - /// Too Many Requests - /// Internal Server Error - [HttpGet(ApiRoutes.EnkelvoudigInformatieObjecten.Download, Name = Operations.EnkelvoudigInformatieObjecten.Download)] - [Scope(AuthorizationScopes.Documenten.Read)] - [Produces("application/octet-stream", "application/json")] - public async Task DownloadAsync( - Guid id, - [FromQuery] GetEnkelvoudigInformatieObjectQueryParameters queryParameters, - CancellationToken cancellationToken - ) - { - _logger.LogDebug("{ControllerMethod} called with {Uuid}, {@FromQuery}", nameof(DownloadAsync), id, queryParameters); - - var filter = _mapper.Map(queryParameters); - - var resultGet = await _mediator.Send( - new GetEnkelvoudigInformatieObjectQuery { Id = id, GetEnkelvoudigInformatieObjectFilter = filter }, - cancellationToken - ); - - if (resultGet.Status == QueryStatus.NotFound) - { - return _errorResponseBuilder.NotFound(); - } - - if (resultGet.Status == QueryStatus.Forbidden) - { - return _errorResponseBuilder.Forbidden(); - } - - var enkelvoudigInformatieObjectVersie = resultGet.Result.LatestEnkelvoudigInformatieObjectVersie; // Note: Latest is mapped to the requested version from GetEnkelvoudigInformatieObjectQuery (so it could be a different one) - - // Note: When a v1.1 multi-part document is uploaded (and not completed yet) the - // download under v1 must be handled with care because inhoud field is null so an extra check is needed here! - // or: - // When a 1.1 mata-only document is added an retrieved by a 1.0 version - if (enkelvoudigInformatieObjectVersie.Inhoud == null) - { - return _errorResponseBuilder.NotFound(); - } - - var documentUrn = new DocumentUrn(enkelvoudigInformatieObjectVersie.Inhoud); - - var resultDwnl = await _mediator.Send( - new DownloadEnkelvoudigInformatieObjectQuery - { - DocumentUrn = documentUrn, - EnkelvoudigInformatieObjectId = enkelvoudigInformatieObjectVersie.EnkelvoudigInformatieObjectId, - }, - cancellationToken - ); - - if (resultDwnl.Status == QueryStatus.NotFound) - { - return _errorResponseBuilder.NotFound(); - } - - await _mediator.Send( - new LogAuditTrailGetObjectCommand - { - RetrieveCatagory = RetrieveCatagory.Minimal, - BaseEntity = resultGet.Result, - SubEntity = enkelvoudigInformatieObjectVersie, - OverruleActieWeergave = "Object gedownload", - AuditTrailOptions = new AuditTrailOptions { Bron = ServiceRoleName.DRC, Resource = "enkelvoudiginformatieobject" }, - LegacyAuditTrail = resultGet.Result.LegacyAuditTrail, - }, - cancellationToken - ); - - var cd = new ContentDisposition { FileName = HttpUtility.UrlPathEncode(enkelvoudigInformatieObjectVersie.Bestandsnaam) }; - - Response.Headers.ContentDisposition = cd.ToString(); - Response.Headers.XContentTypeOptions = "nosniff"; - - var mimeType = string.IsNullOrEmpty(enkelvoudigInformatieObjectVersie.Formaat) - ? MimeTypeHelper.GetMimeType(enkelvoudigInformatieObjectVersie.Bestandsnaam) - : enkelvoudigInformatieObjectVersie.Formaat; - - return File(resultDwnl.Result, mimeType); - } - - // - // HTTP POST https://documenten-api.vng.cloud/api/v1/enkelvoudiginformatieobjecten/b24ee37c-00db-4108-b831-e3b420b35a09/lock - - /// - /// Vergrendel een (ENKELVOUDIG) INFORMATIEOBJECT. - /// Voert een "checkout" uit waardoor het (ENKELVOUDIG) INFORMATIEOBJECT vergrendeld wordt met een lock waarde. - /// Alleen met deze waarde kan het (ENKELVOUDIG) INFORMATIEOBJECT bijgewerkt (PUT, PATCH) en weer ontgrendeld worden. - /// - /// Unauthorized - /// Forbidden - /// Not found - /// EnkelvoudigInformatieObject was modified by another user - /// Too Many Requests - /// Internal Server Error - [HttpPost(ApiRoutes.EnkelvoudigInformatieObjecten.Lock, Name = Operations.EnkelvoudigInformatieObjecten.Lock)] - [Scope(AuthorizationScopes.Documenten.Lock)] - [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] - [SwaggerResponse(StatusCodes.Status409Conflict, Type = typeof(ErrorResponse))] - [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(LockResponseDto))] - public async Task LockAsync(Guid id, CancellationToken cancellationToken) - { - _logger.LogDebug("{ControllerMethod} called with {Uuid}", nameof(LockAsync), id); - - var result = await _mediator.Send(new LockEnkelvoudigInformatieObjectCommand { Id = id, Set = true }, cancellationToken); - - if (result.Status == CommandStatus.NotFound) - { - return _errorResponseBuilder.NotFound(); - } - - if (result.Status == CommandStatus.Forbidden) - { - return _errorResponseBuilder.Forbidden(); - } - - if (result.Status == CommandStatus.ValidationError) - { - return _errorResponseBuilder.BadRequest(result.Errors); - } - - if (result.Status == CommandStatus.Conflict) - { - return _errorResponseBuilder.Conflict(result.Errors); - } - - var response = new LockResponseDto { Lock = result.Result }; - - return Ok(response); - } - - // - // HTTP POST https://documenten-api.vng.cloud/api/v1/enkelvoudiginformatieobjecten/b24ee37c-00db-4108-b831-e3b420b35a09/unlock - - /// - /// Ontgrendel een (ENKELVOUDIG) INFORMATIEOBJECT. - /// Heft de "checkout" op waardoor het (ENKELVOUDIG) INFORMATIEOBJECT ontgrendeld wordt. - /// - /// No content - /// Unauthorized - /// Forbidden - /// Not found - /// EnkelvoudigInformatieObject was modified by another user - /// Too Many Requests - /// Internal Server Error - [HttpPost(ApiRoutes.EnkelvoudigInformatieObjecten.Unlock, Name = Operations.EnkelvoudigInformatieObjecten.Unlock)] - [Scope(AuthorizationScopes.Documenten.Lock, AuthorizationScopes.Documenten.ForcedUnlock)] - [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] - [SwaggerResponse(StatusCodes.Status409Conflict, Type = typeof(ErrorResponse))] - [IgnoreMissingContentType] - public async Task UnlockAsync(Guid id, [FromBody] LockRequestDto request, CancellationToken cancellationToken) - { - _logger.LogDebug("{ControllerMethod} called with {Uuid}, {@FromBody}", nameof(UnlockAsync), id, request); - - var result = await _mediator.Send( - new LockEnkelvoudigInformatieObjectCommand - { - Id = id, - Set = false, - Lock = request?.Lock, - }, - cancellationToken - ); - - if (result.Status == CommandStatus.NotFound) - { - return _errorResponseBuilder.NotFound(); - } - - if (result.Status == CommandStatus.Forbidden) - { - return _errorResponseBuilder.Forbidden(); - } - - if (result.Status == CommandStatus.ValidationError) - { - return _errorResponseBuilder.BadRequest(result.Errors); - } - - if (result.Status == CommandStatus.Conflict) - { - return _errorResponseBuilder.Conflict(result.Errors); - } - - return NoContent(); - } + : base(logger, mediator, mapper, errorResponseBuilder) { } // // HTTP DELETE https://documenten-api.vng.cloud/api/v1/enkelvoudiginformatieobjecten/b24ee37c-00db-4108-b831-e3b420b35a09 @@ -584,7 +61,6 @@ public async Task UnlockAsync(Guid id, [FromBody] LockRequestDto [Scope(AuthorizationScopes.Documenten.Delete)] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] [SwaggerResponse(StatusCodes.Status409Conflict, Type = typeof(ErrorResponse))] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ZgwApiVersion(Api.LatestVersion_1_5)] public async Task DeleteAsync(Guid id, CancellationToken cancellationToken) @@ -629,7 +105,6 @@ public async Task DeleteAsync(Guid id, CancellationToken cancella /// Internal Server Error [HttpGet(ApiRoutes.EnkelvoudigInformatieObjectAudittrail.GetAll, Name = Operations.EnkelvoudigInformatieObjectAudittrail.List)] [Scope(AuthorizationScopes.AuditTrails.Read)] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ZgwApiVersion(Api.LatestVersion_1_5)] [ZgwApiVersion(Api.LatestVersion_1_7)] @@ -675,7 +150,6 @@ public async Task GetAllAuditTrailRegelsAsync(Guid enkelvoudiginf /// Internal Server Error [HttpGet(ApiRoutes.EnkelvoudigInformatieObjectAudittrail.Get, Name = Operations.EnkelvoudigInformatieObjectAudittrail.Read)] [Scope(AuthorizationScopes.AuditTrails.Read)] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ZgwApiVersion(Api.LatestVersion_1_5)] [ZgwApiVersion(Api.LatestVersion_1_7)] diff --git a/src/OneGround.ZGW.Documenten.Web/Controllers/v1/GebruiksRechtenController.cs b/src/OneGround.ZGW.Documenten.Web/Controllers/v1/GebruiksRechtenController.cs index 6998ce36..79a39d76 100644 --- a/src/OneGround.ZGW.Documenten.Web/Controllers/v1/GebruiksRechtenController.cs +++ b/src/OneGround.ZGW.Documenten.Web/Controllers/v1/GebruiksRechtenController.cs @@ -56,7 +56,6 @@ IErrorResponseBuilder errorResponseBuilder [Scope(AuthorizationScopes.Documenten.Read)] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(List))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ServiceFilter(typeof(ValidateQueryParametersFilter))] public async Task GetAllAsync( @@ -85,51 +84,6 @@ await _mediator.Send( return Ok(gebruiksRechtenResponse); } - /// - /// Een specifieke GEBRUIKSRECHT opvragen. - /// - /// Unauthorized - /// Forbidden - /// Not found - /// Too Many Requests - /// Internal Server Error - [HttpGet(ApiRoutes.GebruiksRechten.Get, Name = Operations.GebruiksRechten.Read)] - [Scope(AuthorizationScopes.Documenten.Read)] - [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(GebruiksRechtResponseDto))] - [ZgwApiVersion(Api.LatestVersion_1_0)] - public async Task GetAsync(Guid id, CancellationToken cancellationToken) - { - _logger.LogDebug("{ControllerMethod} called with {Uuid}", nameof(GetAsync), id); - - var result = await _mediator.Send(new GetGebruiksRechtQuery { Id = id }, cancellationToken); - - if (result.Status == QueryStatus.NotFound) - { - return _errorResponseBuilder.NotFound(); - } - - if (result.Status == QueryStatus.Forbidden) - { - return _errorResponseBuilder.Forbidden(); - } - - var gebruiksRechtResponse = _mapper.Map(result.Result); - - await _mediator.Send( - new LogAuditTrailGetObjectCommand - { - RetrieveCatagory = RetrieveCatagory.All, - BaseEntity = result.Result.InformatieObject, - SubEntity = result.Result, - AuditTrailOptions = new AuditTrailOptions { Bron = ServiceRoleName.DRC, Resource = "gebruiksrecht" }, - LegacyAuditTrail = result.Result.InformatieObject.LegacyAuditTrail, - }, - cancellationToken - ); - - return Ok(gebruiksRechtResponse); - } - /// /// Voeg GEBRUIKSRECHTen toe voor een INFORMATIEOBJECT. /// @@ -144,7 +98,6 @@ await _mediator.Send( [Scope(AuthorizationScopes.Documenten.Create)] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] [SwaggerResponse(StatusCodes.Status201Created, Type = typeof(GebruiksRechtResponseDto))] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ZgwApiVersion(Api.LatestVersion_1_5)] [ZgwApiVersion(Api.LatestVersion_1_7)] @@ -188,7 +141,6 @@ public async Task AddAsync([FromBody] GebruiksRechtRequestDto geb [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] [SwaggerResponse(StatusCodes.Status409Conflict, Type = typeof(ErrorResponse))] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(GebruiksRechtResponseDto))] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ZgwApiVersion(Api.LatestVersion_1_5)] [ZgwApiVersion(Api.LatestVersion_1_7)] @@ -247,7 +199,6 @@ CancellationToken cancellationToken [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] [SwaggerResponse(StatusCodes.Status409Conflict, Type = typeof(ErrorResponse))] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(GebruiksRechtResponseDto))] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ZgwApiVersion(Api.LatestVersion_1_5)] [ZgwApiVersion(Api.LatestVersion_1_7)] @@ -301,7 +252,6 @@ public async Task PartialUpdateAsync([FromBody] JObject partialGe [HttpDelete(ApiRoutes.GebruiksRechten.Delete, Name = Operations.GebruiksRechten.Delete)] [Scope(AuthorizationScopes.Documenten.Delete)] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ZgwApiVersion(Api.LatestVersion_1_5)] [ZgwApiVersion(Api.LatestVersion_1_7)] diff --git a/src/OneGround.ZGW.Documenten.Web/Controllers/v1/ObjectInformatieObjectenController.cs b/src/OneGround.ZGW.Documenten.Web/Controllers/v1/ObjectInformatieObjectenController.cs index d6da2012..88ecf885 100644 --- a/src/OneGround.ZGW.Documenten.Web/Controllers/v1/ObjectInformatieObjectenController.cs +++ b/src/OneGround.ZGW.Documenten.Web/Controllers/v1/ObjectInformatieObjectenController.cs @@ -52,7 +52,6 @@ IErrorResponseBuilder errorResponseBuilder [Scope(AuthorizationScopes.Documenten.Read)] [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(List))] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ServiceFilter(typeof(ValidateQueryParametersFilter))] public async Task GetAllAsync( @@ -82,48 +81,6 @@ CancellationToken cancellationToken return Ok(objectInformatieObjectenResponse); } - /// - /// Een specifieke OBJECT-INFORMATIEOBJECT relatie opvragen. - /// - /// Unauthorized - /// Forbidden - /// Not found - /// Too Many Requests - /// Internal Server Error - [HttpGet(ApiRoutes.ObjectInformatieObjecten.Get, Name = Operations.ObjectInformatieObjecten.Read)] - [Scope(AuthorizationScopes.Documenten.Read)] - [SwaggerResponse(StatusCodes.Status200OK, Type = typeof(ObjectInformatieObjectResponseDto))] - [ZgwApiVersion(Api.LatestVersion_1_0)] - public async Task GetAsync(Guid id, CancellationToken cancellationToken) - { - _logger.LogDebug("{ControllerMethod} called with {Uuid}", nameof(GetAsync), id); - - var result = await _mediator.Send(new GetObjectInformatieObjectQuery { Id = id }, cancellationToken); - - if (result.Status == QueryStatus.NotFound) - { - return _errorResponseBuilder.NotFound(); - } - - if (result.Status == QueryStatus.Forbidden) - { - return _errorResponseBuilder.Forbidden(); - } - - var objectInformatieObjectResponse = _mapper.Map(result.Result); - - // TODO: Still deciding if this makes sense (because can generate lot of audittrail logs) - //await _mediator.Send(new LogAuditTrailGetObjectCommand - //{ - // RetrieveCatagory = RetrieveCatagory.All, - // BaseEntity = result.Result.InformatieObject, - // SubEntity = result.Result, - // AuditTrailOptions = new AuditTrailOptions { Bron = "DRC", Resource = "objectinformatieobject" } - //}); - - return Ok(objectInformatieObjectResponse); - } - // // HTTP POST http://documenten.user.local:5007/api/v1/objectinformatieobjecten @@ -142,7 +99,6 @@ public async Task GetAsync(Guid id, CancellationToken cancellatio [Scope(AuthorizationScopes.Documenten.Create)] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] [SwaggerResponse(StatusCodes.Status201Created, Type = typeof(ObjectInformatieObjectResponseDto))] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ZgwApiVersion(Api.LatestVersion_1_5)] [ZgwApiVersion(Api.LatestVersion_1_7)] @@ -198,7 +154,6 @@ CancellationToken cancellationToken [HttpDelete(ApiRoutes.ObjectInformatieObjecten.Delete, Name = Operations.ObjectInformatieObjecten.Delete)] [Scope(AuthorizationScopes.Documenten.Delete)] [SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ErrorResponse))] - [ZgwApiVersion(Api.LatestVersion_1_0)] [ZgwApiVersion(Api.LatestVersion_1_1)] [ZgwApiVersion(Api.LatestVersion_1_5)] [ZgwApiVersion(Api.LatestVersion_1_7)] diff --git a/src/OneGround.ZGW.Documenten.Web/Handlers/v1/CreateEnkelvoudigInformatieObjectCommandHandler.cs b/src/OneGround.ZGW.Documenten.Web/Handlers/v1/CreateEnkelvoudigInformatieObjectCommandHandler.cs deleted file mode 100644 index ac39d236..00000000 --- a/src/OneGround.ZGW.Documenten.Web/Handlers/v1/CreateEnkelvoudigInformatieObjectCommandHandler.cs +++ /dev/null @@ -1,452 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Npgsql; -using OneGround.ZGW.Catalogi.ServiceAgent.v1; -using OneGround.ZGW.Common.Constants; -using OneGround.ZGW.Common.Contracts; -using OneGround.ZGW.Common.Contracts.v1; -using OneGround.ZGW.Common.DataModel; -using OneGround.ZGW.Common.Handlers; -using OneGround.ZGW.Common.MimeTypes; -using OneGround.ZGW.Common.Web.Authorization; -using OneGround.ZGW.Common.Web.Services; -using OneGround.ZGW.Common.Web.Services.AuditTrail; -using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.DataAccess; -using OneGround.ZGW.Documenten.Contracts.v1.Requests; -using OneGround.ZGW.Documenten.Contracts.v1.Responses; -using OneGround.ZGW.Documenten.DataModel; -using OneGround.ZGW.Documenten.Services; -using OneGround.ZGW.Documenten.Web.Authorization; -using OneGround.ZGW.Documenten.Web.BusinessRules.v1; -using OneGround.ZGW.Documenten.Web.Concurrency; -using OneGround.ZGW.Documenten.Web.Extensions; -using OneGround.ZGW.Documenten.Web.Notificaties; - -namespace OneGround.ZGW.Documenten.Web.Handlers.v1; - -class CreateEnkelvoudigInformatieObjectCommandHandler - : DocumentenBaseHandler, - IRequestHandler> -{ - private readonly DrcDbContext _context; - private readonly IEnkelvoudigInformatieObjectBusinessRuleService _enkelvoudigInformatieObjectBusinessRuleService; - private readonly INummerGenerator _nummerGenerator; - private readonly ICatalogiServiceAgent _catalogiServiceAgent; - private readonly IAuditTrailFactory _auditTrailFactory; - private readonly Lazy _lazyDocumentService; - private readonly IEnkelvoudigInformatieObjectMerger _entityMerger; - private readonly ResilienceConcurrencyRetryPipeline _concurrencyRetryPipeline; - - public CreateEnkelvoudigInformatieObjectCommandHandler( - ILogger logger, - IConfiguration configuration, - DrcDbContext context, - IEntityUriService uriService, - INummerGenerator nummerGenerator, - IDocumentServicesResolver documentServicesResolver, - IEnkelvoudigInformatieObjectBusinessRuleService enkelvoudigInformatieObjectBusinessRuleService, - INotificatieService notificatieService, - ICatalogiServiceAgent catalogiServiceAgent, - IAuditTrailFactory auditTrailFactory, - IAuthorizationContextAccessor authorizationContextAccessor, - IDocumentKenmerkenResolver documentKenmerkenResolver, - IEnkelvoudigInformatieObjectMergerFactory entityMergerFactory, - ResilienceConcurrencyRetryPipeline concurrencyRetryPipeline - ) - : base(logger, configuration, uriService, authorizationContextAccessor, notificatieService, documentKenmerkenResolver) - { - _context = context; - _enkelvoudigInformatieObjectBusinessRuleService = enkelvoudigInformatieObjectBusinessRuleService; - _nummerGenerator = nummerGenerator; - _catalogiServiceAgent = catalogiServiceAgent; - _auditTrailFactory = auditTrailFactory; - _concurrencyRetryPipeline = concurrencyRetryPipeline; - - _lazyDocumentService = new Lazy(() => GetDocumentServiceProvider(documentServicesResolver)); - - _entityMerger = entityMergerFactory.Create(); - } - - private IDocumentService GetDocumentServiceProvider(IDocumentServicesResolver documentServicesResolver) - { - var documentService = - documentServicesResolver.GetDefault() ?? throw new InvalidOperationException("Could not resolve default documentservice provider."); - _logger.LogDebug("Default DMS provider '{ProviderPrefix}' [{documentService}]", documentService.ProviderPrefix, documentService); - - return documentService; - } - - public async Task> Handle( - CreateEnkelvoudigInformatieObjectCommand request, - CancellationToken cancellationToken - ) - { - _logger.LogDebug("Creating EnkelvoudigInformatieObject...."); - - var errors = new List(); - - var versie = request.EnkelvoudigInformatieObjectVersie; - - // Use ReadCommitted isolation level: - // - FOR UPDATE provides pessimistic row-level locking (prevents concurrent modifications) - // - xmin (configured in EnkelvoudigInformatieObject) provides optimistic concurrency detection (detects any changes since read) - // - ReadCommitted allows better concurrency than Serializable for this use case - // - The combination prevents both lost updates (via FOR UPDATE) and write skew (via xmin) - using var tx = await _context.Database.BeginTransactionAsync(IsolationLevel.ReadCommitted, cancellationToken); - - bool isPartialUpdate = request.PartialObject != null && request.EnkelvoudigInformatieObjectVersie == null; - - var rsinFilter = GetRsinFilterPredicate(); - - bool legacyAuditTrail = false; - - // Set version first because business-rule wants to verify against - EnkelvoudigInformatieObject existingEnkelvoudigInformatieObject = null; - if (request.ExistingEnkelvoudigInformatieObjectId.HasValue) - { - var result = await _concurrencyRetryPipeline.ExecuteWithResultAsync( - async (token) => - { - // Add new version of the EnkelvoudigInformatieObject, try to acquire lock on the EnkelvoudigInformatieObject - var _existingEnkelvoudigInformatieObject = await _context - .EnkelvoudigInformatieObjecten.LockForUpdate(_context, c => c.Id, [request.ExistingEnkelvoudigInformatieObjectId.Value]) - .Where(rsinFilter) - .Include(e => e.LatestEnkelvoudigInformatieObjectVersie) - .SingleOrDefaultAsync(e => e.Id == request.ExistingEnkelvoudigInformatieObjectId.Value, token); - - // The object might be locked OR not exist - check if it exists without lock - if (_existingEnkelvoudigInformatieObject == null) - { - // The object might be locked OR not exist - check if it exists without lock - var exists = await _context - .EnkelvoudigInformatieObjecten.Where(rsinFilter) - .AnyAsync(e => e.Id == request.ExistingEnkelvoudigInformatieObjectId.Value, token); - - if (!exists) - { - // Object truly doesn't exist - return (enkelvoudiginformatieobject: null, status: CommandStatus.NotFound); - } - - // Throw the exception again so Polly knows a retry is needed (giving up after maximum reached retries) - throw new ConcurrencyConflictException("Concurrency conflict detected.", request.ExistingEnkelvoudigInformatieObjectId.Value); - } - else - { - return (enkelvoudiginformatieobject: _existingEnkelvoudigInformatieObject, status: CommandStatus.OK); - } - }, - cancellationToken - ); - - if (result.status == CommandStatus.NotFound) - { - return new CommandResult(null, CommandStatus.NotFound); - } - - if (result.status == CommandStatus.Conflict) - { - // Object exists but is locked by another process - var error = new ValidationError( - "nonFieldErrors", - ErrorCode.Conflict, - $"Het enkelvoudiginformatieobject {request.ExistingEnkelvoudigInformatieObjectId.Value} is vergrendeld door een andere bewerking." - ); - return new CommandResult(null, CommandStatus.Conflict, error); - } - - existingEnkelvoudigInformatieObject = result.enkelvoudiginformatieobject; - - if (isPartialUpdate) - { - // Partial update (e.g. for PATCH endpoint) so merge the partial object provided by the client with the existing entity - versie = _entityMerger.TryMergeWithPartial(request.PartialObject, existingEnkelvoudigInformatieObject, errors); - if (errors.Count != 0) - { - return new CommandResult(null, CommandStatus.ValidationError, errors.ToArray()); - } - } - else - { - // Full update (e.g. for PUT endpoint) with EnkelvoudigInformatieObjectVersie provided by the client - versie = request.EnkelvoudigInformatieObjectVersie; - } - - if (!_authorizationContext.IsAuthorized(existingEnkelvoudigInformatieObject, AuthorizationScopes.Documenten.Update)) - { - return new CommandResult(null, CommandStatus.Forbidden); - } - - var currentVersie = existingEnkelvoudigInformatieObject.LatestEnkelvoudigInformatieObjectVersie; - - versie.Versie = currentVersie.Versie + 1; - - legacyAuditTrail = existingEnkelvoudigInformatieObject.LegacyAuditTrail; - } - else - { - if ( - !_authorizationContext.IsAuthorized( - versie.InformatieObject.InformatieObjectType, - versie.Vertrouwelijkheidaanduiding, - AuthorizationScopes.Documenten.Create - ) - ) - { - return new CommandResult(null, CommandStatus.Forbidden); - } - - var informatieobjecttype = await _catalogiServiceAgent.GetInformatieObjectTypeByUrlAsync(versie.InformatieObject.InformatieObjectType); - if (!informatieobjecttype.Success) - { - return new CommandResult( - null, - CommandStatus.ValidationError, - new ValidationError("enkelvoudiginformatieobjecttype", informatieobjecttype.Error.Code, informatieobjecttype.Error.Title) - ); - } - var catalogusId = _uriService.GetId(informatieobjecttype.Response.Catalogus); - - versie.Versie = 1; - versie.InformatieObject.CatalogusId = catalogusId; - } - - versie.SetLinkToNullWhenInvalid(); - versie.EscapeBestandsNaamWhenInvalid(); - - await _enkelvoudigInformatieObjectBusinessRuleService.ValidateAsync( - versie, - _applicationConfiguration.IgnoreInformatieObjectTypeValidation, - request.ExistingEnkelvoudigInformatieObjectId, - isPartialUpdate, - apiVersie: 1.0M, - errors, - cancellationToken - ); - - if (errors.Count != 0) - { - return new CommandResult(null, CommandStatus.ValidationError, errors.ToArray()); - } - - // Note: Vertrouwelijkheidaanduiding van een informatieobject (drc-007) => get from request or get from Catalogi.InformatieObjectType - await SetVertrouwelijkheidAanduidingAsync(versie); - - using (var audittrail = _auditTrailFactory.Create(AuditTrailOptions, legacyAuditTrail)) - { - if (existingEnkelvoudigInformatieObject != null) - { - // Add new version of the EnkelvoudigInformatieObject - audittrail.SetOld(existingEnkelvoudigInformatieObject); - - var currentVersie = existingEnkelvoudigInformatieObject.LatestEnkelvoudigInformatieObjectVersie; - - var informatieObjectType = versie.InformatieObject.InformatieObjectType; - - var indicatieGebruiksrecht = versie.InformatieObject.IndicatieGebruiksrecht; - - versie.Bestandsomvang = currentVersie.Bestandsomvang; - versie.BeginRegistratie = DateTime.UtcNow; - versie.EnkelvoudigInformatieObjectId = request.ExistingEnkelvoudigInformatieObjectId.Value; - // Clone the EnkelvoudigInformatieObject from previous version - versie.InformatieObject = existingEnkelvoudigInformatieObject; - versie.InformatieObject.InformatieObjectType = informatieObjectType; - versie.InformatieObject.IndicatieGebruiksrecht = indicatieGebruiksrecht; - - // New base64 encoded Inhoud specified (in request) or existing document-urn from merge operation? - if (!versie.Inhoud.IsAnyDocumentUrn()) - { - // Yes; keep create new version and add it to documentstore - await AddDocumentToDocumentStore(versie, cancellationToken); - } - } - else - { - versie.InformatieObject.Owner = _rsin; - versie.BeginRegistratie = DateTime.UtcNow; - - // Create new (initial) version of the EnkelvoudigInformatieObject - await AddDocumentToDocumentStore(versie, cancellationToken); - } - versie.Owner = versie.InformatieObject.Owner; - - // Use external identificatie if specified generate otherwise - if (string.IsNullOrEmpty(versie.Identificatie)) - { - var owner = versie.Owner; - - var enkelvoudigInformatieObjectNummer = await _nummerGenerator.GenerateAsync( - owner, - "documenten", - id => IsEnkelvoudigInformatieObjectVersieUnique(owner, id), - cancellationToken - ); - - versie.Identificatie = enkelvoudigInformatieObjectNummer; - } - - await _context.EnkelvoudigInformatieObjectVersies.AddAsync(versie, cancellationToken); // Note: Sequential Guid for Id is generated here by the DBMS - - // Saves the new added EnkelvoudigInformationObject and EnkelvoudigInformationObjectVersion. - // Handle potential race condition on INSERT with unique constraint in Postgres: - // if two requests hit this API simultaneously, a "check-then-act" pattern can fail and the INSERT may violate the unique constraint. - try - { - // Try to save changes with potential concurrency conflict - await _context.SaveChangesAsync(cancellationToken); - } - catch (DbUpdateException ex) when (ex.InnerException is PostgresException pgEx && pgEx.SqlState == PostgresErrorCodes.UniqueViolation) - { - // Handle preventing race-condition where another process has created already EnkelvoudigInformatieObject with the same identificatie+owner+versie after our initial read but before our insert - await RollbackDocumentJustAddedToDmsAsync(versie.Inhoud); - - var error = new ValidationError("identificatie", ErrorCode.Unique, "Deze identificatie bestaat al voor deze organisatie."); - return new CommandResult(null, CommandStatus.ValidationError, error); - } - catch (Exception) - { - // Rollback and re-throw the exception to be handled by the global exception handler, but first try to rollback the document just added to DMS if any - await RollbackDocumentJustAddedToDmsAsync(versie.Inhoud); - throw; - } - - // Sets the 'latest' EnkelvoudigInformationObjectVersion in the parent EnkelvoudigInformatieObject - versie.InformatieObject.LatestEnkelvoudigInformatieObjectVersieId = versie.Id; - versie.InformatieObject.LatestEnkelvoudigInformatieObjectVersie = versie; - versie.InformatieObject.LatestVertrouwelijkheidAanduiding = versie.Vertrouwelijkheidaanduiding; - - versie.LatestInformatieObject = versie.InformatieObject; - - audittrail.SetNew(versie.InformatieObject); - - if (request.ExistingEnkelvoudigInformatieObjectId.HasValue) - { - if (isPartialUpdate) - { - await audittrail.PatchedAsync(versie.InformatieObject, versie.InformatieObject, cancellationToken); - } - else - { - await audittrail.UpdatedAsync(versie.InformatieObject, versie.InformatieObject, cancellationToken); - } - } - else - { - await audittrail.CreatedAsync(versie.InformatieObject, versie.InformatieObject, cancellationToken); - } - - await _context.SaveChangesAsync(cancellationToken); - - await tx.CommitAsync(cancellationToken); - } - - _logger.LogDebug("EnkelvoudigInformatieObject {Id} successfully created", versie.InformatieObject.Id); - - var actie = request.ExistingEnkelvoudigInformatieObjectId.HasValue ? Actie.update : Actie.create; - - await SendNotificationAsync(actie, versie.InformatieObject, cancellationToken); - - return new CommandResult(versie, CommandStatus.OK); - } - - private async Task RollbackDocumentJustAddedToDmsAsync(string urnDocument) - { - if (string.IsNullOrEmpty(urnDocument)) - { - return; - } - - try - { - await DocumentService.DeleteDocumentAsync(new DocumentUrn(urnDocument)); - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to rollback document with urn {UrnDocument} just added to DMS", urnDocument); - // Do not throw an exception on the rollback failure - } - } - - private bool IsEnkelvoudigInformatieObjectVersieUnique(string owner, string identificatie) - { - return !_context.EnkelvoudigInformatieObjectVersies.AsNoTracking().Any(e => e.Identificatie == identificatie && e.Owner == owner); - } - - private async Task AddDocumentToDocumentStore( - EnkelvoudigInformatieObjectVersie enkelvoudigInformatieObjectVersie, - CancellationToken cancellationToken - ) - { - var contentType = string.IsNullOrEmpty(enkelvoudigInformatieObjectVersie.Formaat) - ? MimeTypeHelper.GetMimeType(enkelvoudigInformatieObjectVersie.Bestandsnaam) - : enkelvoudigInformatieObjectVersie.Formaat; - - // We have enabled (some) metadata fields for the underlying document provider - var metadata = new DocumentMeta - { - Rsin = enkelvoudigInformatieObjectVersie.InformatieObject.Owner, - Version = enkelvoudigInformatieObjectVersie.Versie, - }; - - try - { - var document = await DocumentService.AddDocumentAsync( - enkelvoudigInformatieObjectVersie.Inhoud, - enkelvoudigInformatieObjectVersie.Bestandsnaam ?? "", - contentType, - metadata, - cancellationToken: cancellationToken - ); - - enkelvoudigInformatieObjectVersie.Inhoud = $"{document.Urn}"; - enkelvoudigInformatieObjectVersie.Bestandsomvang = document.Size; - } - catch (Exception ex) - { - _logger.LogError(ex, "Error adding (a new version of) the document to DMS [{ProviderPrefix}].", DocumentService.ProviderPrefix); - throw; - } - } - - private async Task SetVertrouwelijkheidAanduidingAsync(EnkelvoudigInformatieObjectVersie enkelvoudigInformatieObjectVersie) - { - if (!enkelvoudigInformatieObjectVersie.Vertrouwelijkheidaanduiding.HasValue) - { - // This value is guaranteed to be read from the cache (when validation is enabled which is normally the case of course!) - var informatieObjectType = await _catalogiServiceAgent.GetInformatieObjectTypeByUrlAsync( - enkelvoudigInformatieObjectVersie.InformatieObject.InformatieObjectType - ); - - if ( - Enum.TryParse( - informatieObjectType.Response.VertrouwelijkheidAanduiding, - out var vertrouwelijkheidaanduiding - ) - ) - { - enkelvoudigInformatieObjectVersie.Vertrouwelijkheidaanduiding = vertrouwelijkheidaanduiding; - } - } - } - - private IDocumentService DocumentService => _lazyDocumentService.Value; - - private static AuditTrailOptions AuditTrailOptions => - new AuditTrailOptions { Bron = ServiceRoleName.DRC, Resource = "enkelvoudiginformatieobject" }; -} - -class CreateEnkelvoudigInformatieObjectCommand : IRequest> -{ - public Guid? ExistingEnkelvoudigInformatieObjectId { get; internal set; } - public EnkelvoudigInformatieObjectVersie EnkelvoudigInformatieObjectVersie { get; internal set; } // For PUT endpoint, contains the full update sent by the client, For POST endpoint this is null - public dynamic PartialObject { get; internal set; } // For PATCH endpoint, contains the partial update sent by the client -} diff --git a/src/OneGround.ZGW.Documenten.Web/Handlers/v1/DownloadEnkelvoudigInformatieObjectQueryHandler.cs b/src/OneGround.ZGW.Documenten.Web/Handlers/v1/DownloadEnkelvoudigInformatieObjectQueryHandler.cs deleted file mode 100644 index 9e62276a..00000000 --- a/src/OneGround.ZGW.Documenten.Web/Handlers/v1/DownloadEnkelvoudigInformatieObjectQueryHandler.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using OneGround.ZGW.Common.Handlers; -using OneGround.ZGW.Common.Web.Authorization; -using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.Documenten.DataModel; -using OneGround.ZGW.Documenten.Services; - -namespace OneGround.ZGW.Documenten.Web.Handlers.v1; - -class DownloadEnkelvoudigInformatieObjectQueryHandler - : DocumentenBaseHandler, - IRequestHandler> -{ - private readonly DrcDbContext _context; - private readonly IDocumentServicesResolver _documentServicesResolver; - - public DownloadEnkelvoudigInformatieObjectQueryHandler( - ILogger logger, - IConfiguration configuration, - DrcDbContext context, - IEntityUriService uriService, - IDocumentServicesResolver documentServicesResolver, - IAuthorizationContextAccessor authorizationContextAccessor, - IDocumentKenmerkenResolver documentKenmerkenResolver - ) - : base(logger, configuration, uriService, authorizationContextAccessor, documentKenmerkenResolver) - { - _context = context; - _documentServicesResolver = documentServicesResolver; - } - - public async Task> Handle(DownloadEnkelvoudigInformatieObjectQuery request, CancellationToken cancellationToken) - { - _logger.LogDebug("Download EnkelvoudigInformatieObject...."); - - var rsinFilter = GetRsinFilterPredicate(); - - var enkelvoudigInformatieObject = await _context - .EnkelvoudigInformatieObjecten.AsNoTracking() - .Where(rsinFilter) - .SingleOrDefaultAsync(a => a.Id == request.EnkelvoudigInformatieObjectId, cancellationToken); - - if (enkelvoudigInformatieObject == null) - { - return new QueryResult(null, QueryStatus.NotFound); - } - - var service = _documentServicesResolver.Find(request.DocumentUrn.Type); - if (service == null) - { - _logger.LogError("Could not find a document provider to handle {DocumentUrn}.", request.DocumentUrn); - - return new QueryResult(null, QueryStatus.NotFound); - } - - _logger.LogDebug("DMS provider to retrieve document from '{ProviderPrefix}' [{service}]", service.ProviderPrefix, service); - - var contents = await service.TryGetDocumentAsync(request.DocumentUrn, cancellationToken); - if (contents == null) - { - _logger.LogError( - "Document '{DocumentUrn}' [EnkelvoudigInformatieObjectId={EnkelvoudigInformatieObjectId}] does not exist. Is it moved?", - request.DocumentUrn, - request.EnkelvoudigInformatieObjectId - ); - - return new QueryResult(null, QueryStatus.NotFound); - } - - return new QueryResult(contents, QueryStatus.OK); - } -} - -class DownloadEnkelvoudigInformatieObjectQuery : IRequest> -{ - public DocumentUrn DocumentUrn { get; internal set; } - public Guid EnkelvoudigInformatieObjectId { get; internal set; } -} diff --git a/src/OneGround.ZGW.Documenten.Web/Handlers/v1/GetAllEnkelvoudigInformatieObjectenQueryHandler.cs b/src/OneGround.ZGW.Documenten.Web/Handlers/v1/GetAllEnkelvoudigInformatieObjectenQueryHandler.cs deleted file mode 100644 index a2442330..00000000 --- a/src/OneGround.ZGW.Documenten.Web/Handlers/v1/GetAllEnkelvoudigInformatieObjectenQueryHandler.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; -using System.Threading.Tasks; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using OneGround.ZGW.Common.Caching; -using OneGround.ZGW.Common.Handlers; -using OneGround.ZGW.Common.Helpers; -using OneGround.ZGW.Common.Web.Authorization; -using OneGround.ZGW.Common.Web.Models; -using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.Documenten.DataModel; -using OneGround.ZGW.Documenten.DataModel.Authorization; -using OneGround.ZGW.Documenten.Web.Models.v1; -using OneGround.ZGW.Documenten.Web.Services; - -namespace OneGround.ZGW.Documenten.Web.Handlers.v1; - -class GetAllEnkelvoudigInformatieObjectenQueryHandler - : DocumentenBaseHandler, - IRequestHandler>> -{ - private readonly DrcDbContext _context; - private readonly IDistributedCacheHelper _cache; - private readonly IInformatieObjectAuthorizationTempTableService _informatieObjectAuthorizationTempTableService; - - // TTL for cached count. Count for a given (rsin, page, filter) tuple are stable within this window. - private static readonly TimeSpan CountCacheLifetime = TimeSpan.FromMinutes(5); - - public GetAllEnkelvoudigInformatieObjectenQueryHandler( - ILogger logger, - IConfiguration configuration, - IEntityUriService uriService, - DrcDbContext context, - IAuthorizationContextAccessor authorizationContextAccessor, - IDistributedCacheHelper cache, - IInformatieObjectAuthorizationTempTableService informatieObjectAuthorizationTempTableService, - IDocumentKenmerkenResolver documentKenmerkenResolver - ) - : base(logger, configuration, uriService, authorizationContextAccessor, documentKenmerkenResolver) - { - _context = context; - _cache = cache; - _informatieObjectAuthorizationTempTableService = informatieObjectAuthorizationTempTableService; - } - - public async Task>> Handle( - GetAllEnkelvoudigInformatieObjectenQuery request, - CancellationToken cancellationToken - ) - { - _logger.LogDebug("Get all EnkelvoudigInformatieObjecten...."); - - var filter = GetEnkelvoudigInformatieObjectFilterPredicate(request.GetAllEnkelvoudigInformatieObjectenFilter); - var rsinFilter = GetRsinFilterPredicate(); - - var query = _context.EnkelvoudigInformatieObjecten.AsNoTracking().Where(rsinFilter).Where(filter); - - bool hasAuthorizationFilter = !_authorizationContext.Authorization.HasAllAuthorizations; - if (hasAuthorizationFilter) - { - await _informatieObjectAuthorizationTempTableService.InsertInformatieObjectTypeAuthorizationsToTempTableAsync( - _authorizationContext, - _context, - cancellationToken - ); - - var authPairs = await _context.TempInformatieObjectAuthorization.AsNoTracking().ToListAsync(cancellationToken); - query = query.Where(BuildInlineAuthorizationPredicate(authPairs)); - } - - // Count with authorization reuses the same query (it already carries the inline VHA-grouped - // auth predicate), so no temp-table JOIN is needed. Without a selectivity filter the planner - // may pick a BitmapOr that turns lossy at scale and triggers millions of heap rechecks (~29s); - // GetAuthorizationCountCachedAsync forces enable_bitmapscan=off so it uses the - // (owner, iot, vha) covering index as a single Index-Only Scan + aggregate — O(N), no heap. - var totalCount = hasAuthorizationFilter - ? await GetAuthorizationCountCachedAsync(query, request.GetAllEnkelvoudigInformatieObjectenFilter, cancellationToken) - : await GetTotalCountCachedAsync(query, request.GetAllEnkelvoudigInformatieObjectenFilter, cancellationToken); - - // Phase 1: Get page IDs using a narrow SELECT so the planner uses the (owner, id) index - // with early termination instead of materializing all matching rows. - var pageIds = await query - .OrderByDescending(e => e.CreationTime) - .ThenBy(e => e.Id) - .Skip(request.Pagination.Size * (request.Pagination.Page - 1)) - .Take(request.Pagination.Size) - .Select(e => e.Id) - .ToListAsync(cancellationToken); - - // Phase 2: Fetch complete data for only the matched IDs (PK lookups). - var pagedResult = - pageIds.Count > 0 - ? await _context - .EnkelvoudigInformatieObjecten.AsNoTracking() - .Where(e => pageIds.Contains(e.Id)) - .Include(e => e.LatestEnkelvoudigInformatieObjectVersie) - .OrderByDescending(e => e.CreationTime) - .ThenBy(e => e.Id) - .ToListAsync(cancellationToken) - : []; - - var result = new PagedResult { PageResult = pagedResult, Count = totalCount }; - return new QueryResult>(result, QueryStatus.OK); - } - - private async Task GetAuthorizationCountCachedAsync( - IQueryable query, - GetAllEnkelvoudigInformatieObjectenFilter filterModel, - CancellationToken cancellationToken - ) - { - var key = ObjectHasher.ComputeSha1Hash(new { Rsin = _rsin, GetAllEnkelvoudigInformatieObjectenFilter = filterModel }); - - return await _cache.GetAsync( - key, - factory: async () => - { - // The query already carries the inline VHA-grouped auth predicate, so this is just a - // COUNT over it — no temp-table JOIN. Unlike the paged query there is no LIMIT to - // terminate early, so without a selectivity filter (~1M rows) the planner may pick a - // BitmapOr that turns lossy and triggers millions of heap rechecks (~29s). Forcing - // enable_bitmapscan=off makes it use the (owner, iot, vha) covering index as a single - // Index-Only Scan + filter + aggregate. With a filter the result is small enough that - // the planner chooses correctly on its own. - bool anyFiltersSet = !string.IsNullOrEmpty(filterModel.Identificatie) || !string.IsNullOrEmpty(filterModel.Bronorganisatie); - - if (!anyFiltersSet) - { - // SET LOCAL requires an active transaction. Settings revert automatically when - // the transaction is disposed — safe for pooled connections. - using var transaction = await _context.Database.BeginTransactionAsync(cancellationToken); - await _context.Database.ExecuteSqlRawAsync("SET LOCAL enable_bitmapscan = off;", cancellationToken); - - return await query.CountAsync(cancellationToken); - } - - return await query.CountAsync(cancellationToken); - }, - absoluteExpirationRelativeToNow: CountCacheLifetime, - cancellationToken - ); - } - - private async Task GetTotalCountCachedAsync( - IQueryable query, - GetAllEnkelvoudigInformatieObjectenFilter filter, - CancellationToken cancellationToken - ) - { - var key = ObjectHasher.ComputeSha1Hash(new { Rsin = _rsin, GetAllEnkelvoudigInformatieObjectenFilter = filter }); - - return await _cache.GetAsync( - key, - factory: async () => await query.CountAsync(cancellationToken), - absoluteExpirationRelativeToNow: CountCacheLifetime, - cancellationToken - ); - } - - // Builds: (iot == 'x' && (int)vha.Value <= 5) || (iot == 'y' && (int)vha.Value <= 3) || ... - // Inline constants remove the temp-table reference so PostgreSQL uses the (owner, id) index - // with early termination via LIMIT — no Sort node needed. - private static Expression> BuildInlineAuthorizationPredicate( - List authPairs - ) - { - var param = Expression.Parameter(typeof(EnkelvoudigInformatieObject), "o"); - - if (authPairs.Count == 0) - return Expression.Lambda>(Expression.Constant(false), param); - - Expression? body = null; - foreach (var pair in authPairs) - { - var iotProp = Expression.Property(param, nameof(EnkelvoudigInformatieObject.InformatieObjectType)); - var iotEq = Expression.Equal(iotProp, Expression.Constant(pair.InformatieObjectType)); - - var vhaNullable = Expression.Property(param, nameof(EnkelvoudigInformatieObject.LatestVertrouwelijkheidAanduiding)); - var vhaInt = Expression.Convert(Expression.Property(vhaNullable, "Value"), typeof(int)); - var vhaLe = Expression.LessThanOrEqual(vhaInt, Expression.Constant(pair.MaximumVertrouwelijkheidAanduiding)); - - var pairExpr = Expression.AndAlso(iotEq, vhaLe); - body = body == null ? pairExpr : Expression.OrElse(body, pairExpr); - } - - return Expression.Lambda>(body!, param); - } - - private static Expression> GetEnkelvoudigInformatieObjectFilterPredicate( - GetAllEnkelvoudigInformatieObjectenFilter filter - ) - { - return e => - (filter.Bronorganisatie == null || e.LatestEnkelvoudigInformatieObjectVersie.Bronorganisatie == filter.Bronorganisatie) - && (filter.Identificatie == null || e.LatestEnkelvoudigInformatieObjectVersie.Identificatie == filter.Identificatie); - } -} - -class GetAllEnkelvoudigInformatieObjectenQuery : IRequest>> -{ - public GetAllEnkelvoudigInformatieObjectenFilter GetAllEnkelvoudigInformatieObjectenFilter { get; internal set; } - public PaginationFilter Pagination { get; internal set; } -} diff --git a/src/OneGround.ZGW.Documenten.Web/Handlers/v1/GetEnkelvoudigInformatieObjectQueryHandler.cs b/src/OneGround.ZGW.Documenten.Web/Handlers/v1/GetEnkelvoudigInformatieObjectQueryHandler.cs deleted file mode 100644 index f455fcd4..00000000 --- a/src/OneGround.ZGW.Documenten.Web/Handlers/v1/GetEnkelvoudigInformatieObjectQueryHandler.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using OneGround.ZGW.Common.Handlers; -using OneGround.ZGW.Common.Web.Authorization; -using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.Documenten.DataModel; -using OneGround.ZGW.Documenten.Web.Authorization; -using OneGround.ZGW.Documenten.Web.Models.v1; - -namespace OneGround.ZGW.Documenten.Web.Handlers.v1; - -class GetEnkelvoudigInformatieObjectQueryHandler - : DocumentenBaseHandler, - IRequestHandler> -{ - private readonly DrcDbContext _context; - - public GetEnkelvoudigInformatieObjectQueryHandler( - ILogger logger, - IConfiguration configuration, - IEntityUriService uriService, - DrcDbContext context, - IAuthorizationContextAccessor authorizationContextAccessor, - IDocumentKenmerkenResolver documentKenmerkenResolver - ) - : base(logger, configuration, uriService, authorizationContextAccessor, documentKenmerkenResolver) - { - _context = context; - } - - public async Task> Handle( - GetEnkelvoudigInformatieObjectQuery request, - CancellationToken cancellationToken - ) - { - _logger.LogDebug("Get EnkelvoudigInformatieObject {Id}....", request.Id); - - // Note: The object contains metadata about the document and the download link (content) to the binary data. - // By default, this returns the latest version of the (SINGLE) INFORMATION OBJECT. Specific versions can - // be requested by means of query string parameters. - - var filter = request.GetEnkelvoudigInformatieObjectFilter; - var rsinFilterVersion = GetRsinFilterPredicate(); - - // Build a single query that fetches both the parent object and the correct version - IQueryable versieQuery = _context - .EnkelvoudigInformatieObjectVersies.AsNoTracking() - .AsSplitQuery() - .Where(rsinFilterVersion) - .Where(v => v.EnkelvoudigInformatieObjectId == request.Id) - .Include(v => v.LatestInformatieObject) - .Include(v => v.InformatieObject); // Include parent object in one query - - // Apply version filtering logic - EnkelvoudigInformatieObjectVersie requestedVersie; - - if (!filter.Versie.HasValue && !filter.RegistratieOp.HasValue) - { - // Latest version (Filter: none) - requestedVersie = await versieQuery.OrderBy(v => v.Versie).LastOrDefaultAsync(cancellationToken); - } - else if (filter.Versie.HasValue && !filter.RegistratieOp.HasValue) - { - // Specific version (Filter: 'versie') - requestedVersie = await versieQuery.Where(v => v.Versie == filter.Versie).SingleOrDefaultAsync(cancellationToken); - } - else if (!filter.Versie.HasValue && filter.RegistratieOp.HasValue) - { - // Nearest version before date (Filter: 'registratieOp') - requestedVersie = await versieQuery - .Where(v => v.BeginRegistratie <= filter.RegistratieOp) - .OrderBy(v => v.BeginRegistratie) - .LastOrDefaultAsync(cancellationToken); - } - else - { - // Both filters (Filter: 'registratieOp' and 'versie') - requestedVersie = await versieQuery - .Where(v => v.BeginRegistratie <= filter.RegistratieOp && v.Versie == filter.Versie) - .SingleOrDefaultAsync(cancellationToken); - } - - if (requestedVersie == null) - { - return new QueryResult(null, QueryStatus.NotFound); - } - - var enkelvoudiginformatieobject = requestedVersie.InformatieObject; - - // Authorization check on the requested version - if (!AuthorizationContextAccessor.AuthorizationContext.IsAuthorized(requestedVersie)) - { - return new QueryResult(null, QueryStatus.Forbidden); - } - - // Set up the relationships (both directions) - enkelvoudiginformatieobject.LatestEnkelvoudigInformatieObjectVersie = requestedVersie; - enkelvoudiginformatieobject.LatestEnkelvoudigInformatieObjectVersie.LatestInformatieObject = enkelvoudiginformatieobject; - - // Attach only the requested version - enkelvoudiginformatieobject.EnkelvoudigInformatieObjectVersies.Clear(); - enkelvoudiginformatieobject.EnkelvoudigInformatieObjectVersies.Add(requestedVersie); - - if (request.IgnoreLock) - { - enkelvoudiginformatieobject.Lock = null; - } - - return new QueryResult(enkelvoudiginformatieobject, QueryStatus.OK); - } -} - -class GetEnkelvoudigInformatieObjectQuery : IRequest> -{ - public Guid Id { get; internal set; } - public GetEnkelvoudigInformatieObjectFilter GetEnkelvoudigInformatieObjectFilter { get; internal set; } = - new GetEnkelvoudigInformatieObjectFilter(); - public bool IgnoreLock { get; internal set; } -} diff --git a/src/OneGround.ZGW.Documenten.Web/Handlers/v1/LockEnkelvoudigInformatieObjectCommandHandler.cs b/src/OneGround.ZGW.Documenten.Web/Handlers/v1/LockEnkelvoudigInformatieObjectCommandHandler.cs deleted file mode 100644 index 64b5706e..00000000 --- a/src/OneGround.ZGW.Documenten.Web/Handlers/v1/LockEnkelvoudigInformatieObjectCommandHandler.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Data; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using OneGround.ZGW.Common.Constants; -using OneGround.ZGW.Common.Contracts.v1; -using OneGround.ZGW.Common.Handlers; -using OneGround.ZGW.Common.Web.Authorization; -using OneGround.ZGW.Common.Web.Services.AuditTrail; -using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.DataAccess; -using OneGround.ZGW.Documenten.Contracts.v1.Responses; -using OneGround.ZGW.Documenten.DataModel; -using OneGround.ZGW.Documenten.Web.Authorization; -using OneGround.ZGW.Documenten.Web.Concurrency; - -namespace OneGround.ZGW.Documenten.Web.Handlers.v1; - -class LockEnkelvoudigInformatieObjectCommandHandler - : DocumentenBaseHandler, - IRequestHandler> -{ - private readonly DrcDbContext _context; - private readonly IAuditTrailFactory _auditTrailFactory; - private readonly ResilienceConcurrencyRetryPipeline _concurrencyRetryPipeline; - - public LockEnkelvoudigInformatieObjectCommandHandler( - ILogger logger, - IConfiguration configuration, - DrcDbContext context, - IEntityUriService uriService, - IAuditTrailFactory auditTrailFactory, - IAuthorizationContextAccessor authorizationContextAccessor, - IDocumentKenmerkenResolver documentKenmerkenResolver, - ResilienceConcurrencyRetryPipeline concurrencyRetryPipeline - ) - : base(logger, configuration, uriService, authorizationContextAccessor, documentKenmerkenResolver) - { - _context = context; - _auditTrailFactory = auditTrailFactory; - _concurrencyRetryPipeline = concurrencyRetryPipeline; - } - - public async Task> Handle(LockEnkelvoudigInformatieObjectCommand request, CancellationToken cancellationToken) - { - if (request.Set) - _logger.LogDebug("Locking EnkelvoudigInformatieObject...."); - else - _logger.LogDebug("Unlocking EnkelvoudigInformatieObject...."); - - // Use ReadCommitted isolation level: - // - FOR UPDATE provides pessimistic row-level locking (prevents concurrent modifications) - // - xmin (configured in EnkelvoudigInformatieObject) provides optimistic concurrency detection (detects any changes since read) - // - ReadCommitted allows better concurrency than Serializable for this use case - // - The combination prevents both lost updates (via FOR UPDATE) and write skew (via xmin) - using var tx = await _context.Database.BeginTransactionAsync(IsolationLevel.ReadCommitted, cancellationToken); - - var rsinFilter = GetRsinFilterPredicate(); - - var (enkelvoudigInformatieObject, status) = await _concurrencyRetryPipeline.ExecuteWithResultAsync( - async (token) => - { - // First, try to acquire lock on the EnkelvoudigInformatieObject - var _enkelvoudigInformatieObject = await _context - .EnkelvoudigInformatieObjecten.LockForUpdate(_context, c => c.Id, [request.Id]) - .Where(rsinFilter) - .Include(e => e.LatestEnkelvoudigInformatieObjectVersie) - .SingleOrDefaultAsync(e => e.Id == request.Id, token); - - // The object might be locked OR not exist - check if it exists without lock - if (_enkelvoudigInformatieObject == null) - { - // The object might be locked OR not exist - check if it exists without lock - var exists = await _context.EnkelvoudigInformatieObjecten.Where(rsinFilter).AnyAsync(e => e.Id == request.Id, token); - - if (!exists) - { - // Object truly doesn't exist - return (enkelvoudiginformatieobject: null, status: CommandStatus.NotFound); - } - - // Throw the exception again so Polly knows a retry is needed (giving up after maximum reached retries) - throw new ConcurrencyConflictException("Concurrency conflict detected.", request.Id); - } - else - { - return (enkelvoudiginformatieobject: _enkelvoudigInformatieObject, status: CommandStatus.OK); - } - }, - cancellationToken - ); - - if (status == CommandStatus.NotFound) - { - return new CommandResult(null, CommandStatus.NotFound); - } - - if (status == CommandStatus.Conflict) - { - // Object exists but is locked by another process - var error = new ValidationError( - "nonFieldErrors", - ErrorCode.Conflict, - $"Het enkelvoudiginformatieobject {request.Id} is vergrendeld door een andere bewerking." - ); - return new CommandResult(null, CommandStatus.Conflict, error); - } - - using (var audittrail = _auditTrailFactory.Create(AuditTrailOptions, enkelvoudigInformatieObject.LegacyAuditTrail)) - { - audittrail.SetOld(enkelvoudigInformatieObject); - - if (request.Set) - { - if (enkelvoudigInformatieObject.Locked) - { - var error = new ValidationError("nonFieldErrors", ErrorCode.ExistingLock, "Het document is al gelockt."); - return new CommandResult(null, CommandStatus.ValidationError, error); - } - - enkelvoudigInformatieObject.Locked = true; - enkelvoudigInformatieObject.Lock = Guid.NewGuid().ToString().Replace("-", ""); - } - else - { - if (request.Lock != null && enkelvoudigInformatieObject.Lock != request.Lock) - { - var error = new ValidationError("nonFieldErrors", ErrorCode.IncorrectLockId, "Incorrect lock ID."); - return new CommandResult(null, CommandStatus.ValidationError, error); - } - - if (request.Lock == null) - { - if (!AuthorizationContextAccessor.AuthorizationContext.IsForcedUnlockAuthorized()) - { - var error = new ValidationError("nonFieldErrors", ErrorCode.MissingLockId, "Dit is een verplicht veld."); - return new CommandResult(null, CommandStatus.ValidationError, error); - } - } - enkelvoudigInformatieObject.Locked = false; - enkelvoudigInformatieObject.Lock = null; - } - - audittrail.SetNew(enkelvoudigInformatieObject); - - await audittrail.PatchedAsync(enkelvoudigInformatieObject, enkelvoudigInformatieObject, cancellationToken); - - await _context.SaveChangesAsync(cancellationToken); - - await tx.CommitAsync(cancellationToken); - - if (request.Set) - _logger.LogDebug("EnkelvoudigInformatieObject successfully locked. Lock={Lock}", enkelvoudigInformatieObject.Lock); - else - _logger.LogDebug("EnkelvoudigInformatieObject successfully unlocked."); - } - return new CommandResult(enkelvoudigInformatieObject.Lock, CommandStatus.OK); - } - - private static AuditTrailOptions AuditTrailOptions => - new AuditTrailOptions { Bron = ServiceRoleName.DRC, Resource = "enkelvoudiginformatieobject" }; -} - -class LockEnkelvoudigInformatieObjectCommand : IRequest> -{ - public bool Set { get; internal set; } - public string Lock { get; internal set; } - public Guid Id { get; internal set; } -} diff --git a/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/DomainToResponseRegister.cs b/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/DomainToResponseRegister.cs index 651f7293..3514922a 100644 --- a/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/DomainToResponseRegister.cs +++ b/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/DomainToResponseRegister.cs @@ -4,7 +4,6 @@ using OneGround.ZGW.Common.Contracts.v1.AuditTrail; using OneGround.ZGW.Common.Helpers; using OneGround.ZGW.Common.Web.Mapping.Mapster; -using OneGround.ZGW.Common.Web.Services.UriServices; using OneGround.ZGW.DataAccess.AuditTrail; using OneGround.ZGW.Documenten.Contracts.v1.Requests; using OneGround.ZGW.Documenten.Contracts.v1.Responses; @@ -16,85 +15,6 @@ public class DomainToResponseRegister : IRegister { public void Register(TypeAdapterConfig config) { - config - .NewConfig() - .Map(dest => dest.Url, src => MapsterUrlResolver.ResolveUrl(src)) - .Ignore(dest => dest.Versie) - .Ignore(dest => dest.BeginRegistratie) - .Ignore(dest => dest.Bestandsomvang) - .Ignore(dest => dest.Identificatie) - .Ignore(dest => dest.Bronorganisatie) - .Ignore(dest => dest.CreatieDatum) - .Ignore(dest => dest.Titel) - .Ignore(dest => dest.Vertrouwelijkheidaanduiding) - .Ignore(dest => dest.Auteur) - .Ignore(dest => dest.Status) - .Ignore(dest => dest.Formaat) - .Ignore(dest => dest.Taal) - .Ignore(dest => dest.Bestandsnaam) - .Ignore(dest => dest.Inhoud) - .Ignore(dest => dest.Link) - .Ignore(dest => dest.Beschrijving) - .Ignore(dest => dest.OntvangstDatum) - .Ignore(dest => dest.VerzendDatum) - .Ignore(dest => dest.IndicatieGebruiksrecht) - .Ignore(dest => dest.Ondertekening) - .Ignore(dest => dest.Integriteit) - .AfterMapping((src, dest) => MapLatestVersieToGetResponse(src, dest, MapContext.Current.GetService())); - - config - .NewConfig() - .Map(dest => dest.Url, src => MapsterUrlResolver.ResolveUrl(src.InformatieObject)) - .Map(dest => dest.CreatieDatum, src => ProfileHelper.StringDateFromDate(src.CreatieDatum)) - .Map(dest => dest.OntvangstDatum, src => ProfileHelper.StringDateFromDate(src.OntvangstDatum)) - .Map(dest => dest.BeginRegistratie, src => ProfileHelper.StringDateFromDateTime(src.BeginRegistratie, true)) - .Map(dest => dest.VerzendDatum, src => ProfileHelper.StringDateFromDate(src.VerzendDatum)) - .Map(dest => dest.Ondertekening, src => EnkelvoudigInformatieObjectVersieMapperHelper.CreateOptionalOndertekeningDto(src, true)) - .Map(dest => dest.Integriteit, src => EnkelvoudigInformatieObjectVersieMapperHelper.CreateOptionalIntegriteitDto(src, true)) - .Map(dest => dest.IndicatieGebruiksrecht, src => src.InformatieObject.IndicatieGebruiksrecht) - .Map(dest => dest.Locked, src => src.InformatieObject.Locked) - .Map(dest => dest.Inhoud, src => MapsterUrlResolver.ResolveUrl(src)) - .Map(dest => dest.InformatieObjectType, src => src.InformatieObject.InformatieObjectType); - - config - .NewConfig() - .Map(dest => dest.Url, src => MapsterUrlResolver.ResolveUrl(src.InformatieObject)) - .Map(dest => dest.CreatieDatum, src => ProfileHelper.StringDateFromDate(src.CreatieDatum)) - .Map(dest => dest.OntvangstDatum, src => ProfileHelper.StringDateFromDate(src.OntvangstDatum)) - .Map(dest => dest.BeginRegistratie, src => ProfileHelper.StringDateFromDateTime(src.BeginRegistratie, true)) - .Map(dest => dest.VerzendDatum, src => ProfileHelper.StringDateFromDate(src.VerzendDatum)) - .Map(dest => dest.Ondertekening, src => EnkelvoudigInformatieObjectVersieMapperHelper.CreateOptionalOndertekeningDto(src, true)) - .Map(dest => dest.Integriteit, src => EnkelvoudigInformatieObjectVersieMapperHelper.CreateOptionalIntegriteitDto(src, true)) - .Map(dest => dest.IndicatieGebruiksrecht, src => src.InformatieObject.IndicatieGebruiksrecht) - .Map(dest => dest.Locked, src => src.InformatieObject.Locked) - .Map(dest => dest.Lock, src => src.InformatieObject.Lock) - .Map(dest => dest.Inhoud, src => MapsterUrlResolver.ResolveUrl(src)) - .Map(dest => dest.InformatieObjectType, src => src.InformatieObject.InformatieObjectType); - - // Note: This map is used to merge an existing ENKELVOUDIGINFORMATIEOBJECT(+VERSIE) with the PATCH operation - config - .NewConfig() - .Ignore(dest => dest.Identificatie) - .Ignore(dest => dest.Bronorganisatie) - .Ignore(dest => dest.CreatieDatum) - .Ignore(dest => dest.Titel) - .Ignore(dest => dest.Vertrouwelijkheidaanduiding) - .Ignore(dest => dest.Auteur) - .Ignore(dest => dest.Status) - .Ignore(dest => dest.Formaat) - .Ignore(dest => dest.Taal) - .Ignore(dest => dest.Bestandsnaam) - .Ignore(dest => dest.Inhoud) - .Ignore(dest => dest.Link) - .Ignore(dest => dest.Beschrijving) - .Ignore(dest => dest.OntvangstDatum) - .Ignore(dest => dest.VerzendDatum) - .Ignore(dest => dest.IndicatieGebruiksrecht) - .Ignore(dest => dest.Ondertekening) - .Ignore(dest => dest.Integriteit) - .Ignore(dest => dest.Lock) // Don't merge Lock: the request's own value must be validated, not the one already in the database. - .AfterMapping((src, dest) => MapLatestVersieToUpdateRequest(src, dest)); - config .NewConfig() .Map(dest => dest.Url, src => MapsterUrlResolver.ResolveUrl(src)) @@ -121,69 +41,6 @@ public void Register(TypeAdapterConfig config) .Map(dest => dest.AanmaakDatum, src => ProfileHelper.StringDateFromDateTime(src.AanmaakDatum, true)); } - // Ported verbatim from MapLatestEnkelvoudigInformatieObjectVersieResponse.Process(...) (_uriService -> uriService parameter). - private static void MapLatestVersieToGetResponse( - EnkelvoudigInformatieObject src, - EnkelvoudigInformatieObjectGetResponseDto dest, - IEntityUriService uriService - ) - { - var latestVersion = src.LatestEnkelvoudigInformatieObjectVersie; - - dest.Versie = latestVersion.Versie; - dest.Bronorganisatie = latestVersion.Bronorganisatie; - dest.Identificatie = latestVersion.Identificatie; - dest.Bestandsomvang = latestVersion.Bestandsomvang; - dest.BeginRegistratie = ProfileHelper.StringDateFromDateTime(latestVersion.BeginRegistratie, withTime: true); - dest.CreatieDatum = ProfileHelper.StringDateFromDate(latestVersion.CreatieDatum); - dest.Titel = latestVersion.Titel; - dest.Vertrouwelijkheidaanduiding = $"{latestVersion.Vertrouwelijkheidaanduiding}"; - dest.Auteur = latestVersion.Auteur; - dest.Status = $"{latestVersion.Status}"; - dest.Formaat = latestVersion.Formaat; - dest.Taal = latestVersion.Taal; - dest.Bestandsnaam = latestVersion.Bestandsnaam; - dest.Link = latestVersion.Link; - dest.Inhoud = uriService.GetUri(latestVersion); - dest.Beschrijving = latestVersion.Beschrijving; - dest.OntvangstDatum = ProfileHelper.StringDateFromDate(latestVersion.OntvangstDatum); - dest.VerzendDatum = ProfileHelper.StringDateFromDate(latestVersion.VerzendDatum); - dest.Ondertekening = EnkelvoudigInformatieObjectVersieMapperHelper.CreateOptionalOndertekeningDto(latestVersion, true); - dest.Integriteit = EnkelvoudigInformatieObjectVersieMapperHelper.CreateOptionalIntegriteitDto(latestVersion, true); - - dest.InformatieObjectType = latestVersion.LatestInformatieObject.InformatieObjectType; - dest.IndicatieGebruiksrecht = latestVersion.LatestInformatieObject.IndicatieGebruiksrecht; - dest.Locked = latestVersion.LatestInformatieObject.Locked; - } - - // Ported verbatim from MapLatestEnkelvoudigInformatieObjectVersieRequest.Process(...). - private static void MapLatestVersieToUpdateRequest(EnkelvoudigInformatieObject src, EnkelvoudigInformatieObjectUpdateRequestDto dest) - { - var latestVersion = src.LatestEnkelvoudigInformatieObjectVersie; - - dest.Bronorganisatie = latestVersion.Bronorganisatie; - dest.Identificatie = latestVersion.Identificatie; - dest.CreatieDatum = ProfileHelper.StringDateFromDate(latestVersion.CreatieDatum); - dest.Titel = latestVersion.Titel; - dest.Vertrouwelijkheidaanduiding = $"{latestVersion.Vertrouwelijkheidaanduiding}"; - dest.Auteur = latestVersion.Auteur; - dest.Status = $"{latestVersion.Status}"; - dest.Formaat = latestVersion.Formaat; - dest.Taal = latestVersion.Taal; - dest.Bestandsnaam = latestVersion.Bestandsnaam; - dest.Inhoud = latestVersion.Inhoud; - dest.Link = latestVersion.Link; - dest.Beschrijving = latestVersion.Beschrijving; - dest.OntvangstDatum = ProfileHelper.StringDateFromDate(latestVersion.OntvangstDatum); - dest.VerzendDatum = ProfileHelper.StringDateFromDate(latestVersion.VerzendDatum); - dest.Ondertekening = EnkelvoudigInformatieObjectVersieMapperHelper.CreateOptionalOndertekeningDto(latestVersion, false); - dest.Integriteit = EnkelvoudigInformatieObjectVersieMapperHelper.CreateOptionalIntegriteitDto(latestVersion, false); - dest.InformatieObjectType = latestVersion.LatestInformatieObject.InformatieObjectType; - dest.IndicatieGebruiksrecht = latestVersion.LatestInformatieObject.IndicatieGebruiksrecht; - - // Lock is deliberately not set here -- the request's own value must be validated, not the one already stored. - } - private static WijzigingDto ConvertWijzigingenToDto(string oud, string nieuw) { var result = new WijzigingDto(); diff --git a/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/RequestToDomainRegister.cs b/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/RequestToDomainRegister.cs index 9cf1e4eb..577ff9f3 100644 --- a/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/RequestToDomainRegister.cs +++ b/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/RequestToDomainRegister.cs @@ -11,154 +11,9 @@ public class RequestToDomainRegister : IRegister { public void Register(TypeAdapterConfig config) { + // Shared with v1.1, which reuses these base query parameters (it has its own request/response bodies). config.NewConfig(); - // Create new initial EnkelvoudigInformatieObject: versie 1 - config - .NewConfig() - .Ignore(dest => dest.Id) - .Ignore(dest => dest.CreatedBy) - .Ignore(dest => dest.ModifiedBy) - .Ignore(dest => dest.CreationTime) - .Ignore(dest => dest.ModificationTime) - .Ignore(dest => dest.Locked) - .Ignore(dest => dest.Lock) - .Ignore(dest => dest.ObjectInformatieObjecten) - .Ignore(dest => dest.GebruiksRechten) - .Ignore(dest => dest.EnkelvoudigInformatieObjectVersies) - .Ignore(dest => dest.Owner) - .Ignore(dest => dest.Verzendingen) - .Ignore(dest => dest.LatestEnkelvoudigInformatieObjectVersieId) - .Ignore(dest => dest.LatestEnkelvoudigInformatieObjectVersie) - .Ignore(dest => dest.LatestVertrouwelijkheidAanduiding) - .Ignore(dest => dest.CatalogusId) - .Ignore(dest => dest.LegacyAuditTrail) - .Ignore(dest => dest.RowVersion); - - config - .NewConfig() - // InformatieObject is assigned in .AfterMapping, not .Map: a mapped member would pull the cyclic - // EnkelvoudigInformatieObject/EnkelvoudigInformatieObjectVersie graph into Mapster's compiler and hang the build. - // LatestInformatieObject is .Ignore()'d below for the same reason. - .Ignore(dest => dest.Id) - .Map(dest => dest.CreatieDatum, src => ProfileHelper.DateFromStringOptional(src.CreatieDatum)) - .Map(dest => dest.OntvangstDatum, src => ProfileHelper.DateFromStringOptional(src.OntvangstDatum)) - .Map(dest => dest.VerzendDatum, src => ProfileHelper.DateFromStringOptional(src.VerzendDatum)) - // Ondertekening/Integriteit are optional on the wire; Mapster's .Map lambdas don't null-guard member - // paths the way AutoMapper's MapFrom did, and ?. can't be used because .Map compiles to an expression tree. - .Map( - dest => dest.Ondertekening_Datum, - src => ProfileHelper.DateFromStringOptional(src.Ondertekening == null ? null : src.Ondertekening.Datum) - ) - .Ignore(dest => dest.InformatieObject) - .Map(dest => dest.Ondertekening_Soort, src => src.Ondertekening == null ? null : src.Ondertekening.Soort) - .Map(dest => dest.Integriteit_Algoritme, src => src.Integriteit == null ? null : src.Integriteit.Algoritme) - .Map(dest => dest.Integriteit_Datum, src => ProfileHelper.DateFromStringOptional(src.Integriteit == null ? null : src.Integriteit.Datum)) - .Map(dest => dest.Integriteit_Waarde, src => src.Integriteit == null ? null : src.Integriteit.Waarde) - // Versie, EnkelvoudigInformatieObjectId, Verschijningsvorm, Trefwoorden and InhoudIsVervallen aren't on this DTO. - .Ignore(dest => dest.Versie) - .Map(dest => dest.Taal, src => ProfileHelper.Convert2letterTo3Letter(src.Taal, ProfileHelper.Taal2letterTo3LetterMap)) - // BeginRegistratie, CreationTime, CreatedBy, ModificationTime, ModifiedBy and Owner are set by entity hooks. - .Ignore(dest => dest.BeginRegistratie) - // Bestandsomvang, BestandsDelen and MultiPartDocumentId are upload state, not part of the request payload. - .Ignore(dest => dest.Bestandsomvang) - .Ignore(dest => dest.EnkelvoudigInformatieObjectId) - .Ignore(dest => dest.BestandsDelen) - .Ignore(dest => dest.MultiPartDocumentId) - .Ignore(dest => dest.CreationTime) - .Ignore(dest => dest.CreatedBy) - .Ignore(dest => dest.ModificationTime) - .Ignore(dest => dest.ModifiedBy) - .Ignore(dest => dest.Owner) - .Ignore(dest => dest.Verschijningsvorm) - .Ignore(dest => dest.Trefwoorden) - .Ignore(dest => dest.InhoudIsVervallen) - // v1.7-only fields; not present on this DTO. - .Ignore(dest => dest.IsGereedVoorPublicatie) - .Ignore(dest => dest.TonenAanInitiator) - // Cyclic navigation member (same risk as InformatieObject above) and the RowVersion concurrency token. - .Ignore(dest => dest.LatestInformatieObject) - .Ignore(dest => dest.RowVersion) - .AfterMapping( - (src, dest) => - dest.InformatieObject = new EnkelvoudigInformatieObject - { - InformatieObjectType = src.InformatieObjectType.TrimEnd('/'), - IndicatieGebruiksrecht = src.IndicatieGebruiksrecht, - } - ); - - // Create new version of EnkelvoudigInformatieObject: versie 2, versie 3, etc - config - .NewConfig() - .Ignore(dest => dest.Id) - .Ignore(dest => dest.CreatedBy) - .Ignore(dest => dest.ModifiedBy) - .Ignore(dest => dest.CreationTime) - .Ignore(dest => dest.ModificationTime) - .Ignore(dest => dest.Locked) - .Ignore(dest => dest.ObjectInformatieObjecten) - .Ignore(dest => dest.GebruiksRechten) - .Ignore(dest => dest.EnkelvoudigInformatieObjectVersies) - .Ignore(dest => dest.Owner) - .Ignore(dest => dest.Verzendingen) - .Ignore(dest => dest.LatestEnkelvoudigInformatieObjectVersieId) - .Ignore(dest => dest.LatestEnkelvoudigInformatieObjectVersie) - .Ignore(dest => dest.LatestVertrouwelijkheidAanduiding) - .Ignore(dest => dest.CatalogusId) - .Ignore(dest => dest.LegacyAuditTrail) - .Ignore(dest => dest.RowVersion); - - config - .NewConfig() - // Same cyclic-graph reasoning as the CreateRequestDto config above: InformatieObject is assigned - // in .AfterMapping, not .Map, to keep EnkelvoudigInformatieObject out of Mapster's compiled graph. - .Ignore(dest => dest.Id) - .Map(dest => dest.CreatieDatum, src => ProfileHelper.DateFromStringOptional(src.CreatieDatum)) - .Map(dest => dest.OntvangstDatum, src => ProfileHelper.DateFromStringOptional(src.OntvangstDatum)) - .Map(dest => dest.VerzendDatum, src => ProfileHelper.DateFromStringOptional(src.VerzendDatum)) - .Ignore(dest => dest.InformatieObject) - // Same null-guard reasoning as the CreateRequestDto config above: Ondertekening/Integriteit are optional. - .Map( - dest => dest.Ondertekening_Datum, - src => ProfileHelper.DateFromStringOptional(src.Ondertekening == null ? null : src.Ondertekening.Datum) - ) - .Map(dest => dest.Ondertekening_Soort, src => src.Ondertekening == null ? null : src.Ondertekening.Soort) - .Map(dest => dest.Integriteit_Algoritme, src => src.Integriteit == null ? null : src.Integriteit.Algoritme) - .Map(dest => dest.Integriteit_Datum, src => ProfileHelper.DateFromStringOptional(src.Integriteit == null ? null : src.Integriteit.Datum)) - .Map(dest => dest.Integriteit_Waarde, src => src.Integriteit == null ? null : src.Integriteit.Waarde) - // Same "not on this DTO" / entity-hook / upload-state grouping as the CreateRequestDto config above. - .Ignore(dest => dest.Versie) - .Map(dest => dest.Taal, src => ProfileHelper.Convert2letterTo3Letter(src.Taal, ProfileHelper.Taal2letterTo3LetterMap)) - .Ignore(dest => dest.BeginRegistratie) - .Ignore(dest => dest.Bestandsomvang) - .Ignore(dest => dest.EnkelvoudigInformatieObjectId) - .Ignore(dest => dest.BestandsDelen) - .Ignore(dest => dest.MultiPartDocumentId) - .Ignore(dest => dest.CreationTime) - .Ignore(dest => dest.CreatedBy) - .Ignore(dest => dest.ModificationTime) - .Ignore(dest => dest.ModifiedBy) - .Ignore(dest => dest.Owner) - .Ignore(dest => dest.Verschijningsvorm) - .Ignore(dest => dest.Trefwoorden) - .Ignore(dest => dest.InhoudIsVervallen) - // v1.7-only fields; not present on this DTO. - .Ignore(dest => dest.IsGereedVoorPublicatie) - .Ignore(dest => dest.TonenAanInitiator) - // Cyclic navigation member and the RowVersion concurrency token. - .Ignore(dest => dest.LatestInformatieObject) - .Ignore(dest => dest.RowVersion) - .AfterMapping( - (src, dest) => - dest.InformatieObject = new EnkelvoudigInformatieObject - { - InformatieObjectType = src.InformatieObjectType, - Lock = src.Lock, - IndicatieGebruiksrecht = src.IndicatieGebruiksrecht, - } - ); - config.NewConfig(); config diff --git a/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/RequestToPaginationRegister.cs b/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/RequestToPaginationRegister.cs index 5d488d71..899bacdc 100644 --- a/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/RequestToPaginationRegister.cs +++ b/src/OneGround.ZGW.Documenten.Web/MappingProfiles/v1/RequestToPaginationRegister.cs @@ -13,6 +13,7 @@ public void Register(TypeAdapterConfig config) { config.NewConfig(); + // Shared with v1.1, which reuses these base query parameters (it has its own request/response bodies). config .NewConfig() .Map(dest => dest.RegistratieOp, src => ProfileHelper.DateTimeFromString(src.RegistratieOp)); diff --git a/src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectCreateRequestValidator.cs b/src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectCreateRequestValidator.cs deleted file mode 100644 index b09ee613..00000000 --- a/src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectCreateRequestValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using OneGround.ZGW.Common.Web.Validations; -using OneGround.ZGW.Documenten.Contracts.v1.Requests; - -namespace OneGround.ZGW.Documenten.Web.Validators.v1; - -public class EnkelvoudigInformatieObjectCreateRequestValidator : ZGWValidator -{ - public EnkelvoudigInformatieObjectCreateRequestValidator() - { - Include(new EnkelvoudigInformatieObjectRequestValidator()); - } -} diff --git a/src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectRequestValidator.cs b/src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectRequestValidator.cs deleted file mode 100644 index ab5f6fc5..00000000 --- a/src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectRequestValidator.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.IO; -using FluentValidation; -using OneGround.ZGW.Common.Contracts.v1; -using OneGround.ZGW.Common.DataModel; -using OneGround.ZGW.Common.Helpers; -using OneGround.ZGW.Common.MimeTypes; -using OneGround.ZGW.Common.Web.Validations; -using OneGround.ZGW.Documenten.Contracts.v1.Requests; -using OneGround.ZGW.Documenten.DataModel; -using OneGround.ZGW.Documenten.Web.Extensions; - -namespace OneGround.ZGW.Documenten.Web.Validators.v1; - -public class EnkelvoudigInformatieObjectRequestValidator : ZGWValidator -{ - public EnkelvoudigInformatieObjectRequestValidator() - { - CascadeRuleFor(r => r.Identificatie).MaximumLength(40); - CascadeRuleFor(r => r.Bronorganisatie).IsRsin(required: true); - CascadeRuleFor(r => r.CreatieDatum).IsDate(true); - CascadeRuleFor(r => r.Titel).NotNull().NotEmpty().MaximumLength(200); - CascadeRuleFor(r => r.Vertrouwelijkheidaanduiding).IsEnumName(typeof(VertrouwelijkheidAanduiding)); - CascadeRuleFor(r => r.Auteur).NotNull().NotEmpty().MaximumLength(200); - CascadeRuleFor(r => r.Status).IsEnumName(typeof(Status)).When(r => !string.IsNullOrEmpty(r.Status)); - CascadeRuleFor(r => r.Formaat).IsValidMimeType(v => MimeTypeHelper.IsValidMimeType(v), maxLength: 255, allowEmpty: true); - CascadeRuleFor(r => r.Taal).IsIso639LanguageCode(required: true, ProfileHelper.Taal2letterTo3LetterMap); - CascadeRuleFor(r => r.Bestandsnaam).MaximumLength(255); - - CascadeRuleFor(r => r.Inhoud) - .NotNull() - .NotEmpty() - .Must(_ => false) // Fails always when there is an error file! - .WithMessage("Incorrect base64 data is specified.") - .WithErrorCode(ErrorCode.IncorrectBase64Padding) - .When(r => File.Exists(r.Inhoud + ".error")); // Note: this dummy error-file is created by Middleware when base64 decoding has failed! - - CascadeRuleFor(r => r.Inhoud) - .NotNull() - .NotEmpty() - .WithMessage("No base64 data is specified.") - .WithErrorCode(ErrorCode.Required) - .Must(inhoud => inhoud.IsAnyDocumentUrn() || File.Exists(inhoud)); // Note: inhoud is intercepted by Middleware which writes a file with base64 encoded data - - CascadeRuleFor(r => r.Link).MaximumLength(200); - CascadeRuleFor(r => r.Beschrijving).MaximumLength(1000); - CascadeRuleFor(r => r.OntvangstDatum).IsDate(false); - CascadeRuleFor(r => r.VerzendDatum).IsDate(false); - CascadeRuleFor(r => r.Ondertekening) - .ChildRules(v => - { - v.CascadeRuleFor(r => r.Soort).NotNull().NotEmpty().IsEnumName(typeof(Soort)); - v.CascadeRuleFor(r => r.Datum).IsDate(true); - }); - CascadeRuleFor(r => r.Integriteit) - .ChildRules(v => - { - v.CascadeRuleFor(r => r.Algoritme).NotNull().NotEmpty().IsEnumName(typeof(Algoritme)); - v.CascadeRuleFor(r => r.Waarde).NotNull().NotEmpty().MaximumLength(128); - v.CascadeRuleFor(r => r.Datum).IsDate(true); - }); - CascadeRuleFor(r => r.InformatieObjectType).NotNull().NotEmpty().IsUri().MaximumLength(200); - } -} diff --git a/src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectUpdateRequestValidator.cs b/src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectUpdateRequestValidator.cs deleted file mode 100644 index b458743b..00000000 --- a/src/OneGround.ZGW.Documenten.Web/Validators/v1/EnkelvoudigInformatieObjectUpdateRequestValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using OneGround.ZGW.Common.Web.Validations; -using OneGround.ZGW.Documenten.Contracts.v1.Requests; - -namespace OneGround.ZGW.Documenten.Web.Validators.v1; - -public class EnkelvoudigInformatieObjectUpdateRequestValidator : ZGWValidator -{ - public EnkelvoudigInformatieObjectUpdateRequestValidator() - { - Include(new EnkelvoudigInformatieObjectRequestValidator()); - } -} diff --git a/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/EnkelvoudigeInformatieHandlerTests/CreateEnkelvoudigInformatieObjectVersionsTests.cs b/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/EnkelvoudigeInformatieHandlerTests/CreateEnkelvoudigInformatieObjectVersionsTests.cs index 8358ebd2..3cf73dce 100644 --- a/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/EnkelvoudigeInformatieHandlerTests/CreateEnkelvoudigInformatieObjectVersionsTests.cs +++ b/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/EnkelvoudigeInformatieHandlerTests/CreateEnkelvoudigInformatieObjectVersionsTests.cs @@ -3,23 +3,19 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Http.Resilience; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Moq; using OneGround.ZGW.Common.DataModel; using OneGround.ZGW.Common.Handlers; using OneGround.ZGW.Common.Web.Services; using OneGround.ZGW.Documenten.DataModel; using OneGround.ZGW.Documenten.Services; -using OneGround.ZGW.Documenten.Web.Concurrency; using OneGround.ZGW.Documenten.Web.Handlers.v1._1; -using Polly; using Xunit; using CreateV1_5Command = OneGround.ZGW.Documenten.Web.Handlers.v1._5.CreateEnkelvoudigInformatieObjectCommand; using CreateV1_5Handler = OneGround.ZGW.Documenten.Web.Handlers.v1._5.CreateEnkelvoudigInformatieObjectCommandHandler; -using CreateV1Command = OneGround.ZGW.Documenten.Web.Handlers.v1.CreateEnkelvoudigInformatieObjectCommand; -using CreateV1Handler = OneGround.ZGW.Documenten.Web.Handlers.v1.CreateEnkelvoudigInformatieObjectCommandHandler; +using CreateV1_7Command = OneGround.ZGW.Documenten.Web.Handlers.v1._7.CreateEnkelvoudigInformatieObjectCommand; +using CreateV1_7Handler = OneGround.ZGW.Documenten.Web.Handlers.v1._7.CreateEnkelvoudigInformatieObjectCommandHandler; namespace OneGround.ZGW.Documenten.WebApi.UnitTests.EnkelvoudigeInformatieHandlerTests; @@ -452,32 +448,19 @@ public async Task CreateV1_1_SetsLatestVertrouwelijkheidAanduiding_ToVersieValue } [Fact] - public async Task CreateV1_SetsLatestVertrouwelijkheidAanduiding_ToVersieValue() + public async Task CreateV1_7_SetsLatestVertrouwelijkheidAanduiding_ToVersieValue() { await SetupMocksAsync(); - _mockDocumentService - .Setup(m => - m.AddDocumentAsync( - It.IsAny(), - "vha_test_v1.txt", - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny() - ) - ) - .ReturnsAsync(new Document(new DocumentUrn("urn:dms:unittest:11111111-1111-1111-1111-111111111111"), 0)); + var handler = BuildV1_7CreateHandler(); - var handler = BuildV1CreateHandler(); - - var command = new CreateV1Command + var command = new CreateV1_7Command { EnkelvoudigInformatieObjectVersie = new EnkelvoudigInformatieObjectVersie { Inhoud = null, Bestandsomvang = 0, - Bestandsnaam = "vha_test_v1.txt", + Bestandsnaam = "vha_test_v1_7.txt", Bronorganisatie = "999990482", Formaat = "raw", Taal = "eng", @@ -496,6 +479,51 @@ public async Task CreateV1_SetsLatestVertrouwelijkheidAanduiding_ToVersieValue() Assert.Equal(VertrouwelijkheidAanduiding.intern, savedEio.LatestVertrouwelijkheidAanduiding); } + [Fact] + public async Task CreateV1_7_Persists_New_V1_7_Fields() + { + // Confirms the v1.7 create handler persists (does not strip) the members v1.7 added on top of the + // shared base. The DTO -> domain mapping of these fields is covered in v1_7/RequestToDomainProfileTests; + // this handler test receives an already-mapped domain Versie, matching the sibling CreateV1_x tests. + await SetupMocksAsync(); + + var handler = BuildV1_7CreateHandler(); + + var command = new CreateV1_7Command + { + EnkelvoudigInformatieObjectVersie = new EnkelvoudigInformatieObjectVersie + { + Inhoud = null, + Bestandsomvang = 0, + Bestandsnaam = "new_fields_v1_7.txt", + Bronorganisatie = "999990482", + Formaat = "raw", + Taal = "eng", + Vertrouwelijkheidaanduiding = VertrouwelijkheidAanduiding.openbaar, + Verschijningsvorm = "digitaal", + Trefwoorden = ["vergunning", "aanvraag"], + InhoudIsVervallen = true, + IsGereedVoorPublicatie = true, + TonenAanInitiator = true, + InformatieObject = new EnkelvoudigInformatieObject + { + InformatieObjectType = "http://catalogi.user.local:5011/api/v1/informatieobjecttypen/7ce6dd03-a386-4771-834c-1f4c4deb0f8f", + }, + }, + }; + + var result = await handler.Handle(command, CancellationToken.None); + + Assert.Equal(CommandStatus.OK, result.Status); + + var saved = _mockDbContext.EnkelvoudigInformatieObjectVersies.Single(d => d.Bestandsnaam == "new_fields_v1_7.txt"); + Assert.Equal("digitaal", saved.Verschijningsvorm); + Assert.Equal(["vergunning", "aanvraag"], saved.Trefwoorden); + Assert.True(saved.InhoudIsVervallen); + Assert.True(saved.IsGereedVoorPublicatie); + Assert.True(saved.TonenAanInitiator); + } + [Fact] public async Task CreateV1_5_SetsLatestVertrouwelijkheidAanduiding_ToVersieValue() { @@ -548,39 +576,25 @@ private CreateEnkelvoudigInformatieObjectCommandHandler CreateHandler() ); } - private CreateV1Handler BuildV1CreateHandler() + private CreateV1_7Handler BuildV1_7CreateHandler() { - var mockOptionsMonitor = new Mock>(); - mockOptionsMonitor - .Setup(m => m.CurrentValue) - .Returns( - new HttpRetryStrategyOptions - { - MaxRetryAttempts = 3, - BackoffType = DelayBackoffType.Exponential, - Delay = TimeSpan.FromSeconds(1), - } - ); - - // v1 handler is internal — use NullLogger to avoid DynamicProxyGenAssembly2 visibility issue - return new CreateV1Handler( - logger: Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance, + var mockV1_7Logger = new Mock>(); + + return new CreateV1_7Handler( + logger: mockV1_7Logger.Object, configuration: _configuration, context: _mockDbContext, uriService: _mockUriService.Object, nummerGenerator: _mockNummerGenerator.Object, documentServicesResolver: _mockDocumentServicesResolver.Object, enkelvoudigInformatieObjectBusinessRuleService: _mockEnkvoudigInfObjBusinessRuleService.Object, - notificatieService: _mockNotificatieService.Object, catalogiServiceAgent: _mockCatalogiServiceAgent.Object, auditTrailFactory: _mockAuditTrailFactory.Object, authorizationContextAccessor: _mockAuthorizationContextAccessor.Object, - documentKenmerkenResolver: _mockDocumentKenmerkenResolver.Object, - entityMergerFactory: _mockEntityMergerFactory.Object, - concurrencyRetryPipeline: new ResilienceConcurrencyRetryPipeline( - Microsoft.Extensions.Logging.Abstractions.NullLogger>.Instance, - mockOptionsMonitor.Object - ) + lockGenerator: _mockLockGenerator.Object, + formOptions: _mockFormOptions.Object, + notificatieService: _mockNotificatieService.Object, + documentKenmerkenResolver: _mockDocumentKenmerkenResolver.Object ); } diff --git a/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/DomainToResponseProfileTests.cs b/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/DomainToResponseProfileTests.cs index 4e0a7d82..3cadaea9 100644 --- a/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/DomainToResponseProfileTests.cs +++ b/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/DomainToResponseProfileTests.cs @@ -16,10 +16,6 @@ namespace OneGround.ZGW.Documenten.WebApi.UnitTests.MappingTests; public class DomainToResponseProfileTests : IDisposable { - // Official RvIG test BSN, reused here purely as a safe, non-real 9-digit placeholder for - // Bronorganisatie -- never assigned to a real person or organisation. - private const string TestBronorganisatie = "999993653"; - private readonly OmitOnRecursionFixture _fixture = new OmitOnRecursionFixture(); private readonly DrcMapperTestHost _host = new DrcMapperTestHost(); private readonly IMapper _mapper; @@ -34,244 +30,6 @@ public DomainToResponseProfileTests() public void Dispose() => _host.Dispose(); - [Fact] - public void EnkelvoudigInformatieObject_Maps_To_GetResponseDto_via_AfterMapping_with_DI_resolved_Inhoud() - { - // Covers the MapLatestEnkelvoudigInformatieObjectVersieResponse port: EnkelvoudigInformatieObjectGetResponseDto - // is populated entirely from src.LatestEnkelvoudigInformatieObjectVersie (and its own LatestInformatieObject) - // inside .AfterMapping, since every one of these members is .Ignore()-d in the main config. - var latestVersion = new EnkelvoudigInformatieObjectVersie - { - Id = Guid.NewGuid(), - Versie = 3, - Bronorganisatie = TestBronorganisatie, - Identificatie = "DOC-0001", - Bestandsomvang = 4096, - BeginRegistratie = new DateTime(2024, 1, 2, 3, 4, 5, DateTimeKind.Utc), - CreatieDatum = new DateOnly(2024, 1, 1), - Titel = "Titel-1", - Vertrouwelijkheidaanduiding = VertrouwelijkheidAanduiding.openbaar, - Auteur = "Auteur-1", - Status = Status.definitief, - Formaat = "application/pdf", - Taal = "dut", - Bestandsnaam = "bestand.pdf", - Link = "https://example.test/link", - Beschrijving = "Beschrijving-1", - OntvangstDatum = new DateOnly(2024, 1, 3), - VerzendDatum = new DateOnly(2024, 1, 4), - Ondertekening_Datum = new DateOnly(2024, 1, 5), - Ondertekening_Soort = Soort.digitaal, - Integriteit_Algoritme = Algoritme.sha_256, - Integriteit_Datum = new DateOnly(2024, 1, 6), - Integriteit_Waarde = "abc123", - }; - - var value = new EnkelvoudigInformatieObject - { - Id = Guid.NewGuid(), - InformatieObjectType = "https://example.test/informatieobjecttypen/1", - IndicatieGebruiksrecht = true, - Locked = true, - EnkelvoudigInformatieObjectVersies = [latestVersion], - LatestEnkelvoudigInformatieObjectVersie = latestVersion, - }; - - // Establish bidirectional relationships (as the repository would when loading the aggregate) - latestVersion.InformatieObject = value; - latestVersion.LatestInformatieObject = value; - - // Pin a mock return value for THIS specific version instance, distinguishable from the host's - // default prefixing stub: this proves the register's .AfterMapping actually calls - // uriService.GetUri(latestVersion), not that Inhoud coincidentally matches a resolved Url. - _host.UriService.Setup(s => s.GetUri(latestVersion)).Returns("MOCKED-INHOUD-URL"); - - var result = _mapper.Map(value); - - Assert.Equal(DrcMapperTestHost.Resolved(value), result.Url); - Assert.Equal(latestVersion.Versie, result.Versie); - Assert.Equal(latestVersion.Bronorganisatie, result.Bronorganisatie); - Assert.Equal(latestVersion.Identificatie, result.Identificatie); - Assert.Equal(latestVersion.Bestandsomvang, result.Bestandsomvang); - Assert.Equal( - latestVersion.BeginRegistratie.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", CultureInfo.InvariantCulture), - result.BeginRegistratie - ); - Assert.Equal(latestVersion.CreatieDatum.Value.ToString("yyyy-MM-dd"), result.CreatieDatum); - Assert.Equal(latestVersion.Titel, result.Titel); - Assert.Equal(latestVersion.Vertrouwelijkheidaanduiding.Value.ToString(), result.Vertrouwelijkheidaanduiding); - Assert.Equal(latestVersion.Auteur, result.Auteur); - Assert.Equal(latestVersion.Status.Value.ToString(), result.Status); - Assert.Equal(latestVersion.Formaat, result.Formaat); - Assert.Equal(latestVersion.Taal, result.Taal); - Assert.Equal(latestVersion.Bestandsnaam, result.Bestandsnaam); - Assert.Equal(latestVersion.Link, result.Link); - Assert.Equal(latestVersion.Beschrijving, result.Beschrijving); - Assert.Equal(latestVersion.OntvangstDatum.Value.ToString("yyyy-MM-dd"), result.OntvangstDatum); - Assert.Equal(latestVersion.VerzendDatum.Value.ToString("yyyy-MM-dd"), result.VerzendDatum); - Assert.Equal(latestVersion.Ondertekening_Datum.Value.ToString("yyyy-MM-dd"), result.Ondertekening.Datum); - Assert.Equal(latestVersion.Ondertekening_Soort.Value.ToString(), result.Ondertekening.Soort); - Assert.Equal(latestVersion.Integriteit_Algoritme.ToString(), result.Integriteit.Algoritme); - Assert.Equal(latestVersion.Integriteit_Datum.Value.ToString("yyyy-MM-dd"), result.Integriteit.Datum); - Assert.Equal(latestVersion.Integriteit_Waarde, result.Integriteit.Waarde); - Assert.Equal(value.InformatieObjectType, result.InformatieObjectType); - Assert.Equal(value.IndicatieGebruiksrecht, result.IndicatieGebruiksrecht); - Assert.Equal(value.Locked, result.Locked); - - // The DI-resolved value -- proves the ported IMappingAction's uriService.GetUri(latestVersion) - // call actually ran through MapContext.Current.GetService(). - Assert.Equal("MOCKED-INHOUD-URL", result.Inhoud); - } - - [Fact] - public void EnkelvoudigInformatieObject_Maps_To_UpdateRequestDto_via_AfterMapping_without_DI() - { - // Covers the MapLatestEnkelvoudigInformatieObjectVersieRequest port: this AfterMapping has no DI - // dependency at all, and (unlike the GetResponseDto port above) copies Inhoud verbatim from the - // domain field rather than resolving it through IEntityUriService. - var latestVersion = new EnkelvoudigInformatieObjectVersie - { - Id = Guid.NewGuid(), - Versie = 5, - Bronorganisatie = TestBronorganisatie, - Identificatie = "DOC-0002", - CreatieDatum = new DateOnly(2024, 2, 1), - Titel = "Titel-2", - Vertrouwelijkheidaanduiding = VertrouwelijkheidAanduiding.intern, - Auteur = "Auteur-2", - Status = Status.in_bewerking, - Formaat = "application/xml", - Taal = "dut", - Bestandsnaam = "bestand2.xml", - Inhoud = @"202401\11111111111111111111111111111111.xml", - Link = "https://example.test/link2", - Beschrijving = "Beschrijving-2", - OntvangstDatum = new DateOnly(2024, 2, 2), - VerzendDatum = new DateOnly(2024, 2, 3), - }; - - var value = new EnkelvoudigInformatieObject - { - Id = Guid.NewGuid(), - InformatieObjectType = "https://example.test/informatieobjecttypen/2", - IndicatieGebruiksrecht = false, - Lock = "existing-lock-token", - EnkelvoudigInformatieObjectVersies = [latestVersion], - LatestEnkelvoudigInformatieObjectVersie = latestVersion, - }; - - latestVersion.InformatieObject = value; - latestVersion.LatestInformatieObject = value; - - var result = _mapper.Map(value); - - Assert.Equal(latestVersion.Bronorganisatie, result.Bronorganisatie); - Assert.Equal(latestVersion.Identificatie, result.Identificatie); - Assert.Equal(latestVersion.CreatieDatum.Value.ToString("yyyy-MM-dd"), result.CreatieDatum); - Assert.Equal(latestVersion.Titel, result.Titel); - Assert.Equal(latestVersion.Vertrouwelijkheidaanduiding.Value.ToString(), result.Vertrouwelijkheidaanduiding); - Assert.Equal(latestVersion.Auteur, result.Auteur); - Assert.Equal(latestVersion.Status.Value.ToString(), result.Status); - Assert.Equal(latestVersion.Formaat, result.Formaat); - Assert.Equal(latestVersion.Taal, result.Taal); - Assert.Equal(latestVersion.Bestandsnaam, result.Bestandsnaam); - // Copied straight from the domain field -- NOT resolved through IEntityUriService (contrast with - // the GetResponseDto port above). - Assert.Equal(latestVersion.Inhoud, result.Inhoud); - Assert.Equal(latestVersion.Link, result.Link); - Assert.Equal(latestVersion.Beschrijving, result.Beschrijving); - Assert.Equal(latestVersion.OntvangstDatum.Value.ToString("yyyy-MM-dd"), result.OntvangstDatum); - Assert.Equal(latestVersion.VerzendDatum.Value.ToString("yyyy-MM-dd"), result.VerzendDatum); - Assert.Equal(value.InformatieObjectType, result.InformatieObjectType); - Assert.Equal(value.IndicatieGebruiksrecht, result.IndicatieGebruiksrecht); - - // Deliberately NOT merged: the caller must validate the Lock value from the incoming request, - // not the value currently stored on the entity. - Assert.Null(result.Lock); - } - - [Fact] - public void EnkelvoudigInformatieObjectVersie_Maps_To_CreateResponseDto_with_dates_url_and_optional_dtos() - { - var informatieObject = new EnkelvoudigInformatieObject - { - Id = Guid.NewGuid(), - InformatieObjectType = "https://example.test/informatieobjecttypen/3", - IndicatieGebruiksrecht = true, - Locked = false, - }; - - var value = new EnkelvoudigInformatieObjectVersie - { - Id = Guid.NewGuid(), - Versie = 1, - CreatieDatum = new DateOnly(2024, 3, 1), - OntvangstDatum = new DateOnly(2024, 3, 2), - BeginRegistratie = new DateTime(2024, 3, 3, 4, 5, 6, DateTimeKind.Utc), - VerzendDatum = new DateOnly(2024, 3, 4), - Ondertekening_Datum = new DateOnly(2024, 3, 5), - Ondertekening_Soort = Soort.analoog, - Integriteit_Algoritme = Algoritme.md5, - Integriteit_Datum = new DateOnly(2024, 3, 6), - Integriteit_Waarde = "integriteit-waarde", - InformatieObject = informatieObject, - }; - - var result = _mapper.Map(value); - - Assert.Equal(DrcMapperTestHost.Resolved(informatieObject), result.Url); - Assert.Equal(value.CreatieDatum.Value.ToString("yyyy-MM-dd"), result.CreatieDatum); - Assert.Equal(value.OntvangstDatum.Value.ToString("yyyy-MM-dd"), result.OntvangstDatum); - Assert.Equal( - value.BeginRegistratie.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", CultureInfo.InvariantCulture), - result.BeginRegistratie - ); - Assert.Equal(value.VerzendDatum.Value.ToString("yyyy-MM-dd"), result.VerzendDatum); - // EnkelvoudigInformatieObjectVersieMapperHelper.CreateOptionalOndertekeningDto/CreateOptionalIntegriteitDto - // (an already Mapster-agnostic, untouched helper) populated these from the version's fields. - Assert.Equal(value.Ondertekening_Datum.Value.ToString("yyyy-MM-dd"), result.Ondertekening.Datum); - Assert.Equal(value.Ondertekening_Soort.Value.ToString(), result.Ondertekening.Soort); - Assert.Equal(value.Integriteit_Algoritme.ToString(), result.Integriteit.Algoritme); - Assert.Equal(value.Integriteit_Datum.Value.ToString("yyyy-MM-dd"), result.Integriteit.Datum); - Assert.Equal(value.Integriteit_Waarde, result.Integriteit.Waarde); - Assert.Equal(informatieObject.IndicatieGebruiksrecht, result.IndicatieGebruiksrecht); - Assert.Equal(informatieObject.Locked, result.Locked); - Assert.Equal(DrcMapperTestHost.Resolved(value), result.Inhoud); - Assert.Equal(informatieObject.InformatieObjectType, result.InformatieObjectType); - } - - [Fact] - public void EnkelvoudigInformatieObjectVersie_Maps_To_UpdateResponseDto_includes_Lock_and_default_optional_dtos_when_empty() - { - var informatieObject = new EnkelvoudigInformatieObject - { - Id = Guid.NewGuid(), - InformatieObjectType = "https://example.test/informatieobjecttypen/4", - IndicatieGebruiksrecht = false, - Locked = true, - Lock = "lock-token-4", - }; - - // Ondertekening/Integriteit fields left at their defaults -- CreateOptionalOndertekeningDto/ - // CreateOptionalIntegriteitDto(..., createDefaultWhenEmpty: true) must still return a non-null, - // empty DTO (not null) for the Create/Update response maps. - var value = new EnkelvoudigInformatieObjectVersie - { - Id = Guid.NewGuid(), - Versie = 2, - InformatieObject = informatieObject, - }; - - var result = _mapper.Map(value); - - Assert.Equal(DrcMapperTestHost.Resolved(informatieObject), result.Url); - Assert.Equal(informatieObject.Lock, result.Lock); - Assert.Equal(informatieObject.Locked, result.Locked); - Assert.Equal(informatieObject.InformatieObjectType, result.InformatieObjectType); - Assert.NotNull(result.Ondertekening); - Assert.NotNull(result.Integriteit); - } - [Fact] public void ObjectInformatieObject_Maps_To_ObjectInformatieObjectResponseDto() { diff --git a/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/DrcMapsterWiringTests.cs b/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/DrcMapsterWiringTests.cs index 4ac3449a..fb5ec992 100644 --- a/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/DrcMapsterWiringTests.cs +++ b/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/DrcMapsterWiringTests.cs @@ -8,7 +8,7 @@ using OneGround.ZGW.Common.Web.Extensions.ServiceCollection.ZGWApiExtensions; using OneGround.ZGW.Common.Web.Services.UriServices; using OneGround.ZGW.DataAccess; -using OneGround.ZGW.Documenten.Contracts.v1.Responses; +using OneGround.ZGW.Documenten.Contracts.v1._7.Responses; using OneGround.ZGW.Documenten.DataModel; using OneGround.ZGW.Documenten.Web; using OneGround.ZGW.Documenten.Web.MappingProfiles.v1; @@ -33,7 +33,12 @@ public void AddZgwMapster_discovers_DRC_registers_and_runs_the_url_resolvers_thr var mapper = scope.ServiceProvider.GetRequiredService(); var latestInformatieObject = new EnkelvoudigInformatieObject { Id = Guid.NewGuid(), InformatieObjectType = "https://example.test/iot" }; - var latestVersion = new EnkelvoudigInformatieObjectVersie { Id = Guid.NewGuid(), LatestInformatieObject = latestInformatieObject }; + var latestVersion = new EnkelvoudigInformatieObjectVersie + { + Id = Guid.NewGuid(), + LatestInformatieObject = latestInformatieObject, + BestandsDelen = [], + }; var source = new EnkelvoudigInformatieObject { Id = Guid.NewGuid(), diff --git a/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/RequestToDomainProfileTests.cs b/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/RequestToDomainProfileTests.cs index 40fc12a6..7d2c5970 100644 --- a/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/RequestToDomainProfileTests.cs +++ b/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/RequestToDomainProfileTests.cs @@ -41,124 +41,6 @@ public void GetAllEnkelvoudigInformatieObjectenQueryParameters_Maps_To_GetAllEnk Assert.Equal(value.Bronorganisatie, result.Bronorganisatie); } - [Fact] - public void EnkelvoudigInformatieObjectCreateRequestDto_Maps_To_EnkelvoudigInformatieObjectVersie() - { - // Setup - _fixture.Customize(c => - c.With(p => p.Algoritme, Algoritme.crc_32.ToString()).With(p => p.Waarde, "123").With(p => p.Datum, "2020-11-17") - ); - - _fixture.Customize(c => c.With(p => p.Datum, "2020-11-18").With(p => p.Soort, Soort.digitaal.ToString())); - - _fixture.Customize(c => - c.With(p => p.Identificatie, "DOC-2020-0000001") - .With(p => p.Bronorganisatie, "999990561") - .With(p => p.CreatieDatum, "2020-11-12") - .With(p => p.Titel, "My document") - .With(p => p.Vertrouwelijkheidaanduiding, _fixture.Create().ToString()) - .With(p => p.Auteur, "somebody") - .With(p => p.Status, _fixture.Create().ToString()) - .With(p => p.Formaat, "") - .With(p => p.Taal, "eng") - .With(p => p.Bestandsnaam, "document.pdf") - .With(p => p.Inhoud, "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCAuLi4=") - .With(p => p.Link, "(no link)") - .With(p => p.Beschrijving, "My description of the document") - .With(p => p.OntvangstDatum, "2020-11-13") - .With(p => p.VerzendDatum, "2020-11-14") - .With(p => p.IndicatieGebruiksrecht, true) - .With(p => p.Ondertekening, _fixture.Create) - .With(p => p.Integriteit, _fixture.Create) - .With(p => p.InformatieObjectType, "https://some-informatieobjecttype") - ); - - var value = _fixture.Create(); - - // Act - var result = _mapper.Map(value); - - // Assert - Assert.Equal(value.Identificatie, result.Identificatie); - Assert.Equal(value.Bronorganisatie, result.Bronorganisatie); - Assert.Equal(value.CreatieDatum, result.CreatieDatum.Value.ToString("yyyy-MM-dd")); - Assert.Equal(value.Titel, result.Titel); - Assert.Equal(value.Vertrouwelijkheidaanduiding, result.Vertrouwelijkheidaanduiding.ToString()); - Assert.Equal(value.Auteur, result.Auteur); - Assert.Equal(value.Status, result.Status.ToString()); - Assert.Equal(value.Formaat, result.Formaat); - Assert.Equal(value.Taal, result.Taal); - Assert.Equal(value.Bestandsnaam, result.Bestandsnaam); - Assert.Equal(value.Inhoud, result.Inhoud); - Assert.Equal(value.Link, result.Link); - Assert.Equal(value.Beschrijving, result.Beschrijving); - Assert.Equal(value.IndicatieGebruiksrecht, result.InformatieObject.IndicatieGebruiksrecht); - Assert.Equal(value.OntvangstDatum, result.OntvangstDatum.Value.ToString("yyyy-MM-dd")); - Assert.Equal(value.VerzendDatum, result.VerzendDatum.Value.ToString("yyyy-MM-dd")); - Assert.Equal(value.Ondertekening.Datum, result.Ondertekening_Datum.Value.ToString("yyyy-MM-dd")); - Assert.Equal(value.Ondertekening.Soort, result.Ondertekening_Soort.ToString()); - Assert.Equal(value.Ondertekening.Datum, result.Ondertekening_Datum.Value.ToString("yyyy-MM-dd")); - Assert.Equal(value.Integriteit.Algoritme, result.Integriteit_Algoritme.ToString()); - Assert.Equal(value.Integriteit.Waarde, result.Integriteit_Waarde); - Assert.Equal(value.Integriteit.Datum, result.Integriteit_Datum.Value.ToString("yyyy-MM-dd")); - Assert.Equal(value.InformatieObjectType, result.InformatieObject.InformatieObjectType); - } - - [Fact] - public void EnkelvoudigInformatieObjectCreateRequestDto_With_Null_Ondertekening_And_Integriteit_Maps_Without_Throwing() - { - // Ondertekening/Integriteit are optional on the wire; the register's Mapster .Map lambdas don't - // null-guard member-path access the way AutoMapper's MapFrom did, so a request omitting them must - // not throw a NullReferenceException here. - _fixture.Customize(c => - c.With(p => p.Identificatie, "DOC-2020-0000002") - .With(p => p.Bronorganisatie, "999990561") - .With(p => p.CreatieDatum, "2020-11-12") - .With(p => p.OntvangstDatum, "2020-11-13") - .With(p => p.VerzendDatum, "2020-11-14") - .With(p => p.Taal, "eng") - .With(p => p.InformatieObjectType, "https://some-informatieobjecttype") - .Without(p => p.Ondertekening) - .Without(p => p.Integriteit) - ); - - var value = _fixture.Create(); - - var result = _mapper.Map(value); - - Assert.Null(result.Ondertekening_Datum); - Assert.Null(result.Ondertekening_Soort); - Assert.Equal(default, result.Integriteit_Algoritme); - Assert.Null(result.Integriteit_Datum); - Assert.Null(result.Integriteit_Waarde); - } - - [Fact] - public void EnkelvoudigInformatieObjectUpdateRequestDto_With_Lock_Maps_To_EnkelvoudigInformatieObjectVersie() - { - // Setup - _fixture.Customize(c => c.With(p => p.Algoritme, Algoritme.crc_32.ToString()).With(p => p.Datum, "2020-11-17")); - - _fixture.Customize(c => c.With(p => p.Datum, "2020-11-18").With(p => p.Soort, Soort.digitaal.ToString())); - - _fixture.Customize(c => - c.With(p => p.Lock, "8494eecb2495447a8b29a8e31d10c4b4") - .With(p => p.CreatieDatum, "2020-11-12") - .With(p => p.Vertrouwelijkheidaanduiding, _fixture.Create().ToString()) - .With(p => p.Status, _fixture.Create().ToString()) - .With(p => p.OntvangstDatum, "2020-11-13") - .With(p => p.VerzendDatum, "2020-11-14") - ); - - var value = _fixture.Create(); - - // Act - var result = _mapper.Map(value); - - // Assert - Assert.Equal(value.Lock, result.InformatieObject.Lock); - } - [Fact] public void GetGetAllObjectInformatieObjectenQueryParameters_Maps_To_GetAllObjectInformatieObjectenFilter() { From 5d48ad810350d78a5c33392dbeec7f9893b78f07 Mon Sep 17 00:00:00 2001 From: Aat van den Heuvel Date: Tue, 22 Sep 2026 09:53:28 +0200 Subject: [PATCH 2/4] use the latest DRC service agents --- ...luitInformatieObjectBusinessRuleService.cs | 2 +- src/OneGround.ZGW.Besluiten.Web/Startup.cs | 5 +- .../AddObjectInformatieObjectJob.cs | 2 +- .../DeleteObjectInformatieObjectJob.cs | 2 +- .../InformatieObjectHandlerBase.cs | 2 +- .../ServiceConfiguration.cs | 4 +- .../v1/1/DocumentenServiceAgent.cs | 63 ---------- .../DocumentenServiceAgentExtensions.cs | 14 --- .../v1/1/IDocumentenServiceAgent.cs | 21 ---- .../v1/5/CachedDocumentServiceAgent.cs | 49 ++++++++ .../v1/5/DocumentenServiceAgent.cs | 12 ++ .../DocumentenServiceAgentExtensions.cs | 4 +- .../{ => 5}/ICachedDocumentenServiceAgent.cs | 2 +- .../v1/5/IDocumentenServiceAgent.cs | 9 +- .../v1/7/CachedDocumentServiceAgent.cs | 113 ++++++++++++++++++ .../v1/{ => 7}/DocumentenServiceAgent.cs | 93 +++++++++++++- .../DocumentenServiceAgentExtensions.cs | 4 +- .../v1/7/ICachedDocumentenServiceAgent.cs | 5 + .../v1/{ => 7}/IDocumentenServiceAgent.cs | 23 +++- .../v1/CachedDocumentServiceAgent.cs | 88 -------------- .../BusinessRules/ZaakBusinessRuleService.cs | 2 +- ...ZaakInformatieObjectBusinessRuleService.cs | 2 +- ...reateZaakInformatieObjectCommandHandler.cs | 4 +- .../v1/5/CreateZaakStatusCommandHandler.cs | 2 +- ...reateZaakInformatieObjectCommandHandler.cs | 4 +- .../v1/CreateZaakStatusCommandHandler.cs | 2 +- src/OneGround.ZGW.Zaken.Web/Startup.cs | 5 +- .../ZaakBussinesRuleTest.cs | 2 +- 28 files changed, 312 insertions(+), 228 deletions(-) delete mode 100644 src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/DocumentenServiceAgent.cs delete mode 100644 src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/Extensions/DocumentenServiceAgentExtensions.cs delete mode 100644 src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/IDocumentenServiceAgent.cs create mode 100644 src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/CachedDocumentServiceAgent.cs rename src/OneGround.ZGW.Documenten.ServiceAgent/v1/{ => 5}/ICachedDocumentenServiceAgent.cs (57%) create mode 100644 src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/CachedDocumentServiceAgent.cs rename src/OneGround.ZGW.Documenten.ServiceAgent/v1/{ => 7}/DocumentenServiceAgent.cs (56%) rename src/OneGround.ZGW.Documenten.ServiceAgent/v1/{ => 7}/Extensions/DocumentenServiceAgentExtensions.cs (71%) create mode 100644 src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/ICachedDocumentenServiceAgent.cs rename src/OneGround.ZGW.Documenten.ServiceAgent/v1/{ => 7}/IDocumentenServiceAgent.cs (53%) delete mode 100644 src/OneGround.ZGW.Documenten.ServiceAgent/v1/CachedDocumentServiceAgent.cs diff --git a/src/OneGround.ZGW.Besluiten.Web/BusinessRules/BesluitInformatieObjectBusinessRuleService.cs b/src/OneGround.ZGW.Besluiten.Web/BusinessRules/BesluitInformatieObjectBusinessRuleService.cs index 5a8a2e47..c64e4b66 100644 --- a/src/OneGround.ZGW.Besluiten.Web/BusinessRules/BesluitInformatieObjectBusinessRuleService.cs +++ b/src/OneGround.ZGW.Besluiten.Web/BusinessRules/BesluitInformatieObjectBusinessRuleService.cs @@ -5,7 +5,7 @@ using OneGround.ZGW.Besluiten.DataModel; using OneGround.ZGW.Catalogi.ServiceAgent.v1; using OneGround.ZGW.Common.Contracts.v1; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; namespace OneGround.ZGW.Besluiten.Web.BusinessRules; diff --git a/src/OneGround.ZGW.Besluiten.Web/Startup.cs b/src/OneGround.ZGW.Besluiten.Web/Startup.cs index 7708861d..d4b266d3 100644 --- a/src/OneGround.ZGW.Besluiten.Web/Startup.cs +++ b/src/OneGround.ZGW.Besluiten.Web/Startup.cs @@ -32,7 +32,7 @@ using OneGround.ZGW.Common.Web.Swagger; using OneGround.ZGW.DataAccess; using OneGround.ZGW.Documenten.ServiceAgent.v1._5.Extensions; -using OneGround.ZGW.Documenten.ServiceAgent.v1.Extensions; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7.Extensions; using OneGround.ZGW.Notificaties.ServiceAgent.Extensions; using OneGround.ZGW.Zaken.ServiceAgent.v1.Extensions; @@ -88,8 +88,7 @@ public void ConfigureServices(IServiceCollection services) services.AddBesluitenServiceAgent(Configuration); services.AddCatalogiServiceAgent(Configuration); services.AddCatalogiServiceAgent_v1_3(Configuration); - services.AddDocumentenServiceAgent(Configuration); - services.AddServiceAuthDocumentenServiceAgent_v1_5(Configuration); + services.AddDocumentenServiceAgent_v1_7(Configuration); services.AddUserAuthDocumentenServiceAgent_v1_5(Configuration); // Expanders support _expand in responses diff --git a/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/AddObjectInformatieObjectJob.cs b/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/AddObjectInformatieObjectJob.cs index 83b2191d..2a2a465a 100644 --- a/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/AddObjectInformatieObjectJob.cs +++ b/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/AddObjectInformatieObjectJob.cs @@ -6,7 +6,7 @@ using OneGround.ZGW.Common.ServiceAgent.Extensions; using OneGround.ZGW.Documenten.Contracts.v1.Requests; using OneGround.ZGW.Documenten.Jobs.Extensions; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; namespace OneGround.ZGW.Documenten.Jobs.InformatieObjecten; diff --git a/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/DeleteObjectInformatieObjectJob.cs b/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/DeleteObjectInformatieObjectJob.cs index eff62200..ff10ade5 100644 --- a/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/DeleteObjectInformatieObjectJob.cs +++ b/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/DeleteObjectInformatieObjectJob.cs @@ -4,7 +4,7 @@ using OneGround.ZGW.Common.Authentication; using OneGround.ZGW.Common.ServiceAgent.Extensions; using OneGround.ZGW.Documenten.Jobs.Extensions; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; namespace OneGround.ZGW.Documenten.Jobs.InformatieObjecten; diff --git a/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/InformatieObjectHandlerBase.cs b/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/InformatieObjectHandlerBase.cs index 9e238c85..dd5e6f77 100644 --- a/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/InformatieObjectHandlerBase.cs +++ b/src/OneGround.ZGW.Documenten.Jobs/InformatieObjecten/InformatieObjectHandlerBase.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging; using OneGround.ZGW.Common.Authentication; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; namespace OneGround.ZGW.Documenten.Jobs.InformatieObjecten; diff --git a/src/OneGround.ZGW.Documenten.Messaging/ServiceConfiguration.cs b/src/OneGround.ZGW.Documenten.Messaging/ServiceConfiguration.cs index 79f120c4..2ce97b0f 100644 --- a/src/OneGround.ZGW.Documenten.Messaging/ServiceConfiguration.cs +++ b/src/OneGround.ZGW.Documenten.Messaging/ServiceConfiguration.cs @@ -18,7 +18,7 @@ using OneGround.ZGW.Documenten.DataModel; using OneGround.ZGW.Documenten.Jobs; using OneGround.ZGW.Documenten.Messaging.Configuration; -using OneGround.ZGW.Documenten.ServiceAgent.v1.Extensions; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7.Extensions; using OneGround.ZGW.Notificaties.ServiceAgent.Extensions; namespace OneGround.ZGW.Documenten.Messaging; @@ -56,7 +56,7 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); - services.AddDocumentenServiceAgent(_configuration); + services.AddDocumentenServiceAgent_v1_7(_configuration); services.AddNotificatiesServiceAgent(_configuration); services.AddScoped(); diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/DocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/DocumentenServiceAgent.cs deleted file mode 100644 index ce15abf4..00000000 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/DocumentenServiceAgent.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using OneGround.ZGW.Common.Constants; -using OneGround.ZGW.Common.ServiceAgent; -using OneGround.ZGW.Common.Services; -using OneGround.ZGW.Documenten.Contracts.v1._1.Requests; -using OneGround.ZGW.Documenten.Contracts.v1._1.Responses; - -namespace OneGround.ZGW.Documenten.ServiceAgent.v1._1; - -public class DocumentenServiceAgent : ZGWServiceAgent, IDocumentenServiceAgent -{ - public DocumentenServiceAgent( - ILogger logger, - HttpClient client, - IServiceDiscovery serviceDiscovery, - IServiceAgentResponseBuilder responseBuilder, - IConfiguration configuration - ) - : base(client, logger, serviceDiscovery, configuration, responseBuilder, ServiceRoleName.DRC, "v1") - { - Client.DefaultRequestHeaders.Add("Api-Version", "1.1"); - } - - public Task> AddEnkelvoudigInformatieObjectAsync( - EnkelvoudigInformatieObjectCreateRequestDto enkelvoudigInformatieObject - ) - { - var url = new Uri("/enkelvoudiginformatieobjecten", UriKind.Relative); - - return PostAsync(url, enkelvoudigInformatieObject); - } - - public Task> GetEnkelvoudigInformatieObjectAsync(Guid enkelvoudigInformatieObjectId) - { - if (enkelvoudigInformatieObjectId == Guid.Empty) - throw new ArgumentNullException(nameof(enkelvoudigInformatieObjectId)); - - Logger.LogDebug("Getting document by id: {enkelvoudigInformatieObjectId}", enkelvoudigInformatieObjectId); - - var url = new Uri($"/enkelvoudiginformatieobjecten/{enkelvoudigInformatieObjectId}", UriKind.Relative); - - return GetAsync(url); - } - - public async Task> AddBestandsdeelAsync( - string bestandsdeelUrl, - MultipartFormDataContent multipartFormDataContent - ) - { - return await PutAsync(new Uri(bestandsdeelUrl), multipartFormDataContent); - } - - public async Task UnlockAsync(string enkelvoudigInformatieObjectUrl) - { - var unlockEnkelvoudigInformatieObjectUrl = enkelvoudigInformatieObjectUrl + "/unlock"; - - return await PostAsync(new Uri(unlockEnkelvoudigInformatieObjectUrl)); - } -} diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/Extensions/DocumentenServiceAgentExtensions.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/Extensions/DocumentenServiceAgentExtensions.cs deleted file mode 100644 index 716a1df0..00000000 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/Extensions/DocumentenServiceAgentExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using OneGround.ZGW.Common.Constants; -using OneGround.ZGW.Common.ServiceAgent.Extensions; - -namespace OneGround.ZGW.Documenten.ServiceAgent.v1._1.Extensions; - -public static class DocumentenServiceAgentExtensions -{ - public static void AddDocumentenServiceAgent_v1_1(this IServiceCollection services, IConfiguration configuration) - { - services.AddServiceAgent(ServiceRoleName.DRC, configuration); - } -} diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/IDocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/IDocumentenServiceAgent.cs deleted file mode 100644 index 50dd3cfd..00000000 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/1/IDocumentenServiceAgent.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using OneGround.ZGW.Common.ServiceAgent; -using OneGround.ZGW.Documenten.Contracts.v1._1.Requests; -using OneGround.ZGW.Documenten.Contracts.v1._1.Responses; - -namespace OneGround.ZGW.Documenten.ServiceAgent.v1._1; - -public interface IDocumentenServiceAgent -{ - Task> AddEnkelvoudigInformatieObjectAsync( - EnkelvoudigInformatieObjectCreateRequestDto enkelvoudigInformatieObject - ); - Task> GetEnkelvoudigInformatieObjectAsync(Guid enkelvoudigInformatieObjectId); - Task> AddBestandsdeelAsync( - string bestandsdeelUrl, - MultipartFormDataContent multipartFormDataContent - ); - Task UnlockAsync(string enkelvoudigInformatieObjectUrl); -} diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/CachedDocumentServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/CachedDocumentServiceAgent.cs new file mode 100644 index 00000000..140ee9fc --- /dev/null +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/CachedDocumentServiceAgent.cs @@ -0,0 +1,49 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; +using OneGround.ZGW.Common.ServiceAgent; +using OneGround.ZGW.Documenten.Contracts.v1._5.Responses; +using OneGround.ZGW.Documenten.Contracts.v1.Queries; + +namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5; + +class CachedDocumentServiceAgent : ICachedDocumentenServiceAgent +{ + private readonly IDocumentenServiceAgent _agent; + + private readonly ConcurrentDictionary> _cachedEnkelvoudigInformatieObjectUrl = new(); + + public CachedDocumentServiceAgent(IDocumentenServiceAgent agent) + { + _agent = agent; + } + + public async Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl) + { + if (_cachedEnkelvoudigInformatieObjectUrl.TryGetValue(enkelvoudigInformatieObjectUrl, out var cachedEnkelvoudigInformatieObject)) + { + return cachedEnkelvoudigInformatieObject; + } + + var result = await _agent.GetEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl); + if (result.Success) + { + // Note: Only cache successful responses, so a transient failure isn't replayed for the rest of the scope + _cachedEnkelvoudigInformatieObjectUrl[enkelvoudigInformatieObjectUrl] = result; + } + + return result; + } + + public Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand) + { + // Note: Pass through agent (so no cache) + return _agent.GetEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl, expand); + } + + public Task>> GetObjectInformatieObjectenAsync(GetAllObjectInformatieObjectenQueryParameters parameters) + { + // Note: Pass through agent (so no cache) + return _agent.GetObjectInformatieObjectenAsync(parameters); + } +} diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/DocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/DocumentenServiceAgent.cs index 345ee677..90271710 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/DocumentenServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/DocumentenServiceAgent.cs @@ -28,6 +28,18 @@ IConfiguration configuration Client.DefaultRequestHeaders.Add("Api-Version", "1.5"); } + public async Task> GetEnkelvoudigInformatieObjectByUrlAsync( + string enkelvoudigInformatieObjectUrl + ) + { + if (!EnsureValidResource(ServiceRoleName.DRC, enkelvoudigInformatieObjectUrl, "enkelvoudiginformatieobjecten", out var errorResponse)) + return new ServiceAgentResponse(errorResponse); + + Logger.LogDebug("Query EnkelvoudigInformatieObject {enkelvoudigInformatieObjectUrl}....", enkelvoudigInformatieObjectUrl); + + return await GetAsync(new Uri(enkelvoudigInformatieObjectUrl)); + } + public async Task< ServiceAgentResponse<(EnkelvoudigInformatieObjectResponseDto enkelvoudigInformatieObject, object expandedEnkelvoudigInformatieObject)> > GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand) diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/Extensions/DocumentenServiceAgentExtensions.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/Extensions/DocumentenServiceAgentExtensions.cs index a7a814fa..3f5ba6be 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/Extensions/DocumentenServiceAgentExtensions.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/Extensions/DocumentenServiceAgentExtensions.cs @@ -7,10 +7,10 @@ namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5.Extensions; public static class DocumentenServiceAgentExtensions { - public static void AddServiceAuthDocumentenServiceAgent_v1_5(this IServiceCollection services, IConfiguration configuration) + public static void AddDocumentenServiceAgent_v1_5(this IServiceCollection services, IConfiguration configuration) { - // Service-account authorized ServiceAgent like "oneground-09435039" (for system cross-API like add/deleting mirrored zrc/brc relations) services.AddServiceAgent(ServiceRoleName.DRC, configuration); + services.AddScoped(); } public static void AddUserAuthDocumentenServiceAgent_v1_5(this IServiceCollection services, IConfiguration configuration) diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/ICachedDocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/ICachedDocumentenServiceAgent.cs similarity index 57% rename from src/OneGround.ZGW.Documenten.ServiceAgent/v1/ICachedDocumentenServiceAgent.cs rename to src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/ICachedDocumentenServiceAgent.cs index c208a342..0ddf6b04 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/ICachedDocumentenServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/ICachedDocumentenServiceAgent.cs @@ -1,3 +1,3 @@ -namespace OneGround.ZGW.Documenten.ServiceAgent.v1; +namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5; public interface ICachedDocumentenServiceAgent : IDocumentenServiceAgent; diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IDocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IDocumentenServiceAgent.cs index 89cfc9c8..ca9769c2 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IDocumentenServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IDocumentenServiceAgent.cs @@ -8,10 +8,7 @@ namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5; public interface IDocumentenServiceAgent { - Task< - ServiceAgentResponse<(EnkelvoudigInformatieObjectResponseDto enkelvoudigInformatieObject, object expandedEnkelvoudigInformatieObject)> - > GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand); - Task>> GetObjectInformatieObjectenAsync( - GetAllObjectInformatieObjectenQueryParameters parameters - ); + Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl ); + Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand); + Task>> GetObjectInformatieObjectenAsync(GetAllObjectInformatieObjectenQueryParameters parameters); } diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/CachedDocumentServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/CachedDocumentServiceAgent.cs new file mode 100644 index 00000000..a0129bed --- /dev/null +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/CachedDocumentServiceAgent.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using OneGround.ZGW.Common.Contracts.v1.AuditTrail; +using OneGround.ZGW.Common.ServiceAgent; +using OneGround.ZGW.Documenten.Contracts.v1._7.Queries; +using OneGround.ZGW.Documenten.Contracts.v1._7.Requests; +using OneGround.ZGW.Documenten.Contracts.v1._7.Responses; + +namespace OneGround.ZGW.Documenten.ServiceAgent.v1._7; + +class CachedDocumentServiceAgent : ICachedDocumentenServiceAgent +{ + private readonly IDocumentenServiceAgent _agent; + private readonly ConcurrentDictionary> _cachedEnkelvoudigInformatieObjectUrl = new(); + + public CachedDocumentServiceAgent(IDocumentenServiceAgent agent) + { + _agent = agent; + } + + public Task> AddEnkelvoudigInformatieObjectAsync(EnkelvoudigInformatieObjectCreateRequestDto enkelvoudigInformatieObject) + { + // Note: Pass through agent (so no cache) + return _agent.AddEnkelvoudigInformatieObjectAsync(enkelvoudigInformatieObject); + } + + public Task> AddObjectInformatieObjectAsync(Contracts.v1.Requests.ObjectInformatieObjectRequestDto objectInformatieObject) + { + // Note: Pass through agent (so no cache) + return _agent.AddObjectInformatieObjectAsync(objectInformatieObject); + } + + public Task> GetEnkelvoudigInformatieObjectAsync(Guid enkelvoudigInformatieObjectId) + { + // Note: Pass through agent (so no cache) + return _agent.GetEnkelvoudigInformatieObjectAsync(enkelvoudigInformatieObjectId); + } + + public Task> AddBestandsdeelAsync(string bestandsdeelUrl, MultipartFormDataContent multipartFormDataContent) + { + // Note: Pass through agent (so no cache) + return _agent.AddBestandsdeelAsync(bestandsdeelUrl, multipartFormDataContent); + } + + public Task UnlockAsync(string enkelvoudigInformatieObjectUrl) + { + // Note: Pass through agent (so no cache) + return _agent.UnlockAsync(enkelvoudigInformatieObjectUrl); + } + + public Task DeleteEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl) + { + // Note: Pass through agent (so no cache) + return _agent.DeleteEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl); + } + + public Task DeleteObjectInformatieObjectByUrlAsync(string objectInformatieObjectUrl) + { + // Note: Pass through agent (so no cache) + return _agent.DeleteObjectInformatieObjectByUrlAsync(objectInformatieObjectUrl); + } + + public Task> DownloadEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, int? version = null) + { + // Note: Pass through agent (so no cache) + return _agent.DownloadEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl, version); + } + + public Task>> GetAuditTrailRegelsAsync(string enkelvoudigInformatieObjectUrl) + { + // Note: Pass through agent for now (so no cache yet due to too many memory resources kept in cache; or limit amount of cache-entries) + return _agent.GetAuditTrailRegelsAsync(enkelvoudigInformatieObjectUrl); + } + + public async Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl) + { + if (_cachedEnkelvoudigInformatieObjectUrl.TryGetValue(enkelvoudigInformatieObjectUrl, out var cachedEnkelvoudigInformatieObject)) + { + return cachedEnkelvoudigInformatieObject; + } + + var result = await _agent.GetEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl); + if (result.Success) + { + // Note: Only cache successful responses, so a transient failure isn't replayed for the rest of the scope + _cachedEnkelvoudigInformatieObjectUrl[enkelvoudigInformatieObjectUrl] = result; + } + + return result; + } + + public Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand) + { + // Note: Pass through agent (so no cache) + return _agent.GetEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl, expand); + } + + public Task>> GetObjectInformatieObjectsByInformatieObjectAndObjectAsync(string informatieObject, string @object) + { + // Note: Pass through agent for now (so no cache yet due to too many memory resources kept in cache; or limit amount of cache-entries) + return _agent.GetObjectInformatieObjectsByInformatieObjectAndObjectAsync(informatieObject, @object); + } + + public Task>> GetObjectInformatieObjectenAsync(GetAllObjectInformatieObjectenQueryParameters parameters) + { + // Note: Pass through agent (so no cache) + return _agent.GetObjectInformatieObjectenAsync(parameters); + } +} diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/DocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/DocumentenServiceAgent.cs similarity index 56% rename from src/OneGround.ZGW.Documenten.ServiceAgent/v1/DocumentenServiceAgent.cs rename to src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/DocumentenServiceAgent.cs index 6daf334e..0be532a3 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/DocumentenServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/DocumentenServiceAgent.cs @@ -5,15 +5,17 @@ using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using OneGround.ZGW.Common.Constants; using OneGround.ZGW.Common.Contracts.v1.AuditTrail; using OneGround.ZGW.Common.Extensions; using OneGround.ZGW.Common.ServiceAgent; using OneGround.ZGW.Common.Services; -using OneGround.ZGW.Documenten.Contracts.v1.Requests; -using OneGround.ZGW.Documenten.Contracts.v1.Responses; +using OneGround.ZGW.Documenten.Contracts.v1._7.Queries; +using OneGround.ZGW.Documenten.Contracts.v1._7.Requests; +using OneGround.ZGW.Documenten.Contracts.v1._7.Responses; -namespace OneGround.ZGW.Documenten.ServiceAgent.v1; +namespace OneGround.ZGW.Documenten.ServiceAgent.v1._7; public class DocumentenServiceAgent : ZGWServiceAgent, IDocumentenServiceAgent { @@ -24,7 +26,10 @@ public DocumentenServiceAgent( IServiceAgentResponseBuilder responseBuilder, IConfiguration configuration ) - : base(client, logger, serviceDiscovery, configuration, responseBuilder, ServiceRoleName.DRC) { } + : base(client, logger, serviceDiscovery, configuration, responseBuilder, ServiceRoleName.DRC, "v1") + { + Client.DefaultRequestHeaders.Add("Api-Version", "1.7"); + } public Task> AddEnkelvoudigInformatieObjectAsync( EnkelvoudigInformatieObjectCreateRequestDto enkelvoudigInformatieObject @@ -51,8 +56,15 @@ public async Task< return await GetAsync>(url); } + public async Task>> GetObjectInformatieObjectenAsync( + GetAllObjectInformatieObjectenQueryParameters parameters + ) + { + return await GetAsync("/objectinformatieobjecten", parameters); + } + public Task> AddObjectInformatieObjectAsync( - ObjectInformatieObjectRequestDto objectInformatieObject + Contracts.v1.Requests.ObjectInformatieObjectRequestDto objectInformatieObject ) { ArgumentNullException.ThrowIfNull(objectInformatieObject); @@ -61,7 +73,7 @@ ObjectInformatieObjectRequestDto objectInformatieObject var url = new Uri("/objectinformatieobjecten", UriKind.Relative); - return PostAsync(url, objectInformatieObject); + return PostAsync(url, objectInformatieObject); } public async Task> GetEnkelvoudigInformatieObjectByUrlAsync( @@ -76,6 +88,75 @@ string enkelvoudigInformatieObjectUrl return await GetAsync(new Uri(enkelvoudigInformatieObjectUrl)); } + public async Task< + ServiceAgentResponse<(EnkelvoudigInformatieObjectResponseDto enkelvoudigInformatieObject, object expandedEnkelvoudigInformatieObject)> + > GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand) + { + if (!EnsureValidResource(ServiceRoleName.DRC, enkelvoudigInformatieObjectUrl, "enkelvoudiginformatieobjecten", out var errorResponse)) + return new ServiceAgentResponse<( + EnkelvoudigInformatieObjectResponseDto enkelvoudigInformatieObject, + object expandedEnkelvoudigInformatieObject + )>(errorResponse); + + Logger.LogDebug("EnkelvoudigInformatieObject bevragen op '{enkelvoudigInformatieObjectUrl}'....", enkelvoudigInformatieObjectUrl); + + var url = new Uri(enkelvoudigInformatieObjectUrl); + + if (!string.IsNullOrEmpty(expand)) + { + var result = await GetAsync(url.AddQueryParameter("expand", expand)); + if (!result.Success) + { + return new ServiceAgentResponse<(EnkelvoudigInformatieObjectResponseDto, object)>(result.Error, null); + } + var enkelvoudiginformatieobjectBase = JsonConvert.DeserializeObject(result.Response.ToString()); + + return new ServiceAgentResponse<( + EnkelvoudigInformatieObjectResponseDto enkelvoudigInformatieObject, + object expandedEnkelvoudigInformatieObject + )>((enkelvoudiginformatieobjectBase, result.Response)); + } + else + { + var result = await GetAsync(url); + if (!result.Success) + { + return new ServiceAgentResponse<(EnkelvoudigInformatieObjectResponseDto, object)>(result.Error, null); + } + return new ServiceAgentResponse<( + EnkelvoudigInformatieObjectResponseDto enkelvoudigInformatieObject, + object expandedEnkelvoudigInformatieObject + )>((result.Response, result.Response)); + } + } + + public Task> GetEnkelvoudigInformatieObjectAsync(Guid enkelvoudigInformatieObjectId) + { + if (enkelvoudigInformatieObjectId == Guid.Empty) + throw new ArgumentNullException(nameof(enkelvoudigInformatieObjectId)); + + Logger.LogDebug("Getting document by id: {enkelvoudigInformatieObjectId}", enkelvoudigInformatieObjectId); + + var url = new Uri($"/enkelvoudiginformatieobjecten/{enkelvoudigInformatieObjectId}", UriKind.Relative); + + return GetAsync(url); + } + + public async Task> AddBestandsdeelAsync( + string bestandsdeelUrl, + MultipartFormDataContent multipartFormDataContent + ) + { + return await PutAsync(new Uri(bestandsdeelUrl), multipartFormDataContent); + } + + public async Task UnlockAsync(string enkelvoudigInformatieObjectUrl) + { + var unlockEnkelvoudigInformatieObjectUrl = enkelvoudigInformatieObjectUrl + "/unlock"; + + return await PostAsync(new Uri(unlockEnkelvoudigInformatieObjectUrl)); + } + public async Task DeleteEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl) { if (!EnsureValidResource(ServiceRoleName.DRC, enkelvoudigInformatieObjectUrl, "enkelvoudiginformatieobjecten", out var errorResponse)) diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/Extensions/DocumentenServiceAgentExtensions.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/Extensions/DocumentenServiceAgentExtensions.cs similarity index 71% rename from src/OneGround.ZGW.Documenten.ServiceAgent/v1/Extensions/DocumentenServiceAgentExtensions.cs rename to src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/Extensions/DocumentenServiceAgentExtensions.cs index 47a870fe..946c7e4c 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/Extensions/DocumentenServiceAgentExtensions.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/Extensions/DocumentenServiceAgentExtensions.cs @@ -3,11 +3,11 @@ using OneGround.ZGW.Common.Constants; using OneGround.ZGW.Common.ServiceAgent.Extensions; -namespace OneGround.ZGW.Documenten.ServiceAgent.v1.Extensions; +namespace OneGround.ZGW.Documenten.ServiceAgent.v1._7.Extensions; public static class DocumentenServiceAgentExtensions { - public static void AddDocumentenServiceAgent(this IServiceCollection services, IConfiguration configuration) + public static void AddDocumentenServiceAgent_v1_7(this IServiceCollection services, IConfiguration configuration) { services.AddServiceAgent(ServiceRoleName.DRC, configuration); services.AddScoped(); diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/ICachedDocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/ICachedDocumentenServiceAgent.cs new file mode 100644 index 00000000..a91e1b11 --- /dev/null +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/ICachedDocumentenServiceAgent.cs @@ -0,0 +1,5 @@ +namespace OneGround.ZGW.Documenten.ServiceAgent.v1._7; + +// TODO: Uitzoeken of deze juist is en ook het gebruik ervan. +// Waarom en waar wordt de cached versie gebruikt in v1.5 en moeten we dit ook in 1.7 doen? +public interface ICachedDocumentenServiceAgent : IDocumentenServiceAgent; diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/IDocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/IDocumentenServiceAgent.cs similarity index 53% rename from src/OneGround.ZGW.Documenten.ServiceAgent/v1/IDocumentenServiceAgent.cs rename to src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/IDocumentenServiceAgent.cs index bd01f5e5..111bfed0 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/IDocumentenServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/IDocumentenServiceAgent.cs @@ -1,12 +1,15 @@ +using System; using System.Collections.Generic; using System.IO; +using System.Net.Http; using System.Threading.Tasks; using OneGround.ZGW.Common.Contracts.v1.AuditTrail; using OneGround.ZGW.Common.ServiceAgent; -using OneGround.ZGW.Documenten.Contracts.v1.Requests; -using OneGround.ZGW.Documenten.Contracts.v1.Responses; +using OneGround.ZGW.Documenten.Contracts.v1._7.Queries; +using OneGround.ZGW.Documenten.Contracts.v1._7.Requests; +using OneGround.ZGW.Documenten.Contracts.v1._7.Responses; -namespace OneGround.ZGW.Documenten.ServiceAgent.v1; +namespace OneGround.ZGW.Documenten.ServiceAgent.v1._7; public interface IDocumentenServiceAgent { @@ -16,14 +19,26 @@ EnkelvoudigInformatieObjectCreateRequestDto enkelvoudigInformatieObject Task> GetEnkelvoudigInformatieObjectByUrlAsync( string enkelvoudigInformatieObjectUrl ); + Task< + ServiceAgentResponse<(EnkelvoudigInformatieObjectResponseDto enkelvoudigInformatieObject, object expandedEnkelvoudigInformatieObject)> + > GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand); + Task> GetEnkelvoudigInformatieObjectAsync(Guid enkelvoudigInformatieObjectId); + Task> AddBestandsdeelAsync( + string bestandsdeelUrl, + MultipartFormDataContent multipartFormDataContent + ); + Task UnlockAsync(string enkelvoudigInformatieObjectUrl); Task DeleteEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl); Task> DownloadEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, int? version = null); Task>> GetObjectInformatieObjectsByInformatieObjectAndObjectAsync( string informatieObject, string @object ); + Task>> GetObjectInformatieObjectenAsync( + GetAllObjectInformatieObjectenQueryParameters parameters + ); Task> AddObjectInformatieObjectAsync( - ObjectInformatieObjectRequestDto objectInformatieObject + Contracts.v1.Requests.ObjectInformatieObjectRequestDto objectInformatieObject ); Task DeleteObjectInformatieObjectByUrlAsync(string objectInformatieObjectUrl); Task>> GetAuditTrailRegelsAsync(string enkelvoudigInformatieObjectUrl); diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/CachedDocumentServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/CachedDocumentServiceAgent.cs deleted file mode 100644 index 9d3f7b18..00000000 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/CachedDocumentServiceAgent.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using OneGround.ZGW.Common.Contracts.v1.AuditTrail; -using OneGround.ZGW.Common.ServiceAgent; -using OneGround.ZGW.Documenten.Contracts.v1.Requests; -using OneGround.ZGW.Documenten.Contracts.v1.Responses; - -namespace OneGround.ZGW.Documenten.ServiceAgent.v1; - -class CachedDocumentServiceAgent : ICachedDocumentenServiceAgent -{ - private readonly IDocumentenServiceAgent _agent; - - public CachedDocumentServiceAgent(IDocumentenServiceAgent agent) - { - _agent = agent; - } - - public Task> AddEnkelvoudigInformatieObjectAsync( - EnkelvoudigInformatieObjectCreateRequestDto enkelvoudigInformatieObject - ) - { - // Note: Pass through agent (so no cache) - return _agent.AddEnkelvoudigInformatieObjectAsync(enkelvoudigInformatieObject); - } - - public Task> AddObjectInformatieObjectAsync( - ObjectInformatieObjectRequestDto objectInformatieObject - ) - { - // Note: Pass through agent (so no cache) - return _agent.AddObjectInformatieObjectAsync(objectInformatieObject); - } - - public Task DeleteEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl) - { - // Note: Pass through agent (so no cache) - return _agent.DeleteEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl); - } - - public Task DeleteObjectInformatieObjectByUrlAsync(string objectInformatieObjectUrl) - { - // Note: Pass through agent (so no cache) - return _agent.DeleteObjectInformatieObjectByUrlAsync(objectInformatieObjectUrl); - } - - public Task> DownloadEnkelvoudigInformatieObjectByUrlAsync( - string enkelvoudigInformatieObjectUrl, - int? version = null - ) - { - // Note: Pass through agent (so no cache) - return _agent.DownloadEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl, version); - } - - public Task>> GetAuditTrailRegelsAsync(string enkelvoudigInformatieObjectUrl) - { - // Note: Pass through agent for now (so no cache yet due to too many memory resources kept in cache; or limit amount of cache-entries) - return _agent.GetAuditTrailRegelsAsync(enkelvoudigInformatieObjectUrl); - } - - public async Task> GetEnkelvoudigInformatieObjectByUrlAsync( - string enkelvoudigInformatieObjectUrl - ) - { - if (_cachedEnkelvoudigInformatieObjectUrl.TryGetValue(enkelvoudigInformatieObjectUrl, out var cachedEnkelvoudigInformatieObject)) - { - return cachedEnkelvoudigInformatieObject; - } - _cachedEnkelvoudigInformatieObjectUrl[enkelvoudigInformatieObjectUrl] = await _agent.GetEnkelvoudigInformatieObjectByUrlAsync( - enkelvoudigInformatieObjectUrl - ); - - return _cachedEnkelvoudigInformatieObjectUrl[enkelvoudigInformatieObjectUrl]; - } - - private readonly Dictionary> _cachedEnkelvoudigInformatieObjectUrl = []; - - public Task>> GetObjectInformatieObjectsByInformatieObjectAndObjectAsync( - string informatieObject, - string @object - ) - { - // Note: Pass through agent for now (so no cache yet due to too many memory resources kept in cache; or limit amount of cache-entries) - return _agent.GetObjectInformatieObjectsByInformatieObjectAndObjectAsync(informatieObject, @object); - } -} diff --git a/src/OneGround.ZGW.Zaken.Web/BusinessRules/ZaakBusinessRuleService.cs b/src/OneGround.ZGW.Zaken.Web/BusinessRules/ZaakBusinessRuleService.cs index 1559942b..a293b787 100644 --- a/src/OneGround.ZGW.Zaken.Web/BusinessRules/ZaakBusinessRuleService.cs +++ b/src/OneGround.ZGW.Zaken.Web/BusinessRules/ZaakBusinessRuleService.cs @@ -10,7 +10,7 @@ using OneGround.ZGW.Common.ServiceAgent; using OneGround.ZGW.Common.Services; using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; using OneGround.ZGW.Notificaties.ServiceAgent; using OneGround.ZGW.Zaken.DataModel; using OneGround.ZGW.Zaken.ServiceAgent.v1; diff --git a/src/OneGround.ZGW.Zaken.Web/BusinessRules/ZaakInformatieObjectBusinessRuleService.cs b/src/OneGround.ZGW.Zaken.Web/BusinessRules/ZaakInformatieObjectBusinessRuleService.cs index 215bfc45..da086ba3 100644 --- a/src/OneGround.ZGW.Zaken.Web/BusinessRules/ZaakInformatieObjectBusinessRuleService.cs +++ b/src/OneGround.ZGW.Zaken.Web/BusinessRules/ZaakInformatieObjectBusinessRuleService.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore; using OneGround.ZGW.Common.Contracts.v1; using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; using OneGround.ZGW.Zaken.DataModel; namespace OneGround.ZGW.Zaken.Web.BusinessRules; diff --git a/src/OneGround.ZGW.Zaken.Web/Handlers/v1/5/CreateZaakInformatieObjectCommandHandler.cs b/src/OneGround.ZGW.Zaken.Web/Handlers/v1/5/CreateZaakInformatieObjectCommandHandler.cs index a25740d3..340da83b 100644 --- a/src/OneGround.ZGW.Zaken.Web/Handlers/v1/5/CreateZaakInformatieObjectCommandHandler.cs +++ b/src/OneGround.ZGW.Zaken.Web/Handlers/v1/5/CreateZaakInformatieObjectCommandHandler.cs @@ -16,8 +16,8 @@ using OneGround.ZGW.Common.Web.Services; using OneGround.ZGW.Common.Web.Services.AuditTrail; using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.Documenten.Contracts.v1.Responses; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.Contracts.v1._7.Responses; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; using OneGround.ZGW.Zaken.Contracts.v1._5.Responses; using OneGround.ZGW.Zaken.DataModel; using OneGround.ZGW.Zaken.Web.Authorization; diff --git a/src/OneGround.ZGW.Zaken.Web/Handlers/v1/5/CreateZaakStatusCommandHandler.cs b/src/OneGround.ZGW.Zaken.Web/Handlers/v1/5/CreateZaakStatusCommandHandler.cs index c9c1ea34..a096806a 100644 --- a/src/OneGround.ZGW.Zaken.Web/Handlers/v1/5/CreateZaakStatusCommandHandler.cs +++ b/src/OneGround.ZGW.Zaken.Web/Handlers/v1/5/CreateZaakStatusCommandHandler.cs @@ -18,7 +18,7 @@ using OneGround.ZGW.Common.Web.Services; using OneGround.ZGW.Common.Web.Services.AuditTrail; using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; using OneGround.ZGW.Zaken.Contracts.v1._5.Responses; using OneGround.ZGW.Zaken.DataModel; using OneGround.ZGW.Zaken.Web.Authorization; diff --git a/src/OneGround.ZGW.Zaken.Web/Handlers/v1/CreateZaakInformatieObjectCommandHandler.cs b/src/OneGround.ZGW.Zaken.Web/Handlers/v1/CreateZaakInformatieObjectCommandHandler.cs index 8104b76d..d1473104 100644 --- a/src/OneGround.ZGW.Zaken.Web/Handlers/v1/CreateZaakInformatieObjectCommandHandler.cs +++ b/src/OneGround.ZGW.Zaken.Web/Handlers/v1/CreateZaakInformatieObjectCommandHandler.cs @@ -16,8 +16,8 @@ using OneGround.ZGW.Common.Web.Services; using OneGround.ZGW.Common.Web.Services.AuditTrail; using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.Documenten.Contracts.v1.Responses; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.Contracts.v1._7.Responses; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; using OneGround.ZGW.Zaken.Contracts.v1.Responses; using OneGround.ZGW.Zaken.DataModel; using OneGround.ZGW.Zaken.Web.Authorization; diff --git a/src/OneGround.ZGW.Zaken.Web/Handlers/v1/CreateZaakStatusCommandHandler.cs b/src/OneGround.ZGW.Zaken.Web/Handlers/v1/CreateZaakStatusCommandHandler.cs index b214c6be..d9ce56c0 100644 --- a/src/OneGround.ZGW.Zaken.Web/Handlers/v1/CreateZaakStatusCommandHandler.cs +++ b/src/OneGround.ZGW.Zaken.Web/Handlers/v1/CreateZaakStatusCommandHandler.cs @@ -18,7 +18,7 @@ using OneGround.ZGW.Common.Web.Services; using OneGround.ZGW.Common.Web.Services.AuditTrail; using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; using OneGround.ZGW.Zaken.Contracts.v1.Responses; using OneGround.ZGW.Zaken.DataModel; using OneGround.ZGW.Zaken.Web.Authorization; diff --git a/src/OneGround.ZGW.Zaken.Web/Startup.cs b/src/OneGround.ZGW.Zaken.Web/Startup.cs index b4408b11..626ecfc1 100644 --- a/src/OneGround.ZGW.Zaken.Web/Startup.cs +++ b/src/OneGround.ZGW.Zaken.Web/Startup.cs @@ -27,7 +27,7 @@ using OneGround.ZGW.Common.Web.Swagger; using OneGround.ZGW.DataAccess; using OneGround.ZGW.Documenten.ServiceAgent.v1._5.Extensions; -using OneGround.ZGW.Documenten.ServiceAgent.v1.Extensions; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7.Extensions; using OneGround.ZGW.Notificaties.ServiceAgent.Extensions; using OneGround.ZGW.Zaken.DataModel; using OneGround.ZGW.Zaken.DataModel.ZaakObject; @@ -99,8 +99,7 @@ public void ConfigureServices(IServiceCollection services) services.AddCatalogiServiceAgent(Configuration); services.AddCatalogiServiceAgent_v1_3(Configuration); - services.AddDocumentenServiceAgent(Configuration); - services.AddServiceAuthDocumentenServiceAgent_v1_5(Configuration); + services.AddDocumentenServiceAgent_v1_7(Configuration); services.AddUserAuthDocumentenServiceAgent_v1_5(Configuration); services.AddAutorisatiesServiceAgent(Configuration); services.AddZakenServiceAgent(Configuration); diff --git a/src/Tests/OneGround.ZGW.Zaken.WebApi.UnitTests/BusinessRulesTests/ZaakBussinesRuleTest.cs b/src/Tests/OneGround.ZGW.Zaken.WebApi.UnitTests/BusinessRulesTests/ZaakBussinesRuleTest.cs index 304a2c0c..df3ea400 100644 --- a/src/Tests/OneGround.ZGW.Zaken.WebApi.UnitTests/BusinessRulesTests/ZaakBussinesRuleTest.cs +++ b/src/Tests/OneGround.ZGW.Zaken.WebApi.UnitTests/BusinessRulesTests/ZaakBussinesRuleTest.cs @@ -4,7 +4,7 @@ using OneGround.ZGW.Catalogi.ServiceAgent.v1; using OneGround.ZGW.Common.Contracts.v1; using OneGround.ZGW.Common.Web.Services.UriServices; -using OneGround.ZGW.Documenten.ServiceAgent.v1; +using OneGround.ZGW.Documenten.ServiceAgent.v1._7; using OneGround.ZGW.Notificaties.ServiceAgent; using OneGround.ZGW.Zaken.DataModel; using OneGround.ZGW.Zaken.ServiceAgent.v1; From 746b6e689abeaffe38698f626ecaa9a18141d205 Mon Sep 17 00:00:00 2001 From: Aat van den Heuvel Date: Thu, 24 Sep 2026 12:55:20 +0200 Subject: [PATCH 3/4] cleanup --- .../v1/5/CachedDocumentServiceAgent.cs | 49 ------------------- .../v1/5/DocumentenServiceAgent.cs | 1 + .../DocumentenServiceAgentExtensions.cs | 8 +-- .../v1/5/ICachedDocumentenServiceAgent.cs | 3 -- .../v1/5/IDocumentenServiceAgent.cs | 14 ++++-- .../v1/5/IUserAuthDocumentenServiceAgent.cs | 3 ++ .../v1/5/UserAuthDocumentenServiceAgent.cs | 2 + .../v1/7/CachedDocumentServiceAgent.cs | 42 ++++++++++++---- .../v1/7/ICachedDocumentenServiceAgent.cs | 2 - 9 files changed, 51 insertions(+), 73 deletions(-) delete mode 100644 src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/CachedDocumentServiceAgent.cs delete mode 100644 src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/ICachedDocumentenServiceAgent.cs diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/CachedDocumentServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/CachedDocumentServiceAgent.cs deleted file mode 100644 index 140ee9fc..00000000 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/CachedDocumentServiceAgent.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading.Tasks; -using OneGround.ZGW.Common.ServiceAgent; -using OneGround.ZGW.Documenten.Contracts.v1._5.Responses; -using OneGround.ZGW.Documenten.Contracts.v1.Queries; - -namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5; - -class CachedDocumentServiceAgent : ICachedDocumentenServiceAgent -{ - private readonly IDocumentenServiceAgent _agent; - - private readonly ConcurrentDictionary> _cachedEnkelvoudigInformatieObjectUrl = new(); - - public CachedDocumentServiceAgent(IDocumentenServiceAgent agent) - { - _agent = agent; - } - - public async Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl) - { - if (_cachedEnkelvoudigInformatieObjectUrl.TryGetValue(enkelvoudigInformatieObjectUrl, out var cachedEnkelvoudigInformatieObject)) - { - return cachedEnkelvoudigInformatieObject; - } - - var result = await _agent.GetEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl); - if (result.Success) - { - // Note: Only cache successful responses, so a transient failure isn't replayed for the rest of the scope - _cachedEnkelvoudigInformatieObjectUrl[enkelvoudigInformatieObjectUrl] = result; - } - - return result; - } - - public Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand) - { - // Note: Pass through agent (so no cache) - return _agent.GetEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl, expand); - } - - public Task>> GetObjectInformatieObjectenAsync(GetAllObjectInformatieObjectenQueryParameters parameters) - { - // Note: Pass through agent (so no cache) - return _agent.GetObjectInformatieObjectenAsync(parameters); - } -} diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/DocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/DocumentenServiceAgent.cs index 90271710..4a78992b 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/DocumentenServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/DocumentenServiceAgent.cs @@ -14,6 +14,7 @@ namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5; +[Obsolete("This class is obsolete. Use the new expand/field-selection and registration mechanism instead.")] public class DocumentenServiceAgent : ZGWServiceAgent, IDocumentenServiceAgent { public DocumentenServiceAgent( diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/Extensions/DocumentenServiceAgentExtensions.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/Extensions/DocumentenServiceAgentExtensions.cs index 3f5ba6be..5d0975cd 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/Extensions/DocumentenServiceAgentExtensions.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/Extensions/DocumentenServiceAgentExtensions.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using OneGround.ZGW.Common.Constants; @@ -7,12 +8,7 @@ namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5.Extensions; public static class DocumentenServiceAgentExtensions { - public static void AddDocumentenServiceAgent_v1_5(this IServiceCollection services, IConfiguration configuration) - { - services.AddServiceAgent(ServiceRoleName.DRC, configuration); - services.AddScoped(); - } - + [Obsolete("This class is obsolete. Use the new expand/field-selection and registration mechanism instead.")] public static void AddUserAuthDocumentenServiceAgent_v1_5(this IServiceCollection services, IConfiguration configuration) { // User authorized ServiceAgent (for user cross-API expands like ZRC->DRC / BRC->DRC) diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/ICachedDocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/ICachedDocumentenServiceAgent.cs deleted file mode 100644 index 0ddf6b04..00000000 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/ICachedDocumentenServiceAgent.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5; - -public interface ICachedDocumentenServiceAgent : IDocumentenServiceAgent; diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IDocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IDocumentenServiceAgent.cs index ca9769c2..fe5c19c0 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IDocumentenServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IDocumentenServiceAgent.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Threading.Tasks; using OneGround.ZGW.Common.ServiceAgent; @@ -6,9 +7,16 @@ namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5; +[Obsolete("This class is obsolete. Use the new expand/field-selection and registration mechanism instead.")] public interface IDocumentenServiceAgent { - Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl ); - Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand); - Task>> GetObjectInformatieObjectenAsync(GetAllObjectInformatieObjectenQueryParameters parameters); + Task> GetEnkelvoudigInformatieObjectByUrlAsync( + string enkelvoudigInformatieObjectUrl + ); + Task< + ServiceAgentResponse<(EnkelvoudigInformatieObjectResponseDto enkelvoudigInformatieObject, object expandedEnkelvoudigInformatieObject)> + > GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand); + Task>> GetObjectInformatieObjectenAsync( + GetAllObjectInformatieObjectenQueryParameters parameters + ); } diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IUserAuthDocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IUserAuthDocumentenServiceAgent.cs index 2c3feeb5..7194efa8 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IUserAuthDocumentenServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/IUserAuthDocumentenServiceAgent.cs @@ -1,3 +1,6 @@ +using System; + namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5; +[Obsolete("This class is obsolete. Use the new expand/field-selection and registration mechanism instead.")] public interface IUserAuthDocumentenServiceAgent : IDocumentenServiceAgent; diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/UserAuthDocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/UserAuthDocumentenServiceAgent.cs index 54137652..a5215100 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/UserAuthDocumentenServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/5/UserAuthDocumentenServiceAgent.cs @@ -1,3 +1,4 @@ +using System; using System.Net.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -6,6 +7,7 @@ namespace OneGround.ZGW.Documenten.ServiceAgent.v1._5; +[Obsolete("This class is obsolete. Use the new expand/field-selection and registration mechanism instead.")] public class UserAuthDocumentenServiceAgent : DocumentenServiceAgent, IUserAuthDocumentenServiceAgent { public UserAuthDocumentenServiceAgent( diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/CachedDocumentServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/CachedDocumentServiceAgent.cs index a0129bed..c1fe4530 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/CachedDocumentServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/CachedDocumentServiceAgent.cs @@ -15,20 +15,27 @@ namespace OneGround.ZGW.Documenten.ServiceAgent.v1._7; class CachedDocumentServiceAgent : ICachedDocumentenServiceAgent { private readonly IDocumentenServiceAgent _agent; - private readonly ConcurrentDictionary> _cachedEnkelvoudigInformatieObjectUrl = new(); + private readonly ConcurrentDictionary< + string, + ServiceAgentResponse + > _cachedEnkelvoudigInformatieObjectUrl = new(); public CachedDocumentServiceAgent(IDocumentenServiceAgent agent) { _agent = agent; } - public Task> AddEnkelvoudigInformatieObjectAsync(EnkelvoudigInformatieObjectCreateRequestDto enkelvoudigInformatieObject) + public Task> AddEnkelvoudigInformatieObjectAsync( + EnkelvoudigInformatieObjectCreateRequestDto enkelvoudigInformatieObject + ) { // Note: Pass through agent (so no cache) return _agent.AddEnkelvoudigInformatieObjectAsync(enkelvoudigInformatieObject); } - public Task> AddObjectInformatieObjectAsync(Contracts.v1.Requests.ObjectInformatieObjectRequestDto objectInformatieObject) + public Task> AddObjectInformatieObjectAsync( + Contracts.v1.Requests.ObjectInformatieObjectRequestDto objectInformatieObject + ) { // Note: Pass through agent (so no cache) return _agent.AddObjectInformatieObjectAsync(objectInformatieObject); @@ -40,7 +47,10 @@ public Task> GetEnk return _agent.GetEnkelvoudigInformatieObjectAsync(enkelvoudigInformatieObjectId); } - public Task> AddBestandsdeelAsync(string bestandsdeelUrl, MultipartFormDataContent multipartFormDataContent) + public Task> AddBestandsdeelAsync( + string bestandsdeelUrl, + MultipartFormDataContent multipartFormDataContent + ) { // Note: Pass through agent (so no cache) return _agent.AddBestandsdeelAsync(bestandsdeelUrl, multipartFormDataContent); @@ -64,7 +74,10 @@ public Task DeleteObjectInformatieObjectByUrlAsync(string return _agent.DeleteObjectInformatieObjectByUrlAsync(objectInformatieObjectUrl); } - public Task> DownloadEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, int? version = null) + public Task> DownloadEnkelvoudigInformatieObjectByUrlAsync( + string enkelvoudigInformatieObjectUrl, + int? version = null + ) { // Note: Pass through agent (so no cache) return _agent.DownloadEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl, version); @@ -76,7 +89,9 @@ public Task>> GetAuditTrail return _agent.GetAuditTrailRegelsAsync(enkelvoudigInformatieObjectUrl); } - public async Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl) + public async Task> GetEnkelvoudigInformatieObjectByUrlAsync( + string enkelvoudigInformatieObjectUrl + ) { if (_cachedEnkelvoudigInformatieObjectUrl.TryGetValue(enkelvoudigInformatieObjectUrl, out var cachedEnkelvoudigInformatieObject)) { @@ -92,20 +107,27 @@ public async Task> return result; } - - public Task> GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand) + + public Task< + ServiceAgentResponse<(EnkelvoudigInformatieObjectResponseDto enkelvoudigInformatieObject, object expandedEnkelvoudigInformatieObject)> + > GetEnkelvoudigInformatieObjectByUrlAsync(string enkelvoudigInformatieObjectUrl, string expand) { // Note: Pass through agent (so no cache) return _agent.GetEnkelvoudigInformatieObjectByUrlAsync(enkelvoudigInformatieObjectUrl, expand); } - public Task>> GetObjectInformatieObjectsByInformatieObjectAndObjectAsync(string informatieObject, string @object) + public Task>> GetObjectInformatieObjectsByInformatieObjectAndObjectAsync( + string informatieObject, + string @object + ) { // Note: Pass through agent for now (so no cache yet due to too many memory resources kept in cache; or limit amount of cache-entries) return _agent.GetObjectInformatieObjectsByInformatieObjectAndObjectAsync(informatieObject, @object); } - public Task>> GetObjectInformatieObjectenAsync(GetAllObjectInformatieObjectenQueryParameters parameters) + public Task>> GetObjectInformatieObjectenAsync( + GetAllObjectInformatieObjectenQueryParameters parameters + ) { // Note: Pass through agent (so no cache) return _agent.GetObjectInformatieObjectenAsync(parameters); diff --git a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/ICachedDocumentenServiceAgent.cs b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/ICachedDocumentenServiceAgent.cs index a91e1b11..8a45fef7 100644 --- a/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/ICachedDocumentenServiceAgent.cs +++ b/src/OneGround.ZGW.Documenten.ServiceAgent/v1/7/ICachedDocumentenServiceAgent.cs @@ -1,5 +1,3 @@ namespace OneGround.ZGW.Documenten.ServiceAgent.v1._7; -// TODO: Uitzoeken of deze juist is en ook het gebruik ervan. -// Waarom en waar wordt de cached versie gebruikt in v1.5 en moeten we dit ook in 1.7 doen? public interface ICachedDocumentenServiceAgent : IDocumentenServiceAgent; From 9791227bf30e94f684622f5616f2bca069571f0c Mon Sep 17 00:00:00 2001 From: Aat van den Heuvel Date: Thu, 24 Sep 2026 13:37:03 +0200 Subject: [PATCH 4/4] update --- src/OneGround.ZGW.Besluiten.Web/Startup.cs | 4 +- src/OneGround.ZGW.Zaken.Web/Startup.cs | 4 +- .../v1_7/RequestToDomainProfileTests.cs | 79 +++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/OneGround.ZGW.Besluiten.Web/Startup.cs b/src/OneGround.ZGW.Besluiten.Web/Startup.cs index d4b266d3..6ded15ce 100644 --- a/src/OneGround.ZGW.Besluiten.Web/Startup.cs +++ b/src/OneGround.ZGW.Besluiten.Web/Startup.cs @@ -31,7 +31,7 @@ using OneGround.ZGW.Common.Web.Services; using OneGround.ZGW.Common.Web.Swagger; using OneGround.ZGW.DataAccess; -using OneGround.ZGW.Documenten.ServiceAgent.v1._5.Extensions; +using OneGround.ZGW.Documenten.ServiceAgent.v1._5.Extensions; // For ZRC/BRC v1.5 expands only using OneGround.ZGW.Documenten.ServiceAgent.v1._7.Extensions; using OneGround.ZGW.Notificaties.ServiceAgent.Extensions; using OneGround.ZGW.Zaken.ServiceAgent.v1.Extensions; @@ -89,7 +89,7 @@ public void ConfigureServices(IServiceCollection services) services.AddCatalogiServiceAgent(Configuration); services.AddCatalogiServiceAgent_v1_3(Configuration); services.AddDocumentenServiceAgent_v1_7(Configuration); - services.AddUserAuthDocumentenServiceAgent_v1_5(Configuration); + services.AddUserAuthDocumentenServiceAgent_v1_5(Configuration); // For ZRC/BRC v1.5 expands only // Expanders support _expand in responses services.AddExpandables(); diff --git a/src/OneGround.ZGW.Zaken.Web/Startup.cs b/src/OneGround.ZGW.Zaken.Web/Startup.cs index 626ecfc1..356458ff 100644 --- a/src/OneGround.ZGW.Zaken.Web/Startup.cs +++ b/src/OneGround.ZGW.Zaken.Web/Startup.cs @@ -26,7 +26,7 @@ using OneGround.ZGW.Common.Web.Services; using OneGround.ZGW.Common.Web.Swagger; using OneGround.ZGW.DataAccess; -using OneGround.ZGW.Documenten.ServiceAgent.v1._5.Extensions; +using OneGround.ZGW.Documenten.ServiceAgent.v1._5.Extensions; // For ZRC/BRC v1.5 expands only using OneGround.ZGW.Documenten.ServiceAgent.v1._7.Extensions; using OneGround.ZGW.Notificaties.ServiceAgent.Extensions; using OneGround.ZGW.Zaken.DataModel; @@ -100,7 +100,7 @@ public void ConfigureServices(IServiceCollection services) services.AddCatalogiServiceAgent(Configuration); services.AddCatalogiServiceAgent_v1_3(Configuration); services.AddDocumentenServiceAgent_v1_7(Configuration); - services.AddUserAuthDocumentenServiceAgent_v1_5(Configuration); + services.AddUserAuthDocumentenServiceAgent_v1_5(Configuration); // For ZRC/BRC v1.5 expands only services.AddAutorisatiesServiceAgent(Configuration); services.AddZakenServiceAgent(Configuration); services.AddBesluitenServiceAgent(Configuration); diff --git a/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/v1_7/RequestToDomainProfileTests.cs b/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/v1_7/RequestToDomainProfileTests.cs index 5efa2752..90b6abbf 100644 --- a/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/v1_7/RequestToDomainProfileTests.cs +++ b/src/Tests/OneGround.ZGW.Documenten.WebApi.UnitTests/MappingTests/v1_7/RequestToDomainProfileTests.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using MapsterMapper; +using OneGround.ZGW.Common.DataModel; +using OneGround.ZGW.Documenten.Contracts.v1; using OneGround.ZGW.Documenten.Contracts.v1._7.Queries; using OneGround.ZGW.Documenten.Contracts.v1._7.Requests; using OneGround.ZGW.Documenten.DataModel; @@ -66,6 +68,83 @@ public void Search_request_with_no_trefwoorden_and_no_uuids_leaves_both_filters_ Assert.Null(filter.Uuid_In); } + /// + /// The comprehensive counterpart to v1.1's/v1.5's identically-named fact: every scalar and nested + /// member the create map carries onto EnkelvoudigInformatieObjectVersie in one request, including + /// the two members added to this DTO since 1.5 -- IsGereedVoorPublicatie and TonenAanInitiator -- + /// which carry through on Mapster's name/type convention alone (no explicit .Map in the register). + /// + [Fact] + public void EnkelvoudigInformatieObjectCreateRequestDto_Maps_To_EnkelvoudigInformatieObjectVersie() + { + var value = new EnkelvoudigInformatieObjectCreateRequestDto + { + Identificatie = "DOC-2020-0000001", + Bronorganisatie = TestBronorganisatie, + CreatieDatum = "2020-11-12", + Titel = "My document", + Auteur = "somebody", + Formaat = "", + Taal = "eng", + Bestandsnaam = "document.pdf", + Bestandsomvang = 12345, + Inhoud = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCAuLi4=", + Link = "(no link)", + Beschrijving = "My description of the document", + OntvangstDatum = "2020-11-13", + VerzendDatum = "2020-11-14", + IndicatieGebruiksrecht = true, + Ondertekening = new OndertekeningDto { Soort = Soort.digitaal.ToString(), Datum = "2020-11-18" }, + Integriteit = new IntegriteitDto + { + Algoritme = Algoritme.crc_32.ToString(), + Waarde = "123", + Datum = "2020-11-17", + }, + InformatieObjectType = "https://some-informatieobjecttype", + Vertrouwelijkheidaanduiding = VertrouwelijkheidAanduiding.openbaar.ToString(), + Status = Status.definitief.ToString(), + Verschijningsvorm = "some-verschijningsvorm", + Trefwoorden = ["bouwtekening", "vergunning"], + IsGereedVoorPublicatie = true, + TonenAanInitiator = true, + InhoudIsVervallen = true, + }; + + var result = _mapper.Map(value); + + Assert.Equal(value.Identificatie, result.Identificatie); + Assert.Equal(value.Bronorganisatie, result.Bronorganisatie); + Assert.Equal(value.CreatieDatum, result.CreatieDatum.Value.ToString("yyyy-MM-dd")); + Assert.Equal(value.Titel, result.Titel); + Assert.Equal(value.Vertrouwelijkheidaanduiding, result.Vertrouwelijkheidaanduiding.ToString()); + Assert.Equal(value.Auteur, result.Auteur); + Assert.Equal(value.Status, result.Status.ToString()); + Assert.Equal(value.Formaat, result.Formaat); + Assert.Equal(value.Taal, result.Taal); + Assert.Equal(value.Bestandsnaam, result.Bestandsnaam); + Assert.Equal(value.Bestandsomvang, result.Bestandsomvang); + Assert.Equal(value.Inhoud, result.Inhoud); + Assert.Equal(value.Link, result.Link); + Assert.Equal(value.Beschrijving, result.Beschrijving); + Assert.Equal(value.IndicatieGebruiksrecht, result.InformatieObject.IndicatieGebruiksrecht); + Assert.Equal(value.OntvangstDatum, result.OntvangstDatum.Value.ToString("yyyy-MM-dd")); + Assert.Equal(value.VerzendDatum, result.VerzendDatum.Value.ToString("yyyy-MM-dd")); + Assert.Equal(value.Ondertekening.Datum, result.Ondertekening_Datum.Value.ToString("yyyy-MM-dd")); + Assert.Equal(value.Ondertekening.Soort, result.Ondertekening_Soort.ToString()); + Assert.Equal(value.Integriteit.Algoritme, result.Integriteit_Algoritme.ToString()); + Assert.Equal(value.Integriteit.Waarde, result.Integriteit_Waarde); + Assert.Equal(value.Integriteit.Datum, result.Integriteit_Datum.Value.ToString("yyyy-MM-dd")); + Assert.Equal(value.Verschijningsvorm, result.Verschijningsvorm); + Assert.Equal(value.Trefwoorden, result.Trefwoorden); + Assert.Equal(value.IsGereedVoorPublicatie, result.IsGereedVoorPublicatie); + Assert.Equal(value.TonenAanInitiator, result.TonenAanInitiator); + Assert.Equal(value.InhoudIsVervallen, result.InhoudIsVervallen); + // Create-map InformatieObject: InformatieObjectType is TrimEnd('/')'d - source has no trailing slash here, + // dedicated trim-behavior test above proves the trimming itself. + Assert.Equal(value.InformatieObjectType, result.InformatieObject.InformatieObjectType); + } + /// /// Ondertekening and Integriteit carry no [Required] attribute, so a real request may omit them. /// The source selectors compile to expression trees that cannot use ?., so each needs an explicit