From ba55ebdc144a55918f2995a2ae4ee7cb49083573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Hol=C3=BD?= Date: Mon, 21 Aug 2023 20:47:17 +0200 Subject: [PATCH] Add support for do/raw-txn (#22) * Add support for do/raw-txn Enable users of the save and delete middlewares to add to the generated transactions by adding a list of transaction data under the key do/raw-txn into Pathom env. Also switched to the latest com.datomic/local and got rid of thus unnecessary `dev-local-tu` (Updating to com.datomic/peer is blocked by datomock) --- Makefile | 2 +- README.adoc | 10 ++++--- deps.edn | 7 ++--- .../rad/database_adapters/datomic.clj | 5 ++-- .../rad/database_adapters/datomic_cloud.clj | 5 ++-- .../rad/database_adapters/datomic_common.clj | 12 +++++++-- .../database_adapters/datomic_options.cljc | 21 ++++++++++++++- .../database_adapters/datomic_cloud_spec.clj | 27 +++++++++++-------- .../indexed_access_cloud_spec.clj | 25 ++++++++++------- 9 files changed, 79 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index 88d608a..097ac82 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ tests: - @echo "This will fail if you have not installed Cloud dev-local and on-prem creds in your :mvn/repos and maven settings.xml manually. See Datomic Documentation." + @echo "This will fail if you have not installed on-prem creds in your :mvn/repos and maven settings.xml manually. See Datomic Documentation." clojure -A:test -J-Dguardrails.config=guardrails-test.edn -J-Dguardrails.enabled dev: diff --git a/README.adoc b/README.adoc index f84489a..3666e13 100644 --- a/README.adoc +++ b/README.adoc @@ -4,7 +4,9 @@ image:https://img.shields.io/clojars/v/com.fulcrologic/fulcro-rad-datomic.svg[li This is a plugin for Fulcro RAD that adds support for using Datomic databases as the back-end technology. -NOTE: The current version supports on-prem Datomic Pro (PostgreSQL, MySQL, or Microsoft SQL Server backends), Datomic Free, or Mem stores, as well as Datomic Cloud. Make sure +NOTE: The current version supports on-prem Datomic Pro (PostgreSQL, MySQL, or Microsoft SQL Server backends), Datomic Cloud, and Datomic-Local, as well as the nowadays deprecated Datomic Free. + +Make sure you do not accidentally get Datomic Free and Datomic Pro BOTH on the CLASSPATH. If you're using deps you should make sure you use something like: @@ -115,7 +117,7 @@ TODO: Write the validator, and document it. == Databases It is up to you to configure, create, migrate, and manage your database infrastructure; however, -this plugin comes with various ustilities that can help you set up and manage the runtime +this plugin comes with various utilities that can help you set up and manage the runtime environment. You *must* follow certain conventions for things to work at all, and *may choose* to opt into various features that make it easy to get started and evolve your system. @@ -149,7 +151,7 @@ in the context of the current request. `::datomic/databases` - A map, keyed by schema, of the most recent database value that should be used in the context of the current request (for consistent reads across multiple resolvers). -TODO: Supply helper funtions that can help with this +TODO: Supply helper functions that can help with this == Indexed Access (Version 1.3.1+) @@ -204,7 +206,7 @@ A typical test might look like the following: NOTE: The connection is memoized based on the schema key (not any supplied migration data). You can use `(datomic/reset-test-schema k)` to forget the current memoized version. -For Datomic Cloud, the current recommendation is to use the https://github.com/ComputeSoftware/dev-local-tu[dev-local-tu] library. +For Datomic Cloud, the current recommendation is to use the `com.datomic/local` with the `:mem` storage. See the https://github.com/fulcrologic/fulcro-rad-demo[fulcrologic/fulcro-rad-demo] for an example. == Resolver Generation diff --git a/deps.edn b/deps.edn index d770fee..edfad88 100644 --- a/deps.edn +++ b/deps.edn @@ -15,9 +15,10 @@ com.wsscode/pathom3 {:mvn/version "2022.04.20-alpha"} org.clojure/test.check {:mvn/version "1.1.1"} lambdaisland/kaocha {:mvn/version "1.69.1069"} - dev-local-tu/dev-local-tu {:mvn/version "0.2.2"} - com.datomic/dev-local {:mvn/version "1.0.243"} - com.datomic/client-cloud {:mvn/version "1.0.120"} + com.datomic/local {:mvn/version "1.0.267"} + com.datomic/client-cloud {:mvn/version "1.0.123"} + ;; com.datomic/peer {:mvn/version "1.0.6735" ; TODO use when datamock > 0.2.2 is out + ;; :exclusions [org.slf4j/slf4j-nop]} com.datomic/datomic-pro {:mvn/version "1.0.6165" :exclusions [org.slf4j/slf4j-nop]}}} diff --git a/src/main/com/fulcrologic/rad/database_adapters/datomic.clj b/src/main/com/fulcrologic/rad/database_adapters/datomic.clj index c96c991..f086746 100644 --- a/src/main/com/fulcrologic/rad/database_adapters/datomic.clj +++ b/src/main/com/fulcrologic/rad/database_adapters/datomic.clj @@ -85,12 +85,13 @@ "Delete the given entity, if possible." [{:datomic/keys [transact] ::attr/keys [key->attribute] :as env} params] - (enc/if-let [pk (ffirst params) + (enc/if-let [raw-txn (or (do/raw-txn env) []) + pk (ffirst params) id (get params pk) ident [pk id] {:keys [::attr/schema]} (key->attribute pk) connection (-> env do/connections (get schema)) - txn [[:db/retractEntity ident]]] + txn (into [[:db/retractEntity ident]] raw-txn)] (do (log/info "Deleting" ident) (let [database-atom (get-in env [do/databases schema]) diff --git a/src/main/com/fulcrologic/rad/database_adapters/datomic_cloud.clj b/src/main/com/fulcrologic/rad/database_adapters/datomic_cloud.clj index 58ddac5..d632be4 100644 --- a/src/main/com/fulcrologic/rad/database_adapters/datomic_cloud.clj +++ b/src/main/com/fulcrologic/rad/database_adapters/datomic_cloud.clj @@ -110,12 +110,13 @@ to transact datoms." [{:datomic/keys [transact] ::attr/keys [key->attribute] :as env} params] - (enc/if-let [pk (ffirst params) + (enc/if-let [raw-txn (or (do/raw-txn env) []) + pk (ffirst params) id (get params pk) ident [pk id] {:keys [::attr/schema]} (key->attribute pk) connection (-> env do/connections (get schema)) - txn [[:db/retractEntity ident]]] + txn (into [[:db/retractEntity ident]] raw-txn)] (do (log/info "Deleting" ident) (let [database-atom (get-in env [do/databases schema]) diff --git a/src/main/com/fulcrologic/rad/database_adapters/datomic_common.clj b/src/main/com/fulcrologic/rad/database_adapters/datomic_common.clj index 7135aa6..0b111ed 100644 --- a/src/main/com/fulcrologic/rad/database_adapters/datomic_common.clj +++ b/src/main/com/fulcrologic/rad/database_adapters/datomic_common.clj @@ -252,7 +252,8 @@ (>defn delta->txn [{::attr/keys [key->attribute] :as env} target-schema delta] [map? keyword? map? => map?] - (let [tempid->txid (tempid->intermediate-id env delta) + (let [raw-txn (do/raw-txn env) + tempid->txid (tempid->intermediate-id env delta) tempid->generated-id (tempids->generated-ids env delta) non-native-id-attributes-txn (keep (fn [[k id :as ident]] @@ -268,7 +269,8 @@ (concat non-native-id-attributes-txn (to-one-txn env target-schema delta) - (to-many-txn env target-schema delta)))})) + (to-many-txn env target-schema delta) + raw-txn))})) (defn- attribute-schema [attributes] (mapv @@ -654,3 +656,9 @@ :components [qualified-key rad-id-value]}) first :e)))) + +(defn append-to-raw-txn + "Append raw transaction forms `txn` to `do/raw-txn` in the middleware environment. + To be used by middlewares wrapping the default delete/save middleware." + [mw-env txn] + (update mw-env do/raw-txn (fnil into []) txn)) diff --git a/src/main/com/fulcrologic/rad/database_adapters/datomic_options.cljc b/src/main/com/fulcrologic/rad/database_adapters/datomic_options.cljc index 1e2258c..4b58a75 100644 --- a/src/main/com/fulcrologic/rad/database_adapters/datomic_options.cljc +++ b/src/main/com/fulcrologic/rad/database_adapters/datomic_options.cljc @@ -20,7 +20,7 @@ :com.fulcrologic.rad.database-adapters.datomic/connections) (def databases - "If using the datomic pathom-plugin, the resulting pathm-env will contain + "If using the datomic pathom-plugin, the resulting pathom-env will contain a map from schema->database at this key path" :com.fulcrologic.rad.database-adapters.datomic/databases) @@ -30,3 +30,22 @@ This only affects Datomic autogenerated resolvers." :com.fulcrologic.rad.database-adapters.datomic/wrap-resolve) + +(def raw-txn + "Middleware enviroment key. A sequence of Datomic transaction data in the list and/or map form, which will be added to the + transaction data produced by the currently active (save or delete) middleware. + + You can use this to ensure pre- and post-condition, enrich the transactions, etc. + See also `datomic-common/append-to-raw-txn`. + + Ex.: + ```clj + (let [user ..., order ..., ident ...] + (cond-> middleware-env + (first-order-campaign? order) + (datomic-common/append-to-raw-txn + [(list 'my.txn.fns/ensure-no-prior-order user) ; check pre-condition + {:db/id \"datomic.tx\" :tx/user (:email user)} ; enrich transaction info + {:db/id (datomic-common/failsafe-id env ident) :db/ensure :order/valid-new-order}]))) ; check post-condition + ```" + :com.fulcrologic.rad.database-adapters.datomic/raw-txn) diff --git a/src/test/com/fulcrologic/rad/database_adapters/datomic_cloud_spec.clj b/src/test/com/fulcrologic/rad/database_adapters/datomic_cloud_spec.clj index a039069..129426e 100644 --- a/src/test/com/fulcrologic/rad/database_adapters/datomic_cloud_spec.clj +++ b/src/test/com/fulcrologic/rad/database_adapters/datomic_cloud_spec.clj @@ -13,7 +13,6 @@ [com.fulcrologic.rad.test-schema.person :as person] [com.fulcrologic.rad.test-schema.thing :as thing] [datomic.client.api :as d] - [dev-local-tu.core :as dev-local-tu] [fulcro-spec.core :refer [specification assertions component behavior when-mocking]] [taoensso.timbre :as log])) @@ -35,16 +34,22 @@ (def ^:dynamic *env* {}) (defn with-env [tests] - (with-open [db-env (dev-local-tu/test-env)] - (let [config {:datomic/env :test - :datomic/schema :production - :datomic/database (str (gensym "test-database")) - :datomic/test-client (:client db-env)} - conn (datomic/start-database! all-attributes config {})] - (binding [*conn* conn - *env* {::attr/key->attribute key->attribute - do/connections {:production conn}}] - (tests))))) + (let [dbname (str (gensym "test-database")) + client (d/client {:server-type :datomic-local + :system (str (ids/new-uuid)) + :storage-dir :mem})] + (try + (let [config {:datomic/env :test + :datomic/schema :production + :datomic/database dbname + :datomic/test-client client} + conn (datomic/start-database! all-attributes config {})] + (binding [*conn* conn + *env* {::attr/key->attribute key->attribute + do/connections {:production conn}}] + (tests))) + (finally + (d/delete-database client {:db-name dbname}))))) (use-fixtures :each with-env) diff --git a/src/test/com/fulcrologic/rad/database_adapters/indexed_access_cloud_spec.clj b/src/test/com/fulcrologic/rad/database_adapters/indexed_access_cloud_spec.clj index 5b42b2c..f26da59 100644 --- a/src/test/com/fulcrologic/rad/database_adapters/indexed_access_cloud_spec.clj +++ b/src/test/com/fulcrologic/rad/database_adapters/indexed_access_cloud_spec.clj @@ -4,8 +4,9 @@ [com.fulcrologic.rad.database-adapters.datomic-cloud :as datomic] [com.fulcrologic.rad.database-adapters.datomic-options :as do] [com.fulcrologic.rad.database-adapters.indexed-access-checks :as checks :refer [run-checks]] + [com.fulcrologic.rad.ids :as ids] [com.fulcrologic.rad.type-support.date-time :as dt] - [dev-local-tu.core :as dev-local-tu] + [datomic.client.api :as d] [fulcro-spec.core :refer [specification]])) (use-fixtures :once @@ -24,12 +25,18 @@ (run-checks (assoc datomic/datomic-api :generate-resolvers datomic/generate-resolvers - :make-connection (fn [& args] - (with-open [db-env (dev-local-tu/test-env)] - (let [config {:datomic/env :test - :datomic/schema :main - :datomic/database (str (gensym "test-database")) - :datomic/test-client (:client db-env)} - conn (datomic/start-database! schema-attributes config {})] - conn)))))) + :make-connection (fn [& _args] + (let [dbname (str (gensym "test-database")) + client (d/client {:server-type :datomic-local + :system (str (ids/new-uuid)) + :storage-dir :mem})] + (try + (let [config {:datomic/env :test + :datomic/schema :main + :datomic/database dbname + :datomic/test-client client} + conn (datomic/start-database! schema-attributes config {})] + conn) + (finally + (d/delete-database client {:db-name dbname}))))))))