From deb3a38a0c6167432de1031a187f8ea7f746f960 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Tue, 17 Sep 2024 21:12:03 -0700 Subject: [PATCH 01/27] TODOS: ya win some, ya lose some --- cms/systems/bread/alpha/cms/main.cljc | 2 -- dev/main.edn | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cms/systems/bread/alpha/cms/main.cljc b/cms/systems/bread/alpha/cms/main.cljc index 273e84537..e392c8421 100644 --- a/cms/systems/bread/alpha/cms/main.cljc +++ b/cms/systems/bread/alpha/cms/main.cljc @@ -141,13 +141,11 @@ (defmethod ig/init-key :bread/db [_ {:keys [recreate? force?] :as db-config}] - ;; TODO call datahike API directly (db/create! db-config {:force? force?}) (assoc db-config :db/connection (db/connect db-config))) (defmethod ig/halt-key! :bread/db [_ {:keys [recreate?] :as db-config}] - ;; TODO call datahike API directly (when recreate? (db/delete! db-config))) (defmethod ig/init-key :bread/router [_ router] diff --git a/dev/main.edn b/dev/main.edn index 74c5e1f27..56a1b9e85 100644 --- a/dev/main.edn +++ b/dev/main.edn @@ -1,13 +1,16 @@ {:http {:port 1312 :handler #ig/ref :bread/handler :wrap-defaults #ig/ref :ring/wrap-defaults} + ;; TODO default :ring/wrap-defaults {:ring-defaults :site-defaults [:session :store] #ig/ref :ring/session-store [:security :anti-forgery] false} + ;; TODO default :ring/session-store {:store/type :datalog :store/db #ig/ref :bread/db} :bread/handler #ig/ref :bread/app + ;; TODO app.edn ? :bread/app {:db #ig/ref :bread/db :auth {:lock-seconds 10} @@ -21,6 +24,7 @@ :plugins [{:effects [{:effect/name :systems.bread.alpha.cms.main/hello :effect/data-key :hello}]}]} + ;; TODO default db config? based on profile? :bread/db {:db/type :datahike :store {:backend :mem @@ -44,6 +48,7 @@ #{{:ability/key :publish-posts} {:ability/key :edit-posts} {:ability/key :delete-posts}}}}}]]} + ;; TODO routes.edn ? :bread/router #reitit/router [[["/login" From 39c8ca5a8c079fa454f766570ba8cf789b3ccb0a Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 19 Sep 2024 09:12:18 -0700 Subject: [PATCH 02/27] clean up unused requires/imports in datahike plugin --- plugins/datahike/systems/bread/alpha/plugin/datahike.cljc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/datahike/systems/bread/alpha/plugin/datahike.cljc b/plugins/datahike/systems/bread/alpha/plugin/datahike.cljc index 6bbf7a282..0991e6637 100644 --- a/plugins/datahike/systems/bread/alpha/plugin/datahike.cljc +++ b/plugins/datahike/systems/bread/alpha/plugin/datahike.cljc @@ -4,12 +4,10 @@ [clojure.core.protocols :refer [Datafiable]] [datahike.api :as d] [datahike.db :as dhdb] - [systems.bread.alpha.schema :as schema] [systems.bread.alpha.core :as bread] [systems.bread.alpha.database :as db]) (:import - [java.lang IllegalArgumentException] - [java.util UUID])) + [java.lang IllegalArgumentException])) From 00913bb69405e6d41a331ea3bfd92eb1f564fee6 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 19 Sep 2024 09:13:48 -0700 Subject: [PATCH 03/27] call interface method on db conn intead of deref --- plugins/auth/systems/bread/alpha/plugin/auth.cljc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/auth/systems/bread/alpha/plugin/auth.cljc b/plugins/auth/systems/bread/alpha/plugin/auth.cljc index 5d85a373f..c9a724987 100644 --- a/plugins/auth/systems/bread/alpha/plugin/auth.cljc +++ b/plugins/auth/systems/bread/alpha/plugin/auth.cljc @@ -25,12 +25,12 @@ sk)) (ss/read-session [_ sk] (let [sk (->uuid sk) - data (db/q @conn - '{:find [?data .] - :in [$ ?sk] - :where [[?e :session/data ?data] - [?e :session/uuid ?sk]]} - sk)] + data (db/q (db/db conn) + '{:find [?data .] + :in [$ ?sk] + :where [[?e :session/data ?data] + [?e :session/uuid ?sk]]} + sk)] (edn/read-string data))) (ss/write-session [_ sk data] (let [sk (or (->uuid sk) (UUID/randomUUID))] From 93e8b7feeed1b1307b1eb305d6851aca10de7f9b Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 19 Sep 2024 09:18:20 -0700 Subject: [PATCH 04/27] ignore .db files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ab340ea51..09c44c5a5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ inputFiles.lst /ref /dist *.local.edn +*.db /public/js /node_modules From fe05987c76f6e114929207d97d7a2f18c31ce3eb Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 19 Sep 2024 09:19:17 -0700 Subject: [PATCH 05/27] datahike-cli config --- dev/main.edn | 12 ++++++++++++ dthk.edn | 12 ++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 dthk.edn diff --git a/dev/main.edn b/dev/main.edn index 56a1b9e85..c95a1d835 100644 --- a/dev/main.edn +++ b/dev/main.edn @@ -26,6 +26,18 @@ :effect/data-key :hello}]}]} ;; TODO default db config? based on profile? :bread/db + {:db/type :datahike-cli + :store {:backend :file + :path "/home/tamayo/projects/bread-cms/dthk.db", + :config {:in-place? true}} + :recreate? true + :force? true + :attribute-refs? true + :keep-history? true, + :schema-flexibility :write, + :cli/dthk-path "/home/tamayo/bin/dthk", + :cli/config-path "/home/tamayo/projects/bread-cms/dthk.edn"} + #_ {:db/type :datahike :store {:backend :mem :id "bread-db"} diff --git a/dthk.edn b/dthk.edn new file mode 100644 index 000000000..ce4c0426d --- /dev/null +++ b/dthk.edn @@ -0,0 +1,12 @@ +{:keep-history? true, + :store + {:backend :file, + :path "/home/tamayo/projects/bread-cms/dthk.db", + :config {:in-place? true}}, + :db/type :datahike-cli, + :cli/dthk-path "/home/tamayo/bin/dthk", + :cli/config-path "/home/tamayo/projects/bread-cms/dthk.edn", + :recreate? true, + :attribute-refs? true, + :force? true, + :schema-flexibility :write} From f905c1bf824ed3e374a6a052c14d2bbd8b73684c Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 19 Sep 2024 09:25:51 -0700 Subject: [PATCH 06/27] :action/description for ::migrate and ::transact-initial --- src/systems/bread/alpha/database.cljc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/systems/bread/alpha/database.cljc b/src/systems/bread/alpha/database.cljc index 611a98f33..ee02323cc 100644 --- a/src/systems/bread/alpha/database.cljc +++ b/src/systems/bread/alpha/database.cljc @@ -192,8 +192,12 @@ :db/as-of-tx? as-of-tx?} :hooks {::bread/init - [{:action/name ::migrate :migrations migrations} - {:action/name ::transact-initial :txs initial-txns}] + [{:action/name ::migrate + :action/description "Run database schema migrations." + :migrations migrations} + {:action/name ::transact-initial + :action/description "Transact initial data into the database." + :txs initial-txns}] ::timepoint [{:action/name ::timepoint :action/description From 4237e79f104bbf5b7f49e048ed4471abf659bfda Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 19 Sep 2024 09:26:27 -0700 Subject: [PATCH 07/27] debug stuf --- cms/systems/bread/alpha/cms/main.cljc | 6 ++++- src/systems/bread/alpha/database.cljc | 34 ++++++++++++++++++++------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/cms/systems/bread/alpha/cms/main.cljc b/cms/systems/bread/alpha/cms/main.cljc index e392c8421..168293116 100644 --- a/cms/systems/bread/alpha/cms/main.cljc +++ b/cms/systems/bread/alpha/cms/main.cljc @@ -198,7 +198,11 @@ (comment (set! *print-namespace-maps* false) - (restart! (-> "dev/main.edn" aero/read-config)) + (try (restart! (-> "dev/main.edn" aero/read-config)) + (catch clojure.lang.ExceptionInfo e + (-> e ex-cause ((juxt (comp :action/name :action ex-data) + (comp :out ex-data ex-cause) + (comp :reason ex-data)))))) (deref system) (:http @system) (:ring/wrap-defaults @system) diff --git a/src/systems/bread/alpha/database.cljc b/src/systems/bread/alpha/database.cljc index ee02323cc..28452c04a 100644 --- a/src/systems/bread/alpha/database.cljc +++ b/src/systems/bread/alpha/database.cljc @@ -95,6 +95,11 @@ ks (migration-keys db)] (contains? ks (migration-key migration)))) +(defn unmet-deps [db migration] + (let [deps (:migration/dependencies (meta migration))] + (when (seq deps) + (filter (complement (migration-keys db)) deps)))) + (defmethod bread/effect ::transact [{:keys [conn txs]} _] (transact conn {:tx-data txs})) @@ -121,29 +126,40 @@ (into (:expansion/into query) result) result))) +(comment + migration + $db + + (migration-ran? $db migration) + (migration-keys $db) + (q $db '[:find ?key :where [_ :migration/key ?key]]) + + ) + (defmethod bread/action ::migrate [app {:keys [migrations]} _] (let [conn (connection app)] (doseq [migration migrations] + (prn 'migrate (first migration)) + (def migration migration) ;; Get a new db instance each time, to see the latest migrations (let [db (database app) - unmet-deps (filter - (complement (migration-keys db)) - (:migration/dependencies (meta migration)))] - (when (seq unmet-deps) + unmet (unmet-deps db migration)] + (when (seq unmet) + (prn 'UNMET migration) (throw (ex-info "Migration has one or more unmet dependencies!" - {:unmet-deps (set unmet-deps)}))) + {:migration migration + :unmet-deps (set unmet)}))) (when-not (migration-ran? (database app) migration) - (transact conn migration))))) - app) + (def $db (database app))) + (prn 'TRANSACT (transact conn migration)))))) (defmethod bread/action ::transact-initial [app {:keys [txs]} _] (when (seq txs) (if-let [conn (connection app)] (transact conn txs) - (throw (ex-info "Failed to connect to database." {:type :no-connection})))) - app) + (throw (ex-info "Failed to connect to database." {:type :no-connection}))))) (defmethod bread/action ::timepoint [{:keys [params] :as req} _ _] From 46335aaf1059ae2b5d546149aa9c19bad6f38bda Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 19 Sep 2024 09:31:26 -0700 Subject: [PATCH 08/27] first attempt at datahike-cli plugin!! --- cms/systems/bread/alpha/cms/main.cljc | 2 +- .../bread/alpha/plugin/datahike_cli.cljc | 193 ++++++++++++++++++ 2 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc diff --git a/cms/systems/bread/alpha/cms/main.cljc b/cms/systems/bread/alpha/cms/main.cljc index 168293116..99959ca2b 100644 --- a/cms/systems/bread/alpha/cms/main.cljc +++ b/cms/systems/bread/alpha/cms/main.cljc @@ -20,7 +20,7 @@ [systems.bread.alpha.cms.config.bread] [systems.bread.alpha.cms.config.reitit] [systems.bread.alpha.plugin.auth :as auth] - [systems.bread.alpha.plugin.datahike] + [systems.bread.alpha.plugin.datahike-cli] [systems.bread.alpha.plugin.reitit]) (:import [java.time LocalDateTime] diff --git a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc new file mode 100644 index 000000000..9282aa085 --- /dev/null +++ b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc @@ -0,0 +1,193 @@ +(ns systems.bread.alpha.plugin.datahike-cli + (:require + [clojure.edn :as edn] + [clojure.pprint :refer [pprint]] + [clojure.core.protocols :refer [Datafiable]] + [clojure.java.shell :only [sh]] + [clojure.string :refer [split]] + + [systems.bread.alpha.schema :as schema] + [systems.bread.alpha.core :as bread] + [systems.bread.alpha.database :as db]) + (:import + [java.lang IllegalArgumentException])) + +(defprotocol CliParam + (-to-param [x])) + +(extend-protocol CliParam + clojure.lang.Keyword + (-to-param [k] (name k)) + + java.lang.Object + (-to-param [x] (str x))) + +(defn- dthk [{:cli/keys [dthk-path]} & cmd] + (prn (apply list 'sh dthk-path (map -to-param cmd))) + (apply sh dthk-path (map -to-param cmd))) + +(defn- q* [config & cmd] + (let [{:keys [out err exit] :as result} (apply dthk config cmd)] + (if (zero? exit) + (try + (edn/read-string out) + (catch Throwable ex + (throw (ex-info (str "Error parsing output from `dthk` command: " + (ex-message ex)) + (assoc result + :reason :error-parsing-edn + :config config + :cmd-args cmd) + ex)))) + (let [[msg] (split err #"\n")] + (throw (ex-info (str "Error running `dthk` command: " msg) + (assoc result + :reason :error-running-dthk-command + :config config + :cmd-args cmd))))))) + +(defn- prefix + ([pre config] + (str pre (:cli/config-path config))) + ([config] + (prefix "db:" config))) + +(defn- asof-prefix [instant-ms config] + (prefix (str "asof:" instant-ms) config)) + +(deftype AsOfDatahikeClient [instant-ms config] + db/TemporalDatabase + (q [db query] + (q* config :query (pr-str query) (asof-prefix instant-ms config))) + (q [db query a] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a)) + (q [db query a b] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b)) + ;; TODO more arities... + (pull [db spec ident] + (q* config :pull spec ident))) + +(defn- hist-prefix [config] + (prefix (str "history:") config)) + +(deftype HistoricalDatahikeClient [config] + db/TemporalDatabase + (q [db query] + (q* config :history (pr-str query) (hist-prefix config))) + (q [db query a] + (q* config :history (pr-str query) (hist-prefix config) a)) + (q [db query a b] + (q* config :history (pr-str query) (hist-prefix config) a b)) + ;; TODO + ) + +(deftype DatahikeCommandLineInterfaceClient [config] + db/TemporalDatabase + (as-of [_ instant-ms] + (AsOfDatahikeClient. instant-ms config)) + (history [_] + (HistoricalDatahikeClient. config)) + (q [db query] + (q* config :query (pr-str query) (prefix config))) + (q [db query a] + (q* config :query (pr-str query) (prefix config) a)) + (q [db query a b] + (q* config :query (prefix config) a b (pr-str query))) + (q [db query a b c] + (q* config :query (prefix config) a b c (pr-str query))) + (q [db query a b c d] + (q* config :query (prefix config) a b c d (pr-str query))) + (q [db query a b c d e] + (q* config :query (prefix config) a b c d e (pr-str query))) + (q [db query a b c d e f] + (q* config :query (prefix config) a b c d e f (pr-str query))) + (q [db query a b c d e f g] + (q* config :query (prefix config) a b c d e f g (pr-str query))) + (q [db query a b c d e f g h] + (q* config :query (prefix config) a b c d e f g h (pr-str query))) + (q [db query a b c d e f g h i] + (q* config :query (prefix config) a b c d e f g h i (pr-str query))) + (q [db query a b c d e f g h i j] + (q* config :query (prefix config) a b c d e f g h i j (pr-str query))) + (q [db query a b c d e f g h i j k] + (q* config :query (prefix config) a b c d e f g h i j k (pr-str query))) + (q [db query a b c d e f g h i j k l] + (q* config :query (prefix config) a b c d e f g h i j k l (pr-str query))) + (q [db query a b c d e f g h i j k l m] + (q* config :query (prefix config) a b c d e f g h i j k l m (pr-str query))) + (q [db query a b c d e f g h i j k l m n] + (q* config :query (prefix config) a b c d e f g h i j k l m n (pr-str query))) + (q [db query a b c d e f g h i j k l m n o] + (q* config :query (prefix config) a b c d e f g h i j k l m n o (pr-str query))) + (q [db query a b c d e f g h i j k l m n o p] + (q* config :query (prefix config) a b c d e f g h i j k l m n o p (pr-str query))) + (q [db query a b c d e f g h i j k l m n o p r] + (q* config :query (prefix config) a b c d e f g h i j k l m n o p r (pr-str query))) + (pull [db spec ident] + (q* config :pull (prefix config) (pr-str spec) ident)) + + db/TransactionalDatabaseConnection + (db [conn] + conn) + (transact [_ txs] + (q* config :transact (prefix "conn:" config) (pr-str txs)))) + +(comment + + (require '[aero.core :as aero]) + + (def config + (-> "dev/main.edn" aero/read-config :bread/db)) + + (def config + {:store {:backend :file + :path "/home/tamayo/projects/bread-cms/example.db" + :config {:in-place? true}} + :attribute-refs? true + :keep-history? true + :schema-flexibility :write + :db/type :datahike-cli + :cli/dthk-path "/home/tamayo/bin/dthk" + :cli/config-path "/home/tamayo/projects/bread-cms/example.edn"}) + + (apply sh "/home/tamayo/bin/dthk" (map name [:create-database :dthk.edn])) + + (db/create! config) + (db/delete! config) + + (edn/read-string "{:find [(pull ?e [*])] :where [[?e :person/name]]}") + (edn/read-string "[:find [(pull ?e [*])] :where [?e :person/name]]") + + (def $conn (db/connect config)) + (def $db (db/db $conn)) + + (dthk config :query + (pr-str '{:find [(pull ?e [:db/ident :db/doc])] + :in [$] + :where [[?e :db/ident :attr/migration]]}) + "db:dthk.edn") + (db/q $db '{:find [(pull ?e [:db/ident :db/doc])] + :in [$] + :where [[?e :db/ident :attr/migration]]}) + + ) + +(defmethod db/connect :datahike-cli [config] + (DatahikeCommandLineInterfaceClient. config)) + +(defmethod db/delete! :datahike-cli [{:cli/keys [config-path] :as config}] + (-> (dthk config :delete-database config-path) + :out edn/read-string)) + +(defmethod db/create! :datahike-cli [config & [{:keys [force?]}]] + (let [{:cli/keys [dthk-path config-path]} config] + (println "writing config") + (spit config-path (with-out-str (pprint config))) + (let [{:keys [out err]} (dthk config :create-database config-path)] + ;; TODO parse Java stacktrace + (if (and (re-find #"Database already exists." err) force?) + (do + (dthk config :delete-database config-path) + (-> (dthk config :create-database config-path) + :out edn/read-string)) + (edn/read-string out))))) From f331475209cf8c1a0669e8a042f76ce2e6e93c85 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 19 Sep 2024 09:35:50 -0700 Subject: [PATCH 09/27] fix procotol query impl --- .../bread/alpha/plugin/datahike_cli.cljc | 33 ++++--------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc index 9282aa085..36c61656b 100644 --- a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc +++ b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc @@ -92,37 +92,16 @@ (q [db query a] (q* config :query (pr-str query) (prefix config) a)) (q [db query a b] - (q* config :query (prefix config) a b (pr-str query))) + (q* config :query (pr-str query) (prefix config) a b)) (q [db query a b c] - (q* config :query (prefix config) a b c (pr-str query))) + (q* config :query (pr-str query) (prefix config) a b c)) (q [db query a b c d] - (q* config :query (prefix config) a b c d (pr-str query))) + (q* config :query (pr-str query) (prefix config) a b c d)) (q [db query a b c d e] - (q* config :query (prefix config) a b c d e (pr-str query))) + (q* config :query (pr-str query) (prefix config) a b c d e)) (q [db query a b c d e f] - (q* config :query (prefix config) a b c d e f (pr-str query))) - (q [db query a b c d e f g] - (q* config :query (prefix config) a b c d e f g (pr-str query))) - (q [db query a b c d e f g h] - (q* config :query (prefix config) a b c d e f g h (pr-str query))) - (q [db query a b c d e f g h i] - (q* config :query (prefix config) a b c d e f g h i (pr-str query))) - (q [db query a b c d e f g h i j] - (q* config :query (prefix config) a b c d e f g h i j (pr-str query))) - (q [db query a b c d e f g h i j k] - (q* config :query (prefix config) a b c d e f g h i j k (pr-str query))) - (q [db query a b c d e f g h i j k l] - (q* config :query (prefix config) a b c d e f g h i j k l (pr-str query))) - (q [db query a b c d e f g h i j k l m] - (q* config :query (prefix config) a b c d e f g h i j k l m (pr-str query))) - (q [db query a b c d e f g h i j k l m n] - (q* config :query (prefix config) a b c d e f g h i j k l m n (pr-str query))) - (q [db query a b c d e f g h i j k l m n o] - (q* config :query (prefix config) a b c d e f g h i j k l m n o (pr-str query))) - (q [db query a b c d e f g h i j k l m n o p] - (q* config :query (prefix config) a b c d e f g h i j k l m n o p (pr-str query))) - (q [db query a b c d e f g h i j k l m n o p r] - (q* config :query (prefix config) a b c d e f g h i j k l m n o p r (pr-str query))) + (q* config :query (pr-str query) (prefix config) a b c d e f)) + ;; TODO (pull [db spec ident] (q* config :pull (prefix config) (pr-str spec) ident)) From 295501f35fa92d10802413118ec96f69ba6edac9 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 25 Sep 2024 16:09:12 -0700 Subject: [PATCH 10/27] clean up ::db/migrate action --- src/systems/bread/alpha/database.cljc | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/systems/bread/alpha/database.cljc b/src/systems/bread/alpha/database.cljc index 28452c04a..9a346d79e 100644 --- a/src/systems/bread/alpha/database.cljc +++ b/src/systems/bread/alpha/database.cljc @@ -126,33 +126,19 @@ (into (:expansion/into query) result) result))) -(comment - migration - $db - - (migration-ran? $db migration) - (migration-keys $db) - (q $db '[:find ?key :where [_ :migration/key ?key]]) - - ) - (defmethod bread/action ::migrate [app {:keys [migrations]} _] (let [conn (connection app)] (doseq [migration migrations] - (prn 'migrate (first migration)) - (def migration migration) ;; Get a new db instance each time, to see the latest migrations (let [db (database app) unmet (unmet-deps db migration)] (when (seq unmet) - (prn 'UNMET migration) (throw (ex-info "Migration has one or more unmet dependencies!" {:migration migration :unmet-deps (set unmet)}))) (when-not (migration-ran? (database app) migration) - (def $db (database app))) - (prn 'TRANSACT (transact conn migration)))))) + (transact conn migration)))))) (defmethod bread/action ::transact-initial [app {:keys [txs]} _] From 34c049b4956b73a3aadc1dcba8a8df924c71a44b Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 25 Sep 2024 17:00:04 -0700 Subject: [PATCH 11/27] fix ::db/* hook return values --- src/systems/bread/alpha/database.cljc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/systems/bread/alpha/database.cljc b/src/systems/bread/alpha/database.cljc index 9a346d79e..5b0fa52a0 100644 --- a/src/systems/bread/alpha/database.cljc +++ b/src/systems/bread/alpha/database.cljc @@ -138,14 +138,16 @@ {:migration migration :unmet-deps (set unmet)}))) (when-not (migration-ran? (database app) migration) - (transact conn migration)))))) + (transact conn migration))))) + app) (defmethod bread/action ::transact-initial [app {:keys [txs]} _] (when (seq txs) (if-let [conn (connection app)] (transact conn txs) - (throw (ex-info "Failed to connect to database." {:type :no-connection}))))) + (throw (ex-info "Failed to connect to database." {:type :no-connection})))) + app) (defmethod bread/action ::timepoint [{:keys [params] :as req} _ _] From 7ca3c724e123399e64e04e746a7d1bb3f86f2dea Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 25 Sep 2024 17:00:53 -0700 Subject: [PATCH 12/27] ignore dthk.edn --- .gitignore | 1 + dthk.edn | 12 ------------ 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 dthk.edn diff --git a/.gitignore b/.gitignore index 09c44c5a5..095e320e5 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ inputFiles.lst /dist *.local.edn *.db +/dthk.edn /public/js /node_modules diff --git a/dthk.edn b/dthk.edn deleted file mode 100644 index ce4c0426d..000000000 --- a/dthk.edn +++ /dev/null @@ -1,12 +0,0 @@ -{:keep-history? true, - :store - {:backend :file, - :path "/home/tamayo/projects/bread-cms/dthk.db", - :config {:in-place? true}}, - :db/type :datahike-cli, - :cli/dthk-path "/home/tamayo/bin/dthk", - :cli/config-path "/home/tamayo/projects/bread-cms/dthk.edn", - :recreate? true, - :attribute-refs? true, - :force? true, - :schema-flexibility :write} From 68a0b0275ba61bd52b648fc4ea6297250e45fedd Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 25 Sep 2024 17:01:30 -0700 Subject: [PATCH 13/27] more debug output --- cms/systems/bread/alpha/cms/main.cljc | 1 + 1 file changed, 1 insertion(+) diff --git a/cms/systems/bread/alpha/cms/main.cljc b/cms/systems/bread/alpha/cms/main.cljc index 99959ca2b..d73216f2d 100644 --- a/cms/systems/bread/alpha/cms/main.cljc +++ b/cms/systems/bread/alpha/cms/main.cljc @@ -201,6 +201,7 @@ (try (restart! (-> "dev/main.edn" aero/read-config)) (catch clojure.lang.ExceptionInfo e (-> e ex-cause ((juxt (comp :action/name :action ex-data) + (comp ex-message ex-cause) (comp :out ex-data ex-cause) (comp :reason ex-data)))))) (deref system) From ebe69c9d3d35ea973878a92473d74d38a9699c17 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 25 Sep 2024 22:22:53 -0700 Subject: [PATCH 14/27] split out default config --- cms/systems/bread/alpha/cms/main.cljc | 16 +++- dev/cgi.edn | 102 +++++++++++++++++--------- dev/main.edn | 43 +---------- resources/default.main.edn | 22 ++++++ 4 files changed, 108 insertions(+), 75 deletions(-) create mode 100644 resources/default.main.edn diff --git a/cms/systems/bread/alpha/cms/main.cljc b/cms/systems/bread/alpha/cms/main.cljc index d73216f2d..5cae9c3c9 100644 --- a/cms/systems/bread/alpha/cms/main.cljc +++ b/cms/systems/bread/alpha/cms/main.cljc @@ -20,7 +20,7 @@ [systems.bread.alpha.cms.config.bread] [systems.bread.alpha.cms.config.reitit] [systems.bread.alpha.plugin.auth :as auth] - [systems.bread.alpha.plugin.datahike-cli] + [systems.bread.alpha.plugin.datahike] [systems.bread.alpha.plugin.reitit]) (:import [java.time LocalDateTime] @@ -191,6 +191,11 @@ (doseq [{:keys [tap]} profilers] (remove-tap tap))) +(defn get-merged-config [path] + (merge + (aero/read-config (io/resource "default.main.edn")) + (aero/read-config path))) + (defn restart! [config] (stop!) (start! config)) @@ -198,7 +203,12 @@ (comment (set! *print-namespace-maps* false) - (try (restart! (-> "dev/main.edn" aero/read-config)) + (merge + (-> "default.main.edn" io/resource aero/read-config) + (-> "dev/main.edn" aero/read-config)) + (get-merged-config "dev/main.edn") + + (try (restart! (get-merged-config "dev/main.edn")) (catch clojure.lang.ExceptionInfo e (-> e ex-cause ((juxt (comp :action/name :action ex-data) (comp ex-message ex-cause) @@ -507,7 +517,7 @@ config (start! config) file (if-not (.exists (io/file file)) (show-errors {:errors [(str "No such file: " file)]}) - (let [config (-> file aero/read-config + (let [config (-> file get-merged-config (update-in [:http :port] #(if port port %)))] (start! config))) :else (show-help cli-env)))) diff --git a/dev/cgi.edn b/dev/cgi.edn index 2732c9d7b..83605e341 100644 --- a/dev/cgi.edn +++ b/dev/cgi.edn @@ -1,35 +1,71 @@ -{:bread/handler #ig/ref :bread/app - :bread/app {:db false - :i18n false - :routes - {:router #ig/ref :bread/router} - :plugins []} +{:bread/app + {:db #ig/ref :bread/db + :auth {:lock-seconds 10} + :i18n {:supported-langs #{:en :fr}} + :routes {:router #ig/ref :bread/router} + :components {:not-found #var systems.bread.alpha.cms.theme/NotFoundPage} + :navigation {:menus {:main-nav + {:menu/type :systems.bread.alpha.navigation/location + :menu/location :primary + :route/name :page}}} + :plugins [{:effects + [{:effect/name :systems.bread.alpha.cms.main/hello + :effect/data-key :hello}]}]} + + :bread/db + {:db/type :datahike-cli + :store {:backend :file + :path "/home/tamayo/projects/bread-cms/dthk.db", + :config {:in-place? true}} + :recreate? true + :force? true + :attribute-refs? true + :keep-history? true, + :schema-flexibility :write, + :cli/dthk-path "/home/tamayo/bin/dthk", + :cli/config-path "/home/tamayo/projects/bread-cms/dthk.edn" + :db/initial-txns + #concat [#deref #var systems.bread.alpha.plugin.defaults/initial-data + [{:user/username "coby" + :user/name "Coby Tamayo" + :user/email "coby@bread.systems" + :user/password #buddy/derive ["hello" :bcrypt+blake2b-512] + #_#_ ;; Uncomment to enable 2FA + :user/two-factor-key "AWWMEFM4ADBSQRET" + :user/failed-login-count 0 + :user/lang :en-US + :user/roles + #{{:role/key :author + :role/abilities + #{{:ability/key :publish-posts} + {:ability/key :edit-posts} + {:ability/key :delete-posts}}}}}]]} + + ;; TODO routes.edn ? :bread/router - #router - [["/:lang" - ["" - {:name :home - :bread/dispatcher - {:dispatcher/type :systems.bread.alpha.cms.scratch/static - :dispatcher/component - #var systems.bread.alpha.cms.scratch/home-page}}] - ["/articles" - {:name :articles - :bread/dispatcher - {:dispatcher/type :systems.bread.alpha.cms.blog/article - ;; TODO article component - :dispatcher/component - #var systems.bread.alpha.cms.scratch/interior-page}}] - ["/article/:slug" - {:name :article - :bread/dispatcher - {:dispatcher/type :systems.bread.alpha.cms.blog/article - :dispatcher/component - #var systems.bread.alpha.cms.scratch/interior-page}}] - ["/:slug" - {:name :page - :bread/dispatcher - {:dispatcher/type :systems.bread.alpha.cms.scratch/static - :dispatcher/component - #var systems.bread.alpha.cms.scratch/home-page}}]] + #reitit/router + [[["/login" + {:name :login + :dispatcher/type :systems.bread.alpha.plugin.auth/login + :dispatcher/component #var systems.bread.alpha.plugin.auth/login-page}] + ["/assets/*" + #invoke [reitit.ring/create-resource-handler + {:param :filename}]] + ["/{field/lang}" + ["" + {:name :home + :dispatcher/type :dispatcher.type/page + :dispatcher/component #var systems.bread.alpha.cms.theme/HomePage}] + ["/tag/{thing/slug}" + {:name :tag + :dispatcher/type :dispatcher.type/tag + :dispatcher/component #var systems.bread.alpha.cms.theme/Tag}] + ["/{thing/slug*}" + {:name :page + :dispatcher/type :dispatcher.type/page + :dispatcher/component #var systems.bread.alpha.cms.theme/InteriorPage}] + ["/page/{thing/slug*}" + {:name :page! + :dispatcher/type :dispatcher.type/page + :dispatcher/component #var systems.bread.alpha.cms.theme/InteriorPage}]]] {:conflicts nil}]} diff --git a/dev/main.edn b/dev/main.edn index c95a1d835..b93b1abd5 100644 --- a/dev/main.edn +++ b/dev/main.edn @@ -1,17 +1,4 @@ -{:http {:port 1312 - :handler #ig/ref :bread/handler - :wrap-defaults #ig/ref :ring/wrap-defaults} - ;; TODO default - :ring/wrap-defaults {:ring-defaults :site-defaults - [:session :store] #ig/ref :ring/session-store - [:security :anti-forgery] false} - ;; TODO default - :ring/session-store - {:store/type :datalog - :store/db #ig/ref :bread/db} - :bread/handler #ig/ref :bread/app - ;; TODO app.edn ? - :bread/app +{:bread/app {:db #ig/ref :bread/db :auth {:lock-seconds 10} :i18n {:supported-langs #{:en :fr}} @@ -24,20 +11,8 @@ :plugins [{:effects [{:effect/name :systems.bread.alpha.cms.main/hello :effect/data-key :hello}]}]} - ;; TODO default db config? based on profile? + :bread/db - {:db/type :datahike-cli - :store {:backend :file - :path "/home/tamayo/projects/bread-cms/dthk.db", - :config {:in-place? true}} - :recreate? true - :force? true - :attribute-refs? true - :keep-history? true, - :schema-flexibility :write, - :cli/dthk-path "/home/tamayo/bin/dthk", - :cli/config-path "/home/tamayo/projects/bread-cms/dthk.edn"} - #_ {:db/type :datahike :store {:backend :mem :id "bread-db"} @@ -60,7 +35,7 @@ #{{:ability/key :publish-posts} {:ability/key :edit-posts} {:ability/key :delete-posts}}}}}]]} - ;; TODO routes.edn ? + :bread/router #reitit/router [[["/login" @@ -87,14 +62,4 @@ {:name :page! :dispatcher/type :dispatcher.type/page :dispatcher/component #var systems.bread.alpha.cms.theme/InteriorPage}]]] - {:conflicts nil}] - :bread/profilers - [#_{:hook #{:systems.bread.alpha.core/response} - :action/name #{:systems.bread.alpha.cms.defaults/response} - :f #var systems.bread.alpha.cms.main/log-hook!}] - #_#_ - :bread/debugger - {:http - {:port 1313 - :docroot "public/debugger" - :middleware [#var systems.bread.alpha.cms.main/wrap-debug]}}} + {:conflicts nil}]} diff --git a/resources/default.main.edn b/resources/default.main.edn new file mode 100644 index 000000000..3f6b36857 --- /dev/null +++ b/resources/default.main.edn @@ -0,0 +1,22 @@ +{:http {:port 1312 + :handler #ig/ref :bread/handler + :wrap-defaults #ig/ref :ring/wrap-defaults} + + :ring/wrap-defaults {:ring-defaults :site-defaults + [:session :store] #ig/ref :ring/session-store + [:security :anti-forgery] false} + + :ring/session-store + {:store/type :datalog + :store/db #ig/ref :bread/db} + + :bread/handler #ig/ref :bread/app + + :bread/profilers [] + + #_#_ ;; TODO + :bread/debugger + {:http + {:port 1313 + :docroot "public/debugger" + :middleware [#var systems.bread.alpha.cms.main/wrap-debug]}}} From d6d722ebda7116f239a7bb334dc790cde7ba0cf4 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 25 Sep 2024 22:53:50 -0700 Subject: [PATCH 15/27] rm ::hello dummy hook --- cms/systems/bread/alpha/cms/main.cljc | 4 ---- dev/cgi.edn | 5 +---- dev/main.edn | 5 +---- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/cms/systems/bread/alpha/cms/main.cljc b/cms/systems/bread/alpha/cms/main.cljc index 5cae9c3c9..9ea63371c 100644 --- a/cms/systems/bread/alpha/cms/main.cljc +++ b/cms/systems/bread/alpha/cms/main.cljc @@ -183,10 +183,6 @@ (assoc profiler :tap tap))) profilers)) -(defmethod bread/effect ::hello [effect data] - (throw (ex-info "oh no!" {})) - (future "HELLO!")) - (defmethod ig/halt-key! :bread/profilers [_ profilers] (doseq [{:keys [tap]} profilers] (remove-tap tap))) diff --git a/dev/cgi.edn b/dev/cgi.edn index 83605e341..4e9deed5b 100644 --- a/dev/cgi.edn +++ b/dev/cgi.edn @@ -7,10 +7,7 @@ :navigation {:menus {:main-nav {:menu/type :systems.bread.alpha.navigation/location :menu/location :primary - :route/name :page}}} - :plugins [{:effects - [{:effect/name :systems.bread.alpha.cms.main/hello - :effect/data-key :hello}]}]} + :route/name :page}}}} :bread/db {:db/type :datahike-cli diff --git a/dev/main.edn b/dev/main.edn index b93b1abd5..2c664d69b 100644 --- a/dev/main.edn +++ b/dev/main.edn @@ -7,10 +7,7 @@ :navigation {:menus {:main-nav {:menu/type :systems.bread.alpha.navigation/location :menu/location :primary - :route/name :page}}} - :plugins [{:effects - [{:effect/name :systems.bread.alpha.cms.main/hello - :effect/data-key :hello}]}]} + :route/name :page}}}} :bread/db {:db/type :datahike From 9306bab258e51133e5b280f3e13cbe5917081ef5 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 26 Sep 2024 22:44:08 -0700 Subject: [PATCH 16/27] fix copy/pasta in CLI usage text --- cms/systems/bread/alpha/cms/cgi.cljc | 519 ++++++++++++++++++++++++++ cms/systems/bread/alpha/cms/main.cljc | 2 +- 2 files changed, 520 insertions(+), 1 deletion(-) create mode 100644 cms/systems/bread/alpha/cms/cgi.cljc diff --git a/cms/systems/bread/alpha/cms/cgi.cljc b/cms/systems/bread/alpha/cms/cgi.cljc new file mode 100644 index 000000000..9a522e4f5 --- /dev/null +++ b/cms/systems/bread/alpha/cms/cgi.cljc @@ -0,0 +1,519 @@ +(ns systems.bread.alpha.cms.main + (:require + [clojure.edn :as edn] + [clojure.java.io :as io] + [clojure.string :as string] + [clojure.tools.cli :as cli] + [aero.core :as aero] + [integrant.core :as ig] + [org.httpkit.server :as http] + [reitit.core :as reitit] + [reitit.ring] + [ring.middleware.defaults :as ring] + [sci.core :as sci] + ;; TODO ring middlewares + + [systems.bread.alpha.core :as bread] + [systems.bread.alpha.cms.theme] + [systems.bread.alpha.database :as db] + [systems.bread.alpha.plugin.defaults :as defaults] + [systems.bread.alpha.cms.config.bread] + [systems.bread.alpha.cms.config.reitit] + [systems.bread.alpha.plugin.auth :as auth] + [systems.bread.alpha.plugin.datahike-cli] + [systems.bread.alpha.plugin.reitit]) + (:import + [java.time LocalDateTime] + [java.util Properties]) + (:gen-class)) + +(def status-mappings + {200 "OK" + 400 "Bad Request" + 404 "Not Found" + 500 "Internal Server Error"}) + +(def cli-options + [["-h" "--help" "Show this usage text."] + ["-p" "--port PORT" "Port number to run the HTTP server on." + :parse-fn #(Integer/parseInt %) + :validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536."]] + ["-f" "--file FILE" "Config file path. Ignored if --config is passed." + :default "config.edn"] + ["-c" "--config EDN" + "Full configuration data as EDN. Causes other args to be ignored." + :parse-fn edn/read-string] + ["-g" "--cgi" + "Run Bread as a CGI script" + :default false]]) + +(defn show-help [{:keys [summary]}] + (println summary)) + +(defn show-errors [{:keys [errors]}] + (println (string/join "\n" errors))) + +(defn run-as-cgi [{:keys [options]}] + (try + ;; TODO this is pretty jank, update to parse HTTP requests properly + (let [[uri & _] (some-> (System/getenv "REQUEST_URI") + (clojure.string/split #"\?")) + config (aero/read-config (:file options)) + system (ig/init config) + handler (:bread/handler system) + req {:uri uri + :query-string (System/getenv "QUERY_STRING") + :remote-addr (System/getenv "REMOTE_ADDR") + :server-name (System/getenv "SERVER_NAME") + :server-port (System/getenv "SERVER_PORT") + :content-type (System/getenv "CONTENT_TYPE") + :content-length (Integer. + (or (System/getenv "CONTENT_LENGTH") "0"))} + {:keys [status headers body] :as res} (handler req)] + (println (str "status: " status " " (status-mappings status))) + (doseq [[header header-value] headers] + (println (str header ": " header-value))) + (println) + (println body) + (System/exit 0)) + (catch Throwable e + (println "status: 500 Internal Server Error") + (println "content-type: text/plain") + (println) + (println (.getMessage e)) + (println (.getStackTrace e)) + (System/exit 1)))) + +(defonce system (atom nil)) + +(defn start! [config] + (let [config (assoc config + :initial-config config + ;; These will be initialized by Integrant: + ;; TODO bread version + :clojure-version nil + :started-at nil)] + (reset! system (ig/init config)))) + +(defn stop! [] + (when-let [sys @system] + (ig/halt! sys) + (reset! system nil))) + +(defmethod ig/init-key :initial-config [_ config] + config) + +(defmethod ig/init-key :clojure-version [_ _] + (clojure-version)) + +(defmethod ig/init-key :started-at [_ _] + (LocalDateTime/now)) + +(defmethod ig/init-key :http [_ {:keys [port handler wrap-defaults]}] + (println "Starting HTTP server on port" port) + (let [handler (if wrap-defaults + (ring/wrap-defaults handler wrap-defaults) + handler)] + (http/run-server handler {:port port}))) + +(defmethod ig/halt-key! :http [_ stop-server] + (when-let [prom (stop-server :timeout 100)] + @prom)) + +(defmethod ig/init-key :ring/wrap-defaults [_ value] + (let [default-configs {:api-defaults ring/api-defaults + :site-defaults ring/site-defaults + :secure-api-defaults ring/secure-api-defaults + :secure-site-defaults ring/secure-api-defaults} + k (if (keyword? value) value (get value :ring-defaults)) + defaults (get default-configs k) + defaults (if (map? value) + (reduce #(assoc-in %1 (key %2) (val %2)) + defaults (dissoc value :ring-defaults)) + defaults)] + defaults)) + +(defmethod ig/init-key :ring/session-store + [_ {store-type :store/type {conn :db/connection} :store/db}] + ;; TODO extend with a multimethod?? + (when (= :datalog store-type) + (auth/session-store conn))) + +(defmethod ig/init-key :bread/db + [_ {:keys [recreate? force?] :as db-config}] + (db/create! db-config {:force? force?}) + (assoc db-config :db/connection (db/connect db-config))) + +(defmethod ig/halt-key! :bread/db + [_ {:keys [recreate?] :as db-config}] + (when recreate? (db/delete! db-config))) + +(defmethod ig/init-key :bread/router [_ router] + router) + +(defmethod ig/init-key :bread/app [_ app-config] + (bread/load-app (defaults/app app-config))) + +(defmethod ig/halt-key! :bread/app [_ app] + (bread/shutdown app)) + +(defmethod ig/init-key :bread/handler [_ app] + (bread/handler app)) + +(defn log-hook! [invocation] + (let [{:keys [hook action result]} invocation] + (prn (:action/name action) (select-keys result + [:params + :headers + :status + :session])))) + +(defmethod ig/init-key :bread/profilers [_ profilers] + ;; Enable hook profiling. + (alter-var-root #'bread/*profile-hooks* (constantly true)) + (map + (fn [{h :hook act :action/name f :f :as profiler}] + (let [tap (bread/add-profiler + (fn [{{:keys [action hook] :as invocation} ::bread/profile}] + (if (and (or (nil? (seq h)) ((set h) + hook)) + (or (nil? (seq act)) ((set act) + (:action/name action)))) + (f invocation))))] + (assoc profiler :tap tap))) + profilers)) + +(defmethod ig/halt-key! :bread/profilers [_ profilers] + (doseq [{:keys [tap]} profilers] + (remove-tap tap))) + +(defn get-merged-config [path] + (merge + (aero/read-config (io/resource "default.main.edn")) + (aero/read-config path))) + +(defn restart! [config] + (stop!) + (start! config)) + +(comment + (set! *print-namespace-maps* false) + + (merge + (-> "default.main.edn" io/resource aero/read-config) + (-> "dev/main.edn" aero/read-config)) + (get-merged-config "dev/main.edn") + + (try (restart! (get-merged-config "dev/main.edn")) + (catch clojure.lang.ExceptionInfo e + (-> e ex-cause ((juxt (comp :action/name :action ex-data) + (comp ex-message ex-cause) + (comp :out ex-data ex-cause) + (comp :reason ex-data)))))) + (deref system) + (:http @system) + (:ring/wrap-defaults @system) + (:ring/session-store @system) + (:bread/app @system) + (:bread/router @system) + (:bread/db @system) + (:bread/profilers @system) + + (alter-var-root #'bread/*profile-hooks* not) + + (def $req {:uri "/en" :request-method :get}) + (def $req {:uri "/en/hello" :request-method :get}) + (def $req {:uri "/en/hello/child-page" :request-method :get}) + (def $req {:uri "/en/tag/one" :request-method :get}) + (def $req {:uri "/fr/tag/one" :request-method :get}) + (def $req {:uri "/en/404" :request-method :get}) + + (do + (require '[flow-storm.api :as flow] + '[systems.bread.alpha.tools.util :as util :refer [do-expansions]]) + (def ->app (partial util/->app (:bread/app @system))) + (def diagnose-expansions (partial util/diagnose-expansions (:bread/app @system))) + + (defn db [] + (db/database (->app $req))) + + (defn q [& args] + (apply + db/q + (db/database (->app $req)) + args))) + + (flow/local-connect) + + (diagnose-expansions (->app $req)) + (do-expansions (->app $req) 1) + (do-expansions (->app $req) 2) + (do-expansions (->app $req) 3) + + (as-> (->app $req) $ + (bread/hook $ ::bread/route) + (::bread/dispatcher $)) + (as-> (->app $req) $ + (bread/hook $ ::bread/route) + (bread/hook $ ::bread/dispatch) + (::bread/expansions $)) + (as-> (->app $req) $ + (bread/hook $ ::bread/route) + (bread/hook $ ::bread/dispatch) + (bread/hook $ ::bread/expand) + (::bread/data $)) + (as-> (->app $req) $ + (bread/hook $ ::bread/route) + (bread/hook $ ::bread/dispatch) + (bread/hook $ ::bread/expand) + (bread/hook $ ::bread/render) + (select-keys $ [:status :body :headers])) + + (bread/config (->app $req) :i18n/supported-langs) + + ;; TODO Nice debug mechanism: + (catch-as-> (->app $req) + [::bread/route ::bread/dispatcher] + [::bread/dispatch ::bread/expansions] + [::bread/expand ::bread/data (diagnose-expansions $)] + [::bread/render (select-keys $ [:status :body :headers])]) + + ;; querying for inverse relationships (post <-> taxon): + (q '{:find [(pull ?t [:db/id {:post/_taxons [*]}])] + :in [$ ?slug] + :where [[?t :taxon/taxonomy :taxon.taxonomy/tag] + [?t :thing/slug ?slug]]} + "one") + (q '{:find [(pull ?p [:db/id {:post/taxons [*]}])] + :in [$ ?slug] + :where [[?p :post/type :post.type/page] + [?p :thing/slug ?slug]]} + "hello") + + ;; Menu expansions + + (q '{:find [(pull ?e [:db/id + :taxon/taxonomy + :thing/slug + {:thing/_children [:thing/slug + {:thing/_children ...}]} + {:thing/children ...} + {:translatable/fields [*]}])] + :in [$ ?taxonomy] + :where [[?e :taxon/taxonomy ?taxonomy]]} + :taxon.taxonomy/tag) + + (q '{:find [(pull ?e [;; Post menus don't store their own data in the db: + ;; instead, they follow the post hierarchy itself. + :db/id + :post/type + :post/status + {:translatable/fields [*]} + {:thing/_children [:thing/slug {:thing/_children ...}]} + {:thing/children ...}])] + :in [$ ?type [?status ...]] + :where [[?e :post/type ?type] + [?e :post/status ?status] + (not-join [?e] [?_ :thing/children ?e])]} + :post.type/page + #{:post.status/published}) + + (slurp (io/resource "public/assets/hi.txt")) + (bread/match (:bread/router @system) {:uri "/assets/hi.txt" + :request-method :get}) + (bread/match (:bread/router @system) {:uri "/en" + :request-method :get}) + (bread/match (:bread/router @system) {:uri "/login" + :request-method :get}) + (bread/match (:bread/router @system) {:uri "/login" + :request-method :post}) + + (response ((:bread/handler @system) {:uri "/en"})) + (response ((:bread/handler @system) {:uri "/en/hello"})) + (response ((:bread/handler @system) {:uri "/en/hello/child-page"})) + ;; This should 404: + (response ((:bread/handler @system) {:uri "/en/child-page"})) + + (response ((:bread/handler @system) {:uri "/login"})) + (response ((:bread/handler @system) {:uri "/login" + :request-method :post + :params {:username "coby" + :password "hello"}})) + + + + ;; AUTH + + (def coby + (q '{:find [(pull ?e [:db/id + :user/username + :user/name + :user/email + :user/lang + {:user/roles [:role/key + {:role/abilities [:ability/key]}]}]) .] + :in [$ ?username] + :where [[?e :user/username ?username]]} + "coby")) + (user/can? coby :edit-posts) + (defn retraction [{e :db/id :as entity}] + (mapv #(vector :db/retract e %) (filter #(not= :db/id %) (keys entity)))) + (retraction coby) + (db/transact (db/connection (:bread/app @system)) + (retraction coby)) + (db/transact (db/connection (:bread/app @system)) + [{:user/username "coby" + :user/locked-at (java.util.Date.)}]) + + + + ;; SCI + + (defn- sci-ns [ns-sym] + (let [ns* (sci/create-ns ns-sym) + publics (ns-publics ns-sym)] + (update-vals publics #(sci/copy-var* % ns*)))) + (sci-ns 'systems.bread.alpha.component) + + (defn- sci-context [ns-syms] + (sci/init {:namespaces (into {} (map (juxt identity sci-ns) ns-syms))})) + + (def $theme-ctx + (sci-context ['systems.bread.alpha.component])) + + (sci/eval-string* + $theme-ctx + "(ns my-theme (:require [systems.bread.alpha.component :refer [defc]])) + (defc my-page [_] + {} + [:p \"MY PAGE\"])") + + (sci/eval-string* + $theme-ctx + "(ns my-theme) + (my-page {})") + + + + ;; COMPONENT ROUTING + + (require '[systems.bread.alpha.component :as c :refer [defc]] + '[systems.bread.alpha.route :as route]) + + ;; A "sluggable" thing, with ancestry + (def grandchild + {:thing/slug "c" + :thing/_children [{:thing/slug "b" + :thing/_children [{:thing/slug "a"}]}]}) + + (def $router (route/router (->app $req))) + + (reitit/match->path + (reitit/match-by-path $router "/en/a/b/c") + {:field/lang :en :thing/slug* "a/b/c"}) + (reitit/match->path + (reitit/match-by-name $router :page {:field/lang :en :thing/slug* "x"})) + + (bread/routes $router) + (let [req (->app $req)] + (bread/match (route/router req) req)) + (bread/params $router (bread/match $router $req)) + + ;; route/uri infers params and then just calls bread/path under the hood... + (bread/path $router :page {:field/lang :en :thing/slug* "a/b/c"}) + (route/uri (->app $req) :page (merge {:field/lang :en} grandchild)) + (route/uri (->app $req) :page! (merge {:field/lang :en} grandchild)) + + (route/ancestry grandchild) + (bread/infer-param :thing/slug* grandchild) + (bread/routes (route/router (->app $req))) + + (route/uri (->app $req) :page (merge {:field/lang :en} grandchild)) + (route/uri (->app $req) :page {:field/lang :en}) + (route/uri (->app $req) :page nil) + (route/uri (->app $req) :page {}) + + + + ;; SITEMAP DESIGN + + ;; OK, algorithm time. + ;; We can query for every :db/ident in the database: + (require '[systems.bread.alpha.util.datalog :as datalog]) + (def idents + (map :db/ident (datalog/attrs (db/database (->app $req))))) + + ;; Now we can scan a given route for db idents... + (def route-spec + [:field/lang :thing/slug]) + (def route-idents + (filter (set idents) route-spec)) + + ;; Before the next step, we query for all refs in the db: + (def refs + (datalog/attrs-by-type (db/database (->app $req)) :db.type/ref)) + + ;; One more thing: we need to keep track of the attrs we've seen so far: + (def seen + #{:field/lang}) + + ;; Now, query the db for all entities in the db with refs + ;; to entities with the first attr: + (defn adjacent-attrs [ident ref-attrs] + (reduce (fn [m ref-attr] + (let [entities + (map first + (q '{:find [(pull ?e [*])] + :in [$ ?ident ?ref] + :where [[?referenced ?ident] + [?e ?ref ?referenced]]} + ident + ref-attr + ))] + (if (seq entities) + (assoc m ref-attr (set (flatten (map keys entities)))) + m))) + {} ref-attrs)) + (def adjacents + (adjacent-attrs (first route-idents) refs)) + + ;; Next, gather up all attrs (not including) ones we've already seen in the + ;; entities we just queried: + (defn find-path [entities seen next-attr] + (reduce (fn [path [ref-attr attrs]] + (when (contains? attrs next-attr) + (reduced [ref-attr next-attr]))) + [] entities)) + (def path + (find-path adjacents seen :thing/slug)) + (def full-path + (vec (concat [:field/lang] path))) + + ;; We've now found the path between :field/lang and :thing/slug, the only two + ;; attrs in our route definition. So, we can stop looking in this case. But, + ;; if there were more attrs in the route or if we hadn't found it, we could + ;; simply add the adjacent attrs we just found to seen, and explore each of + ;; those (via references) recursively... + + ;; /experiment + + (require '[kaocha.repl :as k]) + (k/run :unit) + + (-main)) + +(defn -main [& args] + (let [{:keys [options errors] :as cli-env} (cli/parse-opts args cli-options) + {:keys [help port file config cgi]} options + cgi (or cgi (System/getenv "GATEWAY_INTERFACE"))] + (cond + errors (show-errors cli-env) + help (show-help cli-env) + cgi (run-as-cgi cli-env) + config (start! config) + file (if-not (.exists (io/file file)) + (show-errors {:errors [(str "No such file: " file)]}) + (let [config (-> file get-merged-config + (update-in [:http :port] #(if port port %)))] + (start! config))) + :else (show-help cli-env)))) diff --git a/cms/systems/bread/alpha/cms/main.cljc b/cms/systems/bread/alpha/cms/main.cljc index 9ea63371c..e2147a386 100644 --- a/cms/systems/bread/alpha/cms/main.cljc +++ b/cms/systems/bread/alpha/cms/main.cljc @@ -38,7 +38,7 @@ ["-p" "--port PORT" "Port number to run the HTTP server on." :parse-fn #(Integer/parseInt %) :validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536."]] - ["-f" "--file FILE" "Config file path. Ignored if --file is passed." + ["-f" "--file FILE" "Config file path. Ignored if --config is passed." :default "config.edn"] ["-c" "--config EDN" "Full configuration data as EDN. Causes other args to be ignored." From 640d3971d0d3adfe9999a8de1b47a59237a48044 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 26 Sep 2024 22:52:26 -0700 Subject: [PATCH 17/27] rm old TODO --- cms/systems/bread/alpha/cms/cgi.cljc | 1 - cms/systems/bread/alpha/cms/main.cljc | 1 - 2 files changed, 2 deletions(-) diff --git a/cms/systems/bread/alpha/cms/cgi.cljc b/cms/systems/bread/alpha/cms/cgi.cljc index 9a522e4f5..774915d4b 100644 --- a/cms/systems/bread/alpha/cms/cgi.cljc +++ b/cms/systems/bread/alpha/cms/cgi.cljc @@ -11,7 +11,6 @@ [reitit.ring] [ring.middleware.defaults :as ring] [sci.core :as sci] - ;; TODO ring middlewares [systems.bread.alpha.core :as bread] [systems.bread.alpha.cms.theme] diff --git a/cms/systems/bread/alpha/cms/main.cljc b/cms/systems/bread/alpha/cms/main.cljc index e2147a386..511574b66 100644 --- a/cms/systems/bread/alpha/cms/main.cljc +++ b/cms/systems/bread/alpha/cms/main.cljc @@ -11,7 +11,6 @@ [reitit.ring] [ring.middleware.defaults :as ring] [sci.core :as sci] - ;; TODO ring middlewares [systems.bread.alpha.core :as bread] [systems.bread.alpha.cms.theme] From 8bd429a8614737eb60730e8aabbc869de831a652 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 26 Sep 2024 22:55:23 -0700 Subject: [PATCH 18/27] logging TODOs --- cms/systems/bread/alpha/cms/cgi.cljc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cms/systems/bread/alpha/cms/cgi.cljc b/cms/systems/bread/alpha/cms/cgi.cljc index 774915d4b..3630eec7c 100644 --- a/cms/systems/bread/alpha/cms/cgi.cljc +++ b/cms/systems/bread/alpha/cms/cgi.cljc @@ -11,6 +11,7 @@ [reitit.ring] [ring.middleware.defaults :as ring] [sci.core :as sci] + ;; TODO Timbre [systems.bread.alpha.core :as bread] [systems.bread.alpha.cms.theme] @@ -26,6 +27,8 @@ [java.util Properties]) (:gen-class)) +;; TODO log to stderr + (def status-mappings {200 "OK" 400 "Bad Request" From 3ea5fd6d9f198ad18db76f00f40df7e26faf191d Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 26 Sep 2024 22:58:37 -0700 Subject: [PATCH 19/27] s/:only/:refer --- plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc index 36c61656b..e1a84ed74 100644 --- a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc +++ b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc @@ -3,7 +3,7 @@ [clojure.edn :as edn] [clojure.pprint :refer [pprint]] [clojure.core.protocols :refer [Datafiable]] - [clojure.java.shell :only [sh]] + [clojure.java.shell :refer [sh]] [clojure.string :refer [split]] [systems.bread.alpha.schema :as schema] From 408241fc8bd1c55dc9dd599f8f943b961d53896a Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 26 Sep 2024 23:17:48 -0700 Subject: [PATCH 20/27] get CGI kinda working via `clojure` --- NOTES.md | 10 +- cms/systems/bread/alpha/cms/cgi.cljc | 427 +++------------------------ resources/default.cgi.edn | 18 ++ 3 files changed, 74 insertions(+), 381 deletions(-) create mode 100644 resources/default.cgi.edn diff --git a/NOTES.md b/NOTES.md index 5d13e3fb0..98bcc0ee7 100644 --- a/NOTES.md +++ b/NOTES.md @@ -5,11 +5,19 @@ Notes on potential ideas for Bread. Almost all of this is entirely hypothetical, ## Progress - native-image with Markdown: BLOCKED -- native-image with Datahike: BLOCKED +- native-image with Datahike: WIP - Babashka with Datahike: BLOCKED - JVM on Heroku: ??? - JVM on Fly.io: ??? +## CGI + +https://www.cgi101.com/book/ch3/text.html + +``` +REQUEST_URL=/en/hello REMOTE_ADDR=127.0.0.1 CONTENT_TYPE='*/*' clojure -M:cms -m systems.bread.alpha.cms.cgi --file bread.cgi.edn +``` + ## bread.main CGI mode is enabled by default when the `GATEWAY_INTERFACE` env var is detected, or if the `--cgi` flag is passed explicitly. Maybe make a `--no-cgi` flag to disable when env var present? diff --git a/cms/systems/bread/alpha/cms/cgi.cljc b/cms/systems/bread/alpha/cms/cgi.cljc index 3630eec7c..1d40384b1 100644 --- a/cms/systems/bread/alpha/cms/cgi.cljc +++ b/cms/systems/bread/alpha/cms/cgi.cljc @@ -1,4 +1,4 @@ -(ns systems.bread.alpha.cms.main +(ns systems.bread.alpha.cms.cgi (:require [clojure.edn :as edn] [clojure.java.io :as io] @@ -41,13 +41,10 @@ :parse-fn #(Integer/parseInt %) :validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536."]] ["-f" "--file FILE" "Config file path. Ignored if --config is passed." - :default "config.edn"] + :default "bread.edn"] ["-c" "--config EDN" "Full configuration data as EDN. Causes other args to be ignored." - :parse-fn edn/read-string] - ["-g" "--cgi" - "Run Bread as a CGI script" - :default false]]) + :parse-fn edn/read-string]]) (defn show-help [{:keys [summary]}] (println summary)) @@ -55,53 +52,7 @@ (defn show-errors [{:keys [errors]}] (println (string/join "\n" errors))) -(defn run-as-cgi [{:keys [options]}] - (try - ;; TODO this is pretty jank, update to parse HTTP requests properly - (let [[uri & _] (some-> (System/getenv "REQUEST_URI") - (clojure.string/split #"\?")) - config (aero/read-config (:file options)) - system (ig/init config) - handler (:bread/handler system) - req {:uri uri - :query-string (System/getenv "QUERY_STRING") - :remote-addr (System/getenv "REMOTE_ADDR") - :server-name (System/getenv "SERVER_NAME") - :server-port (System/getenv "SERVER_PORT") - :content-type (System/getenv "CONTENT_TYPE") - :content-length (Integer. - (or (System/getenv "CONTENT_LENGTH") "0"))} - {:keys [status headers body] :as res} (handler req)] - (println (str "status: " status " " (status-mappings status))) - (doseq [[header header-value] headers] - (println (str header ": " header-value))) - (println) - (println body) - (System/exit 0)) - (catch Throwable e - (println "status: 500 Internal Server Error") - (println "content-type: text/plain") - (println) - (println (.getMessage e)) - (println (.getStackTrace e)) - (System/exit 1)))) - -(defonce system (atom nil)) - -(defn start! [config] - (let [config (assoc config - :initial-config config - ;; These will be initialized by Integrant: - ;; TODO bread version - :clojure-version nil - :started-at nil)] - (reset! system (ig/init config)))) - -(defn stop! [] - (when-let [sys @system] - (ig/halt! sys) - (reset! system nil))) - +;; TODO mv (defmethod ig/init-key :initial-config [_ config] config) @@ -111,13 +62,6 @@ (defmethod ig/init-key :started-at [_ _] (LocalDateTime/now)) -(defmethod ig/init-key :http [_ {:keys [port handler wrap-defaults]}] - (println "Starting HTTP server on port" port) - (let [handler (if wrap-defaults - (ring/wrap-defaults handler wrap-defaults) - handler)] - (http/run-server handler {:port port}))) - (defmethod ig/halt-key! :http [_ stop-server] (when-let [prom (stop-server :timeout 100)] @prom)) @@ -162,14 +106,6 @@ (defmethod ig/init-key :bread/handler [_ app] (bread/handler app)) -(defn log-hook! [invocation] - (let [{:keys [hook action result]} invocation] - (prn (:action/name action) (select-keys result - [:params - :headers - :status - :session])))) - (defmethod ig/init-key :bread/profilers [_ profilers] ;; Enable hook profiling. (alter-var-root #'bread/*profile-hooks* (constantly true)) @@ -188,317 +124,56 @@ (defmethod ig/halt-key! :bread/profilers [_ profilers] (doseq [{:keys [tap]} profilers] (remove-tap tap))) +;; /mv + +(defn log-hook! [invocation] + (let [{:keys [hook action result]} invocation] + (prn (:action/name action) (select-keys result + [:params + :headers + :status + :session])))) (defn get-merged-config [path] (merge - (aero/read-config (io/resource "default.main.edn")) + (aero/read-config (io/resource "default.cgi.edn")) (aero/read-config path))) -(defn restart! [config] - (stop!) - (start! config)) +(defn run-as-cgi [{:keys [options]}] + (try + ;; TODO this is pretty jank, update to parse HTTP requests properly + (let [[uri & _] (some-> (System/getenv "REQUEST_URI") + (clojure.string/split #"\?")) + ;; TODO merge with defaults + config (get-merged-config (:file options)) + system (ig/init config) + handler (:bread/handler system) + req {:uri uri + :query-string (System/getenv "QUERY_STRING") + :remote-addr (System/getenv "REMOTE_ADDR") + :server-name (System/getenv "SERVER_NAME") + :server-port (System/getenv "SERVER_PORT") + :content-type (System/getenv "CONTENT_TYPE") + :content-length (Integer. + (or (System/getenv "CONTENT_LENGTH") "0"))} + {:keys [status headers body] :as res} (handler req)] + (println (str "status: " status " " (status-mappings status))) + (doseq [[header header-value] headers] + (println (str header ": " header-value))) + (println) + (println body) + (System/exit 0)) + (catch Throwable e + (println "status: 500 Internal Server Error") + (println "content-type: text/plain") + (println) + (println (.getMessage e)) + (println (.getStackTrace e)) + (System/exit 1)))) (comment (set! *print-namespace-maps* false) - (merge - (-> "default.main.edn" io/resource aero/read-config) - (-> "dev/main.edn" aero/read-config)) - (get-merged-config "dev/main.edn") - - (try (restart! (get-merged-config "dev/main.edn")) - (catch clojure.lang.ExceptionInfo e - (-> e ex-cause ((juxt (comp :action/name :action ex-data) - (comp ex-message ex-cause) - (comp :out ex-data ex-cause) - (comp :reason ex-data)))))) - (deref system) - (:http @system) - (:ring/wrap-defaults @system) - (:ring/session-store @system) - (:bread/app @system) - (:bread/router @system) - (:bread/db @system) - (:bread/profilers @system) - - (alter-var-root #'bread/*profile-hooks* not) - - (def $req {:uri "/en" :request-method :get}) - (def $req {:uri "/en/hello" :request-method :get}) - (def $req {:uri "/en/hello/child-page" :request-method :get}) - (def $req {:uri "/en/tag/one" :request-method :get}) - (def $req {:uri "/fr/tag/one" :request-method :get}) - (def $req {:uri "/en/404" :request-method :get}) - - (do - (require '[flow-storm.api :as flow] - '[systems.bread.alpha.tools.util :as util :refer [do-expansions]]) - (def ->app (partial util/->app (:bread/app @system))) - (def diagnose-expansions (partial util/diagnose-expansions (:bread/app @system))) - - (defn db [] - (db/database (->app $req))) - - (defn q [& args] - (apply - db/q - (db/database (->app $req)) - args))) - - (flow/local-connect) - - (diagnose-expansions (->app $req)) - (do-expansions (->app $req) 1) - (do-expansions (->app $req) 2) - (do-expansions (->app $req) 3) - - (as-> (->app $req) $ - (bread/hook $ ::bread/route) - (::bread/dispatcher $)) - (as-> (->app $req) $ - (bread/hook $ ::bread/route) - (bread/hook $ ::bread/dispatch) - (::bread/expansions $)) - (as-> (->app $req) $ - (bread/hook $ ::bread/route) - (bread/hook $ ::bread/dispatch) - (bread/hook $ ::bread/expand) - (::bread/data $)) - (as-> (->app $req) $ - (bread/hook $ ::bread/route) - (bread/hook $ ::bread/dispatch) - (bread/hook $ ::bread/expand) - (bread/hook $ ::bread/render) - (select-keys $ [:status :body :headers])) - - (bread/config (->app $req) :i18n/supported-langs) - - ;; TODO Nice debug mechanism: - (catch-as-> (->app $req) - [::bread/route ::bread/dispatcher] - [::bread/dispatch ::bread/expansions] - [::bread/expand ::bread/data (diagnose-expansions $)] - [::bread/render (select-keys $ [:status :body :headers])]) - - ;; querying for inverse relationships (post <-> taxon): - (q '{:find [(pull ?t [:db/id {:post/_taxons [*]}])] - :in [$ ?slug] - :where [[?t :taxon/taxonomy :taxon.taxonomy/tag] - [?t :thing/slug ?slug]]} - "one") - (q '{:find [(pull ?p [:db/id {:post/taxons [*]}])] - :in [$ ?slug] - :where [[?p :post/type :post.type/page] - [?p :thing/slug ?slug]]} - "hello") - - ;; Menu expansions - - (q '{:find [(pull ?e [:db/id - :taxon/taxonomy - :thing/slug - {:thing/_children [:thing/slug - {:thing/_children ...}]} - {:thing/children ...} - {:translatable/fields [*]}])] - :in [$ ?taxonomy] - :where [[?e :taxon/taxonomy ?taxonomy]]} - :taxon.taxonomy/tag) - - (q '{:find [(pull ?e [;; Post menus don't store their own data in the db: - ;; instead, they follow the post hierarchy itself. - :db/id - :post/type - :post/status - {:translatable/fields [*]} - {:thing/_children [:thing/slug {:thing/_children ...}]} - {:thing/children ...}])] - :in [$ ?type [?status ...]] - :where [[?e :post/type ?type] - [?e :post/status ?status] - (not-join [?e] [?_ :thing/children ?e])]} - :post.type/page - #{:post.status/published}) - - (slurp (io/resource "public/assets/hi.txt")) - (bread/match (:bread/router @system) {:uri "/assets/hi.txt" - :request-method :get}) - (bread/match (:bread/router @system) {:uri "/en" - :request-method :get}) - (bread/match (:bread/router @system) {:uri "/login" - :request-method :get}) - (bread/match (:bread/router @system) {:uri "/login" - :request-method :post}) - - (response ((:bread/handler @system) {:uri "/en"})) - (response ((:bread/handler @system) {:uri "/en/hello"})) - (response ((:bread/handler @system) {:uri "/en/hello/child-page"})) - ;; This should 404: - (response ((:bread/handler @system) {:uri "/en/child-page"})) - - (response ((:bread/handler @system) {:uri "/login"})) - (response ((:bread/handler @system) {:uri "/login" - :request-method :post - :params {:username "coby" - :password "hello"}})) - - - - ;; AUTH - - (def coby - (q '{:find [(pull ?e [:db/id - :user/username - :user/name - :user/email - :user/lang - {:user/roles [:role/key - {:role/abilities [:ability/key]}]}]) .] - :in [$ ?username] - :where [[?e :user/username ?username]]} - "coby")) - (user/can? coby :edit-posts) - (defn retraction [{e :db/id :as entity}] - (mapv #(vector :db/retract e %) (filter #(not= :db/id %) (keys entity)))) - (retraction coby) - (db/transact (db/connection (:bread/app @system)) - (retraction coby)) - (db/transact (db/connection (:bread/app @system)) - [{:user/username "coby" - :user/locked-at (java.util.Date.)}]) - - - - ;; SCI - - (defn- sci-ns [ns-sym] - (let [ns* (sci/create-ns ns-sym) - publics (ns-publics ns-sym)] - (update-vals publics #(sci/copy-var* % ns*)))) - (sci-ns 'systems.bread.alpha.component) - - (defn- sci-context [ns-syms] - (sci/init {:namespaces (into {} (map (juxt identity sci-ns) ns-syms))})) - - (def $theme-ctx - (sci-context ['systems.bread.alpha.component])) - - (sci/eval-string* - $theme-ctx - "(ns my-theme (:require [systems.bread.alpha.component :refer [defc]])) - (defc my-page [_] - {} - [:p \"MY PAGE\"])") - - (sci/eval-string* - $theme-ctx - "(ns my-theme) - (my-page {})") - - - - ;; COMPONENT ROUTING - - (require '[systems.bread.alpha.component :as c :refer [defc]] - '[systems.bread.alpha.route :as route]) - - ;; A "sluggable" thing, with ancestry - (def grandchild - {:thing/slug "c" - :thing/_children [{:thing/slug "b" - :thing/_children [{:thing/slug "a"}]}]}) - - (def $router (route/router (->app $req))) - - (reitit/match->path - (reitit/match-by-path $router "/en/a/b/c") - {:field/lang :en :thing/slug* "a/b/c"}) - (reitit/match->path - (reitit/match-by-name $router :page {:field/lang :en :thing/slug* "x"})) - - (bread/routes $router) - (let [req (->app $req)] - (bread/match (route/router req) req)) - (bread/params $router (bread/match $router $req)) - - ;; route/uri infers params and then just calls bread/path under the hood... - (bread/path $router :page {:field/lang :en :thing/slug* "a/b/c"}) - (route/uri (->app $req) :page (merge {:field/lang :en} grandchild)) - (route/uri (->app $req) :page! (merge {:field/lang :en} grandchild)) - - (route/ancestry grandchild) - (bread/infer-param :thing/slug* grandchild) - (bread/routes (route/router (->app $req))) - - (route/uri (->app $req) :page (merge {:field/lang :en} grandchild)) - (route/uri (->app $req) :page {:field/lang :en}) - (route/uri (->app $req) :page nil) - (route/uri (->app $req) :page {}) - - - - ;; SITEMAP DESIGN - - ;; OK, algorithm time. - ;; We can query for every :db/ident in the database: - (require '[systems.bread.alpha.util.datalog :as datalog]) - (def idents - (map :db/ident (datalog/attrs (db/database (->app $req))))) - - ;; Now we can scan a given route for db idents... - (def route-spec - [:field/lang :thing/slug]) - (def route-idents - (filter (set idents) route-spec)) - - ;; Before the next step, we query for all refs in the db: - (def refs - (datalog/attrs-by-type (db/database (->app $req)) :db.type/ref)) - - ;; One more thing: we need to keep track of the attrs we've seen so far: - (def seen - #{:field/lang}) - - ;; Now, query the db for all entities in the db with refs - ;; to entities with the first attr: - (defn adjacent-attrs [ident ref-attrs] - (reduce (fn [m ref-attr] - (let [entities - (map first - (q '{:find [(pull ?e [*])] - :in [$ ?ident ?ref] - :where [[?referenced ?ident] - [?e ?ref ?referenced]]} - ident - ref-attr - ))] - (if (seq entities) - (assoc m ref-attr (set (flatten (map keys entities)))) - m))) - {} ref-attrs)) - (def adjacents - (adjacent-attrs (first route-idents) refs)) - - ;; Next, gather up all attrs (not including) ones we've already seen in the - ;; entities we just queried: - (defn find-path [entities seen next-attr] - (reduce (fn [path [ref-attr attrs]] - (when (contains? attrs next-attr) - (reduced [ref-attr next-attr]))) - [] entities)) - (def path - (find-path adjacents seen :thing/slug)) - (def full-path - (vec (concat [:field/lang] path))) - - ;; We've now found the path between :field/lang and :thing/slug, the only two - ;; attrs in our route definition. So, we can stop looking in this case. But, - ;; if there were more attrs in the route or if we hadn't found it, we could - ;; simply add the adjacent attrs we just found to seen, and explore each of - ;; those (via references) recursively... - - ;; /experiment - (require '[kaocha.repl :as k]) (k/run :unit) @@ -506,16 +181,8 @@ (defn -main [& args] (let [{:keys [options errors] :as cli-env} (cli/parse-opts args cli-options) - {:keys [help port file config cgi]} options - cgi (or cgi (System/getenv "GATEWAY_INTERFACE"))] + {:keys [help port file config cgi]} options] (cond errors (show-errors cli-env) help (show-help cli-env) - cgi (run-as-cgi cli-env) - config (start! config) - file (if-not (.exists (io/file file)) - (show-errors {:errors [(str "No such file: " file)]}) - (let [config (-> file get-merged-config - (update-in [:http :port] #(if port port %)))] - (start! config))) - :else (show-help cli-env)))) + :else (run-as-cgi cli-env)))) diff --git a/resources/default.cgi.edn b/resources/default.cgi.edn new file mode 100644 index 000000000..69488fa28 --- /dev/null +++ b/resources/default.cgi.edn @@ -0,0 +1,18 @@ +{:ring/wrap-defaults {:ring-defaults :site-defaults + [:session :store] #ig/ref :ring/session-store + [:security :anti-forgery] false} + + :ring/session-store + {:store/type :datalog + :store/db #ig/ref :bread/db} + + :bread/handler #ig/ref :bread/app + + :bread/profilers [] + + #_#_ ;; TODO + :bread/debugger + {:http + {:port 1313 + :docroot "public/debugger" + :middleware [#var systems.bread.alpha.cms.main/wrap-debug]}}} From dc79194aaebbd66590279be4dfa140a952cc880a Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Thu, 26 Sep 2024 23:22:47 -0700 Subject: [PATCH 21/27] mv more config --- cms/systems/bread/alpha/cms/cgi.cljc | 93 +++++++--------- cms/systems/bread/alpha/cms/config/bread.cljc | 54 +++++++++- .../bread/alpha/cms/config/server.cljc | 38 +++++++ cms/systems/bread/alpha/cms/main.cljc | 102 ++---------------- 4 files changed, 140 insertions(+), 147 deletions(-) create mode 100644 cms/systems/bread/alpha/cms/config/server.cljc diff --git a/cms/systems/bread/alpha/cms/cgi.cljc b/cms/systems/bread/alpha/cms/cgi.cljc index 1d40384b1..5829d7e6c 100644 --- a/cms/systems/bread/alpha/cms/cgi.cljc +++ b/cms/systems/bread/alpha/cms/cgi.cljc @@ -18,6 +18,46 @@ [systems.bread.alpha.database :as db] [systems.bread.alpha.plugin.defaults :as defaults] [systems.bread.alpha.cms.config.bread] + +(defmethod ig/init-key :bread/db + [_ {:keys [recreate? force?] :as db-config}] + (db/create! db-config {:force? force?}) + (assoc db-config :db/connection (db/connect db-config))) + +(defmethod ig/halt-key! :bread/db + [_ {:keys [recreate?] :as db-config}] + (when recreate? (db/delete! db-config))) + +(defmethod ig/init-key :bread/router [_ router] + router) + +(defmethod ig/init-key :bread/app [_ app-config] + (bread/load-app (defaults/app app-config))) + +(defmethod ig/halt-key! :bread/app [_ app] + (bread/shutdown app)) + +(defmethod ig/init-key :bread/handler [_ app] + (bread/handler app)) + +(defmethod ig/init-key :bread/profilers [_ profilers] + ;; Enable hook profiling. + (alter-var-root #'bread/*profile-hooks* (constantly true)) + (map + (fn [{h :hook act :action/name f :f :as profiler}] + (let [tap (bread/add-profiler + (fn [{{:keys [action hook] :as invocation} ::bread/profile}] + (if (and (or (nil? (seq h)) ((set h) + hook)) + (or (nil? (seq act)) ((set act) + (:action/name action)))) + (f invocation))))] + (assoc profiler :tap tap))) + profilers)) + +(defmethod ig/halt-key! :bread/profilers [_ profilers] + (doseq [{:keys [tap]} profilers] + (remove-tap tap))) [systems.bread.alpha.cms.config.reitit] [systems.bread.alpha.plugin.auth :as auth] [systems.bread.alpha.plugin.datahike-cli] @@ -53,19 +93,9 @@ (println (string/join "\n" errors))) ;; TODO mv -(defmethod ig/init-key :initial-config [_ config] - config) - (defmethod ig/init-key :clojure-version [_ _] (clojure-version)) -(defmethod ig/init-key :started-at [_ _] - (LocalDateTime/now)) - -(defmethod ig/halt-key! :http [_ stop-server] - (when-let [prom (stop-server :timeout 100)] - @prom)) - (defmethod ig/init-key :ring/wrap-defaults [_ value] (let [default-configs {:api-defaults ring/api-defaults :site-defaults ring/site-defaults @@ -84,46 +114,6 @@ ;; TODO extend with a multimethod?? (when (= :datalog store-type) (auth/session-store conn))) - -(defmethod ig/init-key :bread/db - [_ {:keys [recreate? force?] :as db-config}] - (db/create! db-config {:force? force?}) - (assoc db-config :db/connection (db/connect db-config))) - -(defmethod ig/halt-key! :bread/db - [_ {:keys [recreate?] :as db-config}] - (when recreate? (db/delete! db-config))) - -(defmethod ig/init-key :bread/router [_ router] - router) - -(defmethod ig/init-key :bread/app [_ app-config] - (bread/load-app (defaults/app app-config))) - -(defmethod ig/halt-key! :bread/app [_ app] - (bread/shutdown app)) - -(defmethod ig/init-key :bread/handler [_ app] - (bread/handler app)) - -(defmethod ig/init-key :bread/profilers [_ profilers] - ;; Enable hook profiling. - (alter-var-root #'bread/*profile-hooks* (constantly true)) - (map - (fn [{h :hook act :action/name f :f :as profiler}] - (let [tap (bread/add-profiler - (fn [{{:keys [action hook] :as invocation} ::bread/profile}] - (if (and (or (nil? (seq h)) ((set h) - hook)) - (or (nil? (seq act)) ((set act) - (:action/name action)))) - (f invocation))))] - (assoc profiler :tap tap))) - profilers)) - -(defmethod ig/halt-key! :bread/profilers [_ profilers] - (doseq [{:keys [tap]} profilers] - (remove-tap tap))) ;; /mv (defn log-hook! [invocation] @@ -146,8 +136,7 @@ (clojure.string/split #"\?")) ;; TODO merge with defaults config (get-merged-config (:file options)) - system (ig/init config) - handler (:bread/handler system) + handler (:bread/handler (ig/init config)) req {:uri uri :query-string (System/getenv "QUERY_STRING") :remote-addr (System/getenv "REMOTE_ADDR") diff --git a/cms/systems/bread/alpha/cms/config/bread.cljc b/cms/systems/bread/alpha/cms/config/bread.cljc index d5fe5d420..2a5d2e356 100644 --- a/cms/systems/bread/alpha/cms/config/bread.cljc +++ b/cms/systems/bread/alpha/cms/config/bread.cljc @@ -1,7 +1,13 @@ (ns systems.bread.alpha.cms.config.bread (:require [aero.core :as aero] - [integrant.core :as ig])) + [integrant.core :as ig] + + [systems.bread.alpha.core :as bread] + [systems.bread.alpha.plugin.defaults :as defaults] + [systems.bread.alpha.database :as db]) + (:import + [java.time LocalDateTime])) (defmethod aero/reader 'ig/ref [_ _ value] (ig/ref value)) @@ -25,3 +31,49 @@ (defmethod aero/reader 'concat [_ _ args] (apply concat args)) + +(defmethod ig/init-key :bread/started-at [_ _] + (LocalDateTime/now)) + +(defmethod ig/init-key :bread/initial-config [_ config] + config) + +(defmethod ig/init-key :bread/db + [_ {:keys [recreate? force?] :as db-config}] + (db/create! db-config {:force? force?}) + (assoc db-config :db/connection (db/connect db-config))) + +(defmethod ig/halt-key! :bread/db + [_ {:keys [recreate?] :as db-config}] + (when recreate? (db/delete! db-config))) + +(defmethod ig/init-key :bread/router [_ router] + router) + +(defmethod ig/init-key :bread/app [_ app-config] + (bread/load-app (defaults/app app-config))) + +(defmethod ig/halt-key! :bread/app [_ app] + (bread/shutdown app)) + +(defmethod ig/init-key :bread/handler [_ app] + (bread/handler app)) + +(defmethod ig/init-key :bread/profilers [_ profilers] + ;; Enable hook profiling. + (alter-var-root #'bread/*profile-hooks* (constantly true)) + (map + (fn [{h :hook act :action/name f :f :as profiler}] + (let [tap (bread/add-profiler + (fn [{{:keys [action hook] :as invocation} ::bread/profile}] + (if (and (or (nil? (seq h)) ((set h) + hook)) + (or (nil? (seq act)) ((set act) + (:action/name action)))) + (f invocation))))] + (assoc profiler :tap tap))) + profilers)) + +(defmethod ig/halt-key! :bread/profilers [_ profilers] + (doseq [{:keys [tap]} profilers] + (remove-tap tap))) diff --git a/cms/systems/bread/alpha/cms/config/server.cljc b/cms/systems/bread/alpha/cms/config/server.cljc new file mode 100644 index 000000000..b32c6e134 --- /dev/null +++ b/cms/systems/bread/alpha/cms/config/server.cljc @@ -0,0 +1,38 @@ +(ns systems.bread.alpha.cms.config.server + (:require + [aero.core :as aero] + [integrant.core :as ig] + [org.httpkit.server :as http] + [ring.middleware.defaults :as ring] + + [systems.bread.alpha.plugin.auth :as auth])) + +(defmethod ig/init-key :http [_ {:keys [port handler wrap-defaults]}] + (println "Starting HTTP server on port" port) + (let [handler (if wrap-defaults + (ring/wrap-defaults handler wrap-defaults) + handler)] + (http/run-server handler {:port port}))) + +(defmethod ig/halt-key! :http [_ stop-server] + (when-let [prom (stop-server :timeout 100)] + @prom)) + +(defmethod ig/init-key :ring/wrap-defaults [_ value] + (let [default-configs {:api-defaults ring/api-defaults + :site-defaults ring/site-defaults + :secure-api-defaults ring/secure-api-defaults + :secure-site-defaults ring/secure-api-defaults} + k (if (keyword? value) value (get value :ring-defaults)) + defaults (get default-configs k) + defaults (if (map? value) + (reduce #(assoc-in %1 (key %2) (val %2)) + defaults (dissoc value :ring-defaults)) + defaults)] + defaults)) + +(defmethod ig/init-key :ring/session-store + [_ {store-type :store/type {conn :db/connection} :store/db}] + ;; TODO extend with a multimethod?? + (when (= :datalog store-type) + (auth/session-store conn))) diff --git a/cms/systems/bread/alpha/cms/main.cljc b/cms/systems/bread/alpha/cms/main.cljc index 511574b66..62434087f 100644 --- a/cms/systems/bread/alpha/cms/main.cljc +++ b/cms/systems/bread/alpha/cms/main.cljc @@ -6,10 +6,8 @@ [clojure.tools.cli :as cli] [aero.core :as aero] [integrant.core :as ig] - [org.httpkit.server :as http] [reitit.core :as reitit] [reitit.ring] - [ring.middleware.defaults :as ring] [sci.core :as sci] [systems.bread.alpha.core :as bread] @@ -18,11 +16,11 @@ [systems.bread.alpha.plugin.defaults :as defaults] [systems.bread.alpha.cms.config.bread] [systems.bread.alpha.cms.config.reitit] + [systems.bread.alpha.cms.config.server] [systems.bread.alpha.plugin.auth :as auth] [systems.bread.alpha.plugin.datahike] [systems.bread.alpha.plugin.reitit]) (:import - [java.time LocalDateTime] [java.util Properties]) (:gen-class)) @@ -87,105 +85,21 @@ (defn start! [config] (let [config (assoc config - :initial-config config - ;; These will be initialized by Integrant: - ;; TODO bread version + :bread/initial-config config + ;; These will be initialized by Integrant... :clojure-version nil - :started-at nil)] + ;; TODO bread version + :bread/started-at nil)] (reset! system (ig/init config)))) +(defmethod ig/init-key :clojure-version [_ _] + (clojure-version)) + (defn stop! [] (when-let [sys @system] (ig/halt! sys) (reset! system nil))) -(defmethod ig/init-key :initial-config [_ config] - config) - -(defmethod ig/init-key :clojure-version [_ _] - (clojure-version)) - -(defmethod ig/init-key :started-at [_ _] - (LocalDateTime/now)) - -(defmethod ig/init-key :http [_ {:keys [port handler wrap-defaults]}] - (println "Starting HTTP server on port" port) - (let [handler (if wrap-defaults - (ring/wrap-defaults handler wrap-defaults) - handler)] - (http/run-server handler {:port port}))) - -(defmethod ig/halt-key! :http [_ stop-server] - (when-let [prom (stop-server :timeout 100)] - @prom)) - -(defmethod ig/init-key :ring/wrap-defaults [_ value] - (let [default-configs {:api-defaults ring/api-defaults - :site-defaults ring/site-defaults - :secure-api-defaults ring/secure-api-defaults - :secure-site-defaults ring/secure-api-defaults} - k (if (keyword? value) value (get value :ring-defaults)) - defaults (get default-configs k) - defaults (if (map? value) - (reduce #(assoc-in %1 (key %2) (val %2)) - defaults (dissoc value :ring-defaults)) - defaults)] - defaults)) - -(defmethod ig/init-key :ring/session-store - [_ {store-type :store/type {conn :db/connection} :store/db}] - ;; TODO extend with a multimethod?? - (when (= :datalog store-type) - (auth/session-store conn))) - -(defmethod ig/init-key :bread/db - [_ {:keys [recreate? force?] :as db-config}] - (db/create! db-config {:force? force?}) - (assoc db-config :db/connection (db/connect db-config))) - -(defmethod ig/halt-key! :bread/db - [_ {:keys [recreate?] :as db-config}] - (when recreate? (db/delete! db-config))) - -(defmethod ig/init-key :bread/router [_ router] - router) - -(defmethod ig/init-key :bread/app [_ app-config] - (bread/load-app (defaults/app app-config))) - -(defmethod ig/halt-key! :bread/app [_ app] - (bread/shutdown app)) - -(defmethod ig/init-key :bread/handler [_ app] - (bread/handler app)) - -(defn log-hook! [invocation] - (let [{:keys [hook action result]} invocation] - (prn (:action/name action) (select-keys result - [:params - :headers - :status - :session])))) - -(defmethod ig/init-key :bread/profilers [_ profilers] - ;; Enable hook profiling. - (alter-var-root #'bread/*profile-hooks* (constantly true)) - (map - (fn [{h :hook act :action/name f :f :as profiler}] - (let [tap (bread/add-profiler - (fn [{{:keys [action hook] :as invocation} ::bread/profile}] - (if (and (or (nil? (seq h)) ((set h) - hook)) - (or (nil? (seq act)) ((set act) - (:action/name action)))) - (f invocation))))] - (assoc profiler :tap tap))) - profilers)) - -(defmethod ig/halt-key! :bread/profilers [_ profilers] - (doseq [{:keys [tap]} profilers] - (remove-tap tap))) - (defn get-merged-config [path] (merge (aero/read-config (io/resource "default.main.edn")) From a1704574c9a2981b4592ca9783e11c297a9d8bb4 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 2 Oct 2024 18:57:04 -0700 Subject: [PATCH 22/27] fix handling of keyword cmds --- .../systems/bread/alpha/plugin/datahike_cli.cljc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc index e1a84ed74..34bb936df 100644 --- a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc +++ b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc @@ -22,9 +22,13 @@ java.lang.Object (-to-param [x] (str x))) -(defn- dthk [{:cli/keys [dthk-path]} & cmd] - (prn (apply list 'sh dthk-path (map -to-param cmd))) - (apply sh dthk-path (map -to-param cmd))) +(comment + (pr-str (UUID/randomUUID)) + (-to-param (UUID/randomUUID))) + +(defn- dthk [{:cli/keys [dthk-path]} cmd & args] + (prn (apply list 'sh dthk-path (name cmd) (map -to-param args))) + (apply sh dthk-path (name cmd) (map -to-param args))) (defn- q* [config & cmd] (let [{:keys [out err exit] :as result} (apply dthk config cmd)] From 2cd8d64e71a36e5dfc9c965d367a1e8e976ea61b Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 2 Oct 2024 18:57:20 -0700 Subject: [PATCH 23/27] properly serialize UUIDs --- .../datahike/systems/bread/alpha/plugin/datahike_cli.cljc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc index 34bb936df..fdecc30bf 100644 --- a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc +++ b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc @@ -10,14 +10,15 @@ [systems.bread.alpha.core :as bread] [systems.bread.alpha.database :as db]) (:import - [java.lang IllegalArgumentException])) + [java.lang IllegalArgumentException] + [java.util UUID])) (defprotocol CliParam (-to-param [x])) (extend-protocol CliParam - clojure.lang.Keyword - (-to-param [k] (name k)) + java.util.UUID + (-to-param [uuid] (pr-str (str uuid))) java.lang.Object (-to-param [x] (str x))) From a95138dbe2dce5b766d2c5ebd08108af36223b27 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 2 Oct 2024 19:19:51 -0700 Subject: [PATCH 24/27] rm :app from ex-data in core error handler --- src/systems/bread/alpha/core.cljc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/systems/bread/alpha/core.cljc b/src/systems/bread/alpha/core.cljc index bfa203146..c493ca976 100644 --- a/src/systems/bread/alpha/core.cljc +++ b/src/systems/bread/alpha/core.cljc @@ -233,7 +233,6 @@ (throw (if (-> e# ex-data ::core?) e# (ex-info (.getMessage e#) (merge (ex-data e#) {:hook ~hook - :app ~app :action ~current-action :args ~args ::core? true}) From bc10f537098c1745c558bf8ac7b408e4ed1ce2f5 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 2 Oct 2024 19:20:55 -0700 Subject: [PATCH 25/27] rm :hello event result from theme code --- cms/systems/bread/alpha/cms/theme.cljc | 2 -- 1 file changed, 2 deletions(-) diff --git a/cms/systems/bread/alpha/cms/theme.cljc b/cms/systems/bread/alpha/cms/theme.cljc index 3a5668ef7..bd3f4956a 100644 --- a/cms/systems/bread/alpha/cms/theme.cljc +++ b/cms/systems/bread/alpha/cms/theme.cljc @@ -72,8 +72,6 @@ {:post/taxons [{:translatable/fields [*]}]}]} [:main [:h1 (:title fields)] - [:p "Hello result: " (pr-str @hello)] - [:p "Hello error: " (-> hello meta :errors first (.getMessage))] [:div.tags-list (map (fn [{tag :translatable/fields}] [:span.tag (:name tag)]) From f85cc350d789b124fc394aa629598fdd85c5087e Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 2 Oct 2024 19:33:16 -0700 Subject: [PATCH 26/27] fix hook exception test --- test/core/systems/bread/alpha/core_test.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/test/core/systems/bread/alpha/core_test.clj b/test/core/systems/bread/alpha/core_test.clj index eb28c86c2..4913407f7 100644 --- a/test/core/systems/bread/alpha/core_test.clj +++ b/test/core/systems/bread/alpha/core_test.clj @@ -447,7 +447,6 @@ (is (thrown-with-msg? ExceptionInfo #"something bad happened" (bread/hook app :throw))) (is (= {:hook :throw - :app app :action {:action/name ::throw :ex ex} :args [1 2 3] ::bread/core? true} From 6fb1b80b357ac2583045626db906e4296c4cc4d5 Mon Sep 17 00:00:00 2001 From: Coby Tamayo Date: Wed, 2 Oct 2024 20:05:32 -0700 Subject: [PATCH 27/27] implement all possible arities --- .../bread/alpha/plugin/datahike_cli.cljc | 111 +++++++++++++++--- 1 file changed, 95 insertions(+), 16 deletions(-) diff --git a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc index fdecc30bf..9c60d2b01 100644 --- a/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc +++ b/plugins/datahike/systems/bread/alpha/plugin/datahike_cli.cljc @@ -60,6 +60,9 @@ (defn- asof-prefix [instant-ms config] (prefix (str "asof:" instant-ms) config)) +(defn- hist-prefix [config] + (prefix (str "history:") config)) + (deftype AsOfDatahikeClient [instant-ms config] db/TemporalDatabase (q [db query] @@ -68,24 +71,39 @@ (q* config :query (pr-str query) (asof-prefix instant-ms config) a)) (q [db query a b] (q* config :query (pr-str query) (asof-prefix instant-ms config) a b)) - ;; TODO more arities... + (q [db query a b c] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c)) + (q [db query a b c d] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d)) + (q [db query a b c d e] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e)) + (q [db query a b c d e f] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f)) + (q [db query a b c d e f g] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g)) + (q [db query a b c d e f g h] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g h)) + (q [db query a b c d e f g h i] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g h i)) + (q [db query a b c d e f g h i j] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g h i j)) + (q [db query a b c d e f g h i j k] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g h i j k)) + (q [db query a b c d e f g h i j k l] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g h i j k l)) + (q [db query a b c d e f g h i j k l m] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g h i j k l m)) + (q [db query a b c d e f g h i j k l m n] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g h i j k l m n)) + (q [db query a b c d e f g h i j k l m n o] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g h i j k l m n o)) + (q [db query a b c d e f g h i j k l m n o p] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g h i j k l m n o p)) + (q [db query a b c d e f g h i j k l m n o p r] + (q* config :query (pr-str query) (asof-prefix instant-ms config) a b c d e f g h i j k l m n o p r)) (pull [db spec ident] (q* config :pull spec ident))) -(defn- hist-prefix [config] - (prefix (str "history:") config)) - -(deftype HistoricalDatahikeClient [config] - db/TemporalDatabase - (q [db query] - (q* config :history (pr-str query) (hist-prefix config))) - (q [db query a] - (q* config :history (pr-str query) (hist-prefix config) a)) - (q [db query a b] - (q* config :history (pr-str query) (hist-prefix config) a b)) - ;; TODO - ) - (deftype DatahikeCommandLineInterfaceClient [config] db/TemporalDatabase (as-of [_ instant-ms] @@ -106,7 +124,29 @@ (q* config :query (pr-str query) (prefix config) a b c d e)) (q [db query a b c d e f] (q* config :query (pr-str query) (prefix config) a b c d e f)) - ;; TODO + (q [db query a b c d e f g] + (q* config :query (pr-str query) (prefix config) a b c d e f g)) + (q [db query a b c d e f g h] + (q* config :query (pr-str query) (prefix config) a b c d e f g h)) + (q [db query a b c d e f g h i] + (q* config :query (pr-str query) (prefix config) a b c d e f g h i)) + (q [db query a b c d e f g h i j] + (q* config :query (pr-str query) (prefix config) a b c d e f g h i j)) + (q [db query a b c d e f g h i j k] + (q* config :query (pr-str query) (prefix config) a b c d e f g h i j k)) + (q [db query a b c d e f g h i j k l] + (q* config :query (pr-str query) (prefix config) a b c d e f g h i j k l)) + (q [db query a b c d e f g h i j k l m] + (q* config :query (pr-str query) (prefix config) a b c d e f g h i j k l m)) + (q [db query a b c d e f g h i j k l m n] + (q* config :query (pr-str query) (prefix config) a b c d e f g h i j k l m n)) + (q [db query a b c d e f g h i j k l m n o] + (q* config :query (pr-str query) (prefix config) a b c d e f g h i j k l m n o)) + (q [db query a b c d e f g h i j k l m n o p] + (q* config :query (pr-str query) (prefix config) a b c d e f g h i j k l m n o p)) + (q [db query a b c d e f g h i j k l m n o p r] + (q* config :query (pr-str query) (prefix config) a b c d e f g h i j k l m n o p r)) + (pull [db spec ident] (q* config :pull (prefix config) (pr-str spec) ident)) @@ -116,6 +156,45 @@ (transact [_ txs] (q* config :transact (prefix "conn:" config) (pr-str txs)))) +(deftype HistoricalDatahikeClient [config] + db/TemporalDatabase + (q [db query] + (q* config :history (pr-str query) (hist-prefix config))) + (q [db query a] + (q* config :history (pr-str query) (hist-prefix config) a)) + (q [db query a b] + (q* config :history (pr-str query) (hist-prefix config) a b)) + (q [db query a b c] + (q* config :history (pr-str query) (hist-prefix config) a b c)) + (q [db query a b c d] + (q* config :history (pr-str query) (hist-prefix config) a b c d)) + (q [db query a b c d e] + (q* config :history (pr-str query) (hist-prefix config) a b c d e)) + (q [db query a b c d e f] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f)) + (q [db query a b c d e f g] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g)) + (q [db query a b c d e f g h] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g h)) + (q [db query a b c d e f g h i] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g h i)) + (q [db query a b c d e f g h i j] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g h i j)) + (q [db query a b c d e f g h i j k] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g h i j k)) + (q [db query a b c d e f g h i j k l] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g h i j k l)) + (q [db query a b c d e f g h i j k l m] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g h i j k l m)) + (q [db query a b c d e f g h i j k l m n] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g h i j k l m n)) + (q [db query a b c d e f g h i j k l m n o] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g h i j k l m n o)) + (q [db query a b c d e f g h i j k l m n o p] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g h i j k l m n o p)) + (q [db query a b c d e f g h i j k l m n o p r] + (q* config :history (pr-str query) (hist-prefix config) a b c d e f g h i j k l m n o p r))) + (comment (require '[aero.core :as aero])