Skip to content

SF-3835 Do not allow uploading training data when offline#3985

Open
pmachapman wants to merge 2 commits into
masterfrom
fix/SF-3835
Open

SF-3835 Do not allow uploading training data when offline#3985
pmachapman wants to merge 2 commits into
masterfrom
fix/SF-3835

Conversation

@pmachapman

@pmachapman pmachapman commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Rather than changing the error message, I took the same approach as the chapter audio dialog and disabled uploading when offline


This change is Reviewable

@pmachapman pmachapman added the will require testing PR should not be merged until testers confirm testing is complete label Jul 7, 2026
@pmachapman
pmachapman temporarily deployed to screenshot_diff July 7, 2026 03:06 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

📸 Screenshot diff deployed! (1 change)

View the visual diff at: https://pr-3985--sf-screenshot-diffs.netlify.app

@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.04%. Comparing base (c2631c7) to head (40dfa36).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3985   +/-   ##
=======================================
  Coverage   81.03%   81.04%           
=======================================
  Files         645      645           
  Lines       41531    41541   +10     
  Branches     6743     6745    +2     
=======================================
+ Hits        33656    33668   +12     
+ Misses       6773     6771    -2     
  Partials     1102     1102           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@Nateowami Nateowami left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Rather than changing the error message, I took the same approach as the chapter audio dialog and disabled uploading when offline

The problem is: All errors when uploading a file (regardless of cause; they could be network related, bad file, or something else) are attributed to a malformed file, and the user is shown this message:

Your file needs to have two columns, and every row must have a value in both columns. Please fill in any empty cells and try again.

