Skip to content

Commit fac8612

Browse files
Implement config saving
1 parent 9ca5369 commit fac8612

File tree

11 files changed

+119
-41
lines changed

11 files changed

+119
-41
lines changed

res/flamegraph/script.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const qString = new URLSearchParams(window.location.search)
1010
const transformFilterTemplate = document.getElementById('transformFilterTemplate');
1111
const transformReplaceTemplate = document.getElementById('transformReplaceTemplate');
1212
const minSamplesToShow = 0; // Don't hide any frames for now.
13+
const profileId = "<<<profileId>>>";
1314

1415
/// Config handling
1516

@@ -223,12 +224,16 @@ async function pasteConfigFromClipboard() {
223224
}
224225

225226
async function saveConfigToServer() {
226-
let packedConfig = await constructPackedConfig();
227-
let req = new XMLHttpRequest();
228-
req.open("POST", "/save-config?packed-config=" + packedConfig);
229-
console.log("[clj-async-profiler] Sending save-config request to backend:", req);
230-
req.send();
231-
showToast("Config saved to server.")
227+
let editToken = window.prompt("Paste editToken here to save the profile config", "");
228+
if (editToken != null && editToken != "") {
229+
let packedConfig = await constructPackedConfig();
230+
let req = new XMLHttpRequest();
231+
req.open("POST", `/api/v1/save-profile-config?id=${profileId}&edit-token=${editToken}&config=${packedConfig}`);
232+
console.log("[clj-async-profiler] Sending save-config request to backend:", req);
233+
req.send();
234+
if (req.status == 204)
235+
showToast("Config saved to server.")
236+
}
232237
}
233238

234239
function match(string, obj) {

res/flamegraph/template.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,9 @@
183183
<button id="pasteConfigButton" class="toggle" onclick="pasteConfigFromClipboard()" title="Paste config">
184184
<svg><use href="#paste-icon"/></svg>
185185
</button>
186-
<!-- <button id="saveConfigButton" class="toggle" onclick="saveConfigToServer()" title="Save config"> -->
187-
<!-- <svg><use href="#save-icon"/></svg> -->
188-
<!-- </button> -->
186+
<button id="saveConfigButton" class="toggle" onclick="saveConfigToServer()" title="Save config">
187+
<svg><use href="#save-icon"/></svg>
188+
</button>
189189
</div>
190190
<div class="sidebar-row chip-row">
191191
<button class="chip btn" onclick="addNewTransform('filter')" >+ Filter</button>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE profile
2+
ADD COLUMN config TEXT;

src/flamebin/core.clj

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
[flamebin.rate-limiter :as rl]
66
[flamebin.render :as render]
77
[flamebin.storage :as storage]
8-
[flamebin.util :refer [raise secret-token new-id]])
8+
[flamebin.util :refer [raise secret-token new-id]]
9+
[taoensso.timbre :as log])
910
(:import java.time.Instant))
1011

1112
(defn- ensure-saved-limits [ip length-kb]
@@ -27,7 +28,7 @@
2728
(storage/save-file dpf-array filename)
2829
;; TODO: replace IP with proper owner at some point
2930
(-> (dto/->Profile id filename (:type params) (:total-samples profile) ip
30-
edit-token public (Instant/now))
31+
edit-token public nil (Instant/now))
3132
db/insert-profile
3233
;; Attach read-token to the response here — it's not in the scheme
3334
;; because we don't store it in the DB.
@@ -42,17 +43,27 @@
4243

4344
(-> (storage/get-file file_path)
4445
(proc/read-compressed-profile read-token)
45-
(render/render-html-flamegraph {}))))
46+
(render/render-html-flamegraph profile {}))))
4647

47-
(defn delete-profile [profile-id provided-edit-token]
48-
(let [{:keys [edit_token file_path]} (db/get-profile profile-id)]
49-
;; Authorization
48+
(defn- authorize-profile-editing [profile provided-edit-token]
49+
(let [{:keys [edit_token]} profile]
5050
(when (and edit_token (not= provided-edit-token edit_token))
51-
(raise 403 "Required edit-token to perform this action."))
52-
(println file_path)
51+
(raise 403 "Required edit-token to perform this action."))))
52+
53+
(defn delete-profile [profile-id provided-edit-token]
54+
(let [{:keys [file_path] :as profile} (db/get-profile profile-id)]
55+
(authorize-profile-editing profile provided-edit-token)
5356
(storage/delete-file file_path)
5457
(db/delete-profile profile-id)))
5558

59+
(defn save-profile-config [profile-id config-string provided-edit-token]
60+
(let [{:keys [file_path] :as profile} (db/get-profile profile-id)]
61+
(authorize-profile-editing profile provided-edit-token)
62+
(when (> (count config-string) 2000)
63+
(raise 413 "Config string is too long."))
64+
(log/infof "Setting config for profile %s: %s" profile-id config-string)
65+
(db/save-profile-config profile-id config-string)))
66+
5667
(defn list-public-profiles []
5768
(db/list-public-profiles 20))
5869

src/flamebin/db.clj

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
:lock (ReentrantLock.)}
3232
migrate))
3333

