diff --git a/docs/matrix42-26-1-extension-migration.md b/docs/matrix42-26-1-extension-migration.md index a0a037e..0e771ea 100644 --- a/docs/matrix42-26-1-extension-migration.md +++ b/docs/matrix42-26-1-extension-migration.md @@ -1,20 +1,28 @@ -# Matrix42 26.1 Extension/Webservice Migration +# Matrix42 26.1 Legacy Extension Migration -Diese Notizen dokumentieren die Learnings aus der Migration der alten F4SD Matrix42 Extension inklusive Custom Web Service auf die Matrix42 26.1 Sandboxed Extension Struktur. +Diese Notizen dokumentieren die bisherigen Learnings aus der Migration einer alten Matrix42 Extension inklusive Custom Web Service auf eine Matrix42 26.1 Sandboxed Extension. -Ausgangspunkt der eigentlichen 26.1-Migration war Commit `6c6ac272cd43` (`feat: support Matrix42 26.1 sandboxed web api`, 2026-06-26 11:44 +0200). Die Punkte hier sind bewusst als Checkliste fuer weitere Extensions formuliert. +Die Anleitung ist bewusst als wiederverwendbare Checkliste fuer weitere Legacy-Pakete formuliert. Sie trennt zwischen belastbaren Ergebnissen, verworfenen Zwischenansaetzen und Punkten, die bei jeder Extension erneut geprueft werden muessen. ## Zielbild - Extension als .NET 8 Library mit `Matrix42.WebApi.Contracts`. - Webservice-Controller von `Matrix42.WebApi.Contracts.ApiController` ableiten. - Controller ueber `Matrix42.Hosting.Contracts.IDependencyResolver` konstruieren. -- Matrix42-Fachservices nicht direkt im Controller-Konstruktor injizieren. -- Host-Konfig so klein wie moeglich halten. -- Paketbau in den Visual-Studio-/MSBuild-Prozess integrieren. -- Runtime zuerst mit minimalen Endpoints pruefen (`isAlive`, einfache Datenabfrage), dann erst komplexere Services aktivieren. +- Matrix42-Fachservices nur dort lazy aufloesen, wo sie wirklich gebraucht werden. +- Host-Konfig mit minimalen Modulen starten und gezielt erweitern. +- Paketbau in MSBuild/Visual Studio integrieren. +- Paketversion aus `AssemblyVersion` ableiten. +- Signing nur fuer eine eigene signed Build-Konfiguration aktivieren. +- Postman-Collection als Regressionstest fuer alte Client-URLs pflegen. -## Projektstruktur und Paketbau +## Ausgangspunkt + +Bei einer Portierung eines alten Produktstands muss der Zielstand klar sein. Fuer diesen Branch war der relevante alte Stand `26eac54f94`, nicht der spaetere neue Codezweig. + +Wichtiges Learning: Bei einem 26.1-Kompatibilitaetsport keine neuen fachlichen Methoden, Obsolete-Markierungen oder Verhaltensaenderungen aus einem anderen Codezweig uebernehmen. Erst die alte Funktionalitaet lauffaehig machen, dann separate fachliche Aenderungen behandeln. + +## Basisprojekt Bewaehrte Projekt-Eigenschaften: @@ -23,24 +31,53 @@ Bewaehrte Projekt-Eigenschaften: Library win-x64;linux-x64 true +disable +disable +false false ... -C4ITF4SD*.* +... true ``` -Das Package wird ueber `M42SandboxedExtension.targets` gebaut. Die Paketversion wird automatisch aus der `AssemblyVersion` der gebauten WebApi-Assembly gelesen. In diesem Repo wird diese zentral in `SharedAssemblyInfo.cs` gepflegt. +Referenzen auf Matrix42-DLLs sollen gegen die 26.1 Libraries zeigen und normalerweise `Private=false` haben, damit keine unnoetigen Matrix42-Plattform-DLLs ins Paket kopiert werden. -Ein erfolgreicher Release-Build erzeugt: +Die Host-Konfig muss exakt zum Assembly-Namen passen: ```text -artifacts// v/ -artifacts// v.zip +.dll.host.config ``` -Builds signieren die DLLs nicht automatisch. Signieren ist optional und muss explizit aktiviert werden. Wenn `M42SignAssemblies=true` gesetzt ist, werden die DLLs im Package vor dem Package-Build per Authenticode signiert. +## Paketbau -Konfigurierbare MSBuild-Properties: +Das Paket wird ueber `M42SandboxedExtension.targets` gebaut. + +Aktuelle Regeln in diesem Repo: + +- `Debug` baut Debug-DLLs und ein Debug-Paket. +- `Release` baut ein unsigniertes Release-Paket. +- `Release_signed` baut ein Release-Paket und aktiviert `M42SignAssemblies=true`. +- Artefakte liegen getrennt nach Konfiguration unter `artifacts//`. +- Die Package-Version wird aus der `AssemblyVersion` der gebauten WebApi-Assembly gelesen. +- `M42PackageVersion` wurde entfernt und soll nicht wieder eingefuehrt werden. + +Die zentrale Version liegt in `SharedAssemblyInfo.cs`. + +Beispiele: + +```powershell +dotnet build "C:\Workspace\C4IT FASD\F4SD_M42WebApi\C4IT - F4SD - M42WebApi.sln" -c Debug +dotnet build "C:\Workspace\C4IT FASD\F4SD_M42WebApi\C4IT - F4SD - M42WebApi.sln" -c Release +dotnet build "C:\Workspace\C4IT FASD\F4SD_M42WebApi\C4IT - F4SD - M42WebApi.sln" -c Release_signed +``` + +Visual Studio: + +- `Debug`: Debugging und Testpakete. +- `Release`: Release ohne Signierung. +- `Release_signed`: Release mit Signierung. + +Signierbare MSBuild-Properties: ```powershell /p:M42SignAssemblies=true @@ -52,29 +89,32 @@ Konfigurierbare MSBuild-Properties: /p:M42SignOptions="/a" ``` -Build-Befehl: +## Package-Struktur -```powershell -dotnet build "C:\Workspace\C4IT FASD\F4SD_M42WebApi\C4IT - F4SD - M42WebApi.sln" -c Release -``` +Das neue Paket basiert auf einem `PackageTemplate` mit: -Signierter Release-Build: +- `package.json` +- `install.xml` +- `install/...` +- `BasePackage/Assemblies` als Build-Ziel fuer Assemblies -```powershell -dotnet build "C:\Workspace\C4IT FASD\F4SD_M42WebApi\C4IT - F4SD - M42WebApi.sln" -c Release_signed -``` +Beim Portieren eines Legacy-Pakets: -In Visual Studio kann oben in der Konfiguration `Release` fuer einen unsignierten Build oder `Release_signed` fuer einen signierten Build gewaehlt werden. +1. Altes Paket entpacken. +2. WebAPI-Service (`PLSLServiceTypeWebAPI`) und Operationen (`PLSLWebServiceOperation`) identifizieren. +3. Extension-/Assembly-ID beibehalten, wenn ein Update statt einer Neuinstallation gewollt ist. +4. Operation-IDs nur dann aendern, wenn bewusst neue Operationen entstehen sollen. +5. Obsolete oder entfernte Schema-Attribute aus 26.1 entfernen. -## Host-Konfig +Konkretes 26.1-Learning: -Die Host-Konfig muss exakt zum Assembly-Namen passen: +- `UsedInTypeSPSActivityTypeAlert` gibt es in 26.1 nicht mehr und muss aus dem Paket entfernt werden. -```text -C4ITF4SDM42WebApi.dll.host.config -``` +## Host-Konfig und Module -Final bewaehrte Minimal-Konfig: +Mit so wenig Modulen wie moeglich starten. Jedes zusaetzliche Modul kann transitive Unity-Registrierungen erzwingen, die bereits beim Controller- oder Service-Aufbau scheitern. + +Minimaler Startpunkt fuer einfache Endpoints: ```xml @@ -86,31 +126,35 @@ Final bewaehrte Minimal-Konfig: ``` -Wichtig: Module nicht auf Vorrat laden. Jedes zusaetzliche Modul kann transitive Unity-Registrierungen erzwingen, die fuer den konkreten Webservice gar nicht benoetigt werden. +Der aktuelle Stand dieser Extension benoetigt fuer alle Funktionen mehr Module, insbesondere wegen Journal, Storage, Pandora, Teams Notification, ServiceConnection, Auth und Aurora Search. Aktuelle Host-Konfig: -Module duerfen nicht auf Vorrat geladen werden. Fuer Endpunkte, die Matrix42-Services nutzen, muessen die transitiven Abhaengigkeiten aber vollstaendig als Host-Module registriert werden. Fuer den finalen `IJournalService`-Pfad wurden unter anderem folgende Module benoetigt: - -- `Matrix42.Pandora.Persistence` -- `Matrix42.Pandora.ServiceStore` -- `Matrix42.Pandora.BizLogic` -- `Matrix42.Pandora.Services` -- `Matrix42.MsTeamsNotification.BizLogic` -- `Matrix42.Integration.Aurora.General` -- `Matrix42.Integration.Aurora.Search.BizLogic` -- `Matrix42.ServiceManager.BizLogic` -- eigenes Webservice-Assembly als Host-Modul - -Waehrend der Migration zeigten fehlende Module oder fehlende Sandbox-Host-Registrierungen unter anderem folgende Fehler: - -```text -IEntityDataService is an interface and cannot be constructed -IUserProfileRepository is an interface and cannot be constructed -IApiExplorer is an interface and cannot be constructed -IComplianceRuleManager is an interface and cannot be constructed -IRequestPropertiesProvider is an interface and cannot be constructed -IExternalSearchService is an interface and cannot be constructed +```xml + + + + + + + + + + + + + + + + + + + + + + ``` +Regel: Wenn ein weiteres Interface nicht aufgeloest werden kann, nicht raten. In den Matrix42-DLLs nach dem Registrator/der Implementierung fuer genau dieses Interface suchen und nur das passende Modul laden. + ## Controller-Konstruktion Nicht so: @@ -119,16 +163,17 @@ Nicht so: public MyController(IEntityDataService entityDataService, IJournalService journalService, IPandoraUserProfile userProfile) ``` -Diese direkte Konstruktor-Injection zwingt Matrix42/Unity, alle Services schon beim Erzeugen des Controllers aufzuloesen. Dadurch kann bereits `isAlive` scheitern, obwohl der Endpoint selbst keinen dieser Services nutzt. +Das zwingt Unity, alle Services schon beim Erzeugen des Controllers aufzuloesen. Dadurch kann bereits `isAlive` scheitern, obwohl der Endpoint selbst keinen dieser Services braucht. -Besser: +Bewaehrt: ```csharp private readonly IDependencyResolver _resolver; -public MyController(IDependencyResolver resolver) +public MyController(IDependencyResolver resolver, IEnumerationProvider enumerationProvider) { _resolver = resolver ?? throw new ArgumentNullException(nameof(resolver)); + _enumerationProvider = enumerationProvider ?? throw new ArgumentNullException(nameof(enumerationProvider)); } private T GetRequiredService() where T : class @@ -141,16 +186,23 @@ private T GetRequiredService() where T : class } ``` -Fachservices dann nur im jeweiligen Endpoint oder Helper lazy aufloesen. +Fachservices im jeweiligen Endpoint oder Helper lazy aufloesen. + +## Dependency-Registrator + +Die Extension kann eigene Sandbox-Registrierungen bereitstellen, wenn der Matrix42-Sandbox-Host eine Plattform-Instanz nicht selbst registriert. + +Konkretes Learning: + +- Fuer den Journal-Pfad fehlte `System.Web.Http.Description.IApiExplorer`. +- Die Extension registriert eine konkrete `ApiExplorer`-Instanz ueber `IDependencyRegistrator`. +- Das eigene Assembly muss dafuer als Host-Modul geladen werden. + +Das ist kein Workaround im fachlichen Code, sondern eine Host-Registrierung fuer eine fehlende Infrastruktur-Dependency. ## WebAPI-Rueckgaben -Matrix42 serialisiert manche Rueckgabetypen anders als klassische ASP.NET Web API. - -Bewaehrt: - -- Datenendpoints geben direkt Nutzdaten zurueck, z. B. `Task` oder `Task`. -- `isAlive` gibt `IHttpActionResult` mit `StatusCodeResult(HttpStatusCode.NoContent)` zurueck. +Matrix42 serialisiert `HttpResponseMessage` in dieser Runtime nicht wie klassische ASP.NET Web API, sondern als Nutzdatenobjekt. Nicht fuer normale Datenendpoints verwenden: @@ -159,21 +211,19 @@ Task HttpResponseExtensions.CreateResponse(...) ``` -In dieser Runtime wurde ein `HttpResponseMessage` als JSON-Objekt serialisiert, z. B. mit `Version`, `Content`, `StatusCode`, `Headers`. - -Nicht fuer `isAlive` verwenden: - -```csharp -public void isAlive() -``` - -Das fuehrte zu: +Symptom: ```json -{"result":null,"parameters":{}} +{"Version":"1.1","Content":{...},"StatusCode":200} ``` -Bewaehrter Healthcheck: +Bewaehrt: + +- Datenendpoints geben direkte Nutzdaten zurueck, z. B. `Task>`, `Task` oder `Task`. +- Leere Listen duerfen nicht `null` sein. +- `isAlive` gibt `IHttpActionResult` mit `StatusCodeResult(HttpStatusCode.NoContent)` zurueck. + +Healthcheck: ```csharp [Route("isAlive"), HttpGet] @@ -183,32 +233,94 @@ public IHttpActionResult isAlive() } ``` -## Pickups/Enumerations +Nicht verwenden: -`IEntityDataService.GetEnumeration(...)` ist bequem, zieht aber die Pandora-Service-Schicht herein. Das war in der Sandbox zu schwer und verursachte transitive Dependency-Probleme. +```csharp +public void isAlive() +``` -Bewaehrt ist: +Symptom: + +```json +{"result":null,"parameters":{}} +``` + +## Null-Rueckgaben + +Matrix42/Pandora behandelt `null` als fehlerhafte Operation-Response. + +Symptom: + +```text +Method getTicketList returned null +``` + +Regel: + +- Listenendpoints geben bei "nichts gefunden" `new List()` zurueck. +- Objekte geben ein leeres DTO oder 204 zurueck, aber nicht unbewusst `null`. +- Strings koennen bei Bedarf `string.Empty` statt `null` liefern. + +## Routen und Parameterbindung + +Wichtiges spaetes Learning: Der Matrix42 `m42Services`-Gateway kann Query-Parameter auch dann an die Methode binden, wenn die Controller-Route path-basierte Platzhalter hat. + +Beispiel im Code: + +```csharp +[Route("getDirectLinkF4SD/{eoid}/{type}"), HttpGet] +public async Task getDirectLinkF4SD(Guid eoid, string type) +``` + +Funktionierende Legacy-URL: + +```text +/m42Services/api/c4itf4sdwebapi/getdirectlinkf4sd/?eoid=&type=user +``` + +Daraus folgt fuer diese Migration: + +- Controller nicht doppeln. +- Keine Query-Helper einbauen. +- Kein `[FromUri]` als Rettungsversuch verwenden, wenn das Matrix42-Binding ohnehin funktioniert. +- Die Postman-Collection testet bewusst die alten Query-Routen, damit Client-Kompatibilitaet erhalten bleibt. +- `PLSLWebServiceOperation.RouteTemplate` darf path-basiert sein, die alten Query-Aufrufe muessen trotzdem getestet werden. + +Betroffene alte Testformate: + +```text +getTicketList?sid=...&hours=...&queueoption=...&queues=... +getDirectLinkCreateTicket?sid=...&assetname=... +getDirectLinkF4SD?eoid=...&type=... +``` + +## Pickups und Enumerations + +`IEntityDataService.GetEnumeration(...)` ist bequem, zieht aber Pandora-Services herein. Das war fuer reine Pickups zu schwer. + +Bewaehrt: ```csharp Matrix42.Persistence.Contracts.IEnumerationProvider ``` -Dieser Service wird ueber `Matrix42.DataLayer.Persistence` bereitgestellt und liefert eine `DataTable`. Wenn der alte API-Vertrag `EntityEnumeration` bleiben soll, muss die `DataTable` selbst gemappt werden. +Dieser Service kommt ueber `Matrix42.DataLayer.Persistence` und liefert eine `DataTable`. Wenn der alte API-Vertrag `EntityEnumeration` bleiben soll, muss die `DataTable` selbst gemappt werden. Gepruefte Alternativen: - `Matrix42.Pandora.Contracts.IEntityDataService`: liefert direkt `EntityEnumeration`, benoetigt aber Pandora-Module. -- `update4u.SPS.DataLayer.Contracts.IPickupDataService`: liefert ebenfalls nur `DataTable`, bringt gegenueber `IEnumerationProvider` keinen klaren Vorteil. +- `update4u.SPS.DataLayer.Contracts.IPickupDataService`: liefert ebenfalls `DataTable`, ohne klaren Vorteil gegenueber `IEnumerationProvider`. -Pragmatische Empfehlung: +Empfehlung: - Fuer reine Pickup-Lesefunktionen `IEnumerationProvider` verwenden. -- Nur dann Pandora-Services laden, wenn es keine schlanke Alternative gibt. -- Route-Parameter defensiv behandeln. In dieser Migration kam der Pickup-Name einmal leer an, obwohl die URL ihn enthielt. Ein Fallback auf das letzte URL-Segment hat das stabilisiert. +- Pandora nur laden, wenn ein Endpoint es wirklich benoetigt. ## Aktueller Benutzer -`IPandoraUserProfile` nicht im Controller injizieren. Fuer `getMyRoleMemberships` reicht der Matrix42 Principal: +`IPandoraUserProfile` nicht im Controller injizieren. + +Fuer `getMyRoleMemberships` reicht der Matrix42 Principal: ```csharp var principal = Thread.CurrentPrincipal as IM42Principal; @@ -219,52 +331,81 @@ var userId = principal?.InteractivePrincipal?.M42Identity?.UserFragmentID Danach kann der vorhandene ASQL-/Helper-Code mit der `Guid` weiterarbeiten. -## Journal-Service +## Ticket-Historie und Journal -`IJournalService` ist der bevorzugte Pfad fuer die Ticket-Historie. In 26.1 haengt der konkrete `JournalService` transitiv an Teams Notification, Pandora Services und Aurora Search. Diese Abhaengigkeiten muessen im Host vollstaendig geladen werden. +Ziel: den Matrix42-Service nutzen, nicht eigene Journal-Logik nachbauen. -Status: +Finaler Ansatz: -- Ticket-Historie laeuft ueber `IJournalService`. -- Der Aufruf erfolgt per Reflection gegen die zur Laufzeit geladene `GetJournalList`-Signatur, damit 26.1 Patch-Level-Unterschiede keine direkte MethodRef brechen. -- Keine manuellen `JournalService`-Konstruktionen, Proxies, `JournalManager`-Fallbacks oder HTTP-Forwards verwenden. -- Wenn Unity ein weiteres Interface nicht aufloesen kann, zuerst in den Matrix42-DLLs nach einem `IDependencyRegistrator` fuer genau dieses Interface suchen und das zugehoerige BizLogic-/Services-Modul laden. +- `IJournalService` ueber den Matrix42-Resolver laden. +- Keine direkte Konstruktion von `JournalService`. +- Keine eigenen DispatchProxy-/NoOp-Services. +- Keine HTTP-Weiterleitung an interne Admin-APIs. +- Keine Storage-Fallback-Journal-Implementierung als finaler Pfad. +- `GetJournalList` per Reflection gegen die zur Laufzeit vorhandene Signatur aufrufen, weil sich die Signatur zwischen 26.1 Patch-Leveln unterscheiden kann. -## Typische Fehlersymptome und Ursache - -```text -Cannot create instance of : The current type, , is an interface and cannot be constructed. -``` - -Meistens direkte Konstruktor-Injection oder ein zu breit geladenes Host-Modul. - -```text -Could not load type 'System.Web.Routing.RouteTable' -``` - -Ein ungeeignetes altes `System.Web`-/WebApi-Modul wurde in der Sandbox aktiviert. Ursache entfernen, statt weitere Abhaengigkeiten darum herum zu laden. +Warum Reflection hier akzeptiert wurde: + +- Der alte direkte Compile-Time-MethodRef brach mit `MissingMethodException`. +- Die geladene Runtime-DLL hatte eine andere `IJournalService.GetJournalList`-Signatur. +- Die Reflection sucht kompatible Journal-Reader-Methoden und liefert klare Diagnose, wenn keine passende Signatur existiert. + +Typische Fehler auf dem Weg: ```text +Method not found: IJournalService.GetJournalList(...) +IJournalService could not be resolved +IExternalAuthService is an interface and cannot be constructed +IOAuth2AuthorizeService is an interface and cannot be constructed IApiExplorer is an interface and cannot be constructed +IRequestPropertiesProvider is an interface and cannot be constructed +IExternalSearchService is an interface and cannot be constructed ``` -Der Matrix42-Sandbox-Host stellt nicht automatisch dieselbe WebApi-Explorer-Instanz wie der normale WebApi-Host bereit. Fuer den finalen Journal-Pfad registriert die Extension eine konkrete `System.Web.Http.Description.IApiExplorer`-Instanz per eigenem `IDependencyRegistrator`. +Learning: Bei diesen Fehlern nicht weiter im Endpoint herumprobieren, sondern die fehlende Host-Registrierung bzw. das passende Matrix42-Modul identifizieren. -```json -{"Version":"1.1","Content":{...},"StatusCode":200} +## Overview-Endpoints + +`getTicketOverviewCounts` liefert Counts fuer mehrere Keys. + +`getTicketOverviewRelations` nimmt genau einen `key` und ein `count`. + +Postman-Learning: + +- `getTicketOverviewCounts personal` und `getTicketOverviewCounts role` leiten `scope`, `overviewKey` und `overviewRelationExpectedCount` ab. +- `getTicketOverviewRelations` nutzt diese Variablen. +- Der Relations-Test erwartet, dass `response.length` dem vorher abgeleiteten Count entspricht. +- Im normalen Collection-Lauf ueberschreibt `role` die Werte von `personal`, weil `role` nach `personal` ausgefuehrt wird. + +## Direct Link + +Fuer F4SD Direct Links ist der Typ in der alten Integration nicht der Matrix42 ActivityType, sondern der F4SD-Typ. + +Postman setzt deshalb nach `getTicketList`: + +```javascript +pm.collectionVariables.set("type", "ticket"); ``` -Ein `HttpResponseMessage` wurde als Nutzdatenobjekt serialisiert. Action-Signatur auf direkten Nutzdatentyp umstellen. +`objectType` bleibt separat auf `ticket.ActivityType`, falls der technische Matrix42-Typ fuer andere Tests gebraucht wird. -```json -{"result":null,"parameters":{}} -``` +## Postman-Collection -Ein `void`-Endpoint wurde als Matrix42-Result ohne Nutzdaten serialisiert. Fuer echten NoContent `StatusCodeResult(HttpStatusCode.NoContent)` verwenden. +Die Collection ist Teil der Migration und Regression. + +Regeln: + +- Basic Auth ueber Variablen `basicUsername` und `basicPassword`. +- `baseUrl` enthaelt `/m42Services`. +- Legacy-Query-Routen testen, nicht nur neue path-basierte Formen. +- `getTicketList` zieht `eoid` und `ticketObjectId` aus `TicketObjectId`. +- `type` fuer Direct Link bleibt `ticket`. +- `isAlive` erwartet 204 und keinen Body. +- Allgemeiner Test erkennt versehentlich serialisierte `HttpResponseMessage`-Objekte. ## Smoke-Test-Reihenfolge -Nach jeder Paketinstallation in Matrix42: +Nach jeder Paketinstallation: 1. `GET /m42Services/api//isalive` Erwartung: HTTP 204 No Content. @@ -272,26 +413,89 @@ Nach jeder Paketinstallation in Matrix42: Erwartung: JSON-Nutzdaten, keine `HttpResponseMessage`-Huelle. 3. `GET /m42Services/api//getMyRoleMemberships` Erwartung: aktueller Benutzer und Rollen, kein `IPandoraUserProfile`-Fehler. -4. Komplexere Ticket-/Overview-Endpoints. -5. Journal-/History-Endpoints separat pruefen, weil hier zusaetzliche Services benoetigt werden koennen. +4. `GET /m42Services/api//getTicketList?...` + Erwartung: leere Liste oder Tickets, aber kein `returned null`. +5. Direct Links mit Legacy-Query-Routen. +6. Overview Counts und Relations. +7. Journal-/History-Endpoints zuletzt, weil hier die meisten Matrix42-Services transitiv benoetigt werden. + +## Typische Fehlersymptome + +```text +Cannot create instance of : The current type, , is an interface and cannot be constructed. +``` + +Meist direkte Konstruktor-Injection oder fehlendes Host-Modul. + +```text +Could not load type 'System.Web.Routing.RouteTable' +``` + +Ungeeignetes altes `System.Web`-/WebApi-Modul in der Sandbox. Ursache entfernen, nicht weitere Alt-Abhaengigkeiten nachladen. + +```text +IApiExplorer is an interface and cannot be constructed +``` + +Sandbox-Host stellt diese WebApi-Explorer-Instanz nicht automatisch bereit. Ueber eigenen `IDependencyRegistrator` registrieren. + +```json +{"Version":"1.1","Content":{...},"StatusCode":200} +``` + +Ein `HttpResponseMessage` wurde als DTO serialisiert. Rueckgabe auf direkten Nutzdatentyp umstellen. + +```json +{"result":null,"parameters":{}} +``` + +Void-Endpoint oder falsch behandelte leere Rueckgabe. Fuer NoContent `StatusCodeResult(HttpStatusCode.NoContent)` nutzen. + +```text +Method returned null +``` + +Endpoint gibt `null` zurueck. Leeres DTO oder leere Liste zurueckgeben. + +## Verworfene Zwischenansaetze + +Diese Ansaetze haben nicht nachhaltig geholfen und sollen nicht erneut aufgebaut werden: + +- Matrix42-Fachinterfaces direkt im Controller-Konstruktor injizieren. +- Immer mehr Module auf Vorrat laden, ohne die konkrete fehlende Registrierung zu kennen. +- `HttpResponseMessage` als Datenantwort verwenden. +- Query-Parameter per eigenem Helper aus `RequestUri` lesen. +- `[FromUri]` pauschal an alle GET-Parameter setzen. +- `JournalService` direkt konstruieren. +- NoOp-/Proxy-Abhaengigkeiten fuer Journal-Services erzeugen. +- Journal-Historie aus unvollstaendigen Kommentarfeldern nachbauen. +- Interne Matrix42 Admin-Webservices als finalen Journal-Pfad weiterleiten. ## Migrationsablauf fuer weitere Extensions -1. Alten Package-Inhalt entpacken und Assembly-/Extension-ID identifizieren. -2. Neues 26.1-Projekt ueber Scaffolder/Beispielstruktur anlegen oder bestehendes Projekt angleichen. -3. `Matrix42.WebApi.Contracts` und `Matrix42.Hosting.Contracts` referenzieren. -4. Controller auf `IDependencyResolver` umstellen. -5. Direkte Konstruktor-Injection von Matrix42-Fachinterfaces entfernen. -6. Host-Konfig mit minimalen Modulen starten. -7. Rueckgaben pruefen: direkte Nutzdaten statt `HttpResponseMessage`, `StatusCodeResult` fuer 204. -8. Package-Build ueber MSBuild automatisieren. -9. Paket installieren und Smoke-Tests ausfuehren. -10. Fehlende Services einzeln und begruendet nachladen, nie ganze Pandora-/ServiceManager-Stacks auf Vorrat. +1. Ziel-Commit der Legacy-Funktionalitaet festlegen. +2. Altes Paket entpacken und IDs dokumentieren. +3. Herstellerbeispiel/Scaffolder-Struktur als 26.1-Referenz verwenden. +4. Projekt auf .NET 8 und `Matrix42.WebApi.Contracts.ApiController` umstellen. +5. Controller-Konstruktor auf minimale Infrastruktur-Services reduzieren. +6. Host-Konfig minimal starten. +7. Package-Build in MSBuild integrieren. +8. AssemblyVersion als einzige Package-Versionsquelle verwenden. +9. WebAPI-Rueckgaben auf direkte DTOs/Listen umstellen. +10. `isAlive` auf echten 204 umstellen. +11. Pickups zuerst ueber schlanke Services loesen. +12. Komplexe Matrix42-Services erst bei Bedarf laden. +13. Bei Unity-Fehlern konkrete Implementierung/Registrator suchen. +14. Postman-Collection fuer alte Client-Routen aufbauen. +15. Paket installieren, Service synchronisieren und Smoke-Tests ausfuehren. +16. Erst nach erfolgreicher Kompatibilitaet fachliche Weiterentwicklungen einbringen. -## Git-/Build-Hinweise in diesem Repo +## Git- und Datei-Hinweise - Vor Aenderungen `git status` pruefen. -- Geaenderte Textdateien mit CRLF speichern. -- Nach Aenderungen Release-Build ausfuehren. -- Danach committen und pushen. - +- Unrelated/untracked Dateien nicht mitcommitten. +- Textdateien in diesem Repo mit CRLF speichern. +- `.dat`-Dateien behalten ihren UTF-8-BOM. +- Controller-/JSON-Dateien ohne unnoetigen BOM lassen. +- Debug-Build fuer Debugging, Release/Release_signed fuer Auslieferung. +- Postman-Collection nach JSON-Aenderungen immer parsen/validieren.