docs: document matrix42 sandbox migration learnings
This commit is contained in:
@@ -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:
|
||||
<OutputType>Library</OutputType>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
||||
<AppendRuntimeIdentifierToOutputPath>true</AppendRuntimeIdentifierToOutputPath>
|
||||
<Nullable>disable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<M42ExtensionId>...</M42ExtensionId>
|
||||
<M42AssemblyPattern>C4ITF4SD*.*</M42AssemblyPattern>
|
||||
<M42AssemblyPattern>...</M42AssemblyPattern>
|
||||
<M42BuildPackage>true</M42BuildPackage>
|
||||
```
|
||||
|
||||
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/<Configuration>/<PackageName> v<Version>/
|
||||
artifacts/<Configuration>/<PackageName> v<Version>.zip
|
||||
<AssemblyName>.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/<Configuration>/`.
|
||||
- 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
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
@@ -86,31 +126,35 @@ Final bewaehrte Minimal-Konfig:
|
||||
</host>
|
||||
```
|
||||
|
||||
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
|
||||
<modules>
|
||||
<module assembly="C4ITF4SDM42WebApi" />
|
||||
<module assembly="Matrix42.DataLayer.Persistence" />
|
||||
<module assembly="Matrix42.Services.WebApi" />
|
||||
<module assembly="Matrix42.ServiceRepository.BizLogic" />
|
||||
<module assembly="Matrix42.Pandora.ServiceStore" />
|
||||
<module assembly="Matrix42.Pandora.Persistence" />
|
||||
<module assembly="Matrix42.Pandora.BizLogic" />
|
||||
<module assembly="Matrix42.Pandora.Services" />
|
||||
<module assembly="Matrix42.Distributed.Redis.Cache" />
|
||||
<module assembly="Matrix42.StorageService.BizLogic" />
|
||||
<module assembly="Matrix42.MsTeamsNotification.BizLogic" />
|
||||
<module assembly="Matrix42.ServiceConnection.Persistence" />
|
||||
<module assembly="Matrix42.Auth.BizLogic" />
|
||||
<module assembly="Matrix42.ServiceConnection.BizLogic" />
|
||||
<module assembly="Matrix42.Integration.Aurora.General" />
|
||||
<module assembly="Matrix42.Integration.Aurora.Search.BizLogic" />
|
||||
<module assembly="update4u.SPS.ComplianceRules.BizLogic" />
|
||||
<module assembly="update4u.SPS.Security" />
|
||||
<module assembly="Matrix42.BizLogic.Journal" />
|
||||
<module assembly="Matrix42.ServiceManager.BizLogic" />
|
||||
</modules>
|
||||
```
|
||||
|
||||
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<T>() where T : class
|
||||
@@ -141,16 +186,23 @@ private T GetRequiredService<T>() 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<EntityEnumeration>` oder `Task<object>`.
|
||||
- `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<HttpResponseMessage>
|
||||
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<List<T>>`, `Task<object>` oder `Task<EntityEnumeration>`.
|
||||
- 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<T>()` 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<string> getDirectLinkF4SD(Guid eoid, string type)
|
||||
```
|
||||
|
||||
Funktionierende Legacy-URL:
|
||||
|
||||
```text
|
||||
/m42Services/api/c4itf4sdwebapi/getdirectlinkf4sd/?eoid=<guid>&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 <Controller>: The current type, <Interface>, 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/<prefix>/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/<prefix>/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/<prefix>/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 <Controller>: The current type, <Interface>, 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 <name> 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.
|
||||
|
||||
Reference in New Issue
Block a user