Page MenuHomePhabricator

[native] Update local video messages with realized `thumbnailURI` upon successful upload
ClosedPublic

Authored by atul on Aug 29 2022, 5:26 PM.
Tags
None
Referenced Files
Unknown Object (File)
Sun, Apr 21, 4:50 PM
Unknown Object (File)
Sun, Apr 21, 4:50 PM
Unknown Object (File)
Sun, Apr 21, 4:50 PM
Unknown Object (File)
Sun, Apr 21, 4:50 PM
Unknown Object (File)
Sun, Apr 21, 4:49 PM
Unknown Object (File)
Sun, Apr 21, 4:46 PM
Unknown Object (File)
Mon, Apr 15, 1:04 AM
Unknown Object (File)
Sat, Apr 13, 1:26 AM
Subscribers
None

Details

Summary

Modify payload for updateMultimediaMessageMedia action to include realized thumbnailURI upon successful upload when mediaType === "video".

We need to update the thumbnailURI because the temporary local thumbnail gets unlinked after a successful upload.


Depends on D4979

Test Plan

Used the Redux remote dev tools in Chrome to observe the local media message (and the contents of the local media array within) and make sure that the local thumbnailURI got replaced with the "realized" (aka server) thumbnailURI as expected (on dispatch of updateMultimediaMessageMediaActionType)

Diff Detail

Repository
rCOMM Comm
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

atul requested review of this revision.Aug 29 2022, 5:36 PM

I'm a bit confused on what it means if uploadResult (or uploadThumbnailResult) fails. Is it just to push a failed 'upload' step to steps? @ashoat sort of mentioned this in D932, although he was referring to uploadThumbnailResult only.

If we assign uploadResult and uploadThumbnailResult in a try / catch block, one would assume any reason for it to fail would be caught in there. to be sure, though, we could add an invariant check after the try / catch block. But to have no checks seems...not great. Instead, we have a lot of conditionals that are lengthy and confusing, like on line 713. By this point, the media / related thumbnail, if any, should be uploaded, and if they aren't, we should capture this in error cases instead of having to wrap expected behavior in long conditionals. Even in the original code, there are long sections of code dependent on uploadResult being truthy.

I realize that later on in uploadFile we push an 'upload' step to steps which contains a success property that is dependent on !!uploadResult. So perhaps we don't want to simply exit if the thumbnail upload/media upload failed. But for now, we can definitely refactor this better since the if statement on line 713 is quite confusing and long sections of indented code in a case that is 'expected' can be hard to read.

That is, we expect the upload to occur correctly in uploadFile. So, we should only have conditional checks for if this doesn't occur (like if !uploadResult is true), and handle those error cases accordingly. As a reader, that makes more sense to me, since the non-indented, non-conditional portions of the code represent the "expected" behavior of uploadFile. Whereas currently we have long sections of indented and conditional code if uploadResult is true, which should just be expected behavior.

Maybe this is not making much sense, and someone more senior like @tomek or @jacek may disagree, so I welcome their feedback as well. Plus @ashoat said to keep the refactoring to a minimum, so if a clean solution can't be found quickly (like using a separate function or taking care of error cases first) it is not worth it at the moment.

There is an invariant on line 736, which makes sense there since if that if statement is entered the thumbnail should have been uploaded. But it feels like the differences between the upload succeeding vs. the upload failing need to be factored better.

This revision now requires changes to proceed.Aug 29 2022, 10:43 PM
atul requested review of this revision.EditedAug 30 2022, 10:02 AM
In D4980#144730, @abosh wrote:

I'm a bit confused on what it means if uploadResult (or uploadThumbnailResult) fails. Is it just to push a failed 'upload' step to steps? @ashoat sort of mentioned this in D932, although he was referring to uploadThumbnailResult only.

It means we failed to upload the primary media or thumbnail media.

As of this diff we handle the success/failure of an upload as all or nothing (both primary media and thumbnail successfully uploaded = success, everything else is failure).

In the future we can break out more complex error cases (eg if only thumbnail upload fails, we can avoid re-uploading the primary media) and have separate entries in the media mission steps.

Breaking those steps apart will be addressed as part of related refactoring later in the stack. For now, we're "sneaking" the thumbnail upload step with the existing primary upload step.

If we assign uploadResult and uploadThumbnailResult in a try / catch block, one would assume any reason for it to fail would be caught in there. to be sure, though, we could add an invariant check after the try / catch block. But to have no checks seems...not great. Instead, we have a lot of conditionals that are lengthy and confusing, like on line 713. By this point, the media / related thumbnail, if any, should be uploaded, and if they aren't, we should capture this in error cases instead of having to wrap expected behavior in long conditionals. Even in the original code, there are long sections of code dependent on uploadResult being truthy.

Aren't we doing that?

...
} catch (e) {
      uploadExceptionMessage = getMessageForException(e);
      onUploadFailed(localMediaID, 'upload failed');
      mediaMissionResult = {
        success: false,
        reason: 'http_upload_failed',
        exceptionMessage: uploadExceptionMessage,
      };
    }
...

I realize that later on in uploadFile we push an 'upload' step to steps which contains a success property that is dependent on !!uploadResult. So perhaps we don't want to simply exit if the thumbnail upload/media upload failed. But for now, we can definitely refactor this better since the if statement on line 713 is quite confusing and long sections of indented code in a case that is 'expected' can be hard to read.

What is confusing about the if statement on line 713? We just break out the prerequisites for success for mediaType === 'video' and mediaType === 'photo'?

We definitely don't want to exit if the thumbnail upload/media upload failed because we have steps that occur afterwards regardless of success/failure (media report, cleaning up temporary files, etc).

That is, we expect the upload to occur correctly in uploadFile. So, we should only have conditional checks for if this doesn't occur (like if !uploadResult is true), and handle those error cases accordingly. As a reader, that makes more sense to me, since the non-indented, non-conditional portions of the code represent the "expected" behavior of uploadFile. Whereas currently we have long sections of indented and conditional code if uploadResult is true, which should just be expected behavior.

We have code that needs to run whether or not there is a failure (media mission, cleaning up temporary files, etc). Not sure there's a cleaner way to structure the changes here barring a more invasive refactor (which is "scheduled" for later in the stack)

There is an invariant on line 736, which makes sense there since if that if statement is entered the thumbnail should have been uploaded.

The invariant is just there to appease flow, it should already know a thumbnail exists as a consequence of the outer conditional

(processedMedia.mediaType === 'video' && uploadResult && uploadThumbnailResult)

But it feels like the differences between the upload succeeding vs. the upload failing need to be factored better.

Agree that there's plenty of room for refactoring once we have things working end-to-end.

Yeah, the main thing that is confusing is uploadResult and uploadThumbnailResult (if video) being falsey after the try / catch block. Like as a reader this feels complicated. To me, I think the upload failing be handled in one section before moving onto the rest of the code where we can assume the uploads were successful, and we no longer have to introduce truthy checks. Obviously we may still need checks later on, but if we broke a lot of things into methods we could let this be handled outside of this huge function.

But you're right, we can't just do this now since we have other code that needs to run regardless of if the upload succeeded or failed. At least, not without a large refactor. But it does feel like a lot of the code in uploadFile could be broken into its own method since it accomplishes one specific thing, and that should be taken care of later on.

tomek added inline comments.
native/input/input-state-container.react.js
713–718 ↗(On Diff #16109)

This can be simplified because in both cases we should check uploadResult

This revision is now accepted and ready to land.Aug 31 2022, 4:14 AM
native/input/input-state-container.react.js
713–718 ↗(On Diff #16109)

I thought about reducing the condition but decided against it because I thought the more verbose version where we break out the cases explicitly makes things more obvious to the reader?

I personally prefer processedMedia.mediaType === 'video' to be written out explicitly instead of being an implicit case that requires further context to understand

Not sure if we have a "rule" or whatnot here, but happy to go back and reduce the condition in a separate diff if that's what people prefer.

In D4980#144937, @abosh wrote:

Yeah, the main thing that is confusing is uploadResult and uploadThumbnailResult (if video) being falsey after the try / catch block. Like as a reader this feels complicated. To me, I think the upload failing be handled in one section before moving onto the rest of the code where we can assume the uploads were successful, and we no longer have to introduce truthy checks. Obviously we may still need checks later on, but if we broke a lot of things into methods we could let this be handled outside of this huge function.

Agree there's room for things to be cleaned up, but I'm trying to make changes that are as minimal as possible to get things working. Can handle refactoring once things are working end-to-end.

native/input/input-state-container.react.js
713–718 ↗(On Diff #16109)

That makes sense. If you feel that your approach is ok, then let's keep it. For me, the less code we have, the easier it is to read, understand and maintain it (usually). In this case, if we want video to be explicit, we can also do that:

uploadResult && !(processedMedia.mediaType === 'video' && !uploadThumbnailResult)

which is a little more complicated.

rebase around other diffs before landing