Skip to content

Commit

Permalink
feat: add feed endpoint filtering on persistentLocalId
Browse files Browse the repository at this point in the history
  • Loading branch information
pgallik authored and YusufMavzer committed Oct 2, 2023
1 parent 5809ce4 commit 2cdb061
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 59 deletions.
38 changes: 38 additions & 0 deletions src/AddressRegistry.Api.Legacy/Address/AddressController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,44 @@ public async Task<IActionResult> Sync(CancellationToken cancellationToken = defa
};
}

/// <summary>
/// Vraag een lijst met wijzigingen voor een adres op.
/// </summary>
/// <param name="objectId">De unieke identificator van een adres.</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("sync/{objectId}")]
[Produces("text/xml")]
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
[SwaggerResponseExample(StatusCodes.Status200OK, typeof(AddressSyndicationResponseExamples))]
[SwaggerResponseExample(StatusCodes.Status400BadRequest, typeof(BadRequestResponseExamples))]
[SwaggerResponseExample(StatusCodes.Status500InternalServerError, typeof(InternalServerErrorResponseExamples))]
public async Task<IActionResult> Sync(
[FromRoute] int objectId,
CancellationToken cancellationToken = default)
{
var embedValue = Request.ExtractFilteringRequest<AddressSyndicationPersistentLocalIdFilter>()?.Filter?.Embed ?? new SyncEmbedValue();
var filtering = new FilteringHeader<AddressSyndicationPersistentLocalIdFilter>(new AddressSyndicationPersistentLocalIdFilter
{
PersistentLocalId = objectId,
Embed = embedValue
});
var sorting = Request.ExtractSortingRequest();
var pagination = Request.ExtractPaginationRequest();

var result =
await _mediator.Send(new SyndicationByPersistentLocalIdRequest(filtering, sorting, pagination), cancellationToken);

return new ContentResult
{
Content = result.Content,
ContentType = MediaTypeNames.Text.Xml,
StatusCode = StatusCodes.Status200OK
};
}

