Skip to content

Commit ce3fd80

Browse files
committed
feat: add remove measured building
1 parent ae2d75b commit ce3fd80

File tree

19 files changed

+945
-2
lines changed

19 files changed

+945
-2
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace BuildingRegistry.Api.BackOffice.Abstractions.Building.Requests
2+
{
3+
public sealed class RemoveMeasuredBuildingRequest
4+
{
5+
public int PersistentLocalId { get; set; }
6+
}
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace BuildingRegistry.Api.BackOffice.Abstractions.Building.SqsRequests
2+
{
3+
using Be.Vlaanderen.Basisregisters.Sqs.Requests;
4+
using Requests;
5+
6+
public sealed class RemoveMeasuredBuildingSqsRequest : SqsRequest
7+
{
8+
public RemoveMeasuredBuildingRequest Request { get; set; }
9+
}
10+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace BuildingRegistry.Api.BackOffice.Abstractions.Validation
2+
{
3+
using TicketingService.Abstractions;
4+
5+
public static partial class ValidationErrors
6+
{
7+
public static class RemoveMeasuredBuilding
8+
{
9+
public static class BuildingHasInvalidBuildingGeometryMethod
10+
{
11+
public const string Code = "GebouwGeometrieMethodeIngeschetst";
12+
public const string Message = "Deze actie is enkel toegestaan op gebouwen met geometrieMethode 'ingemeten'.";
13+
14+
public static TicketError ToTicketError() => new(Message, Code);
15+
}
16+
17+
public static class BuildingHasBuildingUnits
18+
{
19+
public const string Code = "GebouwHeeftGebouweenheden";
20+
public const string Message = "Deze actie is enkel toegestaan op gebouwen zonder gebouweenheden.";
21+
22+
public static TicketError ToTicketError() => new(Message, Code);
23+
}
24+
}
25+
}
26+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
namespace BuildingRegistry.Api.BackOffice.Handlers.Lambda.Handlers.Building
2+
{
3+
using Abstractions;
4+
using Abstractions.Validation;
5+
using Be.Vlaanderen.Basisregisters.AggregateSource;
6+
using Be.Vlaanderen.Basisregisters.CommandHandling.Idempotency;
7+
using Be.Vlaanderen.Basisregisters.Sqs.Lambda.Infrastructure;
8+
using Be.Vlaanderen.Basisregisters.Sqs.Responses;
9+
using BuildingRegistry.Building;
10+
using BuildingRegistry.Building.Exceptions;
11+
using Microsoft.Extensions.Configuration;
12+
using Requests.Building;
13+
using TicketingService.Abstractions;
14+
15+
public sealed class RemoveMeasuredBuildingLambdaHandler : BuildingLambdaHandler<RemoveMeasuredBuildingLambdaRequest>
16+
{
17+
private readonly BackOfficeContext _backOfficeContext;
18+
19+
public RemoveMeasuredBuildingLambdaHandler(
20+
IConfiguration configuration,
21+
ICustomRetryPolicy retryPolicy,
22+
ITicketing ticketing,
23+
IIdempotentCommandHandler idempotentCommandHandler,
24+
IBuildings buildings,
25+
BackOfficeContext backOfficeContext)
26+
: base(
27+
configuration,
28+
retryPolicy,
29+
ticketing,
30+
idempotentCommandHandler,
31+
buildings)
32+
{
33+
_backOfficeContext = backOfficeContext;
34+
}
35+
36+
protected override async Task<object> InnerHandle(RemoveMeasuredBuildingLambdaRequest request, CancellationToken cancellationToken)
37+
{
38+
var cmd = request.ToCommand();
39+
40+
try
41+
{
42+
await IdempotentCommandHandler.Dispatch(
43+
cmd.CreateCommandId(),
44+
cmd,
45+
request.Metadata,
46+
cancellationToken);
47+
}
48+
catch (IdempotencyException)
49+
{
50+
// Idempotent: Do Nothing return last etag
51+
}
52+
53+
await _backOfficeContext.RemoveBuildingUnitAddressRelations(cmd.BuildingPersistentLocalId, cancellationToken);
54+
55+
var lastHash = await GetHash(new BuildingPersistentLocalId(request.BuildingPersistentLocalId), cancellationToken);
56+
return new ETagResponse(string.Format(DetailUrlFormat, request.BuildingPersistentLocalId), lastHash);
57+
}
58+
59+
protected override TicketError? InnerMapDomainException(DomainException exception, RemoveMeasuredBuildingLambdaRequest request)
60+
{
61+
return exception switch
62+
{
63+
BuildingHasInvalidGeometryMethodException => ValidationErrors.RemoveMeasuredBuilding.BuildingHasInvalidBuildingGeometryMethod.ToTicketError(),
64+
BuildingHasBuildingUnitsException => ValidationErrors.RemoveMeasuredBuilding.BuildingHasBuildingUnits.ToTicketError(),
65+
_ => null
66+
};
67+
}
68+
}
69+
}

src/BuildingRegistry.Api.BackOffice.Handlers.Lambda/MessageHandler.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ public async Task HandleMessage(object? messageData, MessageMetadata messageMeta
114114
await mediator.Send(new RemoveBuildingUnitLambdaRequest(messageMetadata.MessageGroupId!, request), cancellationToken);
115115
break;
116116

117+
case RemoveMeasuredBuildingSqsRequest request:
118+
await mediator.Send(new RemoveMeasuredBuildingLambdaRequest(messageMetadata.MessageGroupId!, request), cancellationToken);
119+
break;
120+
117121
case DeregulateBuildingUnitSqsRequest request:
118122
await mediator.Send(new DeregulateBuildingUnitLambdaRequest(messageMetadata.MessageGroupId!, request), cancellationToken);
119123
break;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
namespace BuildingRegistry.Api.BackOffice.Handlers.Lambda.Requests.Building
2+
{
3+
using Abstractions.Building.SqsRequests;
4+
using BuildingRegistry.Api.BackOffice.Abstractions.Building.Requests;
5+
using BuildingRegistry.Building;
6+
using BuildingRegistry.Building.Commands;
7+
8+
public sealed record RemoveMeasuredBuildingLambdaRequest : BuildingLambdaRequest, Abstractions.IHasBuildingPersistentLocalId
9+
{
10+
public RemoveMeasuredBuildingRequest Request { get; }
11+
12+
public int BuildingPersistentLocalId => Request.PersistentLocalId;
13+
14+
public RemoveMeasuredBuildingLambdaRequest(string messageGroupId, RemoveMeasuredBuildingSqsRequest sqsRequest)
15+
: base(
16+
messageGroupId,
17+
sqsRequest.TicketId,
18+
sqsRequest.IfMatchHeaderValue,
19+
sqsRequest.ProvenanceData!.ToProvenance(),
20+
sqsRequest.Metadata)
21+
{
22+
Request = sqsRequest.Request;
23+
}
24+
25+
/// <summary>
26+
/// Map to RemoveMeasuredBuilding command.
27+
/// </summary>
28+
/// <returns>RemoveMeasuredBuilding.</returns>
29+
public RemoveMeasuredBuilding ToCommand()
30+
{
31+
return new RemoveMeasuredBuilding(new BuildingPersistentLocalId(BuildingPersistentLocalId), Provenance);
32+
}
33+
}
34+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
namespace BuildingRegistry.Api.BackOffice.Building
2+
{
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Abstractions.Building.Requests;
6+
using Abstractions.Building.Validators;
7+
using Abstractions.Building.SqsRequests;
8+
using Abstractions.Validation;
9+
using Be.Vlaanderen.Basisregisters.Auth.AcmIdm;
10+
using Be.Vlaanderen.Basisregisters.Api.ETag;
11+
using Be.Vlaanderen.Basisregisters.Api.Exceptions;
12+
using Be.Vlaanderen.Basisregisters.GrAr.Provenance;
13+
using BuildingRegistry.Building;
14+
using Infrastructure;
15+
using Microsoft.AspNetCore.Authentication.JwtBearer;
16+
using Microsoft.AspNetCore.Authorization;
17+
using Microsoft.AspNetCore.Http;
18+
using Microsoft.AspNetCore.Mvc;
19+
using Swashbuckle.AspNetCore.Filters;
20+
21+
public partial class BuildingController
22+
{
23+
/// <summary>
24+
/// Ingemeten gebouw verwijderen.
25+
/// </summary>
26+
/// <param name="buildingExistsValidator"></param>
27+
/// <param name="ifMatchHeaderValidator"></param>
28+
/// <param name="request"></param>
29+
/// <param name="ifMatchHeaderValue"></param>
30+
/// <param name="cancellationToken"></param>
31+
/// <response code="202">Aanvraag tot goedkeuring wordt reeds verwerkt.</response>
32+
/// <response code="412">Als de If-Match header niet overeenkomt met de laatste ETag.</response>
33+
/// <returns></returns>
34+
[HttpPost("{persistentLocalId}/acties/verwijder-ingemeten")]
35+
[ProducesResponseType(StatusCodes.Status202Accepted)]
36+
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
37+
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
38+
[SwaggerResponseHeader(StatusCodes.Status202Accepted, "location", "string", "De URL van het aangemaakte ticket.")]
39+
[SwaggerResponseExample(StatusCodes.Status400BadRequest, typeof(BadRequestResponseExamples))]
40+
[SwaggerResponseExample(StatusCodes.Status500InternalServerError, typeof(InternalServerErrorResponseExamples))]
41+
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = PolicyNames.IngemetenGebouw.GrbBijwerker)]
42+
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = PolicyNames.IngemetenGebouw.InterneBijwerker)]
43+
public async Task<IActionResult> RemoveMeasuredBuilding(
44+
[FromServices] BuildingExistsValidator buildingExistsValidator,
45+
[FromServices] IIfMatchHeaderValidator ifMatchHeaderValidator,
46+
[FromRoute] RemoveMeasuredBuildingRequest request,
47+
[FromHeader(Name = "If-Match")] string? ifMatchHeaderValue,
48+
CancellationToken cancellationToken = default)
49+
{
50+
if (!await buildingExistsValidator.Exists(new BuildingPersistentLocalId(request.PersistentLocalId), cancellationToken))
51+
{
52+
throw new ApiException(ValidationErrors.Common.BuildingNotFound.Message, StatusCodes.Status404NotFound);
53+
}
54+
55+
if (!await ifMatchHeaderValidator.IsValidForBuilding(ifMatchHeaderValue, new BuildingPersistentLocalId(request.PersistentLocalId), cancellationToken))
56+
{
57+
return new PreconditionFailedResult();
58+
}
59+
60+
var sqsResult = await Mediator.Send(
61+
new RemoveMeasuredBuildingSqsRequest
62+
{
63+
Request = request,
64+
Metadata = GetMetadata(),
65+
ProvenanceData = new ProvenanceData(CreateProvenance(Modification.Delete)),
66+
IfMatchHeaderValue = ifMatchHeaderValue
67+
}, cancellationToken);
68+
69+
return Accepted(sqsResult);
70+
}
71+
}
72+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
namespace BuildingRegistry.Api.BackOffice.Handlers.Building
2+
{
3+
using System.Collections.Generic;
4+
using Abstractions.Building.SqsRequests;
5+
using Be.Vlaanderen.Basisregisters.Sqs;
6+
using Be.Vlaanderen.Basisregisters.Sqs.Handlers;
7+
using TicketingService.Abstractions;
8+
9+
public sealed class RemoveMeasuredBuildingSqsHandler : SqsHandler<RemoveMeasuredBuildingSqsRequest>
10+
{
11+
public const string Action = "RemoveMeasuredBuilding";
12+
13+
public RemoveMeasuredBuildingSqsHandler(
14+
ISqsQueue sqsQueue,
15+
ITicketing ticketing,
16+
ITicketingUrl ticketingUrl)
17+
: base(sqsQueue, ticketing, ticketingUrl)
18+
{
19+
}
20+
21+
protected override string WithAggregateId(RemoveMeasuredBuildingSqsRequest request)
22+
{
23+
return request.Request.PersistentLocalId.ToString();
24+
}
25+
26+
protected override IDictionary<string, string> WithTicketMetadata(string aggregateId, RemoveMeasuredBuildingSqsRequest sqsRequest)
27+
{
28+
return new Dictionary<string, string>
29+
{
30+
{ RegistryKey, nameof(BuildingRegistry) },
31+
{ ActionKey, Action },
32+
{ AggregateIdKey, aggregateId },
33+
{ ObjectIdKey, sqsRequest.Request.PersistentLocalId.ToString() }
34+
};
35+
}
36+
}
37+
}

src/BuildingRegistry/Building/Building.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,26 @@ public void RemoveConstruction()
206206
ApplyChange(new BuildingWasRemovedV2(BuildingPersistentLocalId));
207207
}
208208

209+
public void RemoveMeasuredBuilding()
210+
{
211+
if (IsRemoved)
212+
{
213+
return;
214+
}
215+
216+
if (BuildingGeometry.Method != BuildingGeometryMethod.MeasuredByGrb)
217+
{
218+
throw new BuildingHasInvalidGeometryMethodException();
219+
}
220+
221+
if(_buildingUnits.GetNotRemovedUnits().Any())
222+
{
223+
throw new BuildingHasBuildingUnitsException();
224+
}
225+
226+
ApplyChange(new BuildingWasRemovedV2(BuildingPersistentLocalId));
227+
}
228+
209229
public void ChangeOutliningConstruction(ExtendedWkbGeometry extendedWkbGeometry)
210230
{
211231
GuardRemovedBuilding();

src/BuildingRegistry/Building/BuildingCommandHandlerModule.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,18 @@ public BuildingCommandHandlerModule(
169169
building.RemoveConstruction();
170170
});
171171

172+
For<RemoveMeasuredBuilding>()
173+
.AddSqlStreamStore(getStreamStore, getUnitOfWork, eventMapping, eventSerializer, getSnapshotStore)
174+
.AddEventHash<RemoveMeasuredBuilding, Building>(getUnitOfWork)
175+
.AddProvenance(getUnitOfWork, provenanceFactory)
176+
.Handle(async (message, ct) =>
177+
{
178+
var streamId = new BuildingStreamId(message.Command.BuildingPersistentLocalId);
179+
var building = await buildingRepository().GetAsync(streamId, ct);
180+
181+
building.RemoveMeasuredBuilding();
182+
});
183+
172184
For<ChangeBuildingOutline>()
173185
.AddSqlStreamStore(getStreamStore, getUnitOfWork, eventMapping, eventSerializer, getSnapshotStore)
174186
.AddEventHash<ChangeBuildingOutline, Building>(getUnitOfWork)

0 commit comments

Comments
 (0)