Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
715574f
Rewrite Tutorial 01 and 02 with real E2E integration through actual E…
Copilot Apr 6, 2026
2f94716
Address code review: add using statement, clean up fully qualified na…
Copilot Apr 6, 2026
66aa107
Chunk 0: Aspire test infrastructure with real NATS/Temporal/SFTP/SMTP…
Copilot Apr 6, 2026
395d7bf
refactor: rewrite Tutorial01 Exam to use real NATS via NatsBrokerEndp…
Copilot Apr 6, 2026
164e2bb
Aspire-hosted test infrastructure for real broker connections in tuto…
Copilot Apr 6, 2026
3c6bd6d
Rewrite Tutorial 02: Temporal.io workflow orchestration (saga, fan-ou…
Copilot Apr 7, 2026
94a7d5d
Rewrite Tutorial 03: envelope anatomy, causation chains, message chan…
Copilot Apr 7, 2026
c7db597
Rewrite Tutorial 04: FaultEnvelope, record immutability, MessageHisto…
Copilot Apr 7, 2026
17241cc
Rewrite Tutorials 05-06: broker patterns, consumer types, channel sem…
Copilot Apr 7, 2026
313c351
Rewrite Tutorials 07-08: Temporal workflows, activities pipeline with…
Copilot Apr 7, 2026
b6355f3
Rewrite Tutorials 09-10: section headers, enhanced comments for route…
Copilot Apr 7, 2026
c8ee4e9
Add section headers to Tutorials 11-12: dynamic router, recipient list
Copilot Apr 7, 2026
724c9b1
Add section headers to Tutorials 11-12: dynamic router, recipient list
Copilot Apr 7, 2026
fce2690
Add section headers to Tutorials 13-14: routing slip, process manager
Copilot Apr 7, 2026
367e7d6
Rewrite Tutorial 03 to use real NATS JetStream via Aspire (no MockEnd…
Copilot Apr 7, 2026
8e3834e
Rewrite Tutorials 04-06, 09 to use real NATS JetStream via Aspire
Copilot Apr 7, 2026
c9a96b4
Rewrite Tutorials 10-12 to use real NATS JetStream via Aspire
Copilot Apr 7, 2026
71ebb1b
Rewrite Tutorial 13/14/15 Labs: replace MockEndpoint with NatsBrokerE…
Copilot Apr 7, 2026
3bbb1bf
Rewrite Tutorial 16-20 Lab.cs to use NatsBrokerEndpoint instead of Mo…
Copilot Apr 7, 2026
90552cc
Fix AspireFixture namespace to global — all T03-T20 verified with rea…
Copilot Apr 7, 2026
72c0fc6
Rewrite Tutorial 21-25 Lab.cs: replace MockEndpoint with NatsBrokerEn…
Copilot Apr 7, 2026
57693c2
Rewrite Tutorials 21-30 to use real NATS JetStream via Aspire — verif…
Copilot Apr 7, 2026
a7a8596
Convert Tutorial 41-45 Lab.cs and Exam.cs from MockEndpoint to NatsBr…
Copilot Apr 7, 2026
2262d67
Convert Tutorial 46 Lab.cs and Exam.cs from MockEndpoint to NatsBroke…
Copilot Apr 7, 2026
bf1165d
Convert Tutorial 47 Lab.cs and Exam.cs from MockEndpoint to NatsBroke…
Copilot Apr 7, 2026
d0bfbb2
Convert Tutorial 49 Lab.cs and Exam.cs from MockEndpoint to NatsBroke…
Copilot Apr 7, 2026
cc4fb7f
Convert Tutorials 31-50 from MockEndpoint to real NATS via Aspire; fi…
Copilot Apr 7, 2026
aaa9bbc
Phase 28 Chunk 103: Add Postgres message broker (Ingestion.Postgres) …
Copilot Apr 7, 2026
6ddf2aa
Mark chunks 103-106 done in completion-log; update milestones next ch…
Copilot Apr 7, 2026
c97ca8b
Fix Tutorial05 BrokerType assertion: update from 3 to 4 protocols (ad…
Copilot Apr 7, 2026
e688e86
Chunk 107 done: 38 broker-agnostic EIP integration tests (DLQ, retry,…
Copilot Apr 7, 2026
570621f
Chunk 108 done: Aspire Postgres container, PostgresBrokerEndpoint tes…
Copilot Apr 7, 2026
2f1148e
Chunk 109 done: 14 Postgres integration tests (routing, splitter, agg…
Copilot Apr 7, 2026
6580b45
Fix Postgres integration tests: wait for readiness, fix schema race c…
Copilot Apr 7, 2026
b92ca2a
Fix race condition in NatsBrokerEndpoint: call handler before enqueui…
Copilot Apr 7, 2026
0a6c567
Fix BrokerTypeTests to include Postgres as 4th broker type
Copilot Apr 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions EnterpriseIntegrationPlatform/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<ItemGroup>
<!-- Aspire -->
<PackageVersion Include="Aspire.Hosting" Version="13.1.2" />
<PackageVersion Include="Aspire.Hosting.Testing" Version="13.1.2" />
<!-- Service Defaults -->
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.1.0" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="10.1.0" />
Expand Down Expand Up @@ -46,6 +47,8 @@
<!-- Health Checks -->
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.5" />
<!-- PostgreSQL message broker -->
<PackageVersion Include="Npgsql" Version="9.0.3" />
<!-- Connectors -->
<PackageVersion Include="SSH.NET" Version="2024.2.0" />
<PackageVersion Include="MailKit" Version="4.15.1" />
Expand Down
45 changes: 45 additions & 0 deletions EnterpriseIntegrationPlatform/EnterpriseIntegrationPlatform.sln
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors", "src\Connector
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testing", "src\Testing\Testing.csproj", "{F13607C8-980A-4EFF-93B5-5D6FE344F08C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestAppHost", "tests\TestAppHost\TestAppHost.csproj", "{AFAA5258-2646-4159-8D88-8CACD16974E4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ingestion.Postgres", "src\Ingestion.Postgres\Ingestion.Postgres.csproj", "{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BrokerAgnosticTests", "tests\BrokerAgnosticTests\BrokerAgnosticTests.csproj", "{F89FE768-35C5-439F-9725-1E54CB8C3009}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -765,6 +771,42 @@ Global
{F13607C8-980A-4EFF-93B5-5D6FE344F08C}.Release|x64.Build.0 = Release|Any CPU
{F13607C8-980A-4EFF-93B5-5D6FE344F08C}.Release|x86.ActiveCfg = Release|Any CPU
{F13607C8-980A-4EFF-93B5-5D6FE344F08C}.Release|x86.Build.0 = Release|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Debug|x64.ActiveCfg = Debug|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Debug|x64.Build.0 = Debug|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Debug|x86.ActiveCfg = Debug|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Debug|x86.Build.0 = Debug|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Release|Any CPU.Build.0 = Release|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Release|x64.ActiveCfg = Release|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Release|x64.Build.0 = Release|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Release|x86.ActiveCfg = Release|Any CPU
{AFAA5258-2646-4159-8D88-8CACD16974E4}.Release|x86.Build.0 = Release|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Debug|x64.ActiveCfg = Debug|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Debug|x64.Build.0 = Debug|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Debug|x86.ActiveCfg = Debug|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Debug|x86.Build.0 = Debug|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Release|Any CPU.Build.0 = Release|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Release|x64.ActiveCfg = Release|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Release|x64.Build.0 = Release|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Release|x86.ActiveCfg = Release|Any CPU
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3}.Release|x86.Build.0 = Release|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Debug|x64.ActiveCfg = Debug|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Debug|x64.Build.0 = Debug|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Debug|x86.ActiveCfg = Debug|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Debug|x86.Build.0 = Debug|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Release|Any CPU.Build.0 = Release|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Release|x64.ActiveCfg = Release|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Release|x64.Build.0 = Release|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Release|x86.ActiveCfg = Release|Any CPU
{F89FE768-35C5-439F-9725-1E54CB8C3009}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -826,5 +868,8 @@ Global
{F8DD5966-EE52-4ADA-BE4F-D23636F424F8} = {A1B2C3D4-0001-0001-0001-000000000002}
{7998C735-EB8F-4DBE-BB32-978E9A465433} = {A1B2C3D4-0001-0001-0001-000000000002}
{F13607C8-980A-4EFF-93B5-5D6FE344F08C} = {A1B2C3D4-0001-0001-0001-000000000001}
{AFAA5258-2646-4159-8D88-8CACD16974E4} = {A1B2C3D4-0001-0001-0001-000000000002}
{E21DA4D4-D9E0-49E4-9C36-2544A3EF4CD3} = {A1B2C3D4-0001-0001-0001-000000000001}
{F89FE768-35C5-439F-9725-1E54CB8C3009} = {A1B2C3D4-0001-0001-0001-000000000002}
EndGlobalSection
EndGlobal
95 changes: 95 additions & 0 deletions EnterpriseIntegrationPlatform/rules/completion-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,101 @@ Detailed record of completed chunks, files created/modified, and notes.

See `milestones.md` for current phase status and next chunk.

## Chunk 109 – Routing + Advanced EIP Patterns on Postgres

- **Date**: 2026-04-07
- **Phase**: 28 — PostgreSQL Message Broker (EIP-Complete, ≤ 5k TPS)
- **Status**: done
- **Goal**: Integration tests proving all EIP routing and advanced patterns work when wired to a real PostgreSQL message broker via PostgresBrokerEndpoint.
- **Architecture**:
- `PostgresRoutingIntegrationTests` (7 tests): ContentBasedRouter (MessageType + Metadata + Regex), MessageFilter, RecipientListRouter, DynamicRouter (register + route), Detour (activate/deactivate)
- `PostgresAdvancedEipTests` (7 tests): Splitter (split + causation chain), DeadLetterPublisher (single + multi-reason), Resequencer (out-of-order → in-order + publish), Retry + DLQ pipeline, Aggregator (count completion + concat), full pipeline (Splitter → Router → DLQ)
- All tests use unique topics (Guid-based) to prevent cross-test interference
- All tests Docker-gated: Assert.Ignore when Aspire Postgres container unavailable
- **Files created**:
- `tests/TutorialLabs/InfrastructureTests/PostgresRoutingIntegrationTests.cs` (7 tests)
- `tests/TutorialLabs/InfrastructureTests/PostgresAdvancedEipTests.cs` (7 tests)
- **Files modified**:
- `rules/milestones.md` — Phase 28 complete, no remaining chunks
- **Test counts**: 38 BrokerAgnosticTests pass. 14 new Postgres integration tests (Docker-gated). 49 src projects, 0 errors, 0 warnings.

## Chunk 108 – DI Wiring + Aspire Postgres Container + PostgresBrokerEndpoint

- **Date**: 2026-04-07
- **Phase**: 28 — PostgreSQL Message Broker (EIP-Complete, ≤ 5k TPS)
- **Status**: done
- **Goal**: Add Postgres container to Aspire TestAppHost, create PostgresBrokerEndpoint test helper (mirrors NatsBrokerEndpoint), add Postgres connectivity integration tests.
- **Architecture**:
- Postgres 17 container added to `tests/TestAppHost/Program.cs` with EIP database credentials
- `SharedTestAppHost.GetPostgresConnectionStringAsync()` returns connection string from Aspire endpoint
- `AspireFixture.PostgresConnectionString` exposed for all test fixtures
- `AspireFixture.CreatePostgresEndpoint(name)` factory method (mirrors CreateNatsEndpoint)
- `PostgresBrokerEndpoint` wraps real `PostgresBrokerProducer`/`PostgresBrokerConsumer` with MockEndpoint-compatible assertion API (AssertReceivedCount, AssertReceivedOnTopic, WaitForConsumedAsync, etc.)
- 3 new connectivity tests: publish+poll round-trip, producer capture assertions, event-driven subscribe+receive
- TutorialLabs csproj now references Ingestion.Postgres
- **Files created**:
- `tests/TutorialLabs/Infrastructure/PostgresBrokerEndpoint.cs`
- **Files modified**:
- `tests/TestAppHost/Program.cs` — Added Postgres 17 container
- `tests/TutorialLabs/Infrastructure/SharedNatsFixture.cs` — Added `GetPostgresConnectionStringAsync()`
- `tests/TutorialLabs/Infrastructure/AspireFixture.cs` — Added `PostgresConnectionString` property + `CreatePostgresEndpoint()`
- `tests/TutorialLabs/InfrastructureTests/ConnectivityTests.cs` — Added 3 Postgres connectivity tests
- `tests/TutorialLabs/TutorialLabs.csproj` — Added Ingestion.Postgres project reference
- **Test counts**: 38 BrokerAgnosticTests pass. 522+ TutorialLabs tests + 3 new Postgres connectivity tests (Docker-gated). 49 src projects, 0 errors, 0 warnings.

## Chunk 107 – Broker-Agnostic EIP Integration Tests (DLQ, Retry, Channels, Routing, Splitter, Aggregator)

- **Date**: 2026-04-07
- **Phase**: 28 — PostgreSQL Message Broker (EIP-Complete, ≤ 5k TPS)
- **Status**: done
- **Goal**: Create comprehensive integration tests proving all EIP components (DLQ, retry, routing, channels, splitter, aggregator) work identically with ANY broker via IMessageBrokerProducer/IMessageBrokerConsumer.
- **Architecture**:
- New `tests/BrokerAgnosticTests/` project: 38 NUnit tests across 5 test files
- All tests use MockEndpoint (IMessageBrokerProducer + IMessageBrokerConsumer) — swap for Postgres/NATS/Kafka/Pulsar and tests pass unchanged
- Covers: DeadLetterPublisher (routing, correlation, wrapping, multi-reason), ExponentialBackoffRetryPolicy (success, retry, exhaustion, backoff, cancellation), PointToPointChannel, PublishSubscribeChannel, DatatypeChannel, InvalidMessageChannel, MessagingBridge (forwarding + deduplication), ContentBasedRouter (message type, source, metadata, regex, priority), MessageFilter, Splitter (split + causation chain), Aggregator (group completion + separate groups)
- BrokerInterchangeabilityTests: full pipeline (Ingest→Route→Split), Route→DLQ, Channel→Route→Invalid, bridge between two brokers
- **Files created**:
- `tests/BrokerAgnosticTests/BrokerAgnosticTests.csproj`
- `tests/BrokerAgnosticTests/DeadLetterTests.cs` (5 tests)
- `tests/BrokerAgnosticTests/RetryPolicyTests.cs` (6 tests)
- `tests/BrokerAgnosticTests/ChannelTests.cs` (12 tests)
- `tests/BrokerAgnosticTests/RoutingTests.cs` (7 tests)
- `tests/BrokerAgnosticTests/SplitterAggregatorTests.cs` (5 tests)
- `tests/BrokerAgnosticTests/BrokerInterchangeabilityTests.cs` (5 tests — 3 full pipelines + enum check + bridge)
- **Files modified**:
- `EnterpriseIntegrationPlatform.sln` — Added BrokerAgnosticTests project
- **Test counts**: 38 BrokerAgnosticTests pass. 522 TutorialLabs tests unchanged. 49 src projects, 0 errors, 0 warnings.

## Chunks 103–106 – PostgreSQL Message Broker (Ingestion.Postgres)

- **Date**: 2026-04-07
- **Phase**: 28 — PostgreSQL Message Broker (EIP-Complete, ≤ 5k TPS)
- **Status**: done
- **Goal**: Add PostgreSQL as a fourth production message broker, implementing all EIP interfaces (IMessageBrokerProducer, IMessageBrokerConsumer, IEventDrivenConsumer, IPollingConsumer, ISelectiveConsumer, ITransactionalClient) so all existing EIP components work unchanged.
- **Architecture**:
- `BrokerType.Postgres = 3` added to enum
- `eip_messages` table with pg_notify trigger for low-latency delivery
- `eip_subscriptions` with `SELECT … FOR UPDATE SKIP LOCKED` for competing consumers
- `eip_dead_letters` table for DLQ
- `eip_durable_subscribers` with auto-fanout trigger
- Native ACID transactions via `NpgsqlTransaction`
- **Files created**:
- `src/Ingestion.Postgres/Ingestion.Postgres.csproj`
- `src/Ingestion.Postgres/PostgresBrokerProducer.cs`
- `src/Ingestion.Postgres/PostgresBrokerConsumer.cs`
- `src/Ingestion.Postgres/PostgresTransactionalClient.cs`
- `src/Ingestion.Postgres/PostgresConnectionFactory.cs`
- `src/Ingestion.Postgres/PostgresBrokerOptions.cs`
- `src/Ingestion.Postgres/PostgresServiceExtensions.cs`
- `src/Ingestion.Postgres/Schema/001_create_tables.sql`
- **Files modified**:
- `src/Ingestion/BrokerType.cs` — Added `Postgres = 3`
- `src/Ingestion/BrokerOptions.cs` — Added Postgres connection string doc
- `src/Ingestion/IngestionServiceExtensions.cs` — Registered Postgres in BrokerRegistrations
- `Directory.Packages.props` — Added `Npgsql 9.0.3`
- `EnterpriseIntegrationPlatform.sln` — Added Ingestion.Postgres project
- **Test counts**: 522 TutorialLabs tests unchanged. Full solution builds: 49 src projects, 0 errors, 0 warnings.

## Chunk 102 – Update tutorials/README.md

- **Date**: 2026-04-06
Expand Down
31 changes: 2 additions & 29 deletions EnterpriseIntegrationPlatform/rules/milestones.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,9 @@

## Completed Phases

✅ Phases 1–24 complete — see `rules/completion-log.md` for full history.
✅ Phases 1–28 complete — see `rules/completion-log.md` for full history.

48 src projects. All 50 tutorials rewritten with BizTalk-style Lab + Exam exercises focused on EIP patterns, scalability, and atomicity.

---

## Phase 27 — Coding Tutorial Labs & Exams

**Goal:** Convert all 50 tutorials from conceptual/MCQ format to coding-only format. Each tutorial gets:
- `tests/TutorialLabs/TutorialXX/Lab.cs` — Complete, runnable NUnit test class demonstrating the pattern
- `tests/TutorialLabs/TutorialXX/Exam.cs` — Coding exam challenges (NOT multiple choice)
- Updated tutorial `.md` file pointing to the implementation folder

**Project:** `tests/TutorialLabs/TutorialLabs.csproj` (added to solution, references all src projects)

**Key API findings for remaining chunks:**
- **DynamicRouter**: implements `IDynamicRouter` + `IRouterControlChannel`. Constructor: `IMessageBrokerProducer`, `IOptions<DynamicRouterOptions>`, `ILogger<DynamicRouter>`. Methods: `RegisterAsync()`, `UnregisterAsync()`, `RouteAsync<T>()`, `GetRoutingTable()`.
- **RecipientListRouter**: implements `IRecipientList`. Constructor: `IMessageBrokerProducer`, `IOptions<RecipientListOptions>`, `ILogger<RecipientListRouter>`. Uses `RecipientListRule` with `RoutingOperator`.
- **RoutingSlipRouter**: implements `IRoutingSlipRouter`. Constructor: `IEnumerable<IRoutingSlipStepHandler>`, `IMessageBrokerProducer`, `ILogger<RoutingSlipRouter>`. Handlers implement `IRoutingSlipStepHandler`.
- **Process Manager**: `PipelineOrchestrator` and `ITemporalWorkflowDispatcher` in `Demo.Pipeline`. Uses `IntegrationPipelineInput`/`IntegrationPipelineResult` from Activities.
- **MessageTranslator<TIn,TOut>**: takes `IPayloadTransform<TIn,TOut>`, `IMessageBrokerProducer`, `IOptions<TranslatorOptions>`, `ILogger`. `FuncPayloadTransform<TIn,TOut>` wraps a delegate.
- **Transform steps**: `JsonToXmlStep`, `XmlToJsonStep`, `RegexReplaceStep`, `JsonPathFilterStep`, `TransformPipeline`.

| Chunk | Scope | Status |
|-------|-------|--------|
✅ Phase 27 complete — see completion-log.md.

522 TutorialLabs tests (350 lab + 150 exam + 22 extra). All 50 tutorials updated with coding lab/exam pointers.

**Next chunk:** None — all chunks complete.
49 src projects. 522 TutorialLabs tests across 50 tutorials. 38 BrokerAgnosticTests. Broker providers: NATS JetStream, Kafka, Pulsar, **PostgreSQL**. All EIP routing patterns (ContentBasedRouter, DynamicRouter, RecipientListRouter, MessageFilter, Detour, Splitter, Aggregator, Resequencer, DLQ, Retry) verified on Postgres transport.

---

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Ingestion\Ingestion.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Npgsql" />
</ItemGroup>
<ItemGroup>
<None Include="Schema\**\*.sql" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>
Loading
Loading