spree_doordash 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4386dd5c8b44b1fa483213fcfae97c5c21ad8143f92650714fb7cc0f1966e059
4
- data.tar.gz: 75d2fbda1b0870f14397a77a042b518c6b5972f1e7d8c3fb7f4c38ca182395ea
3
+ metadata.gz: 6a028415e34fd065f59a11e00034d88ef6ea2dfa8155538bf61f63ba26cf16ed
4
+ data.tar.gz: f0425700d9f63218ee14eae64ce30d6e444c46b2eefae2fe4d3854f38426e13a
5
5
  SHA512:
6
- metadata.gz: b9c8465fcded4299713522d77ddf130c6ad9c8479233644fe9efe41817b90b08f7d6e8dd4edd99d88ee9c2b5b408668315d8f3bb58f2d8be47be708eeb7e34ef
7
- data.tar.gz: 2ad0971dfd7551ebcb0e6ebac5c0fbe94a4ef7d5c8f6022469ee8c937cd31ded0d17c7c0ea2ed6e6fb94c1426d906a0ccd72b1f3de5b7d38513207a0780f6d64
6
+ metadata.gz: 25f7dc0b927d237844b908072f4b3f9fa738fe3db1c6b4036280e51d739c6b5eea92c2578dc730dbd5c89b33de5a10318f9898ee91e295a55deafcdf3426ae5b
7
+ data.tar.gz: b32fb99c09cb7344185f06ca15c72c60253669b0d5ea5374de62490c4a62e007ce39589b6df4a491bcfd8a54e71c4caf266ea348c0b8d9abf1355b01c03dcd23
data/CHANGELOG.md CHANGED
@@ -2,6 +2,42 @@
2
2
 
3
3
  All notable changes to this project are documented here.
4
4
 
5
+ ## 0.1.3
6
+
7
+ A real bug found live: the `doordash_quote_unavailable` warning added in 0.1.2 never actually
8
+ worked in production. `Spree::Calculator::Shipping::DoordashQuote#compute_package` pushed it onto
9
+ `package.order.warnings` — but `package.order` is **not** the same in-memory object as the order
10
+ the caller (`Spree::Order#create_proposed_shipments`) holds. `spree_core`'s own
11
+ `Spree::Stock::InventoryUnitBuilder#units` builds each inventory unit with `order_id: @order.id`
12
+ but deliberately *not* `order: @order` ("avoid loading the association to order until needed," per
13
+ its own comment) — the first thing that touches `.order` on one of those units triggers a fresh
14
+ `Spree::Order.find`, a brand-new Ruby object. So the warning was mutating a throwaway copy,
15
+ discarded the instant `compute_package` returned, and never reached the order this request
16
+ actually serializes back to the storefront as `cart.warnings`. Confirmed via `object_id` tracing
17
+ against production, not assumed — and confirmed the existing spec suite couldn't have caught it:
18
+ `doordash_quote_spec.rb`'s own `instance_double(Spree::Stock::Package, order: order)` makes
19
+ `package.order` the *same* object as the test's `order` by construction, sidestepping the exact
20
+ boundary that breaks in real Spree core.
21
+
22
+ Fixed by bridging across that boundary with the order's stable id instead of Ruby object identity
23
+ — `Spree::Calculator::Shipping::DoordashQuote.mark_unavailable(order_id)` sets a thread-local flag
24
+ (chosen over `Rails.cache`: this gem installs into arbitrary host apps, some of which legitimately
25
+ run `config.cache_store = :null_store`, which would silently degrade this back to the original bug
26
+ with zero indication anything was wrong — `compute_package` and `create_proposed_shipments` always
27
+ run synchronously on the same thread within one request, so a thread-local needs no external store
28
+ and no expiry logic). A new `Spree::OrderDecorator#create_proposed_shipments` reads the flag back
29
+ and merges the warning onto `self.warnings` immediately after `super` returns — `self` there *is*
30
+ the real order, since `create_proposed_shipments` is called directly on it by the checkout flow.
31
+ The flag is cleared unconditionally at the *start* of every call (not just after a successful
32
+ merge), so a stale flag left behind by an earlier request that raised before reaching the merge
33
+ step can never leak into a later, unrelated call that happens to reuse the same thread.
34
+
35
+ New `spec/models/spree/order_decorator_spec.rb` exercises the real
36
+ `order_routing_strategy -> Estimator -> Packer -> InventoryUnitBuilder` path end to end (only
37
+ `SpreeDoordash::Quote.call` is stubbed) — the only way to actually exercise the object-identity
38
+ boundary the fix bridges across, rather than doubling it away. Full suite: 95 examples, 0 failures
39
+ (5 new). Coverage: 94.14%. Brakeman clean.
40
+
5
41
  ## 0.1.2
6
42
 
7
43
  A real bug found live during checkout-validation work: a blank phone on the dropoff address used
@@ -34,16 +34,48 @@ module Spree
34
34
  # with no new API surface needed. The storefront renders its own
35
35
  # localized copy for this code; `message` here is English-only,
36
36
  # a fallback for any other API consumer, not the UI text itself.
