fix update-snippet

Created via bruno",
Updated via bruno"
does a cool thing",
This commit is contained in:
Travis Shears 2026-03-10 14:55:33 +01:00
parent d3babebcc4
commit 5fad04d04c
Signed by: travisshears
GPG key ID: CB9BF1910F3F7469
15 changed files with 90 additions and 102 deletions

View file

@ -18,8 +18,16 @@ This project is written in [Clojure](https://clojure.org/) and data is stored in
### How to run dev server ### How to run dev server
Run the server
``` ```
$ clojure -M -m snippets.infra.api $ clojure -M -m snippets.main
```
Hot reload:
```shell
$ fd -e clj | entr -r clojure -M -m snippets.main
``` ```
### Repl ### Repl

View file

@ -12,9 +12,9 @@ post {
body:json { body:json {
{ {
"title": "TEST from Bruno", "title": "Mock snippet sent via Bruno",
"slug": "bruno-test", "slug": "bruno-test",
"markdown": "this is a test", "markdown": "## MOCK SNIPPET\nCreated via bruno",
"tags": ["test"] "tags": ["mock"]
} }
} }

View file

@ -5,13 +5,13 @@ meta {
} }
delete { delete {
url: {{host}}/api/snippet?id=d77d3463-c76e-4c53-a1d5-ecaf16c6c54e url: {{host}}/api/snippet?slug=bruno-test
body: none body: none
auth: none auth: none
} }
params:query { params:query {
id: d77d3463-c76e-4c53-a1d5-ecaf16c6c54e slug: bruno-test
} }
body:json { body:json {

View file

@ -5,18 +5,18 @@ meta {
} }
patch { patch {
url: {{host}}/api/snippet?id=680a3508-7709-4f71-b5c3-3dcbffe6f5cf url: {{host}}/api/snippet?slug=bruno-test
body: json body: json
auth: none auth: none
} }
params:query { params:query {
id: 680a3508-7709-4f71-b5c3-3dcbffe6f5cf slug: bruno-test
} }
body:json { body:json {
{ {
"title": "quick way to push last jj commit to git", "title": "Mock snippet sent via Bruno with updated title",
"tags": ["jj", "git"] "markdown": "## MOCK SNIPPET\nUpdated via bruno"
} }
} }

View file

@ -1,23 +0,0 @@
meta {
name: get_snippet
type: http
seq: 6
}
get {
url: {{host}}/api/snippet?id=aea69336-5116-49ac-ab52-bc221bdb7830
body: none
auth: none
}
params:query {
id: aea69336-5116-49ac-ab52-bc221bdb7830
}
body:json {
{
"title": "Test Snippet",
"markdown": "## Cool Snippet\ndoes a cool thing",
"tags": ["git", "jj"]
}
}

View file

@ -5,13 +5,13 @@ meta {
} }
get { get {
url: {{host}}/api/snippet-by-slug?slug=netcat-over-ping url: {{host}}/api/snippet-by-slug?slug=bruno-test
body: none body: none
auth: none auth: none
} }
params:query { params:query {
slug: netcat-over-ping slug: bruno-test
} }
body:json { body:json {

View file

@ -5,13 +5,13 @@ meta {
} }
get { get {
url: {{host}}/api/snippets?limit=25&skip=0 url: {{host}}/api/snippets?limit=2&skip=0
body: none body: none
auth: none auth: none
} }
params:query { params:query {
limit: 25 limit: 2
skip: 0 skip: 0
} }

View file

@ -5,13 +5,13 @@ meta {
} }
get { get {
url: {{host}}/api/tag?tag=git url: {{host}}/api/tag?tag=mock
body: none body: none
auth: none auth: none
} }
params:query { params:query {
tag: git tag: mock
} }
body:json { body:json {

View file

@ -23,9 +23,9 @@
{:status 200, :body "snippet created"}) {:status 200, :body "snippet created"})
(defn handle-edit-snippet [{body :body-params params :query-params}] (defn handle-edit-snippet [{body :body-params params :query-params}]
(let [id (get params "id")] (let [slug (get params "slug")]
(t/log! {:level :info, :data {:body body :id id}} "Received request to edit snippet") (t/log! {:level :info, :data {:body body :slug slug}} "Received request to edit snippet")
(let [{success :success :as res} (snippets.use-cases.edit/edit-snippet id body)] (let [{success :success :as res} (snippets.use-cases.edit/edit-snippet slug body)]
(cond (cond
success {:status 200, :body "snippet updated"} success {:status 200, :body "snippet updated"}
(= (:reason res) :invalid-patch) {:status 400, :body "invalid patch"} (= (:reason res) :invalid-patch) {:status 400, :body "invalid patch"}
@ -41,10 +41,13 @@
:body (snippets.use-cases.view/view-snippets nil)})) :body (snippets.use-cases.view/view-snippets nil)}))
(defn handle-delete-snippet [{params :query-params}] (defn handle-delete-snippet [{params :query-params}]
(let [id (get params "id")] (let [slug (get params "slug")
(snippets.use-cases.delete/delete-snippet id) res (snippets.use-cases.delete/delete-snippet slug)]
{:status 200 (if (nil? res)
:body (format "Deleted snippet with id: %s if it existed" id)})) {:status 404
:body "No snippet with that slug found"}
{:status 200
:body (format "Deleted snippet with slug: %s if it existed" slug)})))
(defn handle-view-tags [_args] (defn handle-view-tags [_args]
(let [tags (snippets.use-cases.view/view-tags)] (let [tags (snippets.use-cases.view/view-tags)]
@ -57,9 +60,13 @@
:body (snippets.use-cases.view/view-snippets-by-tag tag)})) :body (snippets.use-cases.view/view-snippets-by-tag tag)}))
(defn handle-view-snippet-by-slug [{params :query-params}] (defn handle-view-snippet-by-slug [{params :query-params}]
(let [slug (get params "slug")] (let [slug (get params "slug")
{:status 200 snippet (snippets.use-cases.view/view-snippet-by-slug slug)]
:body (snippets.use-cases.view/view-snippet-by-slug slug)})) (if (nil? snippet)
{:status 404
:body "No snippet with that slug found"}
{:status 200
:body snippet})))
(defn wrap [handler id] (defn wrap [handler id]
(fn [request] (fn [request]

View file

@ -122,8 +122,10 @@
db (d/db conn) db (d/db conn)
query '[:find (pull ?e [*]) query '[:find (pull ?e [*])
:in $ ?slug :in $ ?slug
:where [?e :snippet/slug ?slug]]] :where [?e :snippet/slug ?slug]]
(ffirst (d/q query db slug)))) snippet (ffirst (d/q query db slug))]
(t/log! {:level :info, :data {:slug slug :snippet snippet}} "Got snippet by slug")
snippet))
(def get-snippet-by-slug (def get-snippet-by-slug
(wrap-snippet-return get-snippet-by-slug-from-db)) (wrap-snippet-return get-snippet-by-slug-from-db))
@ -132,6 +134,7 @@
(def update-schema (def update-schema
"Malli schema for a valid update to a snippet entity." "Malli schema for a valid update to a snippet entity."
[:map [:map
[:db/id :int]
[:snippet/title {:optional true} :string] [:snippet/title {:optional true} :string]
[:snippet/slug {:optional true} :string] [:snippet/slug {:optional true} :string]
[:snippet/markdown {:optional true} :string] [:snippet/markdown {:optional true} :string]
@ -148,17 +151,23 @@
"Update specific fields of a snippet." "Update specific fields of a snippet."
[slug raw-patch] [slug raw-patch]
(let [conn (get-conn) (let [conn (get-conn)
eid (:db/id (get-snippet-by-slug slug)) db (d/db conn)
patch (to-update raw-patch)] ;; query '[:find ?e
(t/log! {:level :info, :data {:patch patch :slug slug}} "Patching snippet") ;; :in $ ?slug
;; :where [?e :snippet/slug ?slug]]
;; eid (ffirst (d/q query db slug))
eid (:db/id (get-snippet-by-slug-from-db slug))
patch (merge (to-update raw-patch) {:db/id eid})]
(t/log! {:level :info, :data {:patch patch :slug slug :eid eid}} "Patching snippet")
(when (nil? eid) (when (nil? eid)
(throw (ex-info "Snippet not found" {:slug slug}))) (throw (ex-info "Snippet not found" {:slug slug})))
(when-not (m/validate update-schema patch) (when-not (m/validate update-schema patch)
(throw (ex-info "Invalid patch" {:errors (m/explain update-schema patch) :patch patch}))) (throw (ex-info "Invalid patch" {:errors (m/explain update-schema patch) :patch patch})))
(let [tx-data [(merge {:db/id eid} patch)]] (d/transact conn {:tx-data [patch]})))
(d/transact conn {:tx-data tx-data}))))
(def update-snippet (wrap-snippet-return patch-snippet-in-db)) (defn update-snippet [& args]
(let [res (apply patch-snippet-in-db args)]
(t/log! {:level :info, :data {:res res :args args}} "Finished patching snippet")))
(defn list-snippets-in-db (defn list-snippets-in-db
"List all the snippets" "List all the snippets"
@ -178,10 +187,10 @@
[slug] [slug]
(t/log! {:level :info, :data {:slug slug}} "Retracting snippet") (t/log! {:level :info, :data {:slug slug}} "Retracting snippet")
(let [conn (get-conn) (let [conn (get-conn)
eid (:db/id (get-snippet-by-slug slug))] eid (:db/id (get-snippet-by-slug-from-db slug))]
(when (nil? eid) (if (nil? eid)
(throw (ex-info "Snippet not found" {:slug slug}))) nil
(d/transact conn {:tx-data [[:db/retractEntity eid]]}))) (d/transact conn {:tx-data [[:db/retractEntity eid]]}))))
(defn list-tags (defn list-tags
"List all tags used in snippets with their counts." "List all tags used in snippets with their counts."

View file

@ -0,0 +1,15 @@
(ns snippets.use-cases.backfill-db2
(:require
[snippets.infra.db :as old-db]
[snippets.infra.db2 :as new-db]
[taoensso.telemere :as t]))
(defn- zdt-to-date [zdt]
(java.util.Date/from (.toInstant zdt)))
(defn backfill []
(t/log! {:level :info} "Backfilling DB2")
(let [old-snippets (old-db/list-snippets {})
new-snippets (map #(assoc % :pub-date (zdt-to-date (:pub-date %))) old-snippets)]
(t/log! {:level :info :data {:count (count new-snippets)}} "Creating snippets")
(new-db/create-snippets new-snippets)))

View file

@ -1,25 +0,0 @@
(ns snippets.use-cases.backfill-from-file
(:require
[clojure.java.io :as io]
[clojure.pprint :as pprint]
[clojure.string :as str]
[frontmatter.core :as fm]))
(defn scrape-files []
(let [dir "./old_snippets"]
(->> (io/file dir)
(.listFiles)
(map #(.getName %))
(map #(hash-map :slug (first (str/split % #"\.")) :full-path (str dir "/" %)))
;; (map #(fm/parse (:full-path %))))))
(map #(let [{frontmatter :frontmatter body :body} (fm/parse (:full-path %))]
(assoc %
:title (:title frontmatter)
:pub-date (:date frontmatter)
:markdown body
:scraped true
:tags (:snippet_types frontmatter))))
(map #(dissoc % :full-path)))))
;; used repl to do backfill
;; (doseq [s old-snippets] (xt/execute-tx db/client [[:put-docs :snippets (merge {:xt/id (:slug s)} s)]]))

View file

@ -1,12 +1,9 @@
(ns snippets.use-cases.create (ns snippets.use-cases.create
(:require (:require
[taoensso.telemere :as t] [taoensso.telemere :as t]
[snippets.infra.db :as db])) [snippets.infra.db2 :as db]))
(defn- uuid [] (str (java.util.UUID/randomUUID)))
(defn create-snippet [{:keys [title slug markdown tags]}] (defn create-snippet [{:keys [title slug markdown tags]}]
(let [id (uuid) (let [pub-date (java.util.Date.)]
pub-date (java.util.Date.)] (t/log! {:level :info, :data {:title title :slug slug}} "Creating snippet")
(t/log! {:level :info, :data {:title title :slug slug :id id}} "Creating snippet") (db/create-snippets [{:title title :slug slug :markdown markdown :tags tags :pub-date pub-date}])))
(db/put-snippet id {:title title :slug slug :markdown markdown :tags tags :pub-date pub-date})))

View file

@ -1,8 +1,8 @@
(ns snippets.use-cases.delete (ns snippets.use-cases.delete
(:require (:require
[snippets.infra.db :as db] [snippets.infra.db2 :as db]
[taoensso.telemere :as t])) [taoensso.telemere :as t]))
(defn delete-snippet [key] (defn delete-snippet [slug]
(t/log! {:level :info, :data {:key key}} "Deleting snippet by id") (t/log! {:level :info, :data {:slug slug}} "Deleting snippet by slug")
(db/delete-snippet key)) (db/delete-snippet-by-slug slug))

View file

@ -2,7 +2,7 @@
(:require (:require
[taoensso.telemere :as t] [taoensso.telemere :as t]
[malli.core :as m] [malli.core :as m]
[snippets.infra.db :as db])) [snippets.infra.db2 :as db]))
(def valid-patch? (def valid-patch?
(m/validator (m/validator
@ -12,11 +12,11 @@
[:tags {:optional true} [:seqable :string]] [:tags {:optional true} [:seqable :string]]
[:slug {:optional true} :string]])) [:slug {:optional true} :string]]))
(defn edit-snippet [id patch] (defn edit-snippet [slug patch]
(t/log! {:level :info, :data {:patch patch :id id}} "Editing snippet") (t/log! {:level :info, :data {:patch patch :slug slug}} "Editing snippet")
(if (valid-patch? patch) (if (valid-patch? patch)
(do (do
(t/log! {:level :info, :data {:patch patch :id id}} "Valid changes editing snippet") (t/log! {:level :info, :data {:patch patch :slug slug}} "Valid changes editing snippet")
(db/patch-snippet id patch) (db/update-snippet slug patch)
{:success true}) {:success true})
{:success false :reason :invalid-patch})) {:success false :reason :invalid-patch}))