Add GraphQL foundation and cart/order resolvers#13
Add GraphQL foundation and cart/order resolvers#13devin-ai-integration[bot] wants to merge 4 commits intodevelop-7.0.xfrom
Conversation
Co-Authored-By: Arjun Mishra <arjunsaxmishra@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| 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); | ||
| } |
There was a problem hiding this comment.
🔴 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.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
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>
Co-Authored-By: Arjun Mishra <arjunsaxmishra@gmail.com>
…e in removePromoCode Co-Authored-By: Arjun Mishra <arjunsaxmishra@gmail.com>
Overview
Adds GraphQL support to
broadleaf-framework-webas 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-graphqldependency added tocore/broadleaf-framework-web/pom.xmland managed in the rootpom.xml.reactive-streamsis pinned to1.0.4in<dependencyManagement>to resolve a convergence error introduced by the GraphQL starter (graphql-java brings1.0.3, reactor-core brings1.0.4).schema.graphqlsundersrc/main/resources/graphql/defining Query/Mutation, domain types (Order,OrderItem,Product,Sku,Customer,Money, etc.),OrderStatusenum, input types, andPromoCodeResult.GraphQLContextInterceptor(WebGraphQlInterceptor) — resolves the currentHttpServletRequestviaRequestContextHolder, ensuresBroadleafRequestContext,CustomerState, andCartStateare populated before resolvers run. Creates an anonymous customer if none exists, mirroringRestApiCustomerStateFilter.GraphQLExceptionResolver(extendsDataFetcherExceptionResolverAdapter) — maps cart/pricing/offer exceptions to stable error codes inextensions.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.propertieswithspring.graphql.graphiql.enabled,spring.graphql.path=/graphql, andspring.graphql.schema.locations=classpath:graphql/.Cart & Order resolvers (
...graphql.resolvers)CartQueryResolver—cart,order(id),orderByNumber(orderNumber),orderHistory(status),customer.orderHistoryconverts the string status viaOrderStatus.getInstance(...)and falls back to the no-status overload when not provided.CartMutationResolver—addToCart,updateCartItemQuantity,removeFromCart,applyPromoCode,removePromoCode. Logic mirrorsBroadleafCartController, including the promo-code error-message mapping and cart re-save on success.addToCartcreates a new cart whenCartState.getCart()is null orNullOrderImpl.OrderFieldResolver—@SchemaMappingfororderItems,fulfillmentGroups,payments,subTotal,total,status(serializesOrderStatus#getType()as the GraphQL enum string).OrderItemFieldResolver—@SchemaMappingforproduct/sku, guarded byinstanceof DiscreteOrderItem.DTOs (
...graphql.dto)AddToCartInput(productId/skuId/quantity).PromoCodeResult(order, promoAdded, errorMessage).Verification
mvn compile -pl core/broadleaf-framework-web -am -DskipTestspasses cleanly (including the dependency convergence enforcer).Things worth a close look during review
reactive-streamsversion pin in the rootpom.xml. Added to satisfy the dependency convergence enforcer; please confirm this version is acceptable repo-wide.OrderStatusenum mapping. The schema exposesIN_PROCESS, NAMED, SUBMITTED, CANCELLED, QUOTE, CSR, while the domainOrderStatusincludesCSR_OWNEDandARCHIVED.OrderFieldResolver#statusreturnsorder.getStatus().getType()directly, so a domain value likeCSR_OWNEDwill fail to serialize against the schema enum. Worth deciding whether to (a) map/alias these or (b) expand the schema enum.GraphQLContextInterceptorcontext population. UsesRequestContextHolderrather than pulling the servlet request fromWebGraphQlRequest. It also eagerly creates an anonymousCustomerwhen none is present — ifCustomerStateFilter/RestApiCustomerStateFilteralready runs on/graphql, this may be redundant. Reviewer should confirm the intended interaction with existing filter chains.graphql-application.propertiesloading. The file is namedgraphql-application.properties, notapplication.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.applyPromoCodeerror handling. WhenlookupAllOfferCodesByCodereturns multiple codes and one fails after another succeeds,errorMessageis set and the save is skipped — this mirrorsBroadleafCartController.addPromo()but please verify the behavior is desired for GraphQL clients.Labels / Milestone
Link to Devin session: https://app.devin.ai/sessions/cbc4a4f5fae740ce9716821195527040
Requested by: @Colhodm