Skip to content

Add GraphQL foundation and cart/order resolvers#13

Open
devin-ai-integration[bot] wants to merge 4 commits intodevelop-7.0.xfrom
devin/1776896179-graphql-foundation-and-cart-resolvers
Open

Add GraphQL foundation and cart/order resolvers#13
devin-ai-integration[bot] wants to merge 4 commits intodevelop-7.0.xfrom
devin/1776896179-graphql-foundation-and-cart-resolvers

Conversation

@devin-ai-integration
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot commented Apr 22, 2026

Overview

Adds GraphQL support to broadleaf-framework-web as a foundation plus the first set of Cart & Order query/mutation resolvers. This is intended as the starting point for a GraphQL surface alongside the existing REST controllers (which remain untouched).

What's included

GraphQL foundation (org.broadleafcommerce.core.web.graphql)

  • spring-boot-starter-graphql dependency added to core/broadleaf-framework-web/pom.xml and managed in the root pom.xml. reactive-streams is pinned to 1.0.4 in <dependencyManagement> to resolve a convergence error introduced by the GraphQL starter (graphql-java brings 1.0.3, reactor-core brings 1.0.4).
  • schema.graphqls under src/main/resources/graphql/ defining Query/Mutation, domain types (Order, OrderItem, Product, Sku, Customer, Money, etc.), OrderStatus enum, input types, and PromoCodeResult.
  • GraphQLContextInterceptor (WebGraphQlInterceptor) — resolves the current HttpServletRequest via RequestContextHolder, ensures BroadleafRequestContext, CustomerState, and CartState are populated before resolvers run. Creates an anonymous customer if none exists, mirroring RestApiCustomerStateFilter.
  • GraphQLExceptionResolver (extends DataFetcherExceptionResolverAdapter) — maps cart/pricing/offer exceptions to stable error codes in extensions.code (ADD_TO_CART_ERROR, REMOVE_FROM_CART_ERROR, UPDATE_CART_ERROR, ILLEGAL_CART_OPERATION, PRICING_ERROR, OFFER_MAX_USE_EXCEEDED, INTERNAL_ERROR).
  • graphql-application.properties with spring.graphql.graphiql.enabled, spring.graphql.path=/graphql, and spring.graphql.schema.locations=classpath:graphql/.

Cart & Order resolvers (...graphql.resolvers)

  • CartQueryResolvercart, order(id), orderByNumber(orderNumber), orderHistory(status), customer. orderHistory converts the string status via OrderStatus.getInstance(...) and falls back to the no-status overload when not provided.
  • CartMutationResolveraddToCart, updateCartItemQuantity, removeFromCart, applyPromoCode, removePromoCode. Logic mirrors BroadleafCartController, including the promo-code error-message mapping and cart re-save on success. addToCart creates a new cart when CartState.getCart() is null or NullOrderImpl.
  • OrderFieldResolver@SchemaMapping for orderItems, fulfillmentGroups, payments, subTotal, total, status (serializes OrderStatus#getType() as the GraphQL enum string).
  • OrderItemFieldResolver@SchemaMapping for product/sku, guarded by instanceof DiscreteOrderItem.

DTOs (...graphql.dto)

  • AddToCartInput (productId/skuId/quantity).
  • PromoCodeResult (order, promoAdded, errorMessage).

Verification

  • mvn compile -pl core/broadleaf-framework-web -am -DskipTests passes cleanly (including the dependency convergence enforcer).
  • No existing REST controllers or tests were modified. No tests added (deferred to a separate ticket).

Things worth a close look during review

  • reactive-streams version pin in the root pom.xml. Added to satisfy the dependency convergence enforcer; please confirm this version is acceptable repo-wide.
  • OrderStatus enum mapping. The schema exposes IN_PROCESS, NAMED, SUBMITTED, CANCELLED, QUOTE, CSR, while the domain OrderStatus includes CSR_OWNED and ARCHIVED. OrderFieldResolver#status returns order.getStatus().getType() directly, so a domain value like CSR_OWNED will fail to serialize against the schema enum. Worth deciding whether to (a) map/alias these or (b) expand the schema enum.
  • GraphQLContextInterceptor context population. Uses RequestContextHolder rather than pulling the servlet request from WebGraphQlRequest. It also eagerly creates an anonymous Customer when none is present — if CustomerStateFilter/RestApiCustomerStateFilter already runs on /graphql, this may be redundant. Reviewer should confirm the intended interaction with existing filter chains.
  • graphql-application.properties loading. The file is named graphql-application.properties, not application.properties, so Spring Boot will not auto-load it. This matches the ticket spec (it's a template for consuming apps) but is worth confirming.
  • applyPromoCode error handling. When lookupAllOfferCodesByCode returns multiple codes and one fails after another succeeds, errorMessage is set and the save is skipped — this mirrors BroadleafCartController.addPromo() but please verify the behavior is desired for GraphQL clients.
  • No runtime/integration testing was performed — only compilation was verified.

Labels / Milestone

  • Type: Feature
  • Status: ready-for-code-review
  • Milestone: next release on the 7.0.x line

Link to Devin session: https://app.devin.ai/sessions/cbc4a4f5fae740ce9716821195527040
Requested by: @Colhodm


Open in Devin Review

Co-Authored-By: Arjun Mishra <arjunsaxmishra@gmail.com>
@devin-ai-integration
Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment thread core/broadleaf-framework-web/src/main/resources/graphql/schema.graphqls Outdated
Comment on lines +118 to +135
for (OfferCode offerCode : offerCodes) {
try {
cart = orderService.addOfferCode(cart, offerCode, false);
promoAdded = true;
} catch (OfferMaxUseExceededException e) {
errorMessage = "Use Limit Exceeded";
} catch (OfferExpiredException e) {
errorMessage = "Offer Has Expired";
} catch (OfferAlreadyAddedException e) {
errorMessage = "Offer Has Already Been Added";
} catch (OfferException e) {
errorMessage = "An Unknown Offer Error Has Occurred";
}
}
if (errorMessage == null) {
cart = orderService.save(cart, true);
CartState.setCart(cart);
}
Copy link
Copy Markdown
Author

@devin-ai-integration devin-ai-integration Bot Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 applyPromoCode persists offers but skips repricing on partial failure

When lookupAllOfferCodesByCode returns multiple offer codes and some succeed while a later one fails, the successfully-added offers are already persisted to the database (because addOfferCode(cart, offerCode, false) internally calls save(order, false) at OrderServiceImpl.java:483). However, since errorMessage is set by the catch block, the save(cart, true) repricing call at line 141 is skipped. This leaves the cart in an inconsistent state: offer codes are persisted but the cart is not repriced, and CartState is not updated. The result also reports promoAdded=true alongside an errorMessage, which is contradictory for API consumers.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mirrors BroadleafCartController.addPromo() lines 427-450 verbatim — same save guard (if (exception.equals(""))) and same promoAdded flag semantics. The task brief explicitly asked to preserve parity with that controller, so I'd rather not diverge behavior here in this PR. If you'd like the GraphQL resolver to diverge (e.g. save on any partial success, or collect per-code errors), happy to do that in a follow-up — likely worth fixing the REST controller at the same time so both surfaces behave consistently. Let me know which direction you prefer.

Co-Authored-By: Arjun Mishra <arjunsaxmishra@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: Arjun Mishra <arjunsaxmishra@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

…e in removePromoCode

Co-Authored-By: Arjun Mishra <arjunsaxmishra@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant