diff --git a/src/api/routes/article.js b/src/api/routes/article.js index 39128e5..3059ff8 100644 --- a/src/api/routes/article.js +++ b/src/api/routes/article.js @@ -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}: diff --git a/src/db/article.js b/src/db/article.js index f91c410..80d72c9 100644 --- a/src/db/article.js +++ b/src/db/article.js @@ -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); diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..9dc7c27 --- /dev/null +++ b/todo.md @@ -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. \ No newline at end of file