diff --git a/backend/src/app/main.clj b/backend/src/app/main.clj index cb650dfdc3..1923b5f054 100644 --- a/backend/src/app/main.clj +++ b/backend/src/app/main.clj @@ -323,7 +323,7 @@ {::http.client/client (ig/ref ::http.client/client) ::db/pool (ig/ref ::db/pool) ::rds/pool (ig/ref ::rds/pool) - :app.nitrate/instance (ig/ref :app.nitrate/instance) + :app.nitrate/client (ig/ref :app.nitrate/client) ::wrk/executor (ig/ref ::wrk/netty-executor) ::session/manager (ig/ref ::session/manager) ::ldap/provider (ig/ref ::ldap/provider) @@ -340,7 +340,7 @@ ::email/blacklist (ig/ref ::email/blacklist) ::email/whitelist (ig/ref ::email/whitelist)} - :app.nitrate/instance + :app.nitrate/client {::http.client/client (ig/ref ::http.client/client)} :app.rpc/management-methods @@ -352,6 +352,7 @@ ::sto/storage (ig/ref ::sto/storage) ::mtx/metrics (ig/ref ::mtx/metrics) ::mbus/msgbus (ig/ref ::mbus/msgbus) + :app.nitrate/client (ig/ref :app.nitrate/client) ::rds/client (ig/ref ::rds/client) ::setup/props (ig/ref ::setup/props)} diff --git a/backend/src/app/nitrate.clj b/backend/src/app/nitrate.clj index 15f66d1cd7..2ff2472870 100644 --- a/backend/src/app/nitrate.clj +++ b/backend/src/app/nitrate.clj @@ -5,8 +5,9 @@ ;; Copyright (c) KALEIDOS INC (ns app.nitrate - "The msgbus abstraction implemented using redis as underlying backend." + "Module that make calls to the external nitrate aplication" (:require + [app.common.logging :as l] [app.common.schema :as sm] [app.config :as cf] [app.http.client :as http] @@ -17,6 +18,10 @@ [integrant.core :as ig])) +;; TODO Extract to env +(def baseuri "http://localhost:3000") + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; HELPERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -28,70 +33,92 @@ (fn [data] (-> data decode-fn check-fn)))) -(defn http-validated-call - [{:keys [::setup/props] :as cfg} method uri schema {:keys [::rpc/profile-id] :as params}] - (let [coercer-http (coercer schema - :type :validation - :hint (str "invalid data received calling " uri)) - management-key (or (cf/get :management-api-key) - (get props :management-key)) - ;; TODO cache - ;; TODO retries - ;; TODO error handling - rsp (try - (http/req! cfg {:method method - :headers {"User-Agent" "curl/7.85.0" - "content-type" "application/json" - "accept" "application/json" - "x-shared-key" management-key - "x-profile-id" (str profile-id)} - :uri uri - :version :http1.1}) - (catch Exception e - (println "Error:" (.getMessage e))))] - (try - (coercer-http (-> rsp :body json/decode)) - (catch Exception e - (println "Error:" (.getMessage e)) - nil)))) + +(defn- request-builder + [cfg method uri management-key profile-id] + (fn [] + (http/req! cfg {:method method + :headers {"content-type" "application/json" + "accept" "application/json" + "x-shared-key" management-key + "x-profile-id" (str profile-id)} + :uri uri + :version :http1.1}))) +(defn- with-retries + [handler max-retries] + (fn [] + (loop [attempt 1] + (let [result (try + (handler) + (catch Exception e + (if (< attempt max-retries) + ::retry + (do + ;; TODO Error handling + (l/error :hint "request fail after multiple retries" :cause e) + nil))))] + (if (= result ::retry) + (recur (inc attempt)) + result))))) + + +(defn- with-validate [handler uri schema] + (fn [] + (let [coercer-http (coercer schema + :type :validation + :hint (str "invalid data received calling " uri))] + (try + (coercer-http (-> (handler) :body json/decode)) + (catch Exception e + ;; TODO Error handling + (l/error :hint "error validating json response" :cause e) + nil))))) + +(defn- request-to-nitrate + [{:keys [::management-key] :as cfg} method uri schema {:keys [::rpc/profile-id] :as params}] + (let [full-http-call (-> (request-builder cfg method uri management-key profile-id) + (with-retries 3) + (with-validate uri schema))] + (full-http-call))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; PUBLIC API +;; API ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn call [cfg method params] (when (contains? cf/flags :nitrate) - (let [instance (get cfg ::instance) - method (get instance method)] + (let [client (get cfg ::client) + method (get client method)] (method params)))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; INITIALIZATION -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; TODO Extract to env -(def baseuri "http://localhost:3000") - (def ^:private schema:organization [:map [:id ::sm/int] [:name ::sm/text]]) + (defn- get-team-org [cfg {:keys [team-id] :as params}] - (http-validated-call cfg :get (str baseuri "/api/teams/" (str team-id)) schema:organization params)) + (request-to-nitrate cfg :get (str baseuri "/api/teams/" (str team-id)) schema:organization params)) -(defmethod ig/init-key ::instance - [_ cfg] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; INITIALIZATION +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmethod ig/init-key ::client + [_ {:keys [::setup/props] :as cfg}] (if (contains? cf/flags :nitrate) - {:get-team-org (partial get-team-org cfg)} + (let [management-key (or (cf/get :management-api-key) + (get props :management-key)) + cfg (assoc cfg ::management-key management-key)] + {:get-team-org (partial get-team-org cfg)}) {})) -(defmethod ig/halt-key! ::instance +(defmethod ig/halt-key! ::client [_ {:keys []}] (do :stuff))