forked from mcohen01/amazonica
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dynamodbv2.clj
127 lines (117 loc) · 4.71 KB
/
dynamodbv2.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
(ns amazonica.aws.dynamodbv2
"Amazon DyanmoDBV2 support - Local Secondary Indexes"
(:use [amazonica.core :only (accessors coerce-value intern-function marshall
register-coercions set-client set-fields IMarshall)]
[clojure.algo.generic.functor :only (fmap)])
(:import [com.amazonaws.services.dynamodbv2
AmazonDynamoDBAsyncClient
AmazonDynamoDBClient]
[com.amazonaws.services.dynamodbv2.model
AttributeValue
KeysAndAttributes
ProvisionedThroughput]
java.nio.ByteBuffer
java.lang.reflect.Method))
(defn- parse-number
[^String ns]
(if (.contains ns ".")
(Double/parseDouble ns)
(try
(Long/parseLong ns)
(catch NumberFormatException e
(Double/parseDouble ns)))))
(defn marshall-allow-empty-maps [obj]
(if (map? obj)
(if (empty? obj)
obj
(apply assoc {}
(interleave
(fmap #(if (string? %) (keyword %) %)
(apply vector (keys obj)))
(fmap marshall-allow-empty-maps
(apply vector (vals obj))))))
(marshall obj)))
(extend-protocol IMarshall
AttributeValue
(marshall [obj]
(let [[type val] (some #(when (not (nil? (val %))) %) (dissoc (bean obj) :class))]
(marshall-allow-empty-maps
(case type
(:s :b :BOOL) val
(:SS :BS) (into #{} val)
:n (parse-number val)
:NS (into #{} (map parse-number val))
:l (into [] (map marshall-allow-empty-maps val))
:m (into {} (map (fn [[k v]] [k (marshall-allow-empty-maps v)]) val))
:NULL nil)))))
(def ^:private byte-array-type (class (byte-array 0)))
(defn- first-key [value]
(key (first value)))
(defn- scalar-attribute-value? [value]
(and (map? value)
(= 1 (count value))
(contains? #{:s :b :BOOL :SS :BS :n :NS :l :m :NULL} (first-key value))))
(register-coercions
KeysAndAttributes
(fn [value]
(let [ka (KeysAndAttributes.)
_ (.setAttributesToGet ka (:attributes-to-get value))
_ (.setConsistentRead ka (:consistent-read value))
_ (.setExpressionAttributeNames ka (:expression-attribute-names value))
_ (.setProjectionExpression ka (:projection-expression value))
coercion (fn [attr-vals]
(fmap #(coerce-value
%
AttributeValue)
attr-vals))]
(.setKeys ka (map coercion (:keys value)))
ka))
AttributeValue
(fn [value]
(let [attr (AttributeValue.)]
(if (coll? value)
(cond
(scalar-attribute-value? value) (set-fields attr value)
(map? value) (.setM attr (into {} (map (fn [[k v]] [(name k) (coerce-value v AttributeValue)]) value)))
(vector? value) (.setL attr (map (fn [v] (coerce-value v AttributeValue)) value))
:else
(let [fvalue (first value)]
(when-not (every? #(instance? (class fvalue) %) value)
(throw (ex-info "Cannot provide heterogenous collection as value"
{:value value})))
(cond
(number? fvalue) (.setNS attr (fmap str value))
(string? fvalue) (.setSS attr value)
(instance? ByteBuffer fvalue) (.setBS attr value)
(instance? byte-array-type fvalue)
(.setBS attr (fmap #(ByteBuffer/wrap %) value))
:else
(throw (ex-info
(format "Values of type %s cannot be used with dynamodb"
(class fvalue))
{:value value})))))
(cond
(nil? value) (.setNULL attr true)
(number? value) (.setN attr (str value))
(string? value) (.setS attr value)
(instance? Boolean value) (.setBOOL attr value)
(instance? byte-array-type value) (.setB attr (ByteBuffer/wrap value))
(instance? ByteBuffer value) (.setB attr value)
:else (throw (ex-info (format "Value of type %s cannot be used with dynamodb"
(class value))
{:value value}))))
attr))
ProvisionedThroughput
(fn [value]
(let [pt (ProvisionedThroughput.)]
(if (map? value)
(set-fields pt value)
(if (sequential? value)
(do
(.setReadCapacityUnits pt (first value))
(.setWriteCapacityUnits pt (last value)))))
pt)))
(->> (.getMethods AmazonDynamoDBClient)
(filter #(= "setSignerRegionOverride" (.getName ^Method %)))
(intern-function AmazonDynamoDBClient *ns* :set-signer-region-override))
(set-client AmazonDynamoDBClient *ns*)