dojo: Plenty Cloud Tag-Manipulation + Silent-Deny-Diagnose
DOJO.md: - #22 (klassische variation_tags-Endpoints) ergänzt um Cloud-Update- Hinweis: in Plenty Cloud gesperrt, Verweis auf #34 - #33 NEU: Silent-Deny erkennen via Content-Type-Check (200+text/html als Erfolg melden ist die häufigste Lügen-Quelle bei Plenty-Schreib- Operationen) - #34 NEU: Variation-Tag-Manipulation in Plenty Cloud — Lese-Pfade, add-only PUT auf /rest/pim/variations für Hinzufügen, Bulk-DELETE /rest/pim/variations/tags für Entfernen; Plenty-Rollen-System hat keine Tag-Edit-Permission ANTI-PATTERNS.md: - #13 (Tag-Operationen idempotent) ergänzt um Cloud-Update-Hinweis - #19 NEU: Klassische /variation_tags-Endpoints in Plenty Cloud nutzen (Silent-Deny-Pattern, Verweis auf DOJO #34 als Fix)
This commit is contained in:
@@ -198,6 +198,8 @@ if (res.status !== 200) throw new Error('Tag konnte nicht gesetzt werden');
|
|||||||
|
|
||||||
**Fix:** Diese HTTP-Codes als Erfolg behandeln, nicht als Fehler.
|
**Fix:** Diese HTTP-Codes als Erfolg behandeln, nicht als Fehler.
|
||||||
|
|
||||||
|
> **⚠ Update 2026-05-01 — Plenty Cloud:** In Plenty Cloud kommen 409/404 für Tag-Operationen auf `/variation_tags` gar nicht erst — der Endpoint ist gesperrt und antwortet mit Silent-Deny (siehe #19). Das hier dokumentierte Idempotenz-Pattern bleibt für On-Premise-Installationen gültig.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 14. Neue Eigenschaften über den Merkmale-Endpoint setzen
|
## 14. Neue Eigenschaften über den Merkmale-Endpoint setzen
|
||||||
@@ -276,3 +278,33 @@ for (const item of items) {
|
|||||||
**Fix:** AIMD Rate Limiting: bei Erfolg schneller werden, bei 429 verdoppeln. Konvergiert automatisch zum Optimum. **Siehe DOJO.md #30**
|
**Fix:** AIMD Rate Limiting: bei Erfolg schneller werden, bei 429 verdoppeln. Konvergiert automatisch zum Optimum. **Siehe DOJO.md #30**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 19. Klassische `/variation_tags`-Endpoints in Plenty Cloud nutzen
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// FALSCH: in Plenty Cloud Silent-Deny — schweigend ignoriert
|
||||||
|
await client.delete(
|
||||||
|
`/rest/items/${itemId}/variations/${vid}/variation_tags/${tagId}`,
|
||||||
|
);
|
||||||
|
// → 200 OK + Content-Type: text/html
|
||||||
|
// → Tag bleibt aber an der Variation hängen
|
||||||
|
```
|
||||||
|
|
||||||
|
**Problem:** Plenty Cloud hat die klassischen Tag-Manipulations-Endpoints für REST-User komplett gesperrt. Plenty antwortet mit der HTML-Login-Page (`x-location: /index.php`), nicht mit 401/403. Naive Clients erkennen das als Erfolg und melden falsch — der Tag bleibt in der Realität aber dran.
|
||||||
|
|
||||||
|
Plenty's Rollen-System bietet **keine eigene Tag-Edit-Permission** — der Endpoint ist nicht über User-Rollen freischaltbar. Stundenlange Permission-Recherche endet im Sackgassen-Modus.
|
||||||
|
|
||||||
|
**Fix:** PIM-Bulk-Endpoint nutzen + Content-Type prüfen. **Siehe DOJO.md #34**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// RICHTIG
|
||||||
|
const resp = await client.delete('/rest/pim/variations/tags', {
|
||||||
|
data: [{ variationId, tagId }],
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
const ct = resp.headers?.['content-type']?.toLowerCase() ?? '';
|
||||||
|
if (!ct.includes('application/json')) throw new Error('Silent-Deny');
|
||||||
|
// + Verify-Read via /rest/pim/variations?with=tags.tag
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
@@ -523,6 +523,8 @@ DELETE /rest/items/{itemId}/variations/{variationId}/variation_tags/{tagId}
|
|||||||
- `404 Not Found` beim Löschen = Tag existiert nicht → ignorieren
|
- `404 Not Found` beim Löschen = Tag existiert nicht → ignorieren
|
||||||
- Beides sind erwartete Zustände, keine Fehler
|
- Beides sind erwartete Zustände, keine Fehler
|
||||||
|
|
||||||
|
> **⚠ Update 2026-05-01 — Plenty Cloud:** Diese klassischen Endpoints sind in Plenty Cloud für REST-User **gesperrt** (Silent-Deny mit 200 + text/html). Tag-Manipulation läuft dort über die PIM-API — siehe **#34**. Die hier dokumentierten Endpoints können in älteren On-Premise-Installationen weiterhin funktionieren, in der Cloud aber nicht.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# IX. Cycle Times & KPIs
|
# IX. Cycle Times & KPIs
|
||||||
@@ -903,3 +905,90 @@ await client.put(`/rest/properties/relations/${existing.id}`, {
|
|||||||
**Entdeckt:** 2026-04-10. Atradius-LimitMgmt. Wert nach PUT immer noch der alte.
|
**Entdeckt:** 2026-04-10. Atradius-LimitMgmt. Wert nach PUT immer noch der alte.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 33. Silent-Deny erkennen: Content-Type-Check bei Schreib-Operationen
|
||||||
|
|
||||||
|
**Lektion:** Plenty antwortet bei nicht-erlaubten Routen mit `200 OK + Content-Type: text/html + Header x-location: /index.php` (statt 401/403). Naive Clients, die nur den HTTP-Status prüfen, melden Erfolg, obwohl Plenty nichts geschrieben hat.
|
||||||
|
|
||||||
|
**Pattern:**
|
||||||
|
```javascript
|
||||||
|
const resp = await client.delete(url);
|
||||||
|
const ct = String(resp.headers?.['content-type'] || '').toLowerCase();
|
||||||
|
if (!ct.includes('application/json')) {
|
||||||
|
throw new Error(
|
||||||
|
'Plenty Silent-Deny: Antwort ohne JSON — vermutlich Permission-Problem ' +
|
||||||
|
'oder Endpoint nicht für den API-User freigegeben.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Anti-Pattern:** Nur `resp.status === 200` prüfen — Plenty lügt freundlich:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// FALSCH: 200 OK reicht nicht
|
||||||
|
await client.delete(url);
|
||||||
|
return true; // ← lügt: Plenty hat HTML geliefert, nichts geändert
|
||||||
|
```
|
||||||
|
|
||||||
|
**Diagnose-Tipps:**
|
||||||
|
- `x-location: /index.php` als zusätzliches Indiz, aber **nicht verlässlich** — manche legitime Endpoints senden den Header auch
|
||||||
|
- Body ist bei Silent-Deny meist leer
|
||||||
|
- Verify-Read nach jeder Schreib-Operation ist die robusteste Variante
|
||||||
|
|
||||||
|
**Entdeckt:** 2026-05-01 bei Tag-DELETE-Operationen, die monatelang Erfolg meldeten ohne Tags zu entfernen. Erst Content-Type-Check + Verify-Read deckte den Lügenmodus auf.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 34. Variation-Tag-Manipulation in Plenty Cloud: PIM-API + Bulk-DELETE-Endpoint
|
||||||
|
|
||||||
|
**Lektion:** Die klassischen Tag-Endpoints unter `/rest/items/{id}/variations/{vid}/variation_tags*` sind in Plenty Cloud für REST-User **komplett gesperrt** (Silent-Deny via #33). Der einzige funktionierende Schreib-Pfad führt über die PIM-API.
|
||||||
|
|
||||||
|
**Lese-Pfade** (beide funktionieren):
|
||||||
|
```
|
||||||
|
GET /rest/pim/variations?ids=in:{vid}&with=tags.tag
|
||||||
|
GET /rest/items/{itemId}/variations/{vid}?with=tags
|
||||||
|
```
|
||||||
|
|
||||||
|
Tag-Beziehungen kommen je nach Endpoint in unterschiedlichen Shapes — robuster Predicate:
|
||||||
|
```javascript
|
||||||
|
const matchesTag = (row, tagId) => {
|
||||||
|
const candidates = [row.tagId, row.id, row.tag?.id, row.tag?.tagId];
|
||||||
|
return candidates.some(v => Number(v) === tagId);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tag-Hinzufügen** — additive PUT auf der Variation (Plenty's eigenes Backend nutzt das):
|
||||||
|
```
|
||||||
|
PUT /rest/pim/variations?with=...&returnAffectedVariations=false
|
||||||
|
Body: [{ id, base: {...}, tags: [...altList, {tagId: neuId}] }]
|
||||||
|
```
|
||||||
|
Wichtig: das `tags`-Array beim PUT ist **add-only**. Tags entfernen via Weglassen oder leeres Array funktioniert NICHT — `_destroy: true`-Marker auch nicht.
|
||||||
|
|
||||||
|
**Tag-Entfernen** — separater Bulk-Endpoint:
|
||||||
|
```
|
||||||
|
DELETE /rest/pim/variations/tags
|
||||||
|
Content-Type: application/json
|
||||||
|
Body: [{ "variationId": <vid>, "tagId": <tid> }, ...]
|
||||||
|
← { "affectedRows": N }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pattern:**
|
||||||
|
```javascript
|
||||||
|
const resp = await client.delete('/rest/pim/variations/tags', {
|
||||||
|
data: items.map(i => ({ variationId: i.variationId, tagId: i.tagId })),
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
const ct = String(resp.headers?.['content-type'] || '').toLowerCase();
|
||||||
|
if (!ct.includes('application/json')) throw new Error('Plenty Silent-Deny');
|
||||||
|
// `affectedRows` kann sporadisch 0 sein, obwohl Tag wirklich weg ist
|
||||||
|
// → Verify-Read über GET ist verbindlich, nicht der affectedRows-Wert.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Permission-Hintergrund:**
|
||||||
|
- Plenty's Rollen-System hat **keine Tag-Edit-Permission** (`tagShow.SHOW` ist die einzige Tag-Permission im ganzen Pool, kein `.EDIT/.DELETE`)
|
||||||
|
- Der PIM-Bulk-Endpoint ist über die generische API-Rolle freigeschaltet, nicht route-spezifisch konfigurierbar
|
||||||
|
- Wer eine Permission-UI sucht, sucht vergeblich — der Endpoint ist eine Plenty-interne Whitelist
|
||||||
|
|
||||||
|
**Entdeckt:** 2026-05-01 nach mehrstündiger Recherche. Korrekter Endpoint gefunden via Browser-Network-Capture des Plenty-Backends; Plenty's eigenes UI nutzt genau diesen Bulk-Pfad.
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user