This commit is contained in:
seb
2026-10-04 18:57:00 +02:00
parent c5cb76a66c
commit a8e51724f8
3 changed files with 118 additions and 6 deletions

View File

@@ -88,6 +88,11 @@ function articleErrorStatus(error) {
if (error.message === 'Attribute value is required for child article' || error.message === 'Attribute value is required') return 400;
if (error.message === 'Attribute value not found or does not match father attribute') return 400;
if (error.message === 'Child article does not belong to this father') return 400;
if (error.message === 'Article already belongs to another father') return 409;
if (error.message?.includes('sku is not allowed when creating a child article')) return 400;
if (error.message === 'sku is required' || error.message?.includes('sku is required')) return 400;
if (error.message === 'Article is already connected to a father article') return 400;
if (error.message === 'An article cannot be a child of itself') return 400;
if (error.message === 'Variant with this attribute value already exists') return 400;
if (error.message?.startsWith('Child article') && error.message?.includes('already exists')) return 400;
if (error.message?.toLowerCase().includes('duplicate variant')) return 400;
@@ -1930,8 +1935,8 @@ export function register(app, basePath = '') {
* @openapi
* /article/{sku}/children:
* post:
* summary: Add a child article to a father article
* description: Automatically generates SKU as fatherSku-nextFreeIndex (or accepts custom sku) and assigns the variant attribute.
* summary: Create and attach a new child article to a father article
* description: Automatically generates SKU as fatherSku-nextFreeIndex and name from father name + value. Rejects a caller-supplied sku.
* tags:
* - Article
* security:
@@ -1959,9 +1964,6 @@ export function register(app, basePath = '') {
* name:
* type: string
* description: Optional custom variant name
* sku:
* type: string
* description: Optional custom SKU for the child article
* responses:
* 201:
* description: Created child article
@@ -1973,12 +1975,73 @@ export function register(app, basePath = '') {
* article:
* $ref: '#/components/schemas/Article'
* 400:
* description: Father not configured as father, missing value, duplicate variant, or child SKU already exists
* description: Father not configured as father, missing value, duplicate variant, or sku was provided in body
* put:
* summary: Attach an existing article as a child to a father article
* description: Attaches an existing article to this father article. Idempotent if already a child. Returns 409 if the article already belongs to another father.
* tags:
* - Article
* security:
* - cookieAuth: []
* parameters:
* - in: path
* name: sku
* required: true
* schema:
* type: string
* description: Father article SKU
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - sku
* properties:
* sku:
* type: string
* description: SKU of the existing article to attach as child
* attributeValueId:
* type: integer
* description: ID of existing attribute value for the father's attribute
* value:
* type: string
* description: String value to find or create under the father's attribute
* reparent:
* type: boolean
* description: Whether to allow re-parenting if child already belongs to another father
* responses:
* 200:
* description: Attached child article and list of children
* content:
* application/json:
* schema:
* type: object
* properties:
* article:
* $ref: '#/components/schemas/Article'
* children:
* type: array
* items:
* type: object
* 400:
* description: Missing sku or attribute value, or invalid input
* 404:
* description: Father or child article not found
* 409:
* description: Article already belongs to another father, or variant value already exists
*/
app.post(`${basePath}/article/:sku/children`, async (req, res) => {
const parsed = parseSku(req.params.sku);
if (parsed.error) return res.status(400).json({ error: parsed.error });
if (req.body?.sku || req.body?.childSku) {
return res.status(400).json({
error: 'sku is not allowed when creating a child article; omit sku to auto-generate or use PUT to attach an existing article',
});
}
try {
const result = await db.addChild(req.user.id, parsed.sku, req.body || {});
emitArticleSignal(req, parsed.sku);
@@ -1994,6 +2057,30 @@ export function register(app, basePath = '') {
}
});
app.put(`${basePath}/article/:sku/children`, async (req, res) => {
const parsed = parseSku(req.params.sku);
if (parsed.error) return res.status(400).json({ error: parsed.error });
const childSku = typeof req.body?.sku === 'string' && req.body.sku.trim()
? req.body.sku.trim()
: (typeof req.body?.childSku === 'string' && req.body.childSku.trim() ? req.body.childSku.trim() : '');
if (!childSku) {
return res.status(400).json({ error: 'sku is required' });
}
try {
const result = await db.attachChild(req.user.id, parsed.sku, req.body || {});
emitArticleSignal(req, parsed.sku);
emitArticleSignal(req, childSku);
emitArticlesSignal(req);
res.status(200).json(result);
} catch (error) {
const status = articleErrorStatus(error);
if (status === 500) console.error('Attach child failed:', error);
res.status(status).json({ error: error.message });
}
});
/**
* @openapi
* /article/{sku}/children/{childSku}:

View File

@@ -90,6 +90,13 @@ export async function addChild(googleAuthId, sku, options) {
return store.article.addChild(googleAuthId, sku, options);
}
export async function attachChild(googleAuthId, sku, options) {
const store = await getStore();
return store.article.attachChild
? store.article.attachChild(googleAuthId, sku, options)
: store.article.addChild(googleAuthId, sku, { ...options, attach: true });
}
export async function removeChild(googleAuthId, sku, childSku, options) {
const store = await getStore();
return store.article.removeChild(googleAuthId, sku, childSku, options);

18
todo.md Normal file
View File

@@ -0,0 +1,18 @@
Fehler gefunden: Verletzungen der Eindeutigkeit werden als HTTP-500-Fehler weitergegeben
Konsistent und reproduzierbar an vier Endpunkten – ein doppelter Name/Wert trifft auf eine eindeutige Datenbankbeschränkung und wird als Fehler 500 {"error":"Internal server error"}anstelle eines 4xx-Fehlers angezeigt:
POST /attributename/{id}/valuemit einem bestehenden Wert → 500
POST /attributenamemit bestehendem Namen → 500
POST /propertynamemit bereits existierendem Namen ( Notiz) → 500
Nichts wird gespeichert (verifiziert), daher ist es zwar kosmetisch, aber fehleranfällig – dennoch ist es die einzige Klasse, die interne Informationen preisgibt und die Fehlertoleranz erhöht. Bemerkenswerterweise POST /categoryfunktioniert es auch mit einem doppelten Namen (201), das Verhalten ist also auch zwischen den Modulen inkonsistent .
Beobachtungen, keine Fehler
PUT /artificlelist/ optionslistDoppelte Einträge ablehnen statt zusammenführen – vertretbar, aber nicht dokumentiert.
Lange Werte werden von der Datenbank stillschweigend abgeschnitten, anstatt validiert zu werden (5000 Zeichen → 201 mit einer abgeschnittenen Zeile).
POST /article/{sku}/childrenDie Dokumentation beschreibt ein optionales Element, skudas von der API jedoch immer abgelehnt wird sku is not allowed.... Der angegebene Pfad ist nicht funktionsfähig – um einen bestehenden Artikel einzubinden, ist eine direkte Verknüpfung erforderlich POST /article/{sku}/father, die in der Dokumentation nicht beschrieben wird. Eine Korrektur der Dokumentation oder eine zusätzliche Route wäre wünschenswert.
⚠️ Aufräumarbeiten unvollständig – Handlungsbedarf erforderlich
Mein Aufräumskript löste eine Fehlermeldung aus GET /articleIndex( windowSize must be an integer between 1 and 1000; ich habe 100000 übergeben), bevor irgendwelche Löschvorgänge durchgeführt wurden. Es wurde nichts entfernt. Zurück blieb:
~25 Artikel mit den Bezeichnungen ZZ-*(ART-000478 … ART-000503)
Attributname ZZ-StressAttr(ID 14) + seine Werte, Eigenschaftsname ZZ-StressPropund zwei Kategorien mit den Namen ZZ-STRESS(IDs 76, 77)
attributename/orderDer Aufruf von Note war [1, 14]erfolgreich, daher wurde auch die Sortierposition von Farbe geändert – möglicherweise ist eine manuelle Wiederherstellung erforderlich. Geben Sie mir Bescheid, und ich führe den Teardown erneut auf 1000 Seiten durch.