37
- package.order.warnings |= [{
38
- code: 'doordash_quote_unavailable',
39
- message: 'We could not get a DoorDash delivery quote for this address.'
40
- }]
37
+ #
38
+ # A SECOND real bug found live (v0.1.2, this fix is v0.1.3):
39
+ # package.order is not the same object as the order the caller
40
+ # holds (spree_core's InventoryUnitBuilder deliberately defers
41
+ # loading that association — see the Spree::OrderDecorator this
42
+ # gem ships for the full story), so pushing the warning directly
43
+ # onto package.order.warnings here mutated a throwaway copy that
44
+ # never reached the real order. Thread.current bridges across
45
+ # that boundary via the order's id instead of Ruby object
46
+ # identity — chosen over Rails.cache deliberately: this gem
47
+ # installs into arbitrary host apps, some of which run
48
+ # `config.cache_store = :null_store` (the Rails test-env
49
+ # default, and a legitimate production choice too), which would
50
+ # silently degrade this back to the original bug with no
51
+ # indication anything was wrong. compute_package and
52
+ # create_proposed_shipments always run synchronously on the same
53
+ # thread within one request (confirmed: this is the exact call
54
+ # chain reproduced live to root-cause the original bug), so a
55
+ # thread-local flag needs no external store and no expiry logic
56
+ # — Spree::OrderDecorator clears it unconditionally at the start
57
+ # of every create_proposed_shipments call, so a stale flag from
58
+ # an earlier request that errored out before consuming it can
59
+ # never leak into a later, unrelated one.
60
+ self.class.mark_unavailable(package.order.id)
41
61
  return nil
42
62
  end
43
63
 
44
64
  result.fee_cents / 100.0
45
65
  end
46
66
 
67
+ def self.mark_unavailable(order_id)
68
+ (Thread.current[:spree_doordash_quote_unavailable_order_ids] ||= []) << order_id
69
+ end
70
+
71
+ def self.unavailable?(order_id)
72
+ Thread.current[:spree_doordash_quote_unavailable_order_ids]&.include?(order_id) || false
73
+ end
74
+
75
+ def self.clear_unavailable(order_id)
76
+ Thread.current[:spree_doordash_quote_unavailable_order_ids]&.delete(order_id)
77
+ end
78
+
47
79
  # Called by Estimator to filter which shipping methods even attempt
48
80
  # a compute_package call. Skips the DoorDash API round-trip entirely
49
81
  # for an order with no ship address yet (early checkout, before
@@ -0,0 +1,54 @@
1
+ module Spree
2
+ # Bridges Spree::Calculator::Shipping::DoordashQuote#compute_package's
3
+ # doordash_quote_unavailable warning back onto the real order object.
4
+ #
5
+ # A real bug found live (confirmed via object_id tracing against
6
+ # production, not assumed): `compute_package` receives a
7
+ # `Spree::Stock::Package`, and `package.order` is NOT the same in-memory
8
+ # object as `self` in `create_proposed_shipments` below. spree_core's own
9
+ # `Spree::Stock::InventoryUnitBuilder#units` builds each inventory unit
10
+ # with `order_id: @order.id` but deliberately *not* `order: @order` — its
11
+ # own comment says why: "avoid loading the association to order until
12
+ # needed." The first thing that touches `.order` on one of those units (or
13
+ # a Package built from them) triggers a fresh `Spree::Order.find`, a
14
+ # brand-new Ruby object. So `package.order.warnings |= [...]` inside the
15
+ # calculator was mutating a throwaway copy that's discarded the instant
16
+ # `compute_package` returns — the warning never reached the order object
17
+ # this request actually serializes back to the storefront as
18
+ # `cart.warnings`. `spree_doordash` v0.1.2's warning never worked in
19
+ # production despite passing its own specs (the dummy app's specs call
20
+ # the calculator directly against a single order instance, which can't
21
+ # exhibit this — it only shows up through the real
22
+ # create_proposed_shipments -> order_routing_strategy -> Estimator path).
23
+ #
24
+ # Fix: since Ruby object identity doesn't survive that boundary, bridge
25
+ # across it with the order's stable id instead, via a thread-local flag
26
+ # (see DoordashQuote.mark_unavailable/unavailable?/clear_unavailable for
27
+ # why Thread.current was chosen over Rails.cache). Cleared unconditionally
28
+ # at the *start* of every call — not just after a successful merge — so a
29
+ # stale flag left behind by an earlier request that raised before
30
+ # reaching the merge step can never leak into a later, unrelated
31
+ # create_proposed_shipments call that happens to reuse the same thread.
32
+ module OrderDecorator
33
+ def create_proposed_shipments
34
+ Spree::Calculator::Shipping::DoordashQuote.clear_unavailable(id)
35
+ result = super
36
+ merge_doordash_quote_warning!
37
+ result
38
+ end
39
+
40
+ private
41
+
42
+ def merge_doordash_quote_warning!
43
+ return unless Spree::Calculator::Shipping::DoordashQuote.unavailable?(id)
44
+
45
+ Spree::Calculator::Shipping::DoordashQuote.clear_unavailable(id)
46
+ self.warnings |= [{
47
+ code: 'doordash_quote_unavailable',
48
+ message: 'We could not get a DoorDash delivery quote for this address.'
49
+ }]
50
+ end
51
+ end
52
+
53
+ Order.prepend OrderDecorator
54
+ end
@@ -1,5 +1,5 @@
1
1
  module SpreeDoordash
2
- VERSION = '0.1.2'.freeze
2
+ VERSION = '0.1.3'.freeze
3
3
 
4
4
  def gem_version
5
5
  Gem::Version.new(VERSION)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_doordash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amit Solanki
@@ -120,6 +120,7 @@ files:
120
120
  - app/jobs/spree_doordash/delivery_dispatch_job.rb
121
121
  - app/jobs/spree_doordash/delivery_webhook_job.rb
122
122
  - app/models/spree/calculator/shipping/doordash_quote.rb
123
+ - app/models/spree/order_decorator.rb
123
124
  - app/models/spree_doordash/credential.rb
124
125
  - app/models/spree_doordash/delivery_mapping.rb
125
126
  - app/models/spree_doordash/location_mapping.rb