diff --git a/config/dev.edn b/config/dev.edn index c9a7e936..08c40b31 100644 --- a/config/dev.edn +++ b/config/dev.edn @@ -14,4 +14,6 @@ :yodlee-user-login "sbMemda48aa19712a83c3ca4e935dd5e5d46b1a1" :yodlee-user-password "sbMemda48aa19712a83c3ca4e935dd5e5d46b1a1#123" :yodlee-base-url "https://developer.api.yodlee.com/ysl" + :yodlee-app "10003600" + :yodlee-fastlink "https://node.developer.yodlee.com/authenticate/restserver/?channelAppName=restserver" } diff --git a/config/local.edn b/config/local.edn index 4a06d221..aa9baba1 100644 --- a/config/local.edn +++ b/config/local.edn @@ -13,4 +13,6 @@ :yodlee-user-login "sbMemda48aa19712a83c3ca4e935dd5e5d46b1a1" :yodlee-user-password "sbMemda48aa19712a83c3ca4e935dd5e5d46b1a1#123" :yodlee-base-url "https://developer.api.yodlee.com/ysl" + :yodlee-app "10003600" + :yodlee-fastlink "https://node.developer.yodlee.com/authenticate/restserver/?channelAppName=restserver" } diff --git a/config/prod.edn b/config/prod.edn index cfd5f221..b5a863c1 100644 --- a/config/prod.edn +++ b/config/prod.edn @@ -14,4 +14,6 @@ :yodlee-user-login "integreat" :yodlee-user-password "Import3transactions!" :yodlee-base-url "https://quickstart2.api.yodlee.com/ysl" + :yodlee-app "10003600" + :yodlee-fastlink "https://quickstartus2node.yodleeinteractive.com/authenticate/qstartus12/?channelAppName=quickstartus2" } diff --git a/config/staging.edn b/config/staging.edn index fe0c898e..4d19dafe 100644 --- a/config/staging.edn +++ b/config/staging.edn @@ -14,4 +14,6 @@ :yodlee-user-login "integreat" :yodlee-user-password "Import3transactions!" :yodlee-base-url "https://quickstart2.api.yodlee.com/ysl" + :yodlee-app "10003600" + :yodlee-fastlink "https://quickstartus2node.yodleeinteractive.com/authenticate/qstartus12/?channelAppName=quickstartus2" } diff --git a/project.clj b/project.clj index 970bdd0c..9c7a8c13 100644 --- a/project.clj +++ b/project.clj @@ -71,7 +71,7 @@ :plugins [[lein-figwheel "0.5.13"] [lein-pdo "0.1.1"] [cider/cider-nrepl "0.16.0"]] - :jvm-opts ["-Dconfig=config/dev.edn" #_#_"--add-modules" "java.xml.bind"]} + :jvm-opts ["-Dconfig=config/dev.edn" "--add-modules" "java.xml.bind"]} :uberjar {:prep-tasks [["cljsbuild" "once" "min"] "compile"]} :provided {:dependencies [[org.clojure/clojurescript "1.10.238"] [reagent "0.7.0"] diff --git a/src/clj/auto_ap/handler.clj b/src/clj/auto_ap/handler.clj index 6ed928c5..ac494468 100644 --- a/src/clj/auto_ap/handler.clj +++ b/src/clj/auto_ap/handler.clj @@ -6,6 +6,7 @@ [auto-ap.routes.reminders :as reminders] [auto-ap.routes.graphql :as graphql] [auto-ap.routes.vendors :as vendors] + [auto-ap.routes.yodlee :as yodlee] [auto-ap.routes.events :as events] [auto-ap.routes.checks :as checks] [auto-ap.routes.exports :as exports] @@ -33,6 +34,7 @@ (defroutes api-routes (context "/api" [] exports/routes + yodlee/routes invoices/routes companies/routes vendors/routes diff --git a/src/clj/auto_ap/routes/events.clj b/src/clj/auto_ap/routes/events.clj index 64c7e2c1..69376127 100644 --- a/src/clj/auto_ap/routes/events.clj +++ b/src/clj/auto_ap/routes/events.clj @@ -17,6 +17,7 @@ (defroutes routes (context "/events" [] + (POST "/yodlee-import" {:keys [query-params headers body] :as x} (let [notification-type (get headers "x-amz-sns-message-type")] (println "Received notification " notification-type) diff --git a/src/clj/auto_ap/routes/yodlee.clj b/src/clj/auto_ap/routes/yodlee.clj new file mode 100644 index 00000000..8fed014b --- /dev/null +++ b/src/clj/auto_ap/routes/yodlee.clj @@ -0,0 +1,34 @@ +(ns auto-ap.routes.yodlee + (:require + [auto-ap.graphql :as graphql] + [clj-http.client :as http] + + [auto-ap.yodlee.core :as yodlee] + [auto-ap.graphql.utils :refer [->graphql assert-admin]] + [auto-ap.routes.utils :refer [wrap-secure]] + [clj-time.coerce :refer [to-date]] + [auto-ap.db.invoices-expense-accounts :as expense-accounts] + [ring.middleware.json :refer [wrap-json-response]] + [compojure.core :refer [GET POST context defroutes wrap-routes]] + [clojure.string :as str] + [config.core :refer [env]] + )) + +(defroutes routes + (wrap-routes + (context "/yodlee" [] + (GET "/fastlink" {: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 {:session session + :token token + :app (:yodlee-app env) + + :url (:yodlee-fastlink env) + + }) }))) + wrap-secure)) diff --git a/src/clj/auto_ap/yodlee/core.clj b/src/clj/auto_ap/yodlee/core.clj index c5500708..26ff53ce 100644 --- a/src/clj/auto_ap/yodlee/core.clj +++ b/src/clj/auto_ap/yodlee/core.clj @@ -75,6 +75,23 @@ (recur (concat transactions transaction-batch) (+ batch-size skip)) transactions))))) +(defn get-access-token [] + (let [cob-session (login-cobrand) + user-session (login-user cob-session) + token (-> + (str (:yodlee-base-url env) "/user/accessTokens?appIds=" 10003600) + + (client/get + {:headers (merge base-headers {"Authorization" (auth-header cob-session user-session)}) + :as :json}) + :body + :user + :accessTokens + first + :value + )] + [user-session token])) + (defn create-user [] (let [cob-session (login-cobrand)] (-> (str (:yodlee-base-url env) "/user/register") diff --git a/src/cljs/auto_ap/routes.cljs b/src/cljs/auto_ap/routes.cljs index ee7d1ae7..041f828d 100644 --- a/src/cljs/auto_ap/routes.cljs +++ b/src/cljs/auto_ap/routes.cljs @@ -10,7 +10,8 @@ "users" :admin-users "reminders" :admin-reminders "vendors" :admin-vendors - "excel-import" :admin-excel-import} + "excel-import" :admin-excel-import + "yodlee" :admin-yodlee} "invoices/" {"" :invoices "import" :import-invoices "unpaid" :unpaid-invoices diff --git a/src/cljs/auto_ap/views/main.cljs b/src/cljs/auto_ap/views/main.cljs index 818c26d0..be93dcc8 100644 --- a/src/cljs/auto_ap/views/main.cljs +++ b/src/cljs/auto_ap/views/main.cljs @@ -27,6 +27,7 @@ :admin-users :admin-left-panel :admin-excel-import :admin-left-panel :admin-vendors :admin-left-panel + :admin-yodlee :admin-left-panel :admin-reminders :admin-left-panel :new-invoice :blank} page :blank)) @@ -96,6 +97,13 @@ [:span {:class "icon"} [:i {:class "fa fa-envelope-o"}]] [:span {:class "name"} "Users"]]] + + + [:li.menu-item + [:a {:href (bidi/path-for routes/routes :admin-yodlee), :class (str "item" (active-when= ap :admin-yodlee))} + [:span {:class "icon"} + [:i {:class "fa fa-envelope-o"}]] + [:span {:class "name"} "Yodlee Link"]]] [:ul ]] [:p.menu-label "History"] diff --git a/src/cljs/auto_ap/views/pages.cljs b/src/cljs/auto_ap/views/pages.cljs index 8761eaee..d54d8ac2 100644 --- a/src/cljs/auto_ap/views/pages.cljs +++ b/src/cljs/auto_ap/views/pages.cljs @@ -10,6 +10,7 @@ [auto-ap.views.pages.needs-activation :refer [needs-activation-page]] [auto-ap.views.pages.check :refer [check-page]] [auto-ap.views.pages.admin.companies :refer [admin-companies-page]] + [auto-ap.views.pages.admin.yodlee :refer [admin-yodlee-page]] [auto-ap.views.pages.admin.users :refer [admin-users-page]] [auto-ap.views.pages.admin.vendors :refer [admin-vendors-page]] [auto-ap.views.pages.admin.reminders :refer [admin-reminders-page]] @@ -54,6 +55,9 @@ (defmethod active-page :admin-reminders [] [admin-reminders-page]) +(defmethod active-page :admin-yodlee [] + [admin-yodlee-page]) + (defmethod active-page :admin-excel-import [] [admin-excel-import-page]) diff --git a/src/cljs/auto_ap/views/pages/admin/yodlee.cljs b/src/cljs/auto_ap/views/pages/admin/yodlee.cljs new file mode 100644 index 00000000..6843be24 --- /dev/null +++ b/src/cljs/auto_ap/views/pages/admin/yodlee.cljs @@ -0,0 +1,74 @@ +(ns auto-ap.views.pages.admin.yodlee + (:require-macros [cljs.core.async.macros :refer [go]]) + (:require [re-frame.core :as re-frame] + [reagent.core :as reagent] + [auto-ap.subs :as subs] + [auto-ap.events.admin.companies :as events] + [auto-ap.entities.companies :as entity] + [auto-ap.views.components.address :refer [address-field]] + [auto-ap.views.utils :refer [login-url dispatch-event dispatch-value-change bind-field horizontal-field]] + [auto-ap.views.components.modal :refer [action-modal]] + [cljs.reader :as edn] + [auto-ap.routes :as routes] + [bidi.bidi :as bidi])) + +(re-frame/reg-event-fx + ::authenticate-with-yodlee + (fn [{:keys [db]} _] + {:db (assoc-in db [::yodlee :loading?] true) + :http {:token (:user db) + :method :get + :headers {"Content-Type" "application/edn"} + :uri (str "/api/yodlee/fastlink") + :on-success [::authenticated] + :on-error [::save-error]}})) + +(re-frame/reg-sub + ::authentication + (fn [db] + (-> db ::yodlee :authentication))) + +(re-frame/reg-sub + ::loading? + (fn [db] + (-> db ::yodlee :loading?))) + + +(re-frame/reg-event-fx + ::authenticated + (fn [{:keys [db]} [_ authentication]] + {:db (-> db + (assoc-in [::yodlee :authentication] authentication) + (assoc-in [::yodlee :loading?] false))})) + + + +(defn admin-yodlee-page [] + [:div + (let [authentication @(re-frame/subscribe [::authentication]) + loading? @(re-frame/subscribe [::loading?])] + + [:div + [:h1.title "Yodlee"] + (if authentication + [:div + "Authentication successful!" + [:form {:action (:url authentication) :method "POST"} + [:input {:type "hidden" + :name "rsession" + :value (:session authentication)}] + [:input {:type "hidden" + :name "token" + :value (:token authentication)}] + [:input {:type "hidden" + :name "app" + :value (:app authentication)}] + + [:input {:type "hidden" + :name "redirectReq" + :value "true"}] + [:button.button.is-primary [:span [:span.icon [:i.fa.fa-external-link]] " Go to yodlee"]]]] + + [:button.button.is-primary {:class (if loading? "is-loading" "") :on-click (dispatch-event [::authenticate-with-yodlee])} "Authenticate with Yodlee"]) + + ])])