Hey, Could someone maybe point me toward some resources that describe how to properly write (unit) tests for re-frame pages or components? What would be best practices here?
There's a documentation page on that topic: https://day8.github.io/re-frame/Testing/
thx ;)
I have the following re-frame handler with which I’m trying to upload a video from a local uri in mobile.
(reg-event-fx
:upload-shot-video
(fn [coeffects _]
(prn "uploading video")
(let [video {:type "video/mov"
:uri (-> coeffects :db :shot-video-uri)}
body (js/FormData.)]
(prn "uri is " (-> coeffects :db :shot-video-uri))
(.append body "video" video)
{:http-xhrio {:method :post
:uri (str "<http://d18a6571c2e5.ngrok.io>" "/api/upload-shot-video")
:body body
:on-success [:upload-success]
:on-failure [:upload-error]
:response-format (edn/edn-response-format)}})))
Here’s my handler in the server:
(defn upload-shot-video [req]
(prn "uploading video")
(prn "video is! " (-> req :params))
(prn "video is " (-> req :body))
(<http://clojure.java.io/copy|clojure.java.io/copy> (-> req :body) (<http://clojure.java.io/file|clojure.java.io/file> "./resources/public/video.mov"))
(r/response {:res "okay!"}))
Even though an Input stream shows up in (-> req :body), and is saved as a file “./resources/public/video.mov”, the file it is saved as is showing up as a file of 0 bytes, suggesting that the data is never sent to the server. How to fix this error?I've seen suggestions for you to check the Network tab in your browser's DevTools. Have you done that? Is the size there larger than 0? If so, then it's something with your server.
But your event handler is still strange.
Your video
is just a map. What do you expect to happen? That something downloads the video with that :type
from that :uri
?
I have checked dev tools but haven’t seen video uploads there after sending the request. I expect the video to be in the inputstream that’s being copied to the file on the server
But what video? Your code does not upload any video. It is trying to upload the map {:type "video/mov", :uri ...}
. This map is not a video.
Here's what your code is essentially doing:
{:http-xhrio {..., :body (doto (js/FormData.) (.append "video" {:type "video/mov", :uri ...}))}}
Nowhere does this code refer to anything video-like, which should be a binary blob.:uri (-> coeffects :db :shot-video-uri)
Here the video uri is passed into the map
The video uri is saved in another handler when the video is done recording
OK. But your code has to use the video data, not its URL.
It has to be something like :body (doto (js/FormData.) (.append "video" binary-video-blob))
.
This is a .mov uri
(-> coeffects :db :shot-video-uri)
What you have is a URL. An address of a thing. What you need is the binary data to which that URL links. The thing itself, not its address.
Got it thanks. Do you know how to convert it?
I wouldn't call it "convert".
In any case, depends. If you initially upload the video to :shot-video-url
somewhere in the same app, then just preserve the data as well and feed it to js/FormData
.
If it's not the same code and you have nothing but the URL, then you will have to download the video and then upload it. Or, if possible, just create adapt your HTTP handler so it can download the video from the URL itself to avoid sending the binary data over the wire one extra time.
my question is. Suppose I have the .mov uri of the local file just shot from the phone
How to get the blob behind this uri?
If your code is executed by a browser, you cannot just magically get that file by a local URL. It's a matter of security - otherwise, any web page could yank any file from your drive.
You will have to use <input type="file>
and manually specify that file.
it’s a react native app
And by "local URL" I mean of course the file://
protocol.
Sorry, I don't have any experience with either react native or mobile development in general.
I have converted it into a blob using js/fetch
(reg-event-fx
:upload-shot-video-server
(fn [coeffects [_ blob]]
(let [body (js/FormData.)]
(.append body "video" blob)
{:http-xhrio {:method :post
:uri (str "<http://d18a6571c2e5.ngrok.io>" "/api/upload-shot-video")
:body body
:on-success [:upload-success]
:on-failure [:upload-error]
:response-format (edn/edn-response-format)}}))
)
(reg-event-fx
:upload-shot-video
(fn [coeffects _]
(prn "uploading video")
(let [response (js/fetch (-> coeffects :db :shot-video-uri))]
(try
(go
(let [blob (<p! (. (<p! response) blob))]
(js/console.log "blob is " blob)
(dispatch [:upload-shot-video-server blob])))
(catch js/Error e (js/console.log "Error is " e)))
{})))
And the handler is as follows:
(defn upload-shot-video [req]
(prn "uploading video")
(prn "video is! " (-> req :params))
(prn "video is " (-> req :body))
(<http://clojure.java.io/copy|clojure.java.io/copy> (-> req :body) (<http://clojure.java.io/file|clojure.java.io/file> "./resources/public/video.mov"))
(let [filename (str (rand-str 100) ".mov")]
(s3/put-object
:bucket-name "humboi-videos"
:key filename
:file "./resources/public/video.mov"
:access-control-list {:grant-permission ["AllUsers" "Read"]})
(db/add-video {:name (-> req :params :name)
:uri (str "<https://humboi-videos.s3-us-west-1.amazonaws.com/>" filename)}))
(r/response {:res "okay!"}))
But the saved file is still 0 bytes
One small issue - don't call dispatch
in event handler. Use effect handlers for that. Move all promise machinery into an effect handler.
But that's not what causing you problems.
(js/console.log "blob is " blob)
- does this print out a blob of the correct non-zero size?
it prints ellipses for everything Blob related
Those ellipses are usually clickable.
not in this case
also how can the dispatch be in an effect handler if it has to be called in a go block?
if it has to be called inside a go block, go returns nil, which means that the effect can’t be specified in the re-frame map
Try logging (.-size blob)
then.
Move the whole go
block into an effect handler.
yes the size is showing as 4462885
which means that the blob is correct
In that case I have no idea, sorry. Maybe it's something about React Native, maybe I missed something, maybe you haven't shown something that's important. Or maybe it's something else altogether. The next step would be to confirm that the data is sent to the backend. The Network tab in the DevTools should help with that. If the data is not sent, then I have no clue what's going on. If the data is sent, then it's something on your backend.
there are absolutely no new entries showing up in the network tab
during the request
what’s the best way to debug this?
No idea. Especially given that you do receive the request on your backend.
another strange thing that happens is that the (-> req :body) only contains a single input stream even when I add multiple key value pairs with (.append …)
if the data is multipart, then why only a single input stream in body?
and why no data in (-> req :params) after adding the wrap-multipart-params middleware?
after changing to
(.append body "video" blob "video.mov")
from
(.append body "video" blob)
I get the error:
unrecognized request format nil
How to fix this?
the server is definitely receiving the request. After adding the request format as (json-request-format), I am seeing the following on (-> req :params)
{:parts [[“video” {:data {:collector nil, :offset 0, :name “D7A13384-801E-4D72-8912-9FA967BD61AB.mov”, :type “video/quicktime”, :size 1286383, :blobId “17FC5EC0-7B3E-4F9E-A666-0A46AD771FD2"}}]]}