Skip to content

Conversation

@georgeweiler
Copy link
Contributor

@georgeweiler georgeweiler commented Feb 3, 2026

Explanation

This is refactor that centralizes four areas of the RampsController: reset logic, abort handling, region validation, and "is result current" checks. The changes reduce a significant amount of duplication.

Changes

  • Reset flow

    • Introduce DEPENDENT_RESOURCE_KEYS and resetResource() so “reset one resource” is defined once and driven by getDefaultRampsControllerState().
    • Refactor resetDependentResources() to call getDefaultRampsControllerState() once and loop over the keys instead of manual assignments.
    • Use resetResource(state, 'paymentMethods') in setSelectedProvider and setSelectedToken so payment methods are fully reset (including isLoading and error), fixing stale loading/error state when changing or clearing provider/token.
  • Typing

    • type reset helpers to accept Draft<RampsControllerState> so they can be used inside update() without casts and avoid TS2589.
    • Centralize the state.requests cast in a single #mutateRequests() helper; #removeRequestState and #updateRequestState use it.
  • Abort handling

    • Add #abortDependentRequests() to abort in-flight requests for dependent resources (providers, tokens, paymentMethods, quotes) and clear their request state.
    • Call it from #cleanupState() and from setUserRegion() when the region changes, before clearing pending counts and resetting state, so “reset” actually cancels dependent work.
    • Use DEPENDENT_RESOURCE_KEYS (and a DEPENDENT_RESOURCE_KEYS_SET for lookup) in #clearPendingResourceCountForDependentResources() so the dependent-resource list is defined in one place.
  • Validation and region checks

    • Add #requireRegion(): string and use it in setSelectedProvider, setSelectedToken, and startQuotePolling with a single region-required error message.
    • Add #isRegionCurrent(normalizedRegion: string): boolean and use it in all isResultCurrent callbacks for getTokens, getProviders, getPaymentMethods, and getQuotes.

Result

Single source of truth for default/reset state and for “dependent resources,” less duplicated code, correct clearing of paymentMethods (and quotes.selected) on region or selection changes, real abort of in-flight dependent requests on reset, and no Draft casts at call sites.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Touches request aborting/caching and resource reset logic that can affect loading/error state and stale data suppression across providers/tokens/payment methods/quotes. Changes are well-covered by updated unit tests but could alter timing/behavior in edge cases around in-flight requests.

Overview
Refactors RampsController to centralize reset behavior for region-dependent resources via DEPENDENT_RESOURCE_KEYS and a shared resetResource() helper, and uses this when clearing provider/token selection to also reset paymentMethods loading/error.

Improves request lifecycle handling by tagging pending requests with resourceType, adding #abortDependentRequests() (invoked on region change and controller cleanup) and tightening loading ref-counting so aborted requests don’t incorrectly clear resource isLoading. Region validation and “is result current” checks are consolidated into #requireRegion()/#is*Current() helpers, and getCountries() defensively stores [] when the service returns a non-array.

Updates tests and changelog to cover the new reset/abort semantics and error messages (including expecting in-flight quotes to reject when aborted).

Written by Cursor Bugbot for commit 4a32b8c. This will update automatically on new commits. Configure here.

@georgeweiler
Copy link
Contributor Author

cursor review

@georgeweiler georgeweiler changed the title refactor: clean up request logic and state clearning mechanism refactor: clean up request logic and state clearing mechanism Feb 3, 2026
@georgeweiler georgeweiler marked this pull request as ready for review February 3, 2026 13:13
@georgeweiler georgeweiler requested review from a team as code owners February 3, 2026 13:13
@georgeweiler georgeweiler changed the title refactor: clean up request logic and state clearing mechanism refactor(ramps): streamline RampsController reset and abort logic Feb 3, 2026
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

* 2. **Cache hit** – If valid, non-expired data exists in state.requests for
* this key and forceRefresh is not set, returns that data without fetching.
*
* 3. **New request** – If options.resourceType is set, aborts any other
Copy link
Contributor

Choose a reason for hiding this comment

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

The JSDoc says executeRequest aborts other in-flight requests for the same resourceType, but I don't see that happening in the implementation. It only stores resourceType on the pending request so #abortDependentRequests can find it later. Should the doc say something more like "stores resourceType for later abort when region changes" or did you mean to add logic here to abort same-resource requests when a new one starts?

});
});

it('does not throw when updating resource field and resource is null (defensive)', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This test name says "does not throw" but the assertion expects it to throw with "Cannot set property of null". Should the name be something like "throws when state.countries is null" instead?

}
}

#abortDependentRequests(): void {
Copy link
Contributor

Choose a reason for hiding this comment

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

When this aborts requests, it doesn't decrement #pendingResourceCount or set loading to false. I see the finally block skips cleanup when aborted is true, and resetDependentResources resets everything after, so loading won't get stuck. Just wanted to confirm that's the intended flow and we're not relying on the aborted request's finally block to clean up loading state?

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