Yodlee is manually refreshable

This commit is contained in:
Bryce Covert
2020-12-22 13:59:50 -08:00
parent 6930a8c7c2
commit bbdb9602aa
6 changed files with 114 additions and 92 deletions

View File

@@ -1,8 +1,8 @@
(ns auto-ap.datomic.clients (ns auto-ap.datomic.clients
(:require [datomic.api :as d] (:require [auto-ap.datomic :refer [conn uri]]
[auto-ap.datomic :refer [uri]] [clj-time.coerce :as coerce]
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[clj-time.coerce :as coerce])) [datomic.api :as d]))
(defn cleanse [e] (defn cleanse [e]
(-> e (-> e
@@ -31,21 +31,14 @@
)) ))
(defn get-by-id [id] (defn get-by-id [id]
(->> (->>
(d/query (-> {:query {:find ['(pull ?e [* (d/pull (d/db conn )
{:client/bank-accounts [* {:bank-account/type [*] '[* {:client/bank-accounts [* {:bank-account/type [*]
:bank-account/yodlee-account [:yodlee-account/name :yodlee-account/id :yodlee-account/number]}]} :bank-account/yodlee-account [:yodlee-account/name :yodlee-account/id :yodlee-account/number]}]}
{:yodlee-provider-account/_client [*]}])] {:yodlee-provider-account/_client [*]}]
:in ['$ '?e] id)
:where [['?e]]} (cleanse)))
:args [(d/db (d/connect uri)) id]}
))
(first)
(first)
(cleanse)
#_(map first)
#_(first)))
(defn code->id [code] (defn code->id [code]
(->> (->>

View File

@@ -80,8 +80,6 @@
:address/city (:city (:address edit_client)) :address/city (:city (:address edit_client))
:address/state (:state (:address edit_client)) :address/state (:state (:address edit_client))
:address/zip (:zip (:address edit_client))}) :address/zip (:zip (:address edit_client))})
:client/bank-accounts (map #(remove-nils :client/bank-accounts (map #(remove-nils
(cond-> {:db/id (:id %) (cond-> {:db/id (:id %)
:bank-account/code (:code %) :bank-account/code (:code %)
@@ -99,11 +97,9 @@
:bank-account/locations (:locations %) :bank-account/locations (:locations %)
:bank-account/yodlee-account-id (:yodlee_account_id %) :bank-account/yodlee-account-id (:yodlee_account_id %)
:bank-account/type (keyword "bank-account-type" (name (:type %)))}
: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))
(:yodlee_account %) (assoc :bank-account/yodlee-account [:yodlee-account/id (:yodlee_account %)]))
) (:bank_accounts edit_client))
}) })
[:reset id :client/forecasted-transactions (map #(remove-nils [:reset id :client/forecasted-transactions (map #(remove-nils

View File

@@ -12,7 +12,8 @@
[clojure.string :as str] [clojure.string :as str]
[config.core :refer [env]] [config.core :refer [env]]
[clojure.tools.logging :as log])) [clojure.tools.logging :as log]
[auto-ap.datomic.clients :as d-clients]))
(defroutes routes (defroutes routes
(wrap-routes (wrap-routes
@@ -52,15 +53,21 @@
:headers {"Content-Type" "application/edn"} :headers {"Content-Type" "application/edn"}
:body (pr-str {:message (.getMessage e) :body (pr-str {:message (.getMessage e)
:error (.toString 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) (assert-admin identity)
(try (try
(let [[session token] (yodlee/get-access-token)] (yodlee/refresh-provider-account (-> (:client-id edn-params)
(yodlee/refresh-provider-account (Long/parseLong id)) Long/parseLong
{:status 200 d-clients/get-by-id
:headers {"Content-Type" "application/edn"} :client/code)
:body (pr-str @yodlee/in-memory-cache) }) (:provider-account-id edn-params))
{:status 200
:headers {"Content-Type" "application/edn"}
:body "{}" }
(catch Exception e (catch Exception e
(log/error e)
{:status 400 {:status 400
:headers {"Content-Type" "application/edn"} :headers {"Content-Type" "application/edn"}
:body (pr-str {:message (.getMessage e) :body (pr-str {:message (.getMessage e)

View File

@@ -14,6 +14,8 @@
[datomic.api :as d] [datomic.api :as d]
[auto-ap.datomic :refer [conn]] [auto-ap.datomic :refer [conn]]
[auto-ap.datomic.clients :as d-clients])) [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] (defn client-code->login [client-code]
(if (< (count client-code) 3) (if (< (count client-code) 3)
(str client-code "_" client-code) (str client-code "_" client-code)
@@ -131,7 +133,8 @@
:as :json} :as :json}
other-config)) other-config))
:body :body
:providerAccount))) :providerAccount
first)))
(defn get-provider-account-detail [client-code id] (defn get-provider-account-detail [client-code id]
(let [cob-session (login-user client-code)] (let [cob-session (login-user client-code)]
@@ -232,7 +235,7 @@
(async/<!! (async/into [] output-chan))))) (async/<!! (async/into [] output-chan)))))
(defn get-accounts-for-providers [client-code provider-account-ids] (defn get-accounts-for-providers [client-code provider-account-ids]
(log/info "looking up " (count provider-account-ids) " provider accounts for client " client-code ".") (log/info "looking up " (count provider-account-ids) " provider accounts's accounts for client " client-code ".")
(into {} (into {}
(mapv (fn [provider-account-id] (mapv (fn [provider-account-id]
(lc/with-context {:provider-account-id provider-account-id} (lc/with-context {:provider-account-id provider-account-id}
@@ -269,29 +272,32 @@
first)) first))
@(d/transact conn [:db/retractEntity [:yodlee-provider-account/id id]])) @(d/transact conn [:db/retractEntity [:yodlee-provider-account/id id]]))
(defn upsert-accounts-tx [client-code] (defn upsert-accounts-tx
(let [provider-accounts (get-provider-accounts client-code) ([client-code]
accounts (get-accounts-for-providers client-code (map :id provider-accounts))] (upsert-accounts-tx (get-provider-accounts (client-code->login client-code))))
(map (fn [pa] ([client-code provider-accounts]
{:yodlee-provider-account/id (:id pa) (let [accounts (get-accounts-for-providers (client-code->login client-code) (map :id provider-accounts))]
:yodlee-provider-account/status (:status pa) (map (fn [pa]
:yodlee-provider-account/detailed-status (-> pa :dataset first :additionalStatus) {:yodlee-provider-account/id (:id pa)
:yodlee-provider-account/client [:client/code client-code] :yodlee-provider-account/status (:status pa)
:yodlee-provider-account/last-updated (-> pa :dataset first :lastUpdated coerce/to-date) :yodlee-provider-account/detailed-status (-> pa :dataset first :additionalStatus)
:yodlee-provider-account/accounts (mapv :yodlee-provider-account/client [:client/code client-code]
(fn [a] :yodlee-provider-account/last-updated (-> pa :dataset first :lastUpdated coerce/to-date)
{:yodlee-account/id (:id a) :yodlee-provider-account/accounts (mapv
:yodlee-account/name (str (:providerName a) " (" (:accountName a) ")") (fn [a]
:yodlee-account/number (:accountNumber a) {:yodlee-account/id (:id a)
:yodlee-account/status (-> a :dataset first :additionalStatus) :yodlee-account/name (str (:providerName a) " (" (:accountName a) ")")
:yodlee-account/available-balance (or (-> a :currentBalance :amount) :yodlee-account/number (:accountNumber a)
0.0)}) :yodlee-account/status (-> a :dataset first :additionalStatus)
(get accounts (:id pa)))}) :yodlee-account/available-balance (or (-> a :currentBalance :amount)
provider-accounts))) 0.0)})
(get accounts (:id pa)))})
provider-accounts))))
(defn refresh-provider-account [client-code id] (defn refresh-provider-account [client-code id]
@(d/transact conn (upsert-accounts-tx (client-code->login (client-code->login client-code) (log/info "refreshing yodlee provider account id" id)
id)))) @(d/transact conn (upsert-accounts-tx client-code
[(get-provider-account (client-code->login client-code) id)])))
(defn upsert-accounts [] (defn upsert-accounts []
(let [concurrent 20 (let [concurrent 20
@@ -301,7 +307,7 @@
(mapcat (fn [client] (mapcat (fn [client]
(log/info "Upserting Yodlee Accounts for " (:client/code client)) (log/info "Upserting Yodlee Accounts for " (:client/code client))
(lc/with-context {:client-code (: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))) (async/to-chan (d-clients/get-all)))
(let [result (async/<!! (async/into [] output-chan))] (let [result (async/<!! (async/into [] output-chan))]
(log/info "Current yodlee state is " result) (log/info "Current yodlee state is " result)

View File

@@ -210,18 +210,7 @@
{:db (assoc-in db [::provider-accounts] result) {:db (assoc-in db [::provider-accounts] result)
:dispatch [::forms/form-closing [::refresh-provider-account i]]})) :dispatch [::forms/form-closing [::refresh-provider-account i]]}))
(re-frame/reg-event-fx
::refresh-provider-account
[with-user ]
(fn [{:keys [user db]} [_ provider-account-id ]]
{:db (forms/loading db [::refresh-provider-account provider-account-id])
:http {:token user
:method :post
:headers {"Content-Type" "application/edn"}
:uri (str "/api/yodlee2/provider-accounts/refresh/" provider-account-id )
:body {}
:on-success [::provider-account-refreshed provider-account-id]
:on-error [::forms/save-error [::refresh-provider-account provider-account-id] ]}}))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::provider-account-deleted ::provider-account-deleted
@@ -410,25 +399,30 @@
:data (grid/virtual-paginate (:start params) (:per-page params) (:yodlee-provider-accounts all-yodlee-provider-accounts))))) :data (grid/virtual-paginate (:start params) (:per-page params) (:yodlee-provider-accounts all-yodlee-provider-accounts)))))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::mounted ::data-requested
(fn [{:keys [db]} _] (fn [{:keys [db]} _]
{:graphql {:token (:user db) {:graphql {:token (:user db)
:owns-state {:single ::page} :owns-state {:single ::page}
:query-obj {:venia/queries [[:yodlee-provider-account-page {:client-id (:id @(re-frame/subscribe [::subs/client]))} :query-obj {:venia/queries [[:yodlee-provider-account-page {:client-id (:id @(re-frame/subscribe [::subs/client]))}
[[:yodlee-provider-accounts [:id :last-updated :status :detailed-status [[:yodlee-provider-accounts [:id :last-updated :status :detailed-status
[:accounts [:id :name :number :available-balance]]]] [:accounts [:id :name :number :available-balance]]]]
:count]]]} :count]]]}
:on-success [::received]} :on-success [::received]}}))
#_#_::forward/register {:id ::edited-yodlee-provider-account
#_#_:events #{::form/saved} (re-frame/reg-event-fx
#_#_:event-fn (fn [[_ query-result]] ::mounted
[::saved query-result])} (fn [{:keys [db]} _]
{:dispatch [::data-requested]
::forward/register {:id ::yodlee-account-refreshed
:events #{::table/refreshed}
:event-fn (fn [[_ query-result]]
[::data-requested])}
:db (dissoc db ::authentication)})) :db (dissoc db ::authentication)}))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::unmounted ::unmounted
(fn [{:keys [db]} _] (fn [{:keys [db]} _]
#_{::forward/dispose {:id ::edited-yodlee-provider-account}})) {::forward/dispose {:id ::yodlee-account-refreshed}}))
(re-frame/reg-event-fx (re-frame/reg-event-fx

View File

@@ -1,11 +1,32 @@
(ns auto-ap.views.pages.admin.yodlee2.table (ns auto-ap.views.pages.admin.yodlee2.table
(:require (:require [auto-ap.status :as status]
[clojure.string :as str] [auto-ap.subs :as subs]
[re-frame.core :as re-frame] [auto-ap.views.components.buttons :as buttons]
[auto-ap.views.utils :refer [action-cell-width date->str]] [auto-ap.views.components.grid :as grid]
[auto-ap.views.pages.admin.users.form :as form] [auto-ap.views.pages.admin.users.form :as form]
[auto-ap.views.components.buttons :as buttons] [auto-ap.views.utils :refer [action-cell-width date->str with-user ->$]]
[auto-ap.views.components.grid :as grid])) [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 (re-frame/reg-event-fx
::params-changed ::params-changed
@@ -18,7 +39,8 @@
(-> db ::params))) (-> db ::params)))
(defn table [{:keys [status page]}] (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 [grid/grid {:status status
:on-params-change (fn [p] :on-params-change (fn [p]
(re-frame/dispatch [::params-changed p])) (re-frame/dispatch [::params-changed p]))
@@ -28,12 +50,12 @@
[grid/table {:fullwidth true} [grid/table {:fullwidth true}
[grid/header [grid/header
[grid/row {} [grid/row {}
[grid/header-cell {} "Provider Account"] [grid/header-cell {:style {:width "18em"}} "Provider Account"]
[grid/header-cell {} "Status"] [grid/header-cell {:style {:width "20em"}} "Status"]
[grid/header-cell {} "Detailed Status"] [grid/header-cell {:style {:width "20em"}} "Detailed Status"]
[grid/header-cell {} "Last Updated"] [grid/header-cell {:style {:width "12em"}} "Last Updated"]
[grid/header-cell {} "Accounts"] [grid/header-cell {} "Accounts"]
[grid/header-cell {:style {:width (action-cell-width 1)}} ]]] [grid/header-cell {:style {:width (action-cell-width 3)}} ]]]
[grid/body [grid/body
(for [{:keys [id name accounts status detailed-status last-updated clients] :as c} (:data page)] (for [{:keys [id name accounts status detailed-status last-updated clients] :as c} (:data page)]
^{:key (str name "-" id )} ^{:key (str name "-" id )}
@@ -46,7 +68,11 @@
[:ul [:ul
(for [a accounts] (for [a accounts]
^{:key (:id a)} ^{:key (:id a)}
[:li (:name a) " - " (:number a)])]] [:li (:name a) " - " (:number a) [:div.tag (->$ (:available-balance a))]])]]
[grid/cell {} [grid/cell {}
[buttons/fa-icon {:event [::form/editing c] [:div.buttons
:icon "fa-pencil"}]]])]]])) [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"}]]]])]]]))