# ED-01-002 – Transport Mapping

**Version:** 0.2  
**Status:** Ready for Implementation  
**Created:** 2026-07-26  
**Owner:** ED  
**Review:** LA  
**Approved by:** PL

## Übergeordnetes Arbeitspaket

[ED-01 – Context Mapping](ED-01_Context_Mapping.md)

## Vorgänger

[ED-01-001 – Domain Model](ED-01-001_Domain_Model.md)

## Nachfolger

[ED-01-003 – Application Integration](ED-01-003_Application_Integration.md)

---

## 1. Ziel

Dieser Arbeitsschritt erweitert das bestehende OpenRefine-Request-Mapping um eine klar definierte Abbildung der im optionalen `properties`-Array gelieferten Context Properties.

Die OpenRefine-Felder werden wie folgt auf die interne Transportrepräsentation abgebildet:

```text
properties[n].pid
    ↓
contextTypeUri

properties[n].v
    ↓
value
```

Am Ende von ED-01-002 liegen die Context Properties strukturell normalisiert im `ReconciliationCommand` vor.

Die Erzeugung von Domain-Objekten des Typs `ContextItem` und ihre Zuordnung zu `SourceValue` erfolgen erst in ED-01-003.

---

## 2. Ausgangssituation

OpenRefine überträgt zusätzliche Kontextinformationen im Reconciliation-Request über das optionale Feld `properties`.

Beispiel:

```json
{
  "query": "Bronze",
  "type": "http://matcult-the.vocnet.org/00000019",
  "properties": [
    {
      "pid": "https://reconcilix.vocnet.org/context/0001",
      "v": "<tei><id>1</id><title>Skulptur</title></tei>"
    }
  ]
}
```

`PRE-ED-01A` hat den standardkonformen Transport dieser Properties von OpenRefine zum Reconcilix-Endpunkt nachgewiesen.

`ED-01-001` hat das Domain-Objekt `ContextItem` eingeführt:

```text
ContextItem
-----------
contextTypeUri
value
```

Der aktuelle `OpenRefineRequestMapper` reicht `query.properties` noch als unstrukturiertes Array über `ReconciliationCommand.contextProperties` weiter.

ED-01-002 ersetzt diese rohe Weitergabe durch ein explizites, getestetes Mapping.

---

## 3. Implementierungsentscheidung

Für ED-01-002 wird bewusst **keine zusätzliche DTO-Klasse** eingeführt.

Der Mapper erzeugt eine geordnete Liste mit einer festen internen Struktur:

```php
/**
 * @var list<array{
 *     contextTypeUri: string,
 *     value: string
 * }>
 */
```

Beispiel:

```php
[
    [
        'contextTypeUri' => 'https://reconcilix.vocnet.org/context/0001',
        'value' => '<tei>...</tei>',
    ],
]
```

Diese Entscheidung folgt dem KISS-Prinzip:

- keine zusätzliche Klasse ausschließlich für einen Transport-Zwischenschritt,
- keine vorzeitige Domain-Erzeugung im Request Mapper,
- klare und statisch dokumentierbare Struktur,
- kompatibel mit dem bestehenden `ReconciliationCommand`.

Eine spätere Einführung eines dedizierten Input-DTOs bleibt möglich, falls weitere Transportquellen oder zusätzliche Context-Metadaten dies rechtfertigen.

---

## 4. Mapping-Regeln

Jeder gültige Eintrag des OpenRefine-`properties`-Arrays wird einzeln abgebildet.

### 4.1 Context Type

```text
properties[n].pid
    ↓
contextTypeUri
```

Der Wert wird als String übernommen.

Eine Prüfung gegen `VOC001_Context_Types` erfolgt nicht.

### 4.2 Context Value

```text
properties[n].v
    ↓
value
```

Der Wert wird als String übernommen und nicht fachlich interpretiert.

Insbesondere erfolgen keine:

