diff --git a/Be.Vlaanderen.Basisregisters.Projector.sln.DotSettings b/Be.Vlaanderen.Basisregisters.Projector.sln.DotSettings
index 74e3237..54173b3 100644
--- a/Be.Vlaanderen.Basisregisters.Projector.sln.DotSettings
+++ b/Be.Vlaanderen.Basisregisters.Projector.sln.DotSettings
@@ -2,7 +2,9 @@
ERROR
True
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aa_bb" /></Policy>
+ <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aa_bb" /></Policy></Policy>
True
True
+ True
True
diff --git a/src/Be.Vlaanderen.Basisregisters.Projector/ApplicationBuilderExtensions.cs b/src/Be.Vlaanderen.Basisregisters.Projector/ApplicationBuilderExtensions.cs
new file mode 100644
index 0000000..be7e16c
--- /dev/null
+++ b/src/Be.Vlaanderen.Basisregisters.Projector/ApplicationBuilderExtensions.cs
@@ -0,0 +1,135 @@
+namespace Be.Vlaanderen.Basisregisters.Projector
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net.Mime;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using ConnectedProjections;
+ using Controllers;
+ using Microsoft.AspNetCore.Builder;
+ using Microsoft.AspNetCore.Http;
+ using Microsoft.Extensions.DependencyInjection;
+ using Newtonsoft.Json;
+ using SqlStreamStore;
+
+ public static class ApplicationBuilderExtensions
+ {
+ public static IApplicationBuilder UseProjectorEndpoints(
+ this IApplicationBuilder builder,
+ string baseUrl,
+ JsonSerializerSettings? jsonSerializerSettings)
+ {
+ ArgumentNullException.ThrowIfNull(baseUrl);
+
+ builder.UseEndpoints(endpoints =>
+ {
+ endpoints.MapGet("/v1/projections", async context => { await GetProjections(builder, context, baseUrl, jsonSerializerSettings).NoContext(); });
+ endpoints.MapGet("/projections", async context => { await GetProjections(builder, context, baseUrl, jsonSerializerSettings).NoContext(); });
+
+ endpoints.MapPost("/projections/start/all", async context => { await StartAll(builder, context).NoContext(); });
+ endpoints.MapPost("/v1/projections/start/all", async context => { await StartAll(builder, context).NoContext(); });
+
+ endpoints.MapPost("/projections/start/{projectionId}", async context
+ => await StartProjection(builder, context.Request.RouteValues["projectionId"].ToString(), context).NoContext());
+ endpoints.MapPost("/v1/projections/start/{projectionId}", async context
+ => await StartProjection(builder, context.Request.RouteValues["projectionId"].ToString(), context).NoContext());
+
+ endpoints.MapPost("/projections/stop/all", async context => { await StopAll(builder, context).NoContext(); });
+ endpoints.MapPost("/v1/projections/stop/all", async context => { await StopAll(builder, context).NoContext(); });
+
+ endpoints.MapPost("/projections/stop/{projectionId}", async context
+ => await StopProjection(builder, context.Request.RouteValues["projectionId"].ToString(), context).NoContext());
+ endpoints.MapPost("/v1/projections/stop/{projectionId}", async context
+ => await StopProjection(builder, context.Request.RouteValues["projectionId"].ToString(), context).NoContext());
+ });
+
+ return builder;
+ }
+
+ private static async Task StopProjection(IApplicationBuilder app, string? projectionId, HttpContext context)
+ {
+ var manager = app.ApplicationServices.GetRequiredService();
+ if (!manager.Exists(projectionId))
+ {
+ context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ await context.Response.WriteAsync("Invalid projection Id.").NoContext();
+ return;
+ }
+
+ await manager.Stop(projectionId, CancellationToken.None).NoContext();
+
+ context.Response.StatusCode = StatusCodes.Status202Accepted;
+ }
+
+ private static async Task StopAll(IApplicationBuilder app, HttpContext context)
+ {
+ var manager = app.ApplicationServices.GetRequiredService();
+ await manager.Stop(CancellationToken.None).NoContext();
+
+ context.Response.StatusCode = StatusCodes.Status202Accepted;
+ }
+
+ private static async Task StartProjection(IApplicationBuilder app, string? projectionId, HttpContext context)
+ {
+ var manager = app.ApplicationServices.GetRequiredService();
+ if (!manager.Exists(projectionId))
+ {
+ context.Response.StatusCode = StatusCodes.Status400BadRequest;
+ await context.Response.WriteAsync("Invalid projection Id.").NoContext();
+ return;
+ }
+
+ await manager.Start(projectionId, CancellationToken.None).NoContext();
+
+ context.Response.StatusCode = StatusCodes.Status202Accepted;
+ }
+
+ private static async Task StartAll(IApplicationBuilder app, HttpContext context)
+ {
+ var manager = app.ApplicationServices.GetRequiredService();
+ await manager.Start(CancellationToken.None).NoContext();
+
+ context.Response.StatusCode = StatusCodes.Status202Accepted;
+ }
+
+ private static async Task GetProjections(
+ IApplicationBuilder app,
+ HttpContext context,
+ string baseUrl,
+ JsonSerializerSettings? jsonSerializerSettings = null)
+ {
+ var manager = app.ApplicationServices.GetRequiredService();
+ var streamStore = app.ApplicationServices.GetRequiredService();
+
+ var registeredConnectedProjections = manager
+ .GetRegisteredProjections()
+ .ToList();
+ var projectionStates = await manager.GetProjectionStates(CancellationToken.None).NoContext();
+ var responses = registeredConnectedProjections.Aggregate(
+ new List(),
+ (list, projection) =>
+ {
+ var projectionState = projectionStates.SingleOrDefault(x => x.Name == projection.Id);
+ list.Add(new ProjectionResponse(
+ projection,
+ projectionState,
+ baseUrl));
+ return list;
+ });
+
+ var streamPosition = await streamStore.ReadHeadPosition().NoContext();
+
+ var projectionResponseList = new ProjectionResponseList(responses, baseUrl)
+ {
+ StreamPosition = streamPosition
+ };
+
+ var json = JsonConvert.SerializeObject(projectionResponseList, jsonSerializerSettings ?? new JsonSerializerSettings());
+
+ context.Response.Headers.ContentType = MediaTypeNames.Application.Json;
+ await context.Response.WriteAsync(json).NoContext();
+ }
+ }
+}