From bbdb9602aae16abeb6d6e60fdf7906a76652c9b5 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Tue, 22 Dec 2020 13:59:50 -0800 Subject: [PATCH] Yodlee is manually refreshable --- src/clj/auto_ap/datomic/clients.clj | 27 ++++----- src/clj/auto_ap/graphql/clients.clj | 10 +--- src/clj/auto_ap/routes/yodlee2.clj | 21 ++++--- src/clj/auto_ap/yodlee/core2.clj | 54 +++++++++-------- .../auto_ap/views/pages/admin/yodlee2.cljs | 36 +++++------- .../views/pages/admin/yodlee2/table.cljs | 58 ++++++++++++++----- 6 files changed, 114 insertions(+), 92 deletions(-) diff --git a/src/clj/auto_ap/datomic/clients.clj b/src/clj/auto_ap/datomic/clients.clj index 3e4a93bf..37b4b9e7 100644 --- a/src/clj/auto_ap/datomic/clients.clj +++ b/src/clj/auto_ap/datomic/clients.clj @@ -1,8 +1,8 @@ (ns auto-ap.datomic.clients - (:require [datomic.api :as d] - [auto-ap.datomic :refer [uri]] + (:require [auto-ap.datomic :refer [conn uri]] + [clj-time.coerce :as coerce] [clojure.tools.logging :as log] - [clj-time.coerce :as coerce])) + [datomic.api :as d])) (defn cleanse [e] (-> e @@ -31,21 +31,14 @@ )) (defn get-by-id [id] + (->> - (d/query (-> {:query {:find ['(pull ?e [* - {:client/bank-accounts [* {:bank-account/type [*] - :bank-account/yodlee-account [:yodlee-account/name :yodlee-account/id :yodlee-account/number]}]} - {:yodlee-provider-account/_client [*]}])] - :in ['$ '?e] - :where [['?e]]} - :args [(d/db (d/connect uri)) id]} - )) - (first) - (first) - (cleanse) - #_(map first) - - #_(first))) + (d/pull (d/db conn ) + '[* {:client/bank-accounts [* {:bank-account/type [*] + :bank-account/yodlee-account [:yodlee-account/name :yodlee-account/id :yodlee-account/number]}]} + {:yodlee-provider-account/_client [*]}] + id) + (cleanse))) (defn code->id [code] (->> diff --git a/src/clj/auto_ap/graphql/clients.clj b/src/clj/auto_ap/graphql/clients.clj index 6239339e..9c46ac22 100644 --- a/src/clj/auto_ap/graphql/clients.clj +++ b/src/clj/auto_ap/graphql/clients.clj @@ -80,8 +80,6 @@ :address/city (:city (:address edit_client)) :address/state (:state (:address edit_client)) :address/zip (:zip (:address edit_client))}) - - :client/bank-accounts (map #(remove-nils (cond-> {:db/id (:id %) :bank-account/code (:code %) @@ -99,11 +97,9 @@ :bank-account/locations (:locations %) :bank-account/yodlee-account-id (:yodlee_account_id %) - - :bank-account/type (keyword "bank-account-type" (name (:type %))) - } - (:yodlee_account %) (assoc :bank-account/yodlee-account [:yodlee-account/id (:yodlee_account %)])) - ) (:bank_accounts edit_client)) + :bank-account/type (keyword "bank-account-type" (name (:type %)))} + (:yodlee_account %) (assoc :bank-account/yodlee-account [:yodlee-account/id (:yodlee_account %)]))) + (:bank_accounts edit_client)) }) [:reset id :client/forecasted-transactions (map #(remove-nils diff --git a/src/clj/auto_ap/routes/yodlee2.clj b/src/clj/auto_ap/routes/yodlee2.clj index b31bc76d..fb05e45b 100644 --- a/src/clj/auto_ap/routes/yodlee2.clj +++ b/src/clj/auto_ap/routes/yodlee2.clj @@ -12,7 +12,8 @@ [clojure.string :as str] [config.core :refer [env]] - [clojure.tools.logging :as log])) + [clojure.tools.logging :as log] + [auto-ap.datomic.clients :as d-clients])) (defroutes routes (wrap-routes @@ -52,15 +53,21 @@ :headers {"Content-Type" "application/edn"} :body (pr-str {:message (.getMessage e) :error (.toString e)})}))) - (POST "/provider-accounts/refresh/:id" {:keys [query-params identity] {:keys [id]} :route-params :as request} + (POST "/provider-accounts/refresh/" {:keys [query-params identity edn-params] + {:keys [id]} :route-params + :as request} (assert-admin identity) (try - (let [[session token] (yodlee/get-access-token)] - (yodlee/refresh-provider-account (Long/parseLong id)) - {:status 200 - :headers {"Content-Type" "application/edn"} - :body (pr-str @yodlee/in-memory-cache) }) + (yodlee/refresh-provider-account (-> (:client-id edn-params) + Long/parseLong + d-clients/get-by-id + :client/code) + (:provider-account-id edn-params)) + {:status 200 + :headers {"Content-Type" "application/edn"} + :body "{}" } (catch Exception e + (log/error e) {:status 400 :headers {"Content-Type" "application/edn"} :body (pr-str {:message (.getMessage e) diff --git a/src/clj/auto_ap/yodlee/core2.clj b/src/clj/auto_ap/yodlee/core2.clj index 79dce5c2..585633ea 100644 --- a/src/clj/auto_ap/yodlee/core2.clj +++ b/src/clj/auto_ap/yodlee/core2.clj @@ -14,6 +14,8 @@ [datomic.api :as d] [auto-ap.datomic :refer [conn]] [auto-ap.datomic.clients :as d-clients])) +;; switch all of this to use tokens instead of passing around client codes, particularly because the codes +;; need to be tweaked for repeats (defn client-code->login [client-code] (if (< (count client-code) 3) (str client-code "_" client-code) @@ -131,7 +133,8 @@ :as :json} other-config)) :body - :providerAccount))) + :providerAccount + first))) (defn get-provider-account-detail [client-code id] (let [cob-session (login-user client-code)] @@ -232,7 +235,7 @@ (async/ pa :dataset first :additionalStatus) - :yodlee-provider-account/client [:client/code client-code] - :yodlee-provider-account/last-updated (-> pa :dataset first :lastUpdated coerce/to-date) - :yodlee-provider-account/accounts (mapv - (fn [a] - {:yodlee-account/id (:id a) - :yodlee-account/name (str (:providerName a) " (" (:accountName a) ")") - :yodlee-account/number (:accountNumber a) - :yodlee-account/status (-> a :dataset first :additionalStatus) - :yodlee-account/available-balance (or (-> a :currentBalance :amount) - 0.0)}) - (get accounts (:id pa)))}) - provider-accounts))) +(defn upsert-accounts-tx + ([client-code] + (upsert-accounts-tx (get-provider-accounts (client-code->login client-code)))) + ([client-code provider-accounts] + (let [accounts (get-accounts-for-providers (client-code->login client-code) (map :id provider-accounts))] + (map (fn [pa] + {:yodlee-provider-account/id (:id pa) + :yodlee-provider-account/status (:status pa) + :yodlee-provider-account/detailed-status (-> pa :dataset first :additionalStatus) + :yodlee-provider-account/client [:client/code client-code] + :yodlee-provider-account/last-updated (-> pa :dataset first :lastUpdated coerce/to-date) + :yodlee-provider-account/accounts (mapv + (fn [a] + {:yodlee-account/id (:id a) + :yodlee-account/name (str (:providerName a) " (" (:accountName a) ")") + :yodlee-account/number (:accountNumber a) + :yodlee-account/status (-> a :dataset first :additionalStatus) + :yodlee-account/available-balance (or (-> a :currentBalance :amount) + 0.0)}) + (get accounts (:id pa)))}) + provider-accounts)))) (defn refresh-provider-account [client-code id] - @(d/transact conn (upsert-accounts-tx (client-code->login (client-code->login client-code) - id)))) + (log/info "refreshing yodlee provider account id" id) + @(d/transact conn (upsert-accounts-tx client-code + [(get-provider-account (client-code->login client-code) id)]))) (defn upsert-accounts [] (let [concurrent 20 @@ -301,7 +307,7 @@ (mapcat (fn [client] (log/info "Upserting Yodlee Accounts for " (:client/code client)) (lc/with-context {:client-code (:client/code client)} - (upsert-accounts-tx (client-code->login (:client/code client)))))) + (upsert-accounts-tx (:client/code client))))) (async/to-chan (d-clients/get-all))) (let [result (async/str]] - [auto-ap.views.pages.admin.users.form :as form] - [auto-ap.views.components.buttons :as buttons] - [auto-ap.views.components.grid :as grid])) + (:require [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components.buttons :as buttons] + [auto-ap.views.components.grid :as grid] + [auto-ap.views.pages.admin.users.form :as form] + [auto-ap.views.utils :refer [action-cell-width date->str with-user ->$]] + [re-frame.core :as re-frame])) + +(re-frame/reg-event-fx + ::refreshed + [with-user ] + (fn [{:keys [user db]} [_ provider-account ]] + ;; this is tracked in yodlee main, for refreshing + {})) + +(re-frame/reg-event-fx + ::request-refresh + [with-user ] + (fn [{:keys [user db]} [_ provider-account ]] + {:http {:token user + :method :post + :headers {"Content-Type" "application/edn"} + :uri (str "/api/yodlee2/provider-accounts/refresh/") + :owns-state {:multi ::refresh + :which provider-account} + :body {:client-id (:id @(re-frame/subscribe [::subs/client])) + :provider-account-id provider-account} + :on-success [::refreshed provider-account]}})) (re-frame/reg-event-fx ::params-changed @@ -18,7 +39,8 @@ (-> db ::params))) (defn table [{:keys [status page]}] - (let [params @(re-frame/subscribe [::params])] + (let [params @(re-frame/subscribe [::params]) + statuses @(re-frame/subscribe [::status/multi ::refresh])] [grid/grid {:status status :on-params-change (fn [p] (re-frame/dispatch [::params-changed p])) @@ -28,12 +50,12 @@ [grid/table {:fullwidth true} [grid/header [grid/row {} - [grid/header-cell {} "Provider Account"] - [grid/header-cell {} "Status"] - [grid/header-cell {} "Detailed Status"] - [grid/header-cell {} "Last Updated"] + [grid/header-cell {:style {:width "18em"}} "Provider Account"] + [grid/header-cell {:style {:width "20em"}} "Status"] + [grid/header-cell {:style {:width "20em"}} "Detailed Status"] + [grid/header-cell {:style {:width "12em"}} "Last Updated"] [grid/header-cell {} "Accounts"] - [grid/header-cell {:style {:width (action-cell-width 1)}} ]]] + [grid/header-cell {:style {:width (action-cell-width 3)}} ]]] [grid/body (for [{:keys [id name accounts status detailed-status last-updated clients] :as c} (:data page)] ^{:key (str name "-" id )} @@ -46,7 +68,11 @@ [:ul (for [a accounts] ^{:key (:id a)} - [:li (:name a) " - " (:number a)])]] + [:li (:name a) " - " (:number a) [:div.tag (->$ (:available-balance a))]])]] [grid/cell {} - [buttons/fa-icon {:event [::form/editing c] - :icon "fa-pencil"}]]])]]])) + [:div.buttons + [buttons/fa-icon {:event [::form/editing c] + :icon "fa-pencil"}] + [buttons/fa-icon {:event [::request-refresh (:id c)] + :class (status/class-for (get statuses (:id c))) + :icon "fa-refresh"}]]]])]]]))