34-
#_(mount/start #'db)
34+
#_(do (mount/stop #'db) (mount/start #'db))
3535

3636
;;;; DB interaction
3737

@@ -51,7 +51,7 @@
5151
(defn insert-profile [profile]
5252
(m/assert Profile profile)
5353
(let [{:keys [id file_path profile_type sample_count owner upload_ts
54-
edit_token is_public]} profile]
54+
edit_token config is_public]} profile]
5555
(log/infof "Inserting profile %s from %s" id owner)
5656
(with-locking (:lock @db)
5757
(jdbc/with-transaction [tx @db]
@@ -63,6 +63,7 @@
6363
:sample_count sample_count
6464
:is_public is_public
6565
:edit_token edit_token
66+
:config config
6667
:owner owner})
6768
(jdbc/execute-one! tx ["UPDATE stats SET val = val + 1 WHERE stat = 'total_uploaded'"])))
6869
profile))
@@ -73,21 +74,21 @@
7374

7475
(defn list-profiles []
7576
(with-locking (:lock @db)
76-
(->> (jdbc/execute! @db ["SELECT id, file_path, profile_type, sample_count, owner, upload_ts, is_public FROM profile"])
77+
(->> (jdbc/execute! @db ["SELECT id, file_path, profile_type, sample_count, owner, config, upload_ts, is_public FROM profile"])
7778
(mapv #(-> (unqualify-keys %)
7879
(assoc :edit_token nil)
7980
(coerce Profile))))))
8081