The error message should be shown only when a specific error code (could be HTTP status code, or something returned via JSONRPC) indicates a problem with the file, and the error message should contemplate the same breadth of potential problem as the code that triggers it (for example, I don't know what happens when you upload an unsupported file type, but if it can trigger the message, then the message should specify to check that the file type is one of the supported types). Disabling upload when we know we're offline doesn't stop network errors from occurring; it only reduces their frequency.

Also, we generally avoid disabling buttons, except when a feature is unavailable due to the user being offline, and then only disabling them in the context where we would show a message saying the user is offline. So in general it would be better to let the user open the upload dialog, but show a message about the user being offline inside the dialog.

@Nateowami made 1 comment.
Reviewable status: 0 of 7 files reviewed, all discussions resolved.

@Nateowami Nateowami self-assigned this Jul 15, 2026

@pmachapman pmachapman left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The error message should be shown only when a specific error code (could be HTTP status code, or something returned via JSONRPC) indicates a problem with the file, and the error message should contemplate the same breadth of potential problem as the code that triggers it (for example, I don't know what happens when you upload an unsupported file type, but if it can trigger the message, then the message should specify to check that the file type is one of the supported types). Disabling upload when we know we're offline doesn't stop network errors from occurring; it only reduces their frequency.

I have updated this PR to catch offline and format errors, and display an appropriate error message. I tried to do it in a way that would not break the other code dependent on the upload returning undefined on error.

Also, we generally avoid disabling buttons, except when a feature is unavailable due to the user being offline, and then only disabling them in the context where we would show a message saying the user is offline. So in general it would be better to let the user open the upload dialog, but show a message about the user being offline inside the dialog.

I agree. I made it offline because all the rest of the form cannot be used if offline, and most of the form was already disabled if offline - uploading the training sources offline, then connecting to click submit seems like an odd user workflow. If connectivity is interrupted during upload, then an offline error will be displayed.

@pmachapman made 1 comment.
Reviewable status: 0 of 12 files reviewed, all discussions resolved (waiting on Nateowami).

@pmachapman
pmachapman temporarily deployed to screenshot_diff July 19, 2026 23:16 — with GitHub Actions Inactive

@Nateowami Nateowami left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@Nateowami made 1 comment.
Reviewable status: 0 of 12 files reviewed, 1 unresolved discussion (waiting on pmachapman).


src/SIL.XForge.Scripture/ClientApp/src/xforge-common/file.service.ts line 129 at r2 (raw file):

   * @param returnUndefinedForErrors If true, errors will be rethrown and undefined will only be returned if offline.
   */
  async onlineUploadFileOrFail(

Wouldn't the canonical way to do this be to have onlineUploadFile and tryOnlineUploadFile (the latter of which wraps the former in try/catch)?

The current method signature creates code that does not make clear what is happening:

const audioUrl: string | undefined = await this.fileService.onlineUploadFileOrFail(
  FileType.Audio,
  this.data.projectId, // can't verify that this arg is in the correct position and didn't get swapped with another string arg
  TextAudioDoc.COLLECTION, // can't verify that this arg is in the correct position and didn't get swapped with another string arg
  objectId(), // can't verify that this arg is in the correct position and didn't get swapped with another string arg
  this.audio!.blob!, // probably correct position since it's a Blob so it would fail type check if it were in wrong position
  this.audio!.fileName!, // can't verify that this arg is in the correct position and didn't get swapped with another string arg
  true, // no idea what this is for unless I look up method
  true // no idea what this is for unless I look up method
);

@pmachapman pmachapman left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@pmachapman made 1 comment.
Reviewable status: 0 of 12 files reviewed, 1 unresolved discussion (waiting on Nateowami).


src/SIL.XForge.Scripture/ClientApp/src/xforge-common/file.service.ts line 129 at r2 (raw file):

Previously, Nateowami wrote…

Wouldn't the canonical way to do this be to have onlineUploadFile and tryOnlineUploadFile (the latter of which wraps the former in try/catch)?

The current method signature creates code that does not make clear what is happening:

const audioUrl: string | undefined = await this.fileService.onlineUploadFileOrFail(
  FileType.Audio,
  this.data.projectId, // can't verify that this arg is in the correct position and didn't get swapped with another string arg
  TextAudioDoc.COLLECTION, // can't verify that this arg is in the correct position and didn't get swapped with another string arg
  objectId(), // can't verify that this arg is in the correct position and didn't get swapped with another string arg
  this.audio!.blob!, // probably correct position since it's a Blob so it would fail type check if it were in wrong position
  this.audio!.fileName!, // can't verify that this arg is in the correct position and didn't get swapped with another string arg
  true, // no idea what this is for unless I look up method
  true // no idea what this is for unless I look up method
);

Done. Thank you!

@pmachapman
pmachapman temporarily deployed to screenshot_diff July 20, 2026 23:31 — with GitHub Actions Inactive

@Nateowami Nateowami left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@Nateowami reviewed 8 files and all commit messages, and made 3 comments.
Reviewable status: 8 of 12 files reviewed, 3 unresolved discussions (waiting on pmachapman).


src/SIL.XForge.Scripture/ClientApp/src/xforge-common/file.service.ts line 129 at r2 (raw file):

Previously, pmachapman (Peter Chapman) wrote…

Done. Thank you!

Thanks for fixing this. Seems like "or fail" doesn't belong in the method name since it no longer swallows errors.


src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/training-data/training-data-multi-select.component.ts line 70 at r3 (raw file):

  openUploadDialog(): void {
    if (this.activatedProjectService.projectId == null) return;
    if (!this.appOnline) return;

Isn't this the wrong place for a check like this? Seems like the caller should be doing the check. Or alternatively the dialog should open and say it's not available offline. Doing nothing doesn't seem like the correct approach.


src/SIL.XForge.Scripture/ClientApp/src/xforge-common/file.service.ts line 145 at r3 (raw file):

      } catch {}
    }
    return undefined;

I really don't think defining undefined as meaning offline is good practice. It's a value you have to remember to check for, it's not obvious what it means, and it's the value that is most likely to accidentally pop up in this language, meaning findOrUpdateCache mistakenly returning undefined would not be improbable. If we want to create a value to check for instead of an exception, a string (that the type system is aware of) would not pop up accidentally, and would be obvious what it means.

That said, I really don't think this method should be checking if we're online at all. Network conditions are inherently unpredictable, and therefore we can't use guards to completely prevent requests that will fail for network reasons. Therefore I think it makes most sense to put such guards at the UI level (e.g. disabled buttons with explanatory text), and not have business logic methods named doThing that really mean doThingButOnlyUnderCertainCircumstances, and to consider such guards nice-to-have, not absolute requirements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

will require testing PR should not be merged until testers confirm testing is complete

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants