wkh237 / react-native-fetch-blob

A project committed to making file access and data transfer easier, efficient for React Native developers.
MIT License
2.61k stars 1.59k forks source link

Error normalization (ongoing, tracking issue) #460

Open lll000111 opened 7 years ago

lll000111 commented 7 years ago

I just wrote a function that renames a temporary file created by a stream to its final name (in my case the SHA-256 hash of the contents of the file is going to be the final name, so I don't know what it is until it's all there).

I use RNFetchBlob.fs.mv (link to fs.js).

That lead me to a hunt for the actual Java and iOS (Swift) methods used to rename the file (File.renameTo for Java, moveItemAtURL for iOS).

There is zero documentation of errors, and when I get down to it, I don't really know what the errors are that I'm going to get.

Some methods have a fixed string set in this project's source code - but a different one for Java and iOS versions? For example, promise.reject("ReadFile Error", err.getLocalizedMessage()); is used in readFile (Android) - but on iOS that string seems to be reject(@"RNFetchBlob failed to read file", err, nil); - and readStream has completely different error messages yet again...

By the way (see readStream, this function seems to have a single error message about "encoding" for everything incl. FileNotFound exceptions? ("Failed to convert data to "+encoding+" encoded string, this might due to the source data is not able to convert using this encoding.")

In my case I could not simply invoke the mv and handle an error - because I simply don't know what "destination file exists" errors are going to look like.

It would be nice to have documentation about (common) errors (like "file not found" for read file operations or "file exists" for write operations - and do they silently overwrite or not?) as well as a way to catch those common errors (fixed Error.name or Error.message (beginning of the string) independent of the platform.

Yeah, that could be a major effort :-( Just a suggestion.

It's of course possible to program without any such assurances, but then, in the specific example to have one, I have to do more I/O and use other functions to check for existence before the actual operation - which causes another issue because now I need to make the function "atomic", since a check for existence followed by access might be interrupted my another I/O event that creates the file in the meantime, even in single-threaded Javascript that's not safe. That's why it would be nice to be able to just do an operation and handle any errors later (like node.js philosophy for file operations).

lll000111 commented 7 years ago

It's a little more equal now, but could still use more improvements, I'll leave this open. I'll work on it.

lll000111 commented 7 years ago

@wkh237 I've prepared a commit that I still need to do at least basic tests on: https://github.com/lll000111/react-native-fetch-blob/commit/7258eb217e2024e68bcf35a043cbecf96ff8b9da

But you may already want to have a look.

wkh237 commented 7 years ago

@lll000111 , you can simply use our dev repo and run the test cases. I think that might be helpful 👍

lll000111 commented 7 years ago

@wkh237 I don't see a file tests.js anywhere? The instructions in the test repo README include

Before install the test app, you should replace constants DROPBOX_TOKEN and TEST_SERVER_URL in src/test/tests.js

After cloning the submodule I ran find . -name tests.js and there is no such file anywhere.

I found it in ./test/test-init.js, I think the README (https://github.com/wkh237/react-native-fetch-blob-dev/blob/master/README.md) has to be changed?

wkh237 commented 7 years ago

Yeah, you're right, it should be test-init.js 👍

lll000111 commented 7 years ago

Status as of PR https://github.com/wkh237/react-native-fetch-blob/pull/489

NOTE: fs functions only.

I will update the Wiki with these details when/if those updates end up in a release.

Goal is the schema supported by react-native - see the Android Java implementation of the reject method (the iOS version does the same): The error object will have a code property in addition to the usual ones. The default code if none is sent by the native code module is "EUNSPECIFIED".

In addition to the general "EUNSPECIFIED" the linked PR introduces the following codes, on both Android and iOS unless noted otherwise. If there is a code marked "Android/iOS only" it doesn't mean that the other platform doesn't raise an error, only that it won't have that code.

The listed errors are not the only errors those functions return, of course, everything else is just "EUNSPECIFIED".

I use the ancient standard of Unix error codes whenever there is an appropriate one.

All functions also have "EINVAL" error codes when a mandatory parameter is missing, and those are TypeError instances, all others are Error.

fs.createFile

fs.writeFile, fs.appendFile

fs.readFile

fs.readStream ("error" events)

fs.writeStream

fs.hash

fs.mkdir

fs.ls

fs.slice

Functions not mentioned here have nevertheless been updated, but they don't (yet) follow the scheme to provide a code property and still only provide text (which is different on iOS and Android). For example, unlink now throws errors in cases where it ignored them previously and still reported success.