Skip to content

Commit

Permalink
EAV diff rows (#9)
Browse files Browse the repository at this point in the history
Implement cedric protocol with mem and csv implementation
  • Loading branch information
verberktstan authored Feb 12, 2024
1 parent d2958e9 commit 327ffd0
Show file tree
Hide file tree
Showing 11 changed files with 497 additions and 260 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,42 @@ Then you can use the Swark utility functions:

```(swark/key-by :id [{:id 1 :name "one"} {:id 2 :name "two"}])```

## Example

Let's say you want to store a user record, some credentials and check their credentials.
You can use swark.cedric for the persistence part, and swark.authom for the authentication part.

1. Let's create/connect to a database via the Csv implementation and store db props related to users.

```
(ns my.ns
(:require [swark.authom :as authom]
[swark.cedric :as cedric]))
(def DB (cedric/Csv. "db.csv"))
(def PROPS (merge authom/CEDRIC-PROPS {:primary-key :user/id}))
```

2. Create a new user record like so:

```
(def USER (-> DB (cedric/upsert-items PROPS [{:user/name "Readme User"}]) first))
```

3. Store credentials by upserting the user

```
(let [user (authom/map-with-meta-token USER :user/id "pass" "SECRET")]
(cedric/upsert-items DB PROPS [user]))
```

4. Retrieve the user and check their credentials

```
(let [user (-> DB (cedric/read-items {::cedric/primary-key #{:user/id}}) first)]
(-> user (authom/map-check-meta-token :user/id "pass" "SECRET") assert))
```

## Tests

Run the tests with `clojure -X:test/run`
Expand Down
5 changes: 3 additions & 2 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{:deps
;; Clojure standard library
{org.clojure/clojure {:mvn/version "1.11.0"}}
:aliases
{org.clojure/clojure {:mvn/version "1.11.0"}
org.clojure/data.csv {:mvn/version "1.0.1"}} ;; NOTE: For testing CSV input/output only..
:aliases
{:repl/reloaded
{:extra-deps {nrepl/nrepl {:mvn/version "1.0.0"}
cider/cider-nrepl {:mvn/version "0.28.7"}
Expand Down
73 changes: 24 additions & 49 deletions src/swark/authom.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,15 @@
(assert secret)
(->hash {::item item ::secret secret} pass)))

(comment
;; TODO: Turn fiddle code into tests
(hash 1)
(hash-unordered-coll 1)
(->hash {:user/id 123})
(->hash {:user/id 123} "password")
(->hash {:user/id 123} "password" "SECRET")
)
(defn- restore-meta-token*
[item token]
(vary-meta item assoc ::token token))

(defn with-meta-token
"Returns the item with the hashed token in it's metadata. `item` should implement IMeta, otherwise this simply returns nil."
[item & [pass secret :as args]]
(try
(vary-meta item assoc ::token (apply ->hash item args))
(restore-meta-token* item (str (apply ->hash item args)))
#?(:cljs (catch :default nil)
:clj (catch Throwable _ nil))))

Expand All @@ -44,13 +39,7 @@
(-> m (get primary-key) assert)
(merge (apply with-meta-token (select-keys m [primary-key]) args) m))

(comment
;; TODO: Turn fiddle code into tests
(-> {:user/id 123} with-meta-token meta)
(-> {:user/id 123} (with-meta-token "password") meta)
(-> {:user/id 123} (with-meta-token "password" "SECRET") meta)
)

;; Simply return the token from the Authom metadata
(def meta-token (comp ::token meta))

(defn check-meta-token
Expand All @@ -59,7 +48,7 @@
[item & [pass secret :as args]]
(let [token (meta-token item)]
(assert token)
(when (= token (apply ->hash item args))
(when (= token (str (apply ->hash item args)))
item)))

(defn map-check-meta-token
Expand All @@ -68,37 +57,23 @@
(-> m (get primary-key) assert)
(apply check-meta-token (select-keys m [primary-key]) args))

(comment
;; TODO: Turn fiddle code into tests
(let [user (with-meta-token {:user/id 123} "password" "SECRET")]
{:valid (check-meta-token user "password" "SECRET")
:invalid (check-meta-token user "wrong-password" "SECRET")})
(defn enrich-token
"Returns map with Authom's meta-token associated with ::token."
[map]
(-> map map? assert)
(let [token (meta-token map)]
(cond-> map
token (assoc ::token token))))

;; Example usage with SQL database via jdbc
(let [primary-key :user/id
user {:user/id 123 :user/name "User Name"}
user' (-> user
(select-keys [primary-key]) ; Generate meta token only with primary map-entry
(with-meta-token "pass")
(merge user))
get-rows (juxt :user/id meta-token :user/name) ; Retrieve id, token and name from user record
rows (get-rows (merge user' user)) ; NOTE: The metadata is preserved from user'
upsert-query (into ["REPLACE INTO users(id,token,name) values(?,?,?)"] rows)] ; Construct a SQL query to upsert user rows in the database.
(with-open [connection (-> {:dbname "test"} jdbc/get-datasource jdbc/get-connection)]
(jdbc/execute! connection upsert-query)))
(defn restore-enriched-token
"Returns map with the value in ::token restored as meta-token."
[{::keys [token] :as map}]
(-> map map? assert)
(cond-> map
token (restore-meta-token* token)
token (dissoc ::token)))

;; Example usage - update user in appdb
(let [primary-key :user/id
user {:user/id 123 :user/name "User Name"}
primary-val (get user primary-key)
user' (-> user
(select-keys [primary-key]) ; Generate meta token only with primary map-entry
(with-meta-token "pass")
(merge user))
db {}]
(-> db
(update-in [:users primary-val] (partial merge user')) ; Update user in db
:users
(get primary-val)
meta-token))
)
;; NOTE: Default props to make swark.cedric serialize and parse Authom tokens automatically
(def CEDRIC-PROPS
{:pre-upsert-serializer enrich-token
:post-merge-parser restore-enriched-token})
Loading

0 comments on commit 327ffd0

Please sign in to comment.