|
3 | 3 | [clojure.java.io :as io] |
4 | 4 | [clojure.string :as str] |
5 | 5 | [jj.majavat :as majavat] |
6 | | - [jj.majavat.renderer :as renderer] |
7 | | - [jj.sql.async-boa :as boa] |
| 6 | + [jj.sql.boa :as boa] |
8 | 7 | [jj.sql.boa.query.vertx-pg :as vertx-adapter] |
9 | 8 | [jj.tassu :refer [GET POST PUT async-route]] |
10 | 9 | [jsonista.core :as json] |
|
17 | 16 | (java.net URI) |
18 | 17 | (java.security KeyStore PEMDecoder PrivateKey) |
19 | 18 | (java.security.cert Certificate CertificateFactory) |
20 | | - (java.util.concurrent Executors) |
| 19 | + (java.util.concurrent CompletableFuture Executors) |
21 | 20 | (java.util.zip GZIPOutputStream)) |
22 | 21 | (:gen-class)) |
23 | 22 |
|
24 | 23 | (def default-executor (Executors/newVirtualThreadPerTaskExecutor)) |
25 | 24 |
|
| 25 | +(defn- on-complete [^CompletableFuture cf on-success on-error] |
| 26 | + (-> cf |
| 27 | + (.thenAccept on-success) |
| 28 | + (.exceptionally (fn [t] (on-error t) nil)))) |
| 29 | + |
26 | 30 | (def ^:private ^:const ct-json "application/json") |
27 | 31 | (def ^:private ^:const ct-text "text/plain") |
28 | 32 | (def ^:private ^:const ct-html "text/html; charset=utf-8") |
|
55 | 59 | (def ^:private html-headers {hdr-ct ct-html hdr-server server-name}) |
56 | 60 |
|
57 | 61 | (def ^:private adapter (vertx-adapter/->VertxPgAdapter)) |
58 | | -(def ^:private pg-query (boa/build-async-query adapter "sql/pg-query")) |
59 | | -(def ^:private crud-list-query (boa/build-async-query adapter "sql/crud-list")) |
60 | | -(def ^:private crud-read-query (boa/build-async-query adapter "sql/crud-read")) |
61 | | -(def ^:private crud-create-query (boa/build-async-query adapter "sql/crud-create")) |
62 | | -(def ^:private crud-update-query (boa/build-async-query adapter "sql/crud-update")) |
63 | | -(def ^:private fortunes-query (boa/build-async-query adapter "sql/fortunes")) |
| 62 | +(def ^:private pg-query (boa/build-query adapter "sql/pg-query")) |
| 63 | +(def ^:private crud-list-query (boa/build-query adapter "sql/crud-list")) |
| 64 | +(def ^:private crud-read-query (boa/build-query adapter "sql/crud-read")) |
| 65 | +(def ^:private crud-create-query (boa/build-query adapter "sql/crud-create")) |
| 66 | +(def ^:private crud-update-query (boa/build-query adapter "sql/crud-update")) |
| 67 | +(def ^:private fortunes-query (boa/build-query adapter "sql/fortunes")) |
64 | 68 | (def ^:private fortunes-render (majavat/build-html-renderer "fortunes.html")) |
65 | 69 |
|
66 | 70 |
|
|
246 | 250 |
|
247 | 251 | (defn- handle-pg [pg-pool req respond _raise] |
248 | 252 | (let [params (parse-qs (:query-string req)) |
249 | | - min-p (safe-parse-double (get params param-min) 10.0) |
250 | | - max-p (safe-parse-double (get params param-max) 50.0) |
251 | | - limit (safe-parse-long (get params param-limit) 50)] |
252 | | - (pg-query pg-pool {:min min-p :max max-p :limit limit} |
253 | | - (fn [rows] |
254 | | - (let [items (map transform-pg-row rows)] |
255 | | - (respond (json-response {:items items :count (count items)})))) |
256 | | - (fn [_] |
257 | | - (respond (json-response {:items [] :count 0})))))) |
| 253 | + min-p (safe-parse-int (get params param-min) 10) |
| 254 | + max-p (safe-parse-int (get params param-max) 50) |
| 255 | + limit (safe-parse-int (get params param-limit) 50)] |
| 256 | + (on-complete (pg-query pg-pool [min-p max-p limit]) |
| 257 | + (fn [rows] |
| 258 | + (let [items (map transform-pg-row rows)] |
| 259 | + (respond (json-response {:items items :count (count items)})))) |
| 260 | + (fn [_] |
| 261 | + (respond (json-response {:items [] :count 0})))))) |
258 | 262 |
|
259 | 263 | (defn- handle-fortunes [pg-pool respond raise] |
260 | | - (fortunes-query |
261 | | - pg-pool |
262 | | - (fn [rows] |
263 | | - (let [fortunes (sort-by :message (conj rows {:id 0 :message "Additional fortune added at request time."})) |
264 | | - body (fortunes-render {:fortunes fortunes})] |
265 | | - (respond {:status 200 :headers html-headers :body body}))) |
266 | | - raise)) |
| 264 | + (on-complete (fortunes-query pg-pool) |
| 265 | + (fn [rows] |
| 266 | + (let [fortunes (sort-by :message (conj rows {:id 0 :message "Additional fortune added at request time."})) |
| 267 | + body (fortunes-render {:fortunes fortunes})] |
| 268 | + (respond {:status 200 :headers html-headers :body body}))) |
| 269 | + raise)) |
267 | 270 |
|
268 | 271 | (def ^:private crud-hit-headers {hdr-ct ct-json hdr-server server-name "X-Cache" "HIT"}) |
269 | 272 | (def ^:private crud-miss-headers {hdr-ct ct-json hdr-server server-name "X-Cache" "MISS"}) |
|
295 | 298 | (defn- handle-crud-list [pg-pool req respond raise] |
296 | 299 | (let [params (parse-qs (:query-string req)) |
297 | 300 | category (or (get params "category") "electronics") |
298 | | - page (max 1 (safe-parse-long (get params "page") 1)) |
299 | | - limit (max 1 (min 50 (safe-parse-long (get params "limit") 10))) |
300 | | - offset (* (dec page) limit)] |
301 | | - (crud-list-query pg-pool {:category category :limit limit :offset offset} |
302 | | - (fn [rows] |
303 | | - (let [items (map transform-crud-row rows)] |
304 | | - (respond (json-response {:items items |
305 | | - :total (count items) |
306 | | - :page page |
307 | | - :limit limit})))) |
308 | | - raise))) |
| 301 | + page (max 1 (safe-parse-int (get params "page") 1)) |
| 302 | + limit (max 1 (min 50 (safe-parse-int (get params "limit") 10))) |
| 303 | + offset (int (* (dec page) limit))] |
| 304 | + (on-complete (crud-list-query pg-pool [category limit offset]) |
| 305 | + (fn [rows] |
| 306 | + (let [items (map transform-crud-row rows)] |
| 307 | + (respond (json-response {:items items |
| 308 | + :total (count items) |
| 309 | + :page page |
| 310 | + :limit limit})))) |
| 311 | + raise))) |
309 | 312 |
|
310 | 313 | (defn- handle-crud-read [pg-pool req respond raise] |
311 | | - (let [id (safe-parse-long (get-in req [:params :id]) nil)] |
| 314 | + (let [id (safe-parse-int (get-in req [:params :id]) nil)] |
312 | 315 | (if (nil? id) |
313 | 316 | (respond {:status 404 :headers json-headers :body not-found-body}) |
314 | 317 | (if-let [cached (crud-cache-get id)] |
315 | 318 | (respond {:status 200 :headers crud-hit-headers :body cached}) |
316 | | - (crud-read-query pg-pool {:id id} |
317 | | - (fn [rows] |
318 | | - (if-let [row (first rows)] |
319 | | - (let [json-str (json/write-value-as-string (transform-crud-row row))] |
320 | | - (crud-cache-set id json-str) |
321 | | - (respond {:status 200 :headers crud-miss-headers :body json-str})) |
322 | | - (respond {:status 404 :headers json-headers :body not-found-body}))) |
323 | | - raise))))) |
| 319 | + (on-complete (crud-read-query pg-pool [id]) |
| 320 | + (fn [rows] |
| 321 | + (if-let [row (first rows)] |
| 322 | + (let [json-str (json/write-value-as-string (transform-crud-row row))] |
| 323 | + (crud-cache-set id json-str) |
| 324 | + (respond {:status 200 :headers crud-miss-headers :body json-str})) |
| 325 | + (respond {:status 404 :headers json-headers :body not-found-body}))) |
| 326 | + raise))))) |
324 | 327 |
|
325 | 328 | (defn- handle-crud-create [pg-pool req respond raise] |
326 | 329 | (let [body (json/read-value (:body req) json/keyword-keys-object-mapper) |
327 | | - id (:id body) |
| 330 | + id (int (or (:id body) 0)) |
328 | 331 | nm (or (:name body) "New Product") |
329 | 332 | category (or (:category body) "test") |
330 | | - price (or (:price body) 0) |
331 | | - quantity (or (:quantity body) 0)] |
332 | | - (crud-create-query pg-pool {:id id :name nm :category category :price price :quantity quantity} |
333 | | - (fn [rows] |
334 | | - (respond {:status 201 |
335 | | - :headers json-headers |
336 | | - :body (json/write-value-as-string |
337 | | - {:id (:id (first rows)) |
338 | | - :name nm |
339 | | - :category category |
340 | | - :price price |
341 | | - :quantity quantity})})) |
342 | | - raise))) |
| 333 | + price (int (or (:price body) 0)) |
| 334 | + quantity (int (or (:quantity body) 0))] |
| 335 | + (on-complete (crud-create-query pg-pool [id nm category price quantity]) |
| 336 | + (fn [rows] |
| 337 | + (respond {:status 201 |
| 338 | + :headers json-headers |
| 339 | + :body (json/write-value-as-string |
| 340 | + {:id (:id (first rows)) |
| 341 | + :name nm |
| 342 | + :category category |
| 343 | + :price price |
| 344 | + :quantity quantity})})) |
| 345 | + raise))) |
343 | 346 |
|
344 | 347 | (defn- handle-crud-update [pg-pool req respond raise] |
345 | | - (let [id (safe-parse-long (get-in req [:params :id]) nil)] |
| 348 | + (let [id (safe-parse-int (get-in req [:params :id]) nil)] |
346 | 349 | (if (nil? id) |
347 | 350 | (respond {:status 404 :headers json-headers :body not-found-body}) |
348 | 351 | (let [body (json/read-value (:body req) json/keyword-keys-object-mapper) |
349 | 352 | nm (or (:name body) "Updated") |
350 | | - price (or (:price body) 0) |
351 | | - quantity (or (:quantity body) 0)] |
352 | | - (crud-update-query pg-pool {:name nm :price price :quantity quantity :id id} |
353 | | - (fn [rows] |
354 | | - (if (seq rows) |
355 | | - (do |
356 | | - (crud-cache-evict id) |
357 | | - (respond {:status 200 |
358 | | - :headers json-headers |
359 | | - :body (json/write-value-as-string |
360 | | - {:id id |
361 | | - :name nm |
362 | | - :price price |
363 | | - :quantity quantity})})) |
364 | | - (respond {:status 404 :headers json-headers :body not-found-body}))) |
365 | | - raise))))) |
| 353 | + price (int (or (:price body) 0)) |
| 354 | + quantity (int (or (:quantity body) 0))] |
| 355 | + (on-complete (crud-update-query pg-pool [nm price quantity id]) |
| 356 | + (fn [rows] |
| 357 | + (if (seq rows) |
| 358 | + (do |
| 359 | + (crud-cache-evict id) |
| 360 | + (respond {:status 200 |
| 361 | + :headers json-headers |
| 362 | + :body (json/write-value-as-string |
| 363 | + {:id id |
| 364 | + :name nm |
| 365 | + :price price |
| 366 | + :quantity quantity})})) |
| 367 | + (respond {:status 404 :headers json-headers :body not-found-body}))) |
| 368 | + raise))))) |
366 | 369 |
|
367 | 370 | (defn- handle-static [req respond _raise] |
368 | 371 | (let [name (get-in req [:params :filename]) |
|
0 commit comments