Skip to content

Commit

Permalink
Merge branch 'main' into craig-latacora-patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
craig-latacora authored May 17, 2024
2 parents 4ea9806 + d307ba1 commit fbfca6c
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 0 deletions.
98 changes: 98 additions & 0 deletions src/com/latacora/backsaws/dynamodb.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
(ns com.latacora.backsaws.dynamodb
(:require [clojure.data.json :as json])
(:import java.util.Base64))

(defmulti decode
"Given a DynamoDB map encoded data structure, returns a simplified clojure data structure."
(fn [encoded]
(-> encoded first key keyword)))

(defmethod decode :BOOL [v]
(:BOOL v))

(defmethod decode :S [v]
(:S v))

(defmethod decode :SS [v]
(set (:SS v)))

(defmethod decode :N [v]
(json/read-str (:N v)))

(defmethod decode :NS [v]
(set (map json/read-str (:NS v))))

(defmethod decode :M [v]
(reduce-kv (fn [m k v]
(assoc m k (decode v)))
{}
(:M v)))

(defmethod decode :L [v]
(mapv decode (:L v)))

(defmethod decode :NULL [v]
nil)

(defmethod decode :B [v]
(.decode (Base64/getDecoder)
(:B v)))

(defn encode-bytes [^bytes input]
(new String (.encode (Base64/getEncoder) input) "utf-8"))

(defprotocol IDynamoDBEncode
(encode [this]))

(extend-protocol IDynamoDBEncode
(Class/forName "[B")
(encode [this]
{:B (encode-bytes this)})

Number
(encode [this] {:N (json/json-str this)})

String
(encode [this] {:S this})

Boolean
(encode [this] {:BOOL (if this true false)})

clojure.lang.IPersistentVector
(encode [this]
{:L (mapv encode this)})

clojure.lang.IPersistentList
(encode [this]
{:L (mapv encode this)})

clojure.lang.IPersistentSet
(encode [this]
(cond
(every? number? this)
{:NS (mapv json/json-str this)}

(every? string? this)
{:SS (mapv identity this)}

(every? bytes? this)
{:BS (mapv encode-bytes this)}

:else
(throw
(ex-info
"Cannot encode set, is invalid."))))

clojure.lang.IPersistentMap
(encode [this]
{:M (reduce-kv (fn [m k v]
(assoc m k (encode v)))
{}
this)}))

(comment
(decode {:M {:foo {:S "asd"} :bar {:N "333"} :baz {:L [{:S "asd"}]}}})

(decode {:N "2"})

(decode {:M (:Item result)}))
52 changes: 52 additions & 0 deletions test/com/latacora/backsaws/dynamodb_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
(ns com.latacora.backsaws.dynamodb-test
(:require [com.latacora.backsaws.dynamodb :as ddb]
[clojure.test :as t]))


(def test-val
{:string "asd"
:number 333
:stringset #{"this" "is" "a" "string" "set"}
:list [1 "string" 3]
:numberset #{1 2 3 4}
:bytes (.getBytes "foo")
:bool-true true
:bool-false false})

;; hand encoded to spec
(def encoded-val
{:M {:string {:S "asd"}
:number {:N "333"}
:stringset {:SS ["this" "is" "a" "string" "set"]}
:list {:L [{:N "1"} {:S "string"} {:N "3"}]}
:numberset {:NS ["1" "2" "3" "4"]}
:bytes {:B "Zm9v"}
:bool-true {:BOOL true}
:bool-false {:BOOL false}}})

(t/deftest test-decode
(t/is (= #{1 2 3} (ddb/decode {:NS ["1" "2" "3"]})))
(t/is (= "foo" (ddb/decode {:S "foo"})))
(t/is (= 3 (ddb/decode {:N "3"})))
(t/is (= 3.33 (ddb/decode {:N "3.33"})))
(t/is (= #{"foo" "bar"} (ddb/decode {:SS ["foo" "bar"]})))
(let [eb {:B "Zm9v"}]
(t/is (= eb (ddb/encode (ddb/decode eb)))))
(t/is
(= (dissoc test-val :bytes)
(dissoc (ddb/decode encoded-val) :bytes))
"Encode and decode are identical, except bytes, for which equality doesn't work"))

(t/deftest test-encode
(t/testing "booleans"
(t/is (= {:BOOL true} (ddb/encode true)))
(t/is (= {:BOOL false} (ddb/encode false))))
(t/testing "lists"
(t/is (= {:L [{:N "1"} {:N "2"} {:N "3"}]} (ddb/encode [1 2 3])))
(t/is (= {:L [{:N "1"} {:S "string"} {:N "3"}]} (ddb/encode [1 "string" 3]))))
(t/testing "map"
(t/is (= {:M {:bar {:S "baz"} :foo {:N "2"} :nested {:M {:another {:S "value"}}}}}
(ddb/encode {:bar "baz" :foo 2 :nested {:another "value"}}))))
(t/testing "sets"
(t/is (= {:NS ["1"]} (ddb/encode #{1})))
(t/is (= {:SS ["foo"]} (ddb/encode #{"foo"})))))

0 comments on commit fbfca6c

Please sign in to comment.