-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into craig-latacora-patch-1
- Loading branch information
Showing
2 changed files
with
150 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)})) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"}))))) |