Skip to content

refactor(app): Drive device reset settings fully from client#17921

Merged
SyntaxColoring merged 8 commits into
chore_release-8.4.0from
device_reset_refactors
Mar 28, 2025
Merged

refactor(app): Drive device reset settings fully from client#17921
SyntaxColoring merged 8 commits into
chore_release-8.4.0from
device_reset_refactors

Conversation

@SyntaxColoring
Copy link
Copy Markdown
Contributor

@SyntaxColoring SyntaxColoring commented Mar 28, 2025

Overview

This refactors the desktop app and ODD components for doing a device reset.

Goes towards EXEC-1281.

Background

Historically, all of the reset options were server-defined. The client would fetch the available options from the server (via GET /settings/reset/options) and display them as-is, with server-defined text.

More recently, that model has been stretched beyond its limits:

  • We want the UI to be localized with frontend-defined strings.
  • We want frontend to control the options' ordering and grouping.
  • We want some reset options, like onDeviceDisplay, to be special: they should be hidden from the UI and selected implicitly depending on the user's selection of other options.
  • We want the server to implement some of these reset options through separate endpoints, instead of all of them through /settings/reset. (See e.g. this discussion, though I sometimes have second thoughts about this.)

So we have been in a weird in-between where we were getting the dynamic list of options from the server, but still making strong assumptions about what that list would contain. This PR rewrites it to be fully client-decided.

Changelog

This is intended to not have any user-facing change.

The one "accidental" change that I'm aware of is that if the client is newer than the server and has an extra reset option, it will now be displayed, and filtered out of the HTTP request. Formerly, it would not even be displayed.

Test Plan and Hands on Testing

  • Make sure all the options are still there, with the grouping and ordering that they had before.
  • Make sure the Flex-specific "select all" button still works as it did before.
  • The network request is not type-safe, so test it on an actual server and make sure it succeeds.

Review requests

Specific things: see review comments below.

Also general things: React and TypeScript idioms, state structuring, and so on.

Risk assessment

Medium. This code is used for last-ditch troubleshooting and system recovery, so it needs to always work.

Possible failures include:

  • Sending the wrong options if I made a typo in the mapping of UI state -> HTTP request

@SyntaxColoring SyntaxColoring changed the base branch from edge to chore_release-8.4.0 March 28, 2025 15:28
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 28, 2025

Codecov Report

Attention: Patch coverage is 0% with 251 lines in your changes missing coverage. Please review.

Project coverage is 25.43%. Comparing base (e1eac6f) to head (7922b38).

Files with missing lines Patch % Lines
...edTab/AdvancedTabSlideouts/DeviceResetSlideout.tsx 0.00% 186 Missing ⚠️
...ganisms/ODD/RobotSettingsDashboard/DeviceReset.tsx 0.00% 65 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                   Coverage Diff                   @@
##           chore_release-8.4.0   #17921      +/-   ##
=======================================================
- Coverage                25.82%   25.43%   -0.39%     
=======================================================
  Files                     3005     2936      -69     
  Lines                   227433   225275    -2158     
  Branches                 18912    19272     +360     
=======================================================
- Hits                     58724    57309    -1415     
+ Misses                  168696   167953     -743     
  Partials                    13       13              
Flag Coverage Δ
app 3.20% <0.00%> (-0.01%) ⬇️
protocol-designer 18.75% <0.00%> (-0.01%) ⬇️
step-generation 4.34% <0.00%> (-0.01%) ⬇️
system-server ?
update-server ?
usb-bridge ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ancedTab/AdvancedTabSlideouts/DeviceResetModal.tsx 0.00% <ø> (ø)
...ganisms/ODD/RobotSettingsDashboard/DeviceReset.tsx 0.00% <0.00%> (ø)
...edTab/AdvancedTabSlideouts/DeviceResetSlideout.tsx 0.00% <0.00%> (ø)