/// <summary>
/// Vraag een adres op.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
namespace AddressRegistry.Api.Legacy.Address.Sync
{
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Be.Vlaanderen.Basisregisters.Api.Search.Pagination;
using Be.Vlaanderen.Basisregisters.Api.Syndication;
using Be.Vlaanderen.Basisregisters.GrAr.Common;
using Infrastructure;
using Infrastructure.Options;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.SyndicationFeed;
using Microsoft.SyndicationFeed.Atom;

public class AddressSyndicationBaseHandler
{
private readonly IConfiguration _configuration;
private readonly IOptions<ResponseOptions> _responseOptions;

public AddressSyndicationBaseHandler(IConfiguration configuration, IOptions<ResponseOptions> responseOptions)
{
_configuration = configuration;
_responseOptions = responseOptions;
}

protected async Task<string> BuildAtomFeed(
DateTimeOffset lastFeedUpdate,
PagedQueryable<AddressSyndicationQueryResult> pagedAddresses)
{
var sw = new StringWriterWithEncoding(Encoding.UTF8);

await using (var xmlWriter = XmlWriter.Create(sw, new XmlWriterSettings { Async = true, Indent = true, Encoding = sw.Encoding }))
{
var formatter = new AtomFormatter(null, xmlWriter.Settings) { UseCDATA = true };
var writer = new AtomFeedWriter(xmlWriter, null, formatter);
var syndicationConfiguration = _configuration.GetSection("Syndication");
var atomFeedConfig = AtomFeedConfigurationBuilder.CreateFrom(syndicationConfiguration, lastFeedUpdate);

await writer.WriteDefaultMetadata(atomFeedConfig);

var addresses = pagedAddresses.Items.ToList();

var nextFrom = addresses.Any()
? addresses.Max(x => x.Position) + 1
: (long?)null;

var nextUri = BuildNextSyncUri(pagedAddresses.PaginationInfo.Limit, nextFrom, syndicationConfiguration["NextUri"]);
if (nextUri != null)
{
await writer.Write(new SyndicationLink(nextUri, "next"));
}

foreach (var address in addresses)
{
await writer.WriteAddress(_responseOptions, formatter, syndicationConfiguration["Category"], address);
}

xmlWriter.Flush();
}

return sw.ToString();
}

private static Uri BuildNextSyncUri(int limit, long? from, string nextUrlBase)
{
return from.HasValue
? new Uri(string.Format(nextUrlBase, from, limit))
: null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
namespace AddressRegistry.Api.Legacy.Address.Sync
{
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Be.Vlaanderen.Basisregisters.Api.Search.Filtering;
using Be.Vlaanderen.Basisregisters.Api.Search.Pagination;
using Be.Vlaanderen.Basisregisters.Api.Search.Sorting;
using Infrastructure.Options;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Projections.Legacy;

public record SyndicationByPersistentLocalIdRequest(
FilteringHeader<AddressSyndicationPersistentLocalIdFilter> Filtering,
SortingHeader Sorting,
IPaginationRequest Pagination)
: IRequest<SyndicationAtomContent>;

public sealed class AddressSyndicationByPersistentLocalIdHandler : AddressSyndicationBaseHandler, IRequestHandler<SyndicationByPersistentLocalIdRequest, SyndicationAtomContent>
{
private readonly LegacyContext _legacyContext;

public AddressSyndicationByPersistentLocalIdHandler(
LegacyContext legacyContext,
IOptions<ResponseOptions> responseOptions,
IConfiguration configuration) : base (configuration, responseOptions)
{
_legacyContext = legacyContext;
}

public async Task<SyndicationAtomContent> Handle(SyndicationByPersistentLocalIdRequest request, CancellationToken cancellationToken)
{
var pagedAddresses =
new AddressSyndicationPersistentLocalIdQuery(_legacyContext, request.Filtering.Filter?.Embed)
.Fetch(request.Filtering, request.Sorting, request.Pagination);

var lastUpdatedDateTime = pagedAddresses.Items
.ToList()
.Last()
.LastChangedOn
.ToDateTimeUtc();

return new SyndicationAtomContent(await BuildAtomFeed(lastUpdatedDateTime, pagedAddresses));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,35 @@ namespace AddressRegistry.Api.Legacy.Address.Sync
{
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Be.Vlaanderen.Basisregisters.Api.Search.Filtering;
using Be.Vlaanderen.Basisregisters.Api.Search.Pagination;
using Be.Vlaanderen.Basisregisters.Api.Search.Sorting;
using Be.Vlaanderen.Basisregisters.Api.Syndication;
using Be.Vlaanderen.Basisregisters.GrAr.Common;
using Infrastructure;
using Infrastructure.Options;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.SyndicationFeed;
using Microsoft.SyndicationFeed.Atom;
using Projections.Legacy;

public sealed record SyndicationAtomContent(string Content);
public sealed record SyndicationRequest(FilteringHeader<AddressSyndicationFilter> Filtering, SortingHeader Sorting, IPaginationRequest Pagination) : IRequest<SyndicationAtomContent>;
public record SyndicationRequest(
FilteringHeader<AddressSyndicationFilter> Filtering,
SortingHeader Sorting,
IPaginationRequest Pagination)
: IRequest<SyndicationAtomContent>;

public sealed class AddressSyndicationHandler : IRequestHandler<SyndicationRequest, SyndicationAtomContent>
public sealed class AddressSyndicationHandler : AddressSyndicationBaseHandler, IRequestHandler<SyndicationRequest, SyndicationAtomContent>
{
private readonly LegacyContext _legacyContext;
private readonly IOptions<ResponseOptions> _responseOptions;
private readonly IConfiguration _configuration;

public AddressSyndicationHandler(
LegacyContext legacyContext,
IOptions<ResponseOptions> responseOptions,
IConfiguration configuration)
IConfiguration configuration) : base (configuration, responseOptions)
{
_legacyContext = legacyContext;
_responseOptions = responseOptions;
_configuration = configuration;
}

public async Task<SyndicationAtomContent> Handle(SyndicationRequest request, CancellationToken cancellationToken)
Expand All @@ -62,50 +55,5 @@ public async Task<SyndicationAtomContent> Handle(SyndicationRequest request, Can

return new SyndicationAtomContent(await BuildAtomFeed(lastFeedUpdate, pagedAddresses));
}

private async Task<string> BuildAtomFeed(
DateTimeOffset lastFeedUpdate,
PagedQueryable<AddressSyndicationQueryResult> pagedAddresses)
{
var sw = new StringWriterWithEncoding(Encoding.UTF8);

await using (var xmlWriter = XmlWriter.Create(sw, new XmlWriterSettings { Async = true, Indent = true, Encoding = sw.Encoding }))
{
var formatter = new AtomFormatter(null, xmlWriter.Settings) { UseCDATA = true };
var writer = new AtomFeedWriter(xmlWriter, null, formatter);
var syndicationConfiguration = _configuration.GetSection("Syndication");
var atomFeedConfig = AtomFeedConfigurationBuilder.CreateFrom(syndicationConfiguration, lastFeedUpdate);

await writer.WriteDefaultMetadata(atomFeedConfig);

var addresses = pagedAddresses.Items.ToList();

var nextFrom = addresses.Any()
? addresses.Max(x => x.Position) + 1
: (long?)null;

var nextUri = BuildNextSyncUri(pagedAddresses.PaginationInfo.Limit, nextFrom, syndicationConfiguration["NextUri"]);
if (nextUri != null)
{
await writer.Write(new SyndicationLink(nextUri, "next"));
}

foreach (var address in addresses)
{
await writer.WriteAddress(_responseOptions, formatter, syndicationConfiguration["Category"], address);
}

xmlWriter.Flush();
}

return sw.ToString();
}

private static Uri BuildNextSyncUri(int limit, long? from, string nextUrlBase)
{
return from.HasValue
? new Uri(string.Format(nextUrlBase, from, limit))
: null;
}
}
}
Loading

0 comments on commit 2cdb061

Please sign in to comment.