Skip to content

GraphQL Foundation — Dependencies, Schema, and Configuration#15

Open
devin-ai-integration[bot] wants to merge 5 commits intodevelop-7.0.xfrom
devin/graphql-ticket1-foundation
Open

GraphQL Foundation — Dependencies, Schema, and Configuration#15
devin-ai-integration[bot] wants to merge 5 commits intodevelop-7.0.xfrom
devin/graphql-ticket1-foundation

Conversation

@devin-ai-integration
Copy link
Copy Markdown

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

Overview

Introduces the foundational GraphQL layer in broadleaf-framework-web: the spring-boot-starter-graphql dependency, a first-pass schema covering catalog / cart / checkout / account, a context interceptor that populates CustomerState and CartState for each request, and a resolver that maps Broadleaf domain exceptions to structured GraphQL errors. No data fetchers are wired up yet — that lands in follow-up tickets.

Changes

Dependencies (root pom.xml)

  • New properties: spring-graphql.version=1.3.4, graphql-java-extended-scalars.version=22.0.
  • Added spring-boot-starter-graphql and graphql-java-extended-scalars to <dependencyManagement>.
  • graphql-java is excluded from the transitive graph of graphql-java-extended-scalars so the version shipped by spring-boot-starter-graphql wins (otherwise the enforcer plugin's DependencyConvergence rule fails: extended-scalars 22.0 pulls graphql-java 22.0, while spring-graphql 1.4.5 pulls 24.3).
  • Pinned org.reactivestreams:reactive-streams:1.0.4 to resolve a separate convergence conflict between java-dataloader (1.0.3) and reactor-core (1.0.4).

core/broadleaf-framework-web/pom.xml

  • Adds spring-boot-starter-graphql and graphql-java-extended-scalars to the module.

Schemacore/broadleaf-framework-web/src/main/resources/graphql/schema.graphqls

  • Query: catalog (product, category, sku, search), cart/orders, account.
  • Mutation: cart ops, promo codes, checkout save/process, account/address management.
  • Types: Product, Category, Sku, Money, Order, OrderItem, FulfillmentGroup, FulfillmentGroupItem, FulfillmentOption, OrderPayment, OfferCode, Customer, CustomerAddress, Address, ProductOption, ProductOptionValue, PromoCodeResult, CheckoutResult.
  • Enum OrderStatus, custom BigDecimal scalar, and input types for mutations.

GraphQLContextInterceptor (implements WebGraphQlInterceptor)

  • Resolves customer id from request attribute → parameter → header (same precedence as RestApiCustomerStateFilter).
  • If a numeric id resolves to a known customer, sets it on CustomerState; otherwise creates an anonymous customer.
  • Populates CartState via OrderService.findCartForCustomer.
  • Populates the blRuleMap request attribute for content rule processing.

GraphQLExceptionResolver (extends DataFetcherExceptionResolverAdapter)

  • Maps AddToCartException, RemoveFromCartException, UpdateCartException, IllegalCartOperationException, OfferMaxUseExceededException, CheckoutExceptionValidationError with distinct code extensions.
  • Maps PricingExceptionDataFetchingException with code=PRICING_ERROR.
  • Falls back to the exception's simple class name when getMessage() is null, since several Broadleaf exceptions have no-arg constructors and GraphqlErrorBuilder asserts a non-null message.

GraphQLConfig

  • Registers a RuntimeWiringConfigurer bean that binds the schema's BigDecimal scalar to ExtendedScalars.GraphQLBigDecimal. This preserves full java.math.BigDecimal precision for monetary amounts; aliasing to GraphQLFloat (as originally drafted) would have silently degraded values to IEEE 754 doubles.

GraphQL endpoint defaults — merged into core/broadleaf-framework-web/src/main/resources/config/bc/web/common.properties so they flow through Broadleaf's FrameworkCommonClasspathPropertySource:

  • spring.graphql.graphiql.enabled=true, spring.graphql.path=/graphql, spring.graphql.schema.locations=classpath:graphql/.

Updates Since Last Revision

  • Replaced NumberUtils.isCreatable with isDigits and wrapped Long.valueOf in a parseCustomerId helper that returns null on NumberFormatException. This handles both non-decimal inputs accepted by isCreatable (hex, scientific, type-qualified) and all-digit strings that overflow Long.MAX_VALUE, falling through to the anonymous-customer path instead of failing the request.
  • Moved GraphQL endpoint defaults from a standalone graphql-config.properties (which was never loaded by Spring Boot or Broadleaf's BroadleafEnvironmentConfigurer) into the module's existing config/bc/web/common.properties, so spring.graphql.graphiql.enabled etc. actually take effect.
  • Switched the BigDecimal scalar wiring from an alias-to-Float to ExtendedScalars.GraphQLBigDecimal to preserve monetary precision.
  • Null-safe exception message handling in GraphQLExceptionResolver.

Build Verification

mvn compile -pl core/broadleaf-framework-web -am -DskipTests — BUILD SUCCESS. No tests added in this ticket.

Reviewer Focus / Known Caveats

  • spring-graphql.version property is declared but unused. Spring Boot 3.5.13 manages spring-graphql to 1.4.5 transitively, so the 1.3.4 property has no effect on the resolved version. Happy to either drop the property or override the spring-graphql version explicitly — please advise on preference.
  • Deviation from ticket text — ServletWebGraphQlRequest cast. The ticket describes casting WebGraphQlRequest to ServletGraphQlRequest / ServletWebGraphQlRequest, but that class does not exist in spring-graphql 1.4.x. The interceptor instead pulls the HttpServletRequest via RequestContextHolder's ServletRequestAttributes. This works on the servlet thread (default for spring-graphql webmvc) but would return null if a future resolver switches to async/off-thread execution.
  • Thread-local CustomerState / CartState vs. reactive pipeline. The interceptor sets thread-local state in intercept() before returning chain.next(...). On the default webmvc path this is fine, but any downstream operator that switches schedulers (e.g. publishOn) would observe empty thread-locals in the data fetcher. If the follow-up data-fetcher ticket uses async/reactive resolvers, we'll likely need to propagate these via GraphQLContext instead.
  • graphql-java-extended-scalars:22.0 vs graphql-java:24.3. The exclusion makes the build pass, but extended-scalars 22.0 was compiled against graphql-java 22.x. GraphQLBigDecimal is a stable scalar definition, so this is low-risk in practice, but broader use of extended scalars may eventually require a newer version.
  • No resolvers / data fetchers. The schema compiles and the endpoint will serve GraphiQL, but queries/mutations have no backing @Controller / @SchemaMapping wiring yet — that's scoped to follow-up tickets.

Labels & Milestone

  • Label: Feature, Status: ready-for-code-review
  • Milestone: next 7.0.x release

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


Open in Devin Review

- Add spring-boot-starter-graphql and graphql-java-extended-scalars to root pom
  dependencyManagement, wired into broadleaf-framework-web
- Exclude transitive graphql-java from extended-scalars to align with the
  graphql-java version managed by spring-boot-starter-graphql
- Pin reactive-streams to 1.0.4 to satisfy dependency convergence
- Add GraphQL schema under core/broadleaf-framework-web/src/main/resources/graphql
  covering catalog, cart, checkout, and account types, queries and mutations
- Add GraphQLContextInterceptor that populates CustomerState and CartState
- Add GraphQLExceptionResolver mapping Broadleaf domain exceptions to
  structured GraphQL errors with stable code extensions
- Add GraphQLConfig registering BigDecimal scalar aliased to GraphQLFloat
- Add graphql-config.properties enabling GraphiQL and the /graphql endpoint
@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

devin-ai-integration[bot]

This comment was marked as resolved.

- Use NumberUtils.isDigits instead of isCreatable to avoid NumberFormatException
  on inputs like hex, decimal, scientific, or type-qualified strings that
  isCreatable accepts but Long.valueOf cannot parse.
- Move GraphQL endpoint defaults into the module's existing
  config/bc/web/common.properties so they are loaded by Broadleaf's
  FrameworkCommonClasspathPropertySource. Remove the classpath-root
  graphql-config.properties, which was never picked up by Spring Boot or
  Broadleaf's environment configurer.
devin-ai-integration[bot]

This comment was marked as resolved.

Some Broadleaf exceptions (e.g. PricingException, AddToCartException) have
no-arg constructors that leave the message null. GraphqlErrorBuilder.build()
asserts message is non-null and would otherwise throw, preventing the
structured error from being returned. Fall back to the exception's simple
class name when no message is present.
devin-ai-integration[bot]

This comment was marked as resolved.

…ecision

Aliasing BigDecimal to GraphQLFloat silently degrades values to IEEE 754
doubles, which can corrupt large monetary amounts that Broadleaf's Money
type stores as java.math.BigDecimal specifically to preserve exact decimal
precision. Register the schema's BigDecimal scalar using
ExtendedScalars.GraphQLBigDecimal, which serializes with full precision.
devin-ai-integration[bot]

This comment was marked as resolved.

…omer ids

isDigits returns true for all-digit strings that overflow Long.MAX_VALUE
(e.g. a very long numeric header supplied by a malicious client), which
Long.valueOf then rejects with NumberFormatException, failing the entire
GraphQL request. Wrap the parse in a helper that returns null on overflow
and falls through to the anonymous-customer path.
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