diff --git a/README.md b/README.md index df3ddf8..6a01a49 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,16 @@ This project is written in [Clojure](https://clojure.org/) and data is stored in ### 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 diff --git a/bruno/CodeSnippets/create_snippet.bru b/bruno/CodeSnippets/create_snippet.bru index 5972683..f1a3072 100644 --- a/bruno/CodeSnippets/create_snippet.bru +++ b/bruno/CodeSnippets/create_snippet.bru @@ -12,9 +12,9 @@ post { body:json { { - "title": "TEST from Bruno", + "title": "Mock snippet sent via Bruno", "slug": "bruno-test", - "markdown": "this is a test", - "tags": ["test"] + "markdown": "## MOCK SNIPPET\nCreated via bruno", + "tags": ["mock"] } } diff --git a/bruno/CodeSnippets/delete_snippet.bru b/bruno/CodeSnippets/delete_snippet.bru index 8e9a782..9311985 100644 --- a/bruno/CodeSnippets/delete_snippet.bru +++ b/bruno/CodeSnippets/delete_snippet.bru @@ -5,13 +5,13 @@ meta { } delete { - url: {{host}}/api/snippet?id=d77d3463-c76e-4c53-a1d5-ecaf16c6c54e + url: {{host}}/api/snippet?slug=bruno-test body: none auth: none } params:query { - id: d77d3463-c76e-4c53-a1d5-ecaf16c6c54e + slug: bruno-test } body:json { diff --git a/bruno/CodeSnippets/edit_snippet.bru b/bruno/CodeSnippets/edit_snippet.bru index dbfd39d..69fc83c 100644 --- a/bruno/CodeSnippets/edit_snippet.bru +++ b/bruno/CodeSnippets/edit_snippet.bru @@ -5,18 +5,18 @@ meta { } patch { - url: {{host}}/api/snippet?id=680a3508-7709-4f71-b5c3-3dcbffe6f5cf + url: {{host}}/api/snippet?slug=bruno-test body: json auth: none } params:query { - id: 680a3508-7709-4f71-b5c3-3dcbffe6f5cf + slug: bruno-test } body:json { { - "title": "quick way to push last jj commit to git", - "tags": ["jj", "git"] + "title": "Mock snippet sent via Bruno with updated title", + "markdown": "## MOCK SNIPPET\nUpdated via bruno" } } diff --git a/bruno/CodeSnippets/get_snippet.bru b/bruno/CodeSnippets/get_snippet.bru deleted file mode 100644 index 7dd3683..0000000 --- a/bruno/CodeSnippets/get_snippet.bru +++ /dev/null @@ -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"] - } -} diff --git a/bruno/CodeSnippets/get_snippet_by_slug.bru b/bruno/CodeSnippets/get_snippet_by_slug.bru index 3c17768..3bea0bb 100644 --- a/bruno/CodeSnippets/get_snippet_by_slug.bru +++ b/bruno/CodeSnippets/get_snippet_by_slug.bru @@ -5,13 +5,13 @@ meta { } get { - url: {{host}}/api/snippet-by-slug?slug=netcat-over-ping + url: {{host}}/api/snippet-by-slug?slug=bruno-test body: none auth: none } params:query { - slug: netcat-over-ping + slug: bruno-test } body:json { diff --git a/bruno/CodeSnippets/get_snippets.bru b/bruno/CodeSnippets/get_snippets.bru index e2de66e..4b186b6 100644 --- a/bruno/CodeSnippets/get_snippets.bru +++ b/bruno/CodeSnippets/get_snippets.bru @@ -5,13 +5,13 @@ meta { } get { - url: {{host}}/api/snippets?limit=25&skip=0 + url: {{host}}/api/snippets?limit=2&skip=0 body: none auth: none } params:query { - limit: 25 + limit: 2 skip: 0 } diff --git a/bruno/CodeSnippets/get_tag.bru b/bruno/CodeSnippets/get_tag.bru index c040ed9..c976fa3 100644 --- a/bruno/CodeSnippets/get_tag.bru +++ b/bruno/CodeSnippets/get_tag.bru @@ -5,13 +5,13 @@ meta { } get { - url: {{host}}/api/tag?tag=git + url: {{host}}/api/tag?tag=mock body: none auth: none } params:query { - tag: git + tag: mock } body:json { diff --git a/src/snippets/infra/api.clj b/src/snippets/infra/api.clj index 65b87eb..1bae601 100644 --- a/src/snippets/infra/api.clj +++ b/src/snippets/infra/api.clj @@ -23,9 +23,9 @@ {:status 200, :body "snippet created"}) (defn handle-edit-snippet [{body :body-params params :query-params}] - (let [id (get params "id")] - (t/log! {:level :info, :data {:body body :id id}} "Received request to edit snippet") - (let [{success :success :as res} (snippets.use-cases.edit/edit-snippet id body)] + (let [slug (get params "slug")] + (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 slug body)] (cond success {:status 200, :body "snippet updated"} (= (:reason res) :invalid-patch) {:status 400, :body "invalid patch"} @@ -41,10 +41,13 @@ :body (snippets.use-cases.view/view-snippets nil)})) (defn handle-delete-snippet [{params :query-params}] - (let [id (get params "id")] - (snippets.use-cases.delete/delete-snippet id) - {:status 200 - :body (format "Deleted snippet with id: %s if it existed" id)})) + (let [slug (get params "slug") + res (snippets.use-cases.delete/delete-snippet slug)] + (if (nil? res) + {: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] (let [tags (snippets.use-cases.view/view-tags)] @@ -57,9 +60,13 @@ :body (snippets.use-cases.view/view-snippets-by-tag tag)})) (defn handle-view-snippet-by-slug [{params :query-params}] - (let [slug (get params "slug")] - {:status 200 - :body (snippets.use-cases.view/view-snippet-by-slug slug)})) + (let [slug (get params "slug") + snippet (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] (fn [request] diff --git a/src/snippets/infra/db2.clj b/src/snippets/infra/db2.clj index 9df0f27..9cbf100 100644 --- a/src/snippets/infra/db2.clj +++ b/src/snippets/infra/db2.clj @@ -122,8 +122,10 @@ db (d/db conn) query '[:find (pull ?e [*]) :in $ ?slug - :where [?e :snippet/slug ?slug]]] - (ffirst (d/q query db slug)))) + :where [?e :snippet/slug ?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 (wrap-snippet-return get-snippet-by-slug-from-db)) @@ -132,6 +134,7 @@ (def update-schema "Malli schema for a valid update to a snippet entity." [:map + [:db/id :int] [:snippet/title {:optional true} :string] [:snippet/slug {:optional true} :string] [:snippet/markdown {:optional true} :string] @@ -148,17 +151,23 @@ "Update specific fields of a snippet." [slug raw-patch] (let [conn (get-conn) - eid (:db/id (get-snippet-by-slug slug)) - patch (to-update raw-patch)] - (t/log! {:level :info, :data {:patch patch :slug slug}} "Patching snippet") + db (d/db conn) + ;; query '[:find ?e + ;; :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) (throw (ex-info "Snippet not found" {:slug slug}))) (when-not (m/validate update-schema 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 tx-data})))) + (d/transact conn {:tx-data [patch]}))) -(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 "List all the snippets" @@ -178,10 +187,10 @@ [slug] (t/log! {:level :info, :data {:slug slug}} "Retracting snippet") (let [conn (get-conn) - eid (:db/id (get-snippet-by-slug slug))] - (when (nil? eid) - (throw (ex-info "Snippet not found" {:slug slug}))) - (d/transact conn {:tx-data [[:db/retractEntity eid]]}))) + eid (:db/id (get-snippet-by-slug-from-db slug))] + (if (nil? eid) + nil + (d/transact conn {:tx-data [[:db/retractEntity eid]]})))) (defn list-tags "List all tags used in snippets with their counts." diff --git a/src/snippets/use_cases/backfill_db2.clj b/src/snippets/use_cases/backfill_db2.clj new file mode 100644 index 0000000..c2bf21a --- /dev/null +++ b/src/snippets/use_cases/backfill_db2.clj @@ -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))) diff --git a/src/snippets/use_cases/backfill_from_file.clj b/src/snippets/use_cases/backfill_from_file.clj deleted file mode 100644 index 4bda21e..0000000 --- a/src/snippets/use_cases/backfill_from_file.clj +++ /dev/null @@ -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)]])) diff --git a/src/snippets/use_cases/create.clj b/src/snippets/use_cases/create.clj index 347f18d..244ead9 100644 --- a/src/snippets/use_cases/create.clj +++ b/src/snippets/use_cases/create.clj @@ -1,12 +1,9 @@ (ns snippets.use-cases.create (:require [taoensso.telemere :as t] - [snippets.infra.db :as db])) - -(defn- uuid [] (str (java.util.UUID/randomUUID))) + [snippets.infra.db2 :as db])) (defn create-snippet [{:keys [title slug markdown tags]}] - (let [id (uuid) - pub-date (java.util.Date.)] - (t/log! {:level :info, :data {:title title :slug slug :id id}} "Creating snippet") - (db/put-snippet id {:title title :slug slug :markdown markdown :tags tags :pub-date pub-date}))) + (let [pub-date (java.util.Date.)] + (t/log! {:level :info, :data {:title title :slug slug}} "Creating snippet") + (db/create-snippets [{:title title :slug slug :markdown markdown :tags tags :pub-date pub-date}]))) diff --git a/src/snippets/use_cases/delete.clj b/src/snippets/use_cases/delete.clj index cfd024a..9febf99 100644 --- a/src/snippets/use_cases/delete.clj +++ b/src/snippets/use_cases/delete.clj @@ -1,8 +1,8 @@ (ns snippets.use-cases.delete (:require - [snippets.infra.db :as db] + [snippets.infra.db2 :as db] [taoensso.telemere :as t])) -(defn delete-snippet [key] - (t/log! {:level :info, :data {:key key}} "Deleting snippet by id") - (db/delete-snippet key)) +(defn delete-snippet [slug] + (t/log! {:level :info, :data {:slug slug}} "Deleting snippet by slug") + (db/delete-snippet-by-slug slug)) diff --git a/src/snippets/use_cases/edit.clj b/src/snippets/use_cases/edit.clj index 8e9ac2b..1929822 100644 --- a/src/snippets/use_cases/edit.clj +++ b/src/snippets/use_cases/edit.clj @@ -2,7 +2,7 @@ (:require [taoensso.telemere :as t] [malli.core :as m] - [snippets.infra.db :as db])) + [snippets.infra.db2 :as db])) (def valid-patch? (m/validator @@ -12,11 +12,11 @@ [:tags {:optional true} [:seqable :string]] [:slug {:optional true} :string]])) -(defn edit-snippet [id patch] - (t/log! {:level :info, :data {:patch patch :id id}} "Editing snippet") +(defn edit-snippet [slug patch] + (t/log! {:level :info, :data {:patch patch :slug slug}} "Editing snippet") (if (valid-patch? patch) (do - (t/log! {:level :info, :data {:patch patch :id id}} "Valid changes editing snippet") - (db/patch-snippet id patch) + (t/log! {:level :info, :data {:patch patch :slug slug}} "Valid changes editing snippet") + (db/update-snippet slug patch) {:success true}) {:success false :reason :invalid-patch}))