Skip to content

Re-land Storefront API 2026-07 migration + fix review issues (v3.0.0) - #147

Merged
imsujan276 merged 3 commits into
imsujan276:mainfrom
qeepcologne:reland-2026-07
Jul 17, 2026
Merged

Re-land Storefront API 2026-07 migration + fix review issues (v3.0.0)#147
imsujan276 merged 3 commits into
imsujan276:mainfrom
qeepcologne:reland-2026-07

Conversation

@qeepcologne

Copy link
Copy Markdown
Contributor

Re-lands #143 (reverted in #146) with fixes for the issues found in review.

Why a new PR (and not a re-merge of the branch)

The reverted migration commits are still in main's history, so simply re-merging qeepcologne:main would bring only the new fix commits and leave the migration reverted. This branch is a clean re-land committed on top of the current (post-revert) main, so merging it restores the full migration and the fixes in one go.

Addressing the revert notes

The collections are not showing the collection products.

Not a query/parsing bug — I confirmed Collection.fromGraphJson parses real 2026-07 collection products correctly. The example's collection tab hard-coded a filters: {price: {min: 500, max: 600}}, so any store whose products fall outside that range showed an empty collection while the unfiltered product list showed everything. Removed the hard-coded filter (kept a comment showing how to add one).

'CartAddressInput' requires exactly one argument, but 2 were provided when adding a delivery address.

CartAddressInput is a Storefront "one of" input — exactly one field may be present. The generated toJson emitted both copyFromCustomerAddressId and deliveryAddress, so the null one counted as a second argument. Marked both fields @JsonKey(includeIfNull: false) so the unused one is omitted. Verified live: both cartCreate(delivery:) and cartDeliveryAddressesAdd now return userErrors: [] and persist the address.