8182
(defn list-public-profiles [n]
8283
(with-locking (:lock @db)
83-
(->> (jdbc/execute! @db ["SELECT id, file_path, profile_type, sample_count, owner, upload_ts, is_public, edit_token FROM profile
84+
(->> (jdbc/execute! @db ["SELECT id, file_path, profile_type, sample_count, owner, config, upload_ts, is_public, edit_token FROM profile
8485
WHERE is_public = 1 ORDER BY upload_ts DESC LIMIT ?" n])
8586
(mapv #(-> (unqualify-keys %)
8687
(coerce Profile))))))
8788

8889
(defn get-profile [profile-id]
8990
(with-locking (:lock @db)
90-
(let [q ["SELECT id, file_path, profile_type, sample_count, owner, upload_ts, edit_token, is_public FROM profile WHERE id = ?" profile-id]
91+
(let [q ["SELECT id, file_path, profile_type, sample_count, owner, config, upload_ts, edit_token, is_public FROM profile WHERE id = ?" profile-id]
9192
row (some-> (jdbc/execute-one! @db q)
9293
unqualify-keys
9394
(coerce Profile))]
@@ -103,6 +104,10 @@ WHERE is_public = 1 ORDER BY upload_ts DESC LIMIT ?" n])
103104
(when (zero? update-count)
104105
(raise 404 (format "Profile with ID '%s' not found." profile-id))))))
105106

107+
(defn save-profile-config [profile-id config]
108+
(with-locking (:lock @db)
109+
(sql-helpers/update! @db :profile {:config config} {:id profile-id})))
110+
106111
(defn clear-db []
107112
(with-locking (:lock @db)
108113
(.delete (clojure.java.io/file (@config :db :path)))

src/flamebin/dto.clj

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,47 +10,50 @@
1010

1111
(defmacro ^:private defschema-and-constructor [schema-name schema-val]
1212
(assert (= (first schema-val) '->))
13-
(assert (map? (second schema-val)))
14-
(let [ks (keys (second schema-val))]
15-
`(let [sch# ~schema-val]
16-
(def ~schema-name ~schema-val)
17-
(defn ~(symbol (str "->" schema-name)) ~(mapv symbol ks)
18-
(coerce ~(into {} (map #(vector % (symbol %)) ks)) ~schema-name)))))
13+
(assert (= (first (second schema-val)) 'array-map))
14+
(let [ks (mapv first (partition 2 (rest (second schema-val))))]
15+
`(do (def ~schema-name ~schema-val)
16+
(defn ~(symbol (str "->" schema-name)) ~(mapv symbol ks)
17+
(coerce ~(into {} (map #(vector % (symbol %)) ks)) ~schema-name)))))
1918

2019
;;;; Profile
2120

2221
(defschema-and-constructor Profile
23-
(-> {:id :nano-id
22+
(-> (array-map
23+
:id :nano-id
2424
:file_path [:and {:gen/fmap #(str % ".dpf")} :string]
2525
:profile_type :keyword
2626
:sample_count [:maybe nat-int?]
2727
:owner [:maybe :string]
2828
:edit_token [:maybe :string]
2929
:is_public :boolean
30+
:config [:maybe :string]
3031
:upload_ts [:and {:gen/gen (gen/fmap Instant/ofEpochSecond
3132
(gen/choose 1500000000 1700000000))}
32-
:time/instant]}
33+
:time/instant])
3334
mlite/schema))
3435

3536
#_((requiring-resolve 'malli.generator/sample) Profile)
3637

3738
;;;; DenseProfile
3839

3940
(defschema-and-constructor DenseProfile
40-
(-> {:stacks [:vector [:tuple [:vector nat-int?] pos-int?]]
41+
(-> (array-map
42+
:stacks [:vector [:tuple [:vector nat-int?] pos-int?]]
4143
:id->frame [:vector string?]
42-
:total-samples pos-int?}
44+
:total-samples pos-int?)
4345
mlite/schema
4446
(mu/optional-keys [:total-samples])
4547
mu/closed-schema))
4648

4749
;;;; UploadProfileRequest
4850

4951
(defschema-and-constructor UploadProfileRequestParams
50-
(-> {:format [:enum :collapsed :dense-edn]
52+
(-> (array-map
53+
:format [:enum :collapsed :dense-edn]
5154
:kind [:schema {:default :flamegraph} [:enum :flamegraph :diffgraph]]
5255
:type [:re #"[\w\.]+"]
53-
:public [:schema {:default false} :boolean]}
56+
:public [:schema {:default false} :boolean])
5457
mlite/schema))
5558

5659
#_(->UploadProfileRequestParams "collapsed" nil "cpu" true)

src/flamebin/render.clj

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,23 @@
44

55
;;;; Flamegraph rendering
66

7-
(defn render-html-flamegraph [dense-profile options]
7+
(defn render-html-flamegraph [dense-profile profile-dto options]
88
(let [{:keys [stacks id->frame]} dense-profile
9+
{:keys [config]} profile-dto
10+
config (if config
11+
(str "\"" config "\"")
12+
"null")
913
idToFrame (#'cljap.render/print-id-to-frame id->frame)
1014
data (#'cljap.render/print-add-stacks stacks false)
1115
user-transforms nil
1216
full-js (-> (slurp (io/resource "flamegraph/script.js"))
1317
(cljap.render/render-template
1418
{:graphTitle (pr-str (or (:title options) ""))
19+
:profileId (:id profile-dto)
1520
:isDiffgraph false
1621
:userTransforms ""
1722
:idToFrame idToFrame
18-
:config "null"
23+
:config config
1924
:stacks data}))]
2025
(-> (slurp (io/resource "flamegraph/template.html"))
2126
(cljap.render/render-template {:script full-js}))))

src/flamebin/web.clj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@
5757
(core/delete-profile id edit-token)
5858
(resp 200 {:message (str "Successfully deleted profile: " id)})))
5959

60+
(defn $save-config [{:keys [query-params]}]
61+
(let [{:keys [id edit-token config]} query-params]
62+
(core/save-profile-config id config edit-token)
63+
(resp 204 nil)))
64+
6065
;; Endpoints: web pages
6166

6267
(defn $page-upload-file [req]
@@ -107,6 +112,10 @@
107112
["/upload-profile" {:middleware [wrap-gzip-request]
108113
:post {:handler #'$upload-profile
109114
:parameters {:query' UploadProfileRequestParams}}}]
115+
["/save-profile-config" {:post {:handler #'$save-config
116+
:parameters {:query' {:id :nano-id
117+
:edit-token :string
118+
:config :string}}}}]
110119
;; GET so that user can easily do it in the browser.
111120
["/delete-profile" {:name ::api-delete-profile
112121
:get {:handler #'$delete-profile

test/flamebin/db_test.clj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@
2727
(deftest manual-test
2828
(with-temp-db-and-state
2929
(db/insert-profile (dto/->Profile "QcXAqv" "some-path.dpf" "cpu" 12345
30-
nil "alhdslfglksjdfhg" true inst1))
30+
nil "alhdslfglksjdfhg" true "HAA" inst1))
3131
(is (= {:id "QcXAqv", :file_path "some-path.dpf", :profile_type :cpu,
3232
:upload_ts inst1, :sample_count 12345, :owner nil, :is_public true
33-
:edit_token "alhdslfglksjdfhg"}
33+
:config "HAA", :edit_token "alhdslfglksjdfhg"}
3434
(db/get-profile "QcXAqv")))
3535
(is (inst? (:upload_ts (db/get-profile "QcXAqv"))))
3636

3737
(db/insert-profile (dto/->Profile "tX8nuc" "another-path.dpf" "alloc" 54321
38-
"me" nil false inst1))
38+
"me" nil false nil inst1))
3939
(is (= {:id "tX8nuc", :file_path "another-path.dpf", :edit_token nil
4040
:owner "me", :sample_count 54321, :profile_type :alloc, :is_public false
41-
:upload_ts inst1}
41+
:config nil, :upload_ts inst1}
4242
(db/get-profile "tX8nuc")))))
4343

4444
;;;; Generative testing

0 commit comments

Comments
 (0)