Fork me on GitHub
#code-reviews
<
2018-03-27
>
sooheon14:03:37

Hi all, I have a code review request, if anyone has a min. I’m writing some code to interface with APIs, requesting creation of reports, waiting for creation, downloading or terminating.

sooheon14:03:52

My problem is that it looks very imperative, not very clojure-y:

sooheon14:03:57

(defn download-stats
  "Requests creation of `report-type` for `date`. Once the report job is
   BUILT, returns the data as a java.io.BufferedReader. The report is in tsv
   format.

   Cleans up after itself by deleting requested job."
  [customer-id report-type date]
  (let [c  (creds customer-id)
        id (-> (reports.stat/create! c {:reportTp report-type :statDt date})
               :body :reportJobId)]
    (loop [job (reports.stat/fetch-job c id)]
      (Thread/sleep 50)
      (if-not (#{"RUNNING" "REGIST"} (:status job))
        (if (= "BUILT" (:status job))
          (let [buf-r (reports.common/download c job)]
            (reports.stat/delete! c id)
            buf-r)
          (do
            (reports.stat/delete! c id)
            (throw
             (Exception.
              (str "Requested job returned status of: " (:status job))))))
        (recur (reports.stat/fetch-job c id))))))

sooheon14:03:05

Any pointers would be much appreciated!

seancorfield16:03:37

@sooheon To avoid the duplicated call to delete! I'd probably wrap the loop in try / finally (containing the delete! call) to make it clearer this is a post-operation cleanup. That would allow the inner let to be removed. I'd use a case to avoid the nested if.

seancorfield16:03:56

(defn download-stats
  "Requests creation of `report-type` for `date`. Once the report job is
   BUILT, returns the data as a java.io.BufferedReader. The report is in tsv
   format.

   Cleans up after itself by deleting requested job."
  [customer-id report-type date]
  (let [c  (creds customer-id)
        id (-> (reports.stat/create! c {:reportTp report-type :statDt date})
               :body :reportJobId)
        fetch (fn []
                (Thread/sleep 50)
                (reports.stat/fetch-job c id))]
    (try
      (loop [job (fetch)]
        (case (:status job)
          ("RUNNING" "REGIST")
          (recur (fetch))

          "BUILT"
          (reports.common/download c job)

          (throw
           (Exception.
            (str "Requested job returned status of: " (:status job))))))
      (finally
        (reports.stat/delete! c id)))))
(edited to use a helper that sleeps then fetches the job)

seancorfield16:03:09

Also, since you're fetching the job then sleeping, I'd probably add a helper function that accepted c and id and slept first, then fetched the job -- I'll edit the above code.

sooheon23:03:34

Great, try/catch/throw/finally are constructs I need to get more familiar with, this was a really helpful example. Thanks @seancorfield