And a few others.

  • Blog/Pages tabs spun forever. Root cause was a missing unauthenticated_read_content scope on the test token (ACCESS_DENIED), but the example also had a bug: the catch cleared the loading flag without setState, so the spinner never went away. Fixed all three fetches to clear via setState and render the actual error (e.g. the scope message) or a "No blogs/pages" empty state.
  • Example wouldn't build on a current toolchain (JDK 25 / AGP 9). Updated to Gradle 9.6.1 + AGP 9.3.0 (stable) + built-in Kotlin, and bumped flutter_inappwebview to 6.2.0-beta.3 (the stable line's Android package uses a proguard file AGP 9 removed). Also enabled the .env asset (the example's main.dart loads .env but the asset was commented out, so it never actually loaded config).

Testing

  • Ran the example app end-to-end on an Android emulator against a live 2026-07 store — products, variant prices, and categories render; both delivery paths succeed.
  • Replayed every document the example uses against a live 2026-07 storefront (products, search, collections, collection-products, full cart lifecycle, delivery) — all clean.
  • Fed real responses through the migrated models (Cart, Collection, Products) — nothing dropped on parse.
  • Added a regression test for the CartAddressInput one-of serialization. flutter analyze clean; tests pass.

Everything else from #143 is unchanged (the full 2026-07 migration and deprecation cleanup). See #144 for the underlying issue and CHANGELOG for the migration guide.

Re-applies PR imsujan276#143 (reverted in imsujan276#146) together with the fixes for the
issues found in review. Squashed re-land on top of the revert so the
migration is fully restored (a plain re-merge of the branch would not
re-introduce the reverted commits).

Fixes since the revert:
- CartAddressInput is a Storefront "one of" input; toJson emitted both
  fields, so adding a cart delivery address failed with "'CartAddressInput'
  requires exactly one argument, but 2 were provided". The null field is
  now omitted. (This is the delivery error from the revert note.)
- Example collection tab hard-coded a price {min:500,max:600} filter that
  hid all products on most stores (the "collections show no products" from
  the revert note). Removed it.
- Example Blog/Pages tabs spun forever on a failed fetch (catch cleared the
  flag without setState); they now clear the spinner and show the error or
  an empty state.
- Example builds on the Java 25 / AGP 9 toolchain (Gradle 9.6.1, AGP 9.3.0,
  built-in Kotlin; flutter_inappwebview 6.2.0-beta.3 for AGP 9).
- Dependency floors bumped to current versions (intl kept >=0.20.2 for
  flutter_localizations compatibility).

Verified every migrated document and both delivery paths against a live
2026-07 storefront, ran the example app end-to-end, and confirmed model
parsing on real responses. flutter analyze clean; tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
qeepcologne and others added 2 commits July 15, 2026 17:39
Nothing imports analyzer directly; it is only a transitive codegen
dependency. Pinning analyzer ^10.2.0 (which requires meta ^1.18.0) made
the package unresolvable on Flutter SDKs whose flutter_test pins the
older meta 1.17.0 (e.g. the CI runner), even though it resolved on newer
local SDKs. Removed the pin so pub picks an analyzer matching the active
SDK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`dart analyze --fatal-infos` (CI) fails with `asset_does_not_exist` when
example/pubspec.yaml declares `.env` as an asset but the file is absent —
which it is on any clean checkout, since .env is git-ignored (it holds a
Storefront token). Re-commented the asset with instructions to create
.env and uncomment it locally to run the example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@imsujan276
imsujan276 merged commit b9ce0ed into imsujan276:main Jul 17, 2026
2 checks passed
@imsujan276

Copy link
Copy Markdown
Owner

Hi @qeepcologne

Thank you for the update

IN the cart tab in the example app, the line item quantity is not being set. The line item quantity is always 0 on line item add, remove, or add to cart.

Except for this error in cart module, others seems to be good to go.

Screenshot 2026-07-17 at 2 41 09 PM

@qeepcologne

Copy link
Copy Markdown
Contributor Author

Thanks for testing — I dug into this one and it turns out to be an example-app bug, not a package bug, and it's not new in this PR. Fixed in the example either way, plus I added a regression test on the package side so the parse path is pinned.

What's actually happening

Shopify creates the line with quantity: 0 when the merchandise isn't purchasable (availableForSale: false), and it reports no userErrors and no errors while doing it. The example's cart tab adds product.productVariants.first.id — the first variant, whether or not it's sellable. On a store whose first variant is out of stock, every add lands as a 0x line, and the +/- buttons then look dead because the server clamps the quantity straight back to 0.

Reproduced against the public Hydrogen demo store (hydrogen-preview.myshopify.com), using this PR's cartLinesAdd document verbatim:

# variant 41007290482744 -> availableForSale: false, quantityAvailable: 0
cartLinesAdd(lines: [{merchandiseId: "...482744", quantity: 1}])
  -> errors: null, userErrors: [], totalQuantity: 0, lines: [{quantity: 0}]

# variant 41007289630776 -> availableForSale: true, quantityAvailable: 10061
cartLinesAdd(lines: [{merchandiseId: "...630776", quantity: 1}])
  -> errors: null, totalQuantity: 1, lines: [{quantity: 1}]

It isn't a regression

I ran the pre-migration document from v2.8.x against the old 2024-07 API on that same store and same out-of-stock variant — identical result, quantity: 0. So this behaviour predates the 2026-07 migration; it just shows up on stores where the first variant happens to be out of stock.

The package parses quantity correctly

To be sure it wasn't us dropping the field, I checked the whole path:

  • Raw HTTP responses for cartCreate, cartLinesAdd, cartLinesUpdate on a live 2026-07 store all carry quantity — the documents request it and the server returns it.
  • Driving the real client (ShopifyConfig.setConfigGraphQLClient + normalized cache → ShopifyCart) against a live store: addLineItemsToCart(qty 1)quantity=1, updateLineItemsInCart(qty 2)quantity=2, getCartByIdquantity=2. All correct.

Added a test (Cart line parses quantity from a cartLinesAdd payload) covering lines.edges -> node -> quantity, including a quantity: 0 out-of-stock line, so a 0 can only ever mean "the server said 0".

What I changed in the example

  • addLineItemToCart now picks the first variant with availableForSale == true instead of blindly taking productVariants.first, and refuses with an "out of stock" message if the product has none.
  • The product list disables the add button and labels unbuyable products "Out of stock".
  • The cart list flags a line whose merchandise is unavailable ("Out of stock — Shopify keeps this line at 0").
  • onCartItemUpdate now reports it when Shopify clamps the quantity to less than what was requested, instead of silently looking like the button did nothing.
  • Restored the try/catch in addLineItemToCart (it was commented out, so failures there were silently unhandled).

@imsujan276

Copy link
Copy Markdown
Owner

@qeepcologne Thank you for your update and contribution to the package. The new version 3.0.0 is now available in pub.dev.

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.

2 participants