Problem
/wxycdb's Manage Labels screen (companyAdmin, linked from webapps/wxycdb/src/main/webapp/jsp/mainmenu.jsp:36) is a full create / read / update / delete / merge surface over tubafrenzy's COMPANY table. Backend-Service serves a fraction of it. apps/backend/routes/labels.route.ts mounts exactly four operations — GET /labels, GET /labels/info, GET /labels/search, POST /labels — of which only the POST writes, and it writes two fields (labels.controller.ts:27-33, taking label_name and an optional parent_label_id).
Today the only writer is dj-site's release-creation mutation, which upserts a label name. Once /wxycdb retires there is no surface anywhere that can rename a mistyped label, merge the several spellings of one imprint, re-parent a subsidiary, or delete a row created by accident — while the create path keeps adding rows to the same table. Write-without-repair is the failure mode.
The gaps, precisely
- No update. No
PATCH/PUT is mounted. A label name is immutable once created.
- No delete. No
DELETE is mounted. /wxycdb offers delete only when the label has zero releases and zero child labels (jsp/labelAdmin/labelModify.jsp:105, and the childCount guard in CompanyAdminServlet); Backend's equivalent guard has three reference sources rather than one — see item 5.
- No merge.
/wxycdb merges N labels into one: lowest ID survives, the rest have their references reassigned and are then deleted, and the survivor is renamed to a librarian-chosen final name (CompanyAdminServlet.processMerge). Nothing here does any part of that.
- The column set is three wide where the legacy table was fourteen.
shared/database/src/schema.ts:2157-2161 defines labels as id, label_name (unique, varchar 128) and parent_label_id. tubafrenzy's Company (libs/entities/src/main/java/org/wxyc/rotation/Company.java:7-23) carries address, city, stateProvince, zipCode, country, phoneNumber, faxNumber, webSiteUrl, emailAddress, contactPerson, comments, timeLastModified and timeCreated in addition to the three we have. Every one of those is an input on labelModify.jsp, and none of them has a column here. Whether the label-contact fields come across is a real product decision, not a mechanical port — the music department used them to reach promo contacts.
- Merge has more work to do here than it did in tubafrenzy. The legacy merge reassigns rotation-release references only, and the merge preview screen says exactly that. In this schema
labels.id is referenced from two places — library.label_id (schema.ts:623) and flowsheet.label_id (schema.ts:1201) — and the label name is additionally denormalized into three text columns: library.label (schema.ts:622), flowsheet.record_label (schema.ts:1200) and rotation.record_label (schema.ts:1007). Note that rotation carries no label_id at all, which is the inverse of the legacy arrangement where rotation releases were the FK holders. A merge that repoints only the two FKs leaves three text columns asserting a name that no longer exists.
Contract drift worth fixing in the same pass
api.yaml:10377 declares /labels with a GET and a POST, and nothing else. Two mounted operations are undeclared: GET /labels/info and GET /labels/search. dj-site's lib/features/labels/api.ts already calls /labels/search, so a live consumer depends on an operation the published contract does not contain. Separately, the declared GET /labels q parameter is not read by the handler — labelsController.getLabels calls getAllLabels() with no argument (labels.controller.ts:10-13), so the documented search behaviour on that path does not exist.
Desired end state
An endpoint set sufficient to back a librarian-facing label admin screen in dj-site:
- read one label by id, with the counts the screens display (releases referencing it, child labels referencing it as parent);
- update a label's fields;
- delete a label, refused server-side when references exist rather than pre-checked by the client;
- merge N labels into one with a librarian-chosen surviving name, atomically, reporting how many references moved.
Reads gated catalog: ['read'], writes catalog: ['write'], matching the existing route file. api.yaml updated to match what is actually mounted, including the two undeclared reads.
Decisions to settle before implementing
- Which legacy contact fields survive? All eleven, some, or none. If none,
labelModify.jsp's field list shrinks to Name + Parent and that divergence gets recorded on the consumer ticket rather than silently dropped.
- What makes a label undeletable? The legacy rule is "no rotation releases and no child labels". The honest equivalent here is "no
library.label_id, no flowsheet.label_id, no child labels" — and possibly also no rotation.record_label text match, which is not a referential check and may not be worth making one.
- Does merge rewrite the denormalized text columns? If it does, the merge is a multi-table write and wants a transaction plus a stated row-count report. If it does not, the ticket must say so plainly so the consumer screen does not promise a cleanup it will not perform.
- Is a label rename allowed to leave
library.label / flowsheet.record_label / rotation.record_label stale? Same question as above, for update rather than merge.
Related
Problem
/wxycdb's Manage Labels screen (companyAdmin, linked fromwebapps/wxycdb/src/main/webapp/jsp/mainmenu.jsp:36) is a full create / read / update / delete / merge surface over tubafrenzy'sCOMPANYtable. Backend-Service serves a fraction of it.apps/backend/routes/labels.route.tsmounts exactly four operations —GET /labels,GET /labels/info,GET /labels/search,POST /labels— of which only the POST writes, and it writes two fields (labels.controller.ts:27-33, takinglabel_nameand an optionalparent_label_id).Today the only writer is dj-site's release-creation mutation, which upserts a label name. Once
/wxycdbretires there is no surface anywhere that can rename a mistyped label, merge the several spellings of one imprint, re-parent a subsidiary, or delete a row created by accident — while the create path keeps adding rows to the same table. Write-without-repair is the failure mode.The gaps, precisely
PATCH/PUTis mounted. A label name is immutable once created.DELETEis mounted./wxycdboffers delete only when the label has zero releases and zero child labels (jsp/labelAdmin/labelModify.jsp:105, and thechildCountguard inCompanyAdminServlet); Backend's equivalent guard has three reference sources rather than one — see item 5./wxycdbmerges N labels into one: lowest ID survives, the rest have their references reassigned and are then deleted, and the survivor is renamed to a librarian-chosen final name (CompanyAdminServlet.processMerge). Nothing here does any part of that.shared/database/src/schema.ts:2157-2161defineslabelsasid,label_name(unique, varchar 128) andparent_label_id. tubafrenzy'sCompany(libs/entities/src/main/java/org/wxyc/rotation/Company.java:7-23) carriesaddress,city,stateProvince,zipCode,country,phoneNumber,faxNumber,webSiteUrl,emailAddress,contactPerson,comments,timeLastModifiedandtimeCreatedin addition to the three we have. Every one of those is an input onlabelModify.jsp, and none of them has a column here. Whether the label-contact fields come across is a real product decision, not a mechanical port — the music department used them to reach promo contacts.labels.idis referenced from two places —library.label_id(schema.ts:623) andflowsheet.label_id(schema.ts:1201) — and the label name is additionally denormalized into three text columns:library.label(schema.ts:622),flowsheet.record_label(schema.ts:1200) androtation.record_label(schema.ts:1007). Note thatrotationcarries nolabel_idat all, which is the inverse of the legacy arrangement where rotation releases were the FK holders. A merge that repoints only the two FKs leaves three text columns asserting a name that no longer exists.Contract drift worth fixing in the same pass
api.yaml:10377declares/labelswith aGETand aPOST, and nothing else. Two mounted operations are undeclared:GET /labels/infoandGET /labels/search. dj-site'slib/features/labels/api.tsalready calls/labels/search, so a live consumer depends on an operation the published contract does not contain. Separately, the declaredGET /labelsqparameter is not read by the handler —labelsController.getLabelscallsgetAllLabels()with no argument (labels.controller.ts:10-13), so the documented search behaviour on that path does not exist.Desired end state
An endpoint set sufficient to back a librarian-facing label admin screen in dj-site:
Reads gated
catalog: ['read'], writescatalog: ['write'], matching the existing route file.api.yamlupdated to match what is actually mounted, including the two undeclared reads.Decisions to settle before implementing
labelModify.jsp's field list shrinks to Name + Parent and that divergence gets recorded on the consumer ticket rather than silently dropped.library.label_id, noflowsheet.label_id, no child labels" — and possibly also norotation.record_labeltext match, which is not a referential check and may not be worth making one.library.label/flowsheet.record_label/rotation.record_labelstale? Same question as above, for update rather than merge.Related
/wxycdbthis unblocks a screen for