... and 69 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment on lines +139 to +140
// handleClearData assumes options are loaded.
!areServerOptionsLoaded
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Disabling this button if the server options haven't been loaded yet is important to prevent this:

  1. The slideout slides out
  2. We try fetching the available options from the server, but it's slow
  3. The user selects a bunch of checkboxes and hits the submit button
  4. The request for available options from the server still hasn't returned, so serverOptions is still [], so it looks to buildResetRequest() as if all of the options are too new for the server to understand, so it filters them all out. The robot restarts, but we haven't actually reset anything.

Is there a safer, more foolproof way to deal with this?

Failing that, is there an easy way to throw in a loading indicator of some sort if this button is disabled because the server options haven't been loaded yet?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unfortunately, no, I don't think there is an easy way of doing this given how this network request is tied to a selector in the manner that it is. I think the actual solution would be to modern the way we get this data, by placing it in a bone fide hook that wraps react-query, which calls for the settings.

Barring that, I think we could "fake" a loading state by initializing the settings to something like 'INITIALIZING', and explicitly check for the presence of that string, showing loading state as needed.

Comment on lines +483 to +491
// If the server is older than this app, we might send it options that it doesn't
// understand, which it could choke on. Filter to send only the options that the
// server advertises.
const serverResetOptionIds = serverResetOptions.map(o => o.id)
requestToReturn = Object.fromEntries(
Object.entries(requestToReturn).filter(([k, _v]) =>
serverResetOptionIds.includes(k)
)
)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We were accounting for this before by not even showing the options that were missing from the server's advertisement. Now all of the options are always shown, so we need to filter them here.

@SyntaxColoring SyntaxColoring requested review from a team and mjhuff and removed request for a team March 28, 2025 17:16
@SyntaxColoring SyntaxColoring marked this pull request as ready for review March 28, 2025 17:17
@SyntaxColoring SyntaxColoring requested review from a team as code owners March 28, 2025 17:17
@SyntaxColoring SyntaxColoring requested a review from a team as a code owner March 28, 2025 17:21
@SyntaxColoring SyntaxColoring requested review from a team and removed request for a team March 28, 2025 18:08
Copy link
Copy Markdown
Contributor

@mjhuff mjhuff left a comment

Choose a reason for hiding this comment

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

Looks great, thanks! Nice and clean. If you're keen on adding explict loading state in this PR, we could do so, but I don't think it's necessary for merging.

Copy link
Copy Markdown
Contributor

@TamarZanzouri TamarZanzouri left a comment

Choose a reason for hiding this comment

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

YOU DID IT!!!! nice job Max!

@SyntaxColoring SyntaxColoring merged commit 8ec0aa2 into chore_release-8.4.0 Mar 28, 2025
37 checks passed
@SyntaxColoring SyntaxColoring deleted the device_reset_refactors branch March 28, 2025 19:07
sfoster1 pushed a commit that referenced this pull request May 5, 2025
…8244)

## Overview

Fixes RQA-4161.

In PR #17921, I apparently wrote a stupid bug where I just outright
forgot to send `runsHistory: true` when that option was selected in the
UI.

## Test Plan and Hands on Testing

* [x] Push it to a Flex and test manually.
* [x] Manually review the reset code (both ODD and desktop app) for
other similar bugs

[This exact case *was* already unit
tested.](https://github.com/Opentrons/opentrons/blob/00b83c9c5102041e5a22fb615f1fd660ca44c796/app/src/organisms/ODD/RobotSettingsDashboard/__tests__/DeviceReset.test.tsx#L72)
However, there appears to be some kind of bug in the test causing that
assertion to always pass, no matter how wrong the value is. ~~I suspect
this has to do with a misconfigured mock somewhere, but I haven't
figured it out yet.~~ See [comment
below](#18244 (comment)).

## Review requests

Any ideas for making the `buildResetRequest()` code inherently safer?

See [comment
below](#18244 (comment))
for the test bug. Does this fix make sense?

## Risk assessment

The fix is low-risk. I need therapy for the test bug, though.
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.

3 participants