Adds Yodlee2 Option.

This commit is contained in:
Bryce Covert
2020-12-22 08:45:12 -08:00
parent c66244a8fb
commit c28bd9635d
11 changed files with 842 additions and 17 deletions

View File

@@ -6,6 +6,7 @@
[auto-ap.routes.graphql :as graphql]
[auto-ap.routes.invoices :as invoices]
[auto-ap.routes.yodlee :as yodlee]
[auto-ap.routes.yodlee2 :as yodlee2]
[buddy.auth.backends.token :refer [jws-backend]]
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]
[clojure.tools.logging :as log]
@@ -54,6 +55,7 @@
(context "/api" []
exports/routes
yodlee/routes
yodlee2/routes
invoices/routes
graphql/routes
auth/routes

View File

@@ -0,0 +1,92 @@
(ns auto-ap.routes.yodlee2
(:require
[auto-ap.graphql :as graphql]
[clj-http.client :as http]
[auto-ap.yodlee.core2 :as yodlee]
[auto-ap.graphql.utils :refer [->graphql assert-admin]]
[auto-ap.routes.utils :refer [wrap-secure]]
[clj-time.coerce :refer [to-date]]
[ring.middleware.json :refer [wrap-json-response]]
[compojure.core :refer [GET POST context defroutes wrap-routes]]
[clojure.string :as str]
[config.core :refer [env]]
[clojure.tools.logging :as log]))
(defroutes routes
(wrap-routes
(context "/yodlee2" []
(GET "/fastlink" {:keys [query-params identity] :as request}
(assert-admin identity)
(let [token (yodlee/get-access-token)]
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str {:token token
:url (:yodlee2-fastlink env)}) }))
(GET "/accounts" {:keys [query-params identity] :as request}
(assert-admin identity)
(let [[session token] (yodlee/get-access-token)]
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str (yodlee/get-accounts)) }))
(GET "/provider-accounts" {:keys [query-params identity] :as request}
(assert-admin identity)
(log/info "working on provider accounts...")
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str @yodlee/in-memory-cache) })
(POST "/reauthenticate/:id" {:keys [query-params identity] {:keys [id]} :route-params
data :edn-params
:as request}
(assert-admin identity)
(try
(let [[session token] (yodlee/get-access-token)]
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str (yodlee/reauthenticate (Long/parseLong id) data)) })
(catch Exception e
(log/error e)
{:status 500
: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}
(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) })
(catch Exception e
{:status 400
:headers {"Content-Type" "application/edn"}
:body (pr-str {:message (.getMessage e)
:error (.toString e)})})))
(POST "/provider-accounts/delete/:id" {:keys [query-params identity] {:keys [id]} :route-params :as request}
(assert-admin identity)
(try
(let [[session token] (yodlee/get-access-token)]
(yodlee/delete-provider-account (Long/parseLong id))
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str @yodlee/in-memory-cache) })
(catch Exception e
{:status 400
:headers {"Content-Type" "application/edn"}
:body (pr-str {:message (.getMessage e)
:error (.toString e)})})))
(POST "/provider-accounts/:id" {:keys [query-params identity] {:keys [id]} :route-params :as request}
(assert-admin identity)
(try
(let [[session token] (yodlee/get-access-token)]
{:status 200
:headers {"Content-Type" "application/edn"}
:body (pr-str (yodlee/update-yodlee (Long/parseLong id))) })
(catch Exception e
{:status 400
:headers {"Content-Type" "application/edn"}
:body (pr-str e)}))))
wrap-secure))

View File

@@ -26,18 +26,22 @@
false)}))
(def base-headers {"Api-Version" "1.1"
"loginName" (:yodlee-client-user-new env)
"Cobrand-Name" (:yodlee-cobrand-name env)
"Content-Type" "application/json"})
(defn login-cobrand []
(-> (str (:yodlee-base-url env) "/auth/token")
(client/post (merge {:headers (assoc base-headers
"Content-Type" "application/x-www-form-urlencoded")
:body (str "clientId=" (:yodlee-client-id env) " &secret=" (:yodlee-client-secret env))
(-> (str (:yodlee-base-url env) "/cobrand/login")
(client/post (merge {:headers base-headers
:body
(json/write-str {:cobrand {:cobrandLogin (:yodlee-cobrand-login env)
:cobrandPassword (:yodlee-cobrand-password env)
:locale "en_US"}})
:as :json}
other-config)
)
:body))
:body
:session
:cobSession))
(defn login-user

View File

@@ -0,0 +1,301 @@
(ns auto-ap.yodlee.core2
(:require [clj-http.client :as client]
[auto-ap.utils :refer [by]]
[cemerick.url :as u]
[unilog.context :as lc]
[clojure.tools.logging :as log]
[clojure.data.json :as json]
[clojure.core.async :as async]
[config.core :refer [env]]
[mount.core :as mount]
[yang.scheduler :as scheduler]))
(defn auth-header
([cob-session] (str "Bearer " cob-session)))
(def other-config
(if (:yodlee2-proxy-host env)
{:proxy-host (:yodlee2-proxy-host env)
:proxy-port (:yodlee2-proxy-port env)
:retry-handler (fn [ex try-count http-context]
(log/error "yodlee Error." ex)
false)}
{:retry-handler (fn [ex try-count http-context]
(log/error "yodlee Error." ex)
false)}))
(def base-headers {"Api-Version" "1.1"
"Content-Type" "application/json"})
(defn login-cobrand []
(-> (str (:yodlee2-base-url env) "/auth/token")
(client/post (merge {:headers (assoc base-headers
"loginName" (:yodlee2-admin-user env)
"Content-Type" "application/x-www-form-urlencoded")
:body (str "clientId=" (:yodlee2-client-id env) " &secret=" (:yodlee2-client-secret env))
:as :json}
other-config)
)
:body
:token
:accessToken))
(defn login-user []
(-> (str (:yodlee2-base-url env) "/auth/token")
(client/post (merge {:headers (assoc base-headers
"loginName" (:yodlee2-integreat-user env)
"Content-Type" "application/x-www-form-urlencoded")
:body (str "clientId=" (:yodlee2-client-id env) " &secret=" (:yodlee2-client-secret env))
:as :json}
other-config)
)
:body
:token
:accessToken))
(defn get-accounts []
(let [cob-session (login-user)]
(-> (str (:yodlee2-base-url env) "/accounts")
(client/get (merge {:headers (merge base-headers {"Authorization" (str "Bearer " cob-session)})
:as :json}
other-config))
:body
:account)))
(defn get-accounts-for-provider-account [provider-account-id]
(try
(let [cob-session (login-user)]
(-> (str (:yodlee2-base-url env) "/accounts?providerAccountId=" provider-account-id)
(client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:as :json}
other-config))
:body
:account))
(catch Exception e
(log/error (str "Couldn't get accounts for provider account '" provider-account-id "'")
e)
[])))
(defn get-provider-accounts []
(let [cob-session (login-user)]
(-> (str (:yodlee2-base-url env) "/providerAccounts")
(-> (client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session )})
:as :json}
other-config))
:body
:providerAccount))))
(defn get-transactions []
(let [cob-session (login-user)
batch-size 100
get-transaction-batch (fn [skip]
(-> (str (:yodlee2-base-url env) "/transactions?top=" batch-size "&skip=" skip)
(client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:as :json}
other-config))
:body
:transaction
))]
(loop [transactions []
skip 0]
(let [transaction-batch (get-transaction-batch skip)]
(if (seq transaction-batch)
(recur (concat transactions transaction-batch) (+ batch-size skip))
transactions)))))
(defn get-provider-account [id]
(let [cob-session (login-user)
batch-size 100]
(-> (str (:yodlee2-base-url env) "/providerAccounts/" id)
(client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:as :json}
other-config))
:body
:providerAccount)))
(defn get-provider-account-detail [id]
(let [cob-session (login-user)]
(-> (str (:yodlee2-base-url env) "/providerAccounts/" id )
(client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:query-params {"include" "credentials,preferences"}
:as :json}
other-config))
:body
:providerAccount
first)))
(defn update-provider-account [pa]
(let [cob-session (login-user)]
(-> (str (:yodlee2-base-url env) "/providerAccounts?providerAccountIds=" pa)
(client/put (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:body "{\"dataSetName\": [\"BASIC_AGG_DATA\"]}"
:as :json}
other-config)))))
(defn get-specific-transactions [account]
(let [cob-session (login-user)
batch-size 100
get-transaction-batch (fn [skip]
(-> (str (:yodlee2-base-url env) "/transactions?top=" batch-size "&skip=" skip "&accountId=" account)
(client/get (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:as :json}
other-config))
:body
:transaction
))]
(loop [transactions []
skip 0]
(let [transaction-batch (get-transaction-batch skip)]
(if (seq transaction-batch)
(recur (concat transactions transaction-batch) (+ batch-size skip))
transactions)))))
(defn get-access-token []
(try
(let [cob-session (login-user)]
cob-session)
(catch Exception e
(log/error e)
(throw e))))
(defn create-user []
(let [cob-session (login-cobrand)]
(-> (str (:yodlee2-base-url env) "/user/register")
(client/post (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:body (json/write-str {"user" {
"loginName" "integreat-main"
"email" "bryce@integreatconsult.com"
"name" {"first" "Bryce"
"last" "Covert"}
"address" {"address1" "200 Lincoln Ave"
"state" "CA"
"city" "Salinas"
"zip" "93901"
"country" "USA"}
"preferences" {"currency" "USD"
"timeZone" "GMT"
"dateFormat" "YYYY-MMM-DD"
"locale" "en_US"}}})
:as :json}
other-config))
:body)))
(defn get-provider-accounts-with-details []
(let [provider-accounts (get-provider-accounts)]
(let [concurrent 20
output-chan (async/chan)]
(async/pipeline-blocking concurrent
output-chan
(map (fn [provider-account]
(lc/with-context {:provider-account-id (:id provider-account)}
(get-provider-account-detail (:id provider-account)))))
(async/to-chan provider-accounts))
(async/<!! (async/into [] output-chan)))))
(defn concurrent-get-accounts-for-providers [provider-account-ids]
(let [concurrent 20
output-chan (async/chan)]
(async/pipeline-blocking concurrent
output-chan
(map (fn [provider-account-id]
(lc/with-context {:provider-account-id provider-account-id}
[provider-account-id
(get-accounts-for-provider-account provider-account-id)])))
(async/to-chan provider-account-ids))
(async/<!! (async/into {} output-chan))))
(defn get-provider-accounts-with-accounts []
(let [provider-accounts (by :id (get-provider-accounts-with-details))
accounts (concurrent-get-accounts-for-providers (keys provider-accounts))]
(->> accounts
(reduce
(fn [provider-accounts [which accounts]]
(assoc-in provider-accounts [which :accounts] accounts))
provider-accounts)
vals)))
(mount/defstate in-memory-cache
:start (atom []))
(defn refresh-in-memory-cache []
(lc/with-context {:source "refreshing-in-memory-cache"}
(try
(log/info "Refreshing Yodlee in memory cache")
(reset! in-memory-cache (get-provider-accounts-with-accounts))
(catch Exception e
(log/error e)))))
(mount/defstate in-memory-cache-worker
:start (scheduler/every (* 5 60 1000) refresh-in-memory-cache)
:stop (scheduler/stop in-memory-cache-worker))
(defn refresh-provider-account [id]
(swap! in-memory-cache
(fn [i]
(-> (by :id i)
(assoc id (assoc (get-provider-account-detail id)
:accounts (get-accounts-for-provider-account id)))
vals))))
(defn delete-provider-account [id]
(let [cob-session (login-user)]
(-> (str (:yodlee2-base-url env) "/providerAccounts/" id )
(client/delete (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:as :json}
other-config))
:body
:providerAccount
first))
(swap! in-memory-cache
(fn [i]
(-> (by :id i)
(dissoc id)
vals))))
(defn update-yodlee [id]
(update-provider-account id)
(refresh-provider-account id))
(defn reauthenticate [pa data]
(let [cob-session (login-cobrand)]
(try
(doto (-> (str (:yodlee2-base-url env) "/providerAccounts?providerAccountIds=" pa)
(client/put (merge {:headers (merge base-headers {"Authorization" (auth-header cob-session)})
:body (json/write-str data)
:as :json}
other-config)))
log/info)
(refresh-provider-account pa)
(catch Exception e
(log/error e)))))