- Normalisierung,
- XML-Verarbeitung,
- Datumsinterpretation,
- Koordinateninterpretation,
- Kürzung oder Bereinigung.

### 4.3 Reihenfolge

Die Reihenfolge der Einträge im OpenRefine-Request bleibt erhalten.

```text
properties[0] → contextProperties[0]
properties[1] → contextProperties[1]
properties[2] → contextProperties[2]
```

---

## 5. Validierungsstrategie

ED-01-002 führt keine neue globale Fehlerstrategie ein.

Die Verarbeitung orientiert sich am bestehenden Verhalten des `OpenRefineRequestMapper`:

- Ein vollständiger und ausführbarer Query-Eintrag wird in einen Command überführt.
- Fehlende optionale Context Properties verhindern die Verarbeitung des Queries nicht.
- Strukturell unbrauchbare einzelne Property-Einträge werden nicht in die interne Context-Liste übernommen.

Ein Property-Eintrag ist für das Mapping verwendbar, wenn:

- der Eintrag ein Array ist,
- `pid` vorhanden ist,
- `pid` als String nicht leer ist,
- `v` vorhanden ist.

`v` wird auf String abgebildet. Ein leerer String bleibt als Wert erhalten; die Domain-Invariante wird erst bei der Erzeugung von `ContextItem` in ED-01-003 wirksam.

Damit bleibt die Verantwortlichkeit getrennt:

```text
Transport Mapping
    → strukturelle Verwendbarkeit

Domain Model
    → Domain-Invarianten
```

---

## 6. Verhalten ohne Context Properties

Fehlt das Feld `properties`, ist es `null` oder kein Array, wird eine leere Context-Liste an den Command übergeben.

```text
contextProperties = []
```

Das bisherige Verhalten von Requests ohne Context bleibt unverändert.

Beispiel:

```json
{
  "query": "Bronze",
  "type": "http://matcult-the.vocnet.org/00000019"
}
```

Ergebnis:

```php
$command->contextProperties === [];
```

---

## 7. Verhalten bei einem Context Property

Eingabe:

```json
{
  "pid": "https://reconcilix.vocnet.org/context/0001",
  "v": "<tei>...</tei>"
}
```

Ergebnis:

```php
[
    [
        'contextTypeUri' => 'https://reconcilix.vocnet.org/context/0001',
        'value' => '<tei>...</tei>',
    ],
]
```

`pid` und `v` werden nicht mehr unter ihren OpenRefine-spezifischen Feldnamen in die Application-Schicht weitergereicht.

---

## 8. Verhalten bei mehreren Context Properties

Eingabe:

```json
{
  "query": "Kiel",
  "type": "PLACE",
  "properties": [
    {
      "pid": "https://reconcilix.vocnet.org/context/0003",
      "v": "Schleswig-Holstein"
    },
    {
      "pid": "https://reconcilix.vocnet.org/context/0004",
      "v": "54.3233,10.1228"
    },
    {
      "pid": "https://reconcilix.vocnet.org/context/0009",
      "v": "Landeshauptstadt an der Kieler Förde"
    }
  ]
}
```

Ergebnis:

```php
[
    [
        'contextTypeUri' => 'https://reconcilix.vocnet.org/context/0003',
        'value' => 'Schleswig-Holstein',
    ],
    [
        'contextTypeUri' => 'https://reconcilix.vocnet.org/context/0004',
        'value' => '54.3233,10.1228',
    ],
    [
        'contextTypeUri' => 'https://reconcilix.vocnet.org/context/0009',
        'value' => 'Landeshauptstadt an der Kieler Förde',
    ],
]
```

Alle verwendbaren Einträge werden vollständig und in stabiler Reihenfolge übernommen.

---

## 9. Betroffene Komponenten

### Zu ändern

```text
src/OpenRefine/OpenRefineRequestMapper.php
```

Der Mapper erhält eine private Mapping-Funktion für das `properties`-Array.

Sinngemäße Signatur:

```php
/**
 * @param mixed $properties
 * @return list<array{contextTypeUri: string, value: string}>
 */
private function mapContextProperties(mixed $properties): array
```

### Für Tests neu anzulegen oder zu erweitern

```text
tests/OpenRefine/OpenRefineRequestMapperTest.php
```

Falls im aktuellen Projektstand bereits ein geeigneter Mapper-Test besteht, wird dieser erweitert statt ein paralleles Testskript anzulegen.

### In ED-01-002 nicht zu ändern

- `ContextItem`
- `SourceValue`
- `ReconciliationApplicationService`
- Candidate Discovery
- Repositories
- Persistence Mapper
- Datenbankschema

---

## 10. Testfälle

Die Tests werden als eigenständig ausführbare PHP-Testskripte im bestehenden Reconcilix-Teststil umgesetzt.

### T001 – Request ohne `properties`

Erwartung:

```text
contextProperties = []
```

### T002 – Ein vollständiges Property

Erwartung:

- genau ein Context-Eintrag,
- `pid` wird vollständig als `contextTypeUri` übernommen,
- `v` wird vollständig als `value` übernommen.

### T003 – Mehrere Properties

Erwartung:

- alle verwendbaren Einträge werden übernommen,
- Reihenfolge bleibt stabil.

### T004 – XML als Context Value

Erwartung:

- XML bleibt ein unveränderter String,
- keine Interpretation oder Normalisierung.

### T005 – Freitext als Context Value

Erwartung:

- Freitext wird unverändert übernommen.

### T006 – Strukturell ungültige Einträge

Mindestens zu prüfen:

- Eintrag ist kein Array,
- `pid` fehlt,
- `pid` ist leer,
- `v` fehlt.

Erwartung:

- der jeweilige Eintrag wird nicht übernommen,
- übrige gültige Properties bleiben erhalten,
- der gesamte Query wird nicht allein wegen eines ungültigen Context-Eintrags verworfen.

### T007 – Rückwärtskompatibilität

Erwartung:

- bestehende Requests ohne Context funktionieren unverändert,
- bestehende OpenRefine- und Application-Tests bleiben erfolgreich.

---

## 11. Nicht Bestandteil

Nicht Bestandteil von ED-01-002 sind:

- Erzeugung von `ContextItem`-Domain-Objekten,
- Zuordnung der ContextItems zu `SourceValue`,
- fachliche Validierung gegen `VOC001_Context_Types`,
- Entity-Type-spezifische Zulässigkeitsprüfung,
- Persistierung,
- Candidate Discovery,
- Interpretation des Context Values,
- SourceDelivery- oder SourceSystem-Integration,
- Einführung eines neuen Testframeworks.

---

## 12. Abnahmekriterien

ED-01-002 ist abgeschlossen, wenn:

- `OpenRefineRequestMapper` das `properties`-Array explizit mappt,
- `pid` als `contextTypeUri` übernommen wird,
- `v` als `value` übernommen wird,
- OpenRefine-spezifische Feldnamen nicht ungeprüft in die Application-Schicht weitergereicht werden,
- Requests ohne `properties` eine leere Context-Liste erhalten,
- mehrere Context Properties vollständig und in stabiler Reihenfolge verarbeitet werden,
- strukturell unbrauchbare einzelne Einträge konsistent ignoriert werden,
- XML und Freitext unverändert transportiert werden,
- die definierten Tests im bestehenden Reconcilix-Teststil erfolgreich laufen,
- alle vorhandenen Tests weiterhin erfolgreich sind.

---

## 13. Ergebnis für ED-01-003

ED-01-003 erhält im `ReconciliationCommand` eine normalisierte Liste der Form:

```php
list<array{
    contextTypeUri: string,
    value: string
}>
```

Die Application-Schicht kann daraus ohne Kenntnis des OpenRefine-Formats die Domain-Objekte erzeugen:

```text
contextProperties[]
        ↓
ContextItem[]
        ↓
SourceValue.contextItems
```
