#11288 Ancillary item service + GraphQL layers#11333
Merged
ria8651 merged 3 commits intov2.19.0-RCfrom May 4, 2026
Merged
Conversation
Service layer wraps the schema/repo work from #11287 with insert/update (via upsert) and soft-delete operations, validating central-only authz, item_quantity > 0 and ancillary_quantity > 0, both item_link FKs, duplicate (principal, ancillary) pairs, and reusing validate_ancillary_item_link for self-link/cycle/depth. GraphQL layer exposes upsertAncillaryItem and deleteAncillaryItem under centralServer.ancillaryItem (matching the bundledItem pattern), plus ancillaryItems and ancillaryFor fields on ItemNode backed by two new DataLoaders. AncillaryItemNode exposes the pair directly as itemQuantity and ancillaryQuantity (not a derived ratio) plus nested item and ancillaryItem resolved via the ItemLoader. Loaders currently treat item_link_id as item_id -- merged items aren't supported yet, matching bundled_item's existing limitation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 21, 2026
Contributor
Test report (sqlite)1 007 tests 1 006 ✅ 7m 31s ⏱️ For more details on these failures, see this check. Results for commit e352c74. ♻️ This comment has been updated with latest results. |
Contributor
Test report (postgres)1 008 tests 1 007 ✅ 49m 55s ⏱️ For more details on these failures, see this check. Results for commit e352c74. ♻️ This comment has been updated with latest results. |
jmbrunskill
reviewed
May 4, 2026
jmbrunskill
reviewed
May 4, 2026
jmbrunskill
requested changes
May 4, 2026
Contributor
jmbrunskill
left a comment
There was a problem hiding this comment.
Looks good, lets just remove the link_id from the graphql response...
Mainly just changing item link to item now that repository layer hides item link
Contributor
Author
|
Updated with repository layer item link changes. Should handle merging properly now. Addressed other comments too. |
jmbrunskill
approved these changes
May 4, 2026
Contributor
jmbrunskill
left a comment
There was a problem hiding this comment.
Updates look fine, feel free to merge when tests pass
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #11288
Stacked on #11332 — please merge that first. #11289 and #11290 will follow stacked on this.
👩🏻💻 What does this PR do?
Adds the service and GraphQL layers on top of the schema from #11287, exposing the ancillary-item CRUD surface the frontend will drive.
Service layer (
server/service/src/item/ancillary_item/):upsert_ancillary_item— handles both insert and update; validates central-only authz,item_quantity > 0andancillary_quantity > 0, bothitem_link_idFKs, duplicate (principal, ancillary) pairs, and reusesvalidate_ancillary_item_linkfrom Ancillary items: schema + sync for ancillary_item table #11287 for self-link / cycle / max-depth.delete_ancillary_item— soft delete with central-only guard.get_ancillary_items— paginated list query.ItemServiceTraitalongside existing bundled-item / item-variant CRUD.GraphQL layer (new
graphql_ancillary_itemcrate, matches thebundled_itempattern):centralServer.ancillaryItem.upsertAncillaryItemand.deleteAncillaryItem.itemQuantity/ancillaryQuantitydirectly (not a derived ratio) so the client sends the user's originalx:y.AncillaryItemNodetype: exposesid,itemQuantity,ancillaryQuantity,itemLinkId,ancillaryItemLinkId, plus nesteditem/ancillaryItemresolved via the existingItemLoader.ItemNode.ancillaryItemsandItemNode.ancillaryForfields: backed by two new DataLoaders so the frontend can fetch an item's ancillary links (as principal or as ancillary) in one round-trip.Tests: one consolidated service-layer test covering CRUD + every error variant (central-only, FK, ratio, self-link, cycle, duplicate, update-excludes-self), plus two GraphQL mutation tests covering success + error-bucket mapping.
💌 Any notes for the reviewer?
Known limitation (same as
bundled_item): the loaders treatitem_link_idasitem_id, which is the common case for newly-created items but breaks for items that have been merged (multiple link IDs pointing at the same item). Documented inline at graphql/core/src/loader/ancillary_item.rs. Follow-up if/when it becomes a real issue.Service-layer central-only check is belt-and-suspenders with the GraphQL
central_serverwrapper — both reject non-central mutations, the service-layer variant just produces a cleanerNotCentralServerservice error for future callers (e.g. sync-triggered paths).Tests use a process-wide central-server flag. The service test consolidates all sub-cases into a single
#[actix_rt::test]to avoid races with cargo's parallel runner since other test modules set the same flag. Covered inline at service/src/item/ancillary_item/test/mod.rs.No divergence from the issue scope. All four items in the issue's Scope section (queries, mutations, validation, tests) implemented.
🧪 Testing
Prerequisite: #11287 merged (or running this branch which includes it).
cargo test --package service ancillary_item— expect 1 test (consolidated, covers all scenarios).cargo test --package graphql_ancillary_item— expect 2 tests (upsert + delete happy/error paths).upsertAncillaryItemwith valid input →AncillaryItemNodewith both quantities returned.(itemLinkId, ancillaryItemLinkId)pair but differentid→DuplicateAncillaryItem(BadUserInput).itemLinkId == ancillaryItemLinkId→CanNotLinkItemWithItself(BadUserInput).A→B, then tryB→A→CycleDetected.itemQuantity: 0→RatioMustBePositive.deleteAncillaryItem→DeleteResponseon success; check DB:deleted_datetimepopulated.Forbidden(via thecentral_serverwrapper).ItemNode.ancillaryItemsandancillaryForfor a linked item → returns the expectedAncillaryItemNodes with nesteditem/ancillaryItem.Note that this testing with manual graphql queries can be skipped and just wait for the next two PRs which will add UI for this.
📃 Documentation
1.
2.
📃 Reviewer Checklist
The PR Reviewer(s) should fill out this section before approving the PR
Breaking Changes
Issue Review
Tests Pass