mirror of
https://github.com/penpot/penpot.git
synced 2026-09-10 14:09:17 +00:00
✨ Send telemetry event batch as plain JSON vector
The event batch sent to the telemetry server was encoded as a fressian+zstd base64 blob. Send it as a plain vector of event maps instead: the JSON encoder handles UUID and temporal types natively, the payload becomes inspectable, and the receiver schema coerces values back to proper types. The receiver (penpot-telemetry) now accepts both the blob and the plain vector, so it must be deployed before this backend change. AI-assisted-by: omen-alpha
This commit is contained in:
parent
3b1290c6a2
commit
e96a75d366
@ -17,7 +17,6 @@
|
|||||||
[app.http.client :as http]
|
[app.http.client :as http]
|
||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.setup :as-alias setup]
|
[app.setup :as-alias setup]
|
||||||
[app.util.blob :as blob]
|
|
||||||
[app.util.json :as json]
|
[app.util.json :as json]
|
||||||
[integrant.core :as ig]
|
[integrant.core :as ig]
|
||||||
[promesa.exec :as px]))
|
[promesa.exec :as px]))
|
||||||
@ -248,20 +247,16 @@
|
|||||||
:props (or (some-> props db/decode-transit-pgobject) {})
|
:props (or (some-> props db/decode-transit-pgobject) {})
|
||||||
:context (or (some-> context db/decode-transit-pgobject) {})}))
|
:context (or (some-> context db/decode-transit-pgobject) {})}))
|
||||||
|
|
||||||
(defn- encode-batch
|
|
||||||
"Encode a sequence of event maps into a fressian+zstd base64 string
|
|
||||||
suitable for JSON transport."
|
|
||||||
^String [events]
|
|
||||||
(blob/encode-str events {:version 4}))
|
|
||||||
|
|
||||||
(defn send-event-batch
|
(defn send-event-batch
|
||||||
"Send a single batch of events to the telemetry endpoint. Returns
|
"Send a single batch of events to the telemetry endpoint. Returns
|
||||||
true on success."
|
true on success. The events are sent as a plain vector of event
|
||||||
|
maps; the JSON encoder handles UUID and temporal types natively and
|
||||||
|
the receiver coerces them back to proper types."
|
||||||
[{:keys [::setup/props] :as cfg} batch]
|
[{:keys [::setup/props] :as cfg} batch]
|
||||||
(let [payload {:type :telemetry-events
|
(let [payload {:type :telemetry-events
|
||||||
:version (:full cf/version)
|
:version (:full cf/version)
|
||||||
:instance-id (:instance-id props)
|
:instance-id (:instance-id props)
|
||||||
:events (encode-batch batch)}
|
:events (vec batch)}
|
||||||
request {:method :post
|
request {:method :post
|
||||||
:uri (cf/get :telemetry-uri)
|
:uri (cf/get :telemetry-uri)
|
||||||
:headers {"content-type" "application/json"}
|
:headers {"content-type" "application/json"}
|
||||||
|
|||||||
@ -12,7 +12,6 @@
|
|||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.loggers.audit :as audit]
|
[app.loggers.audit :as audit]
|
||||||
[app.tasks.telemetry :as telemetry]
|
[app.tasks.telemetry :as telemetry]
|
||||||
[app.util.blob :as blob]
|
|
||||||
[app.util.json :as json]
|
[app.util.json :as json]
|
||||||
[backend-tests.helpers :as th]
|
[backend-tests.helpers :as th]
|
||||||
[clojure.test :as t]
|
[clojure.test :as t]
|
||||||
@ -59,11 +58,6 @@
|
|||||||
:cnt
|
:cnt
|
||||||
long))
|
long))
|
||||||
|
|
||||||
(defn- decode-event-batch
|
|
||||||
"Decode the base64+fressian+zstd event-batch sent to the mock."
|
|
||||||
[b64-str]
|
|
||||||
(blob/decode-str b64-str))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; STATS / REPORT STRUCTURE TESTS (existing behaviour, extended)
|
;; STATS / REPORT STRUCTURE TESTS (existing behaviour, extended)
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@ -245,21 +239,19 @@
|
|||||||
(t/is (not (contains? ev :ip-addr)))))))))
|
(t/is (not (contains? ev :ip-addr)))))))))
|
||||||
|
|
||||||
(t/deftest test-batch-encoding-is-decodable
|
(t/deftest test-batch-encoding-is-decodable
|
||||||
;; Verify that encode-batch produces a blob that round-trips back
|
;; Events are sent as a plain vector of raw event maps (no blob
|
||||||
;; through blob/decode to the original data.
|
;; encoding): every batch must JSON round-trip unchanged, because
|
||||||
|
;; the receiver coerces types from the plain JSON representation.
|
||||||
(let [events [{:name "navigate" :type "action" :source "telemetry"
|
(let [events [{:name "navigate" :type "action" :source "telemetry"
|
||||||
:tracked-at (ct/now)}
|
:tracked-at (ct/now)}
|
||||||
{:name "create-file" :type "action" :source "telemetry"
|
{:name "create-file" :type "action" :source "telemetry"
|
||||||
:tracked-at (ct/now)}]
|
:tracked-at (ct/now)}]
|
||||||
;; Call the private fn through the ns-mapped var
|
encoded (json/encode-str {:events (vec events)})
|
||||||
encode (ns-resolve 'app.tasks.telemetry 'encode-batch)
|
decoded (json/decode encoded)]
|
||||||
encoded (encode events)
|
(t/is (vector? (:events decoded)))
|
||||||
decoded (decode-event-batch encoded)]
|
(t/is (= (count events) (count (:events decoded))))
|
||||||
(t/is (string? encoded))
|
(t/is (= "navigate" (:name (first (:events decoded)))))
|
||||||
(t/is (seq decoded))
|
(t/is (= "create-file" (:name (second (:events decoded)))))))
|
||||||
(t/is (= (count events) (count decoded)))
|
|
||||||
(t/is (= "navigate" (:name (first decoded))))
|
|
||||||
(t/is (= "create-file" (:name (second decoded))))))
|
|
||||||
|
|
||||||
(t/deftest test-multiple-batches-when-many-events
|
(t/deftest test-multiple-batches-when-many-events
|
||||||
;; Lower batch-size to 1 so that 3 events produce 3 separate
|
;; Lower batch-size to 1 so that 3 events produce 3 separate
|
||||||
@ -787,9 +779,13 @@
|
|||||||
(t/is (= "telemetry-events" (name (:type body))))
|
(t/is (= "telemetry-events" (name (:type body))))
|
||||||
(t/is (string? (:version body)))
|
(t/is (string? (:version body)))
|
||||||
(t/is (some? (:instance-id body)))
|
(t/is (some? (:instance-id body)))
|
||||||
;; :events is a base64-encoded blob
|
;; :events is a plain vector of raw event maps
|
||||||
(t/is (string? (:events body)))
|
(t/is (vector? (:events body)))
|
||||||
(t/is (pos? (count (:events body))))))))))
|
(t/is (pos? (count (:events body))))
|
||||||
|
(doseq [ev (:events body)]
|
||||||
|
(t/is (string? (:name ev)))
|
||||||
|
(t/is (string? (:source ev)))
|
||||||
|
(t/is (string? (:tracked-at ev))))))))))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; TASK BRANCH COVERAGE
|
;; TASK BRANCH COVERAGE
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user