Add retry and validation to nitrate module

This commit is contained in:
Pablo Alba 2025-11-25 15:28:39 +01:00
parent df834f2e90
commit 63d4d76fd8
2 changed files with 72 additions and 44 deletions

View File

@ -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)}

View File

@ -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))