Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add expressions support to put-item and update-item. #73

Merged
merged 1 commit into from Nov 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
83 changes: 58 additions & 25 deletions src/taoensso/faraday.clj
Expand Up @@ -680,22 +680,37 @@
(ExpectedAttributeValue. (clj-val->db-val %))))
expected-map)))

(defn clj->db-expr-values-map [m]
(into {} (for [[k v] m]
[k (clj-val->db-val v)])))

(defn put-item-request
[table item & [{:keys [return expected return-cc?]
[table item & [{:keys [return expected return-cc? condition-expression expression-attr-names expression-attr-values]
:or {return :none}}]]
(doto-cond [g (PutItemRequest.)]
:always (.setTableName (name table))
:always (.setItem (clj-item->db-item item))
expected (.setExpected (expected-values g))
return (.setReturnValues (utils/enum g))
return-cc? (.setReturnConsumedCapacity (utils/enum :total))))
:always (.setTableName (name table))
:always (.setItem (clj-item->db-item item))
expected (.setExpected (expected-values g))
condition-expression (.setConditionExpression condition-expression)
expression-attr-names (.withExpressionAttributeNames expression-attr-names)
expression-attr-values (.withExpressionAttributeValues
(clj->db-expr-values-map expression-attr-values))
return (.setReturnValues (utils/enum g))
return-cc? (.setReturnConsumedCapacity (utils/enum :total))))

(defn put-item
"Adds an item (Clojure map) to a table with options:
:return - e/o #{:none :all-old}.
:expected - {<attr> <#{:exists :not-exists [comparison-operators <value>] <value>}> ...}."
[client-opts table item & [{:keys [return expected return-cc?]
:condition-expression - \"attribute_exists(attr_name) AND|OR ...\"
:expression-attr-names - {\"#attr_name\" \"name\"}
:expression-attr-values - {\":attr_value\" \"value\"}"
[client-opts table item & [{:keys [return expected return-cc? condition-expression]
:as opts}]]
(assert (not (and expected
condition-expression))
"Only one of :expected or :condition-expression should be provided.")
(when expected
(prn "WARNING - :expected is a legacy option and has been deprecated. Please use :condition-expression instead"))
(as-map
(.putItem (db-client client-opts)
(put-item-request table item opts))))
Expand All @@ -714,29 +729,47 @@
update-map)))

(defn update-item-request
[table prim-kvs update-map & [{:keys [return expected return-cc?]
[table prim-kvs update-map & [{:keys [return expected return-cc?
condition-expression
update-expression expression-attr-names expression-attr-values]
:or {return :none}}]]
(doto-cond [g (UpdateItemRequest.)]
:always (.setTableName (name table))
:always (.setKey (clj-item->db-item prim-kvs))
:always (.setAttributeUpdates (attribute-updates update-map))
expected (.setExpected (expected-values g))
return (.setReturnValues (utils/enum g))
return-cc? (.setReturnConsumedCapacity (utils/enum :total))))
:always (.setTableName (name table))
:always (.setKey (clj-item->db-item prim-kvs))
update-map (.setAttributeUpdates (attribute-updates update-map))
update-expression (.setUpdateExpression update-expression)
expression-attr-names (.withExpressionAttributeNames expression-attr-names)
expression-attr-values (.withExpressionAttributeValues
(clj->db-expr-values-map expression-attr-values))
expected (.setExpected (expected-values g))
condition-expression (.setConditionExpression condition-expression)
return (.setReturnValues (utils/enum g))
return-cc? (.setReturnConsumedCapacity (utils/enum :total))))

(defn update-item
"Updates an item in a table by its primary key with options:
prim-kvs - {<hash-key> <val>} or {<hash-key> <val> <range-key> <val>}.
update-map - {<attr> [<#{:put :add :delete}> <optional value>]}.
:return - e/o #{:none :all-old :updated-old :all-new :updated-new}.
:expected - {<attr> <#{:exists :not-exists [comparison-operators <value>] <value>}> ...}.

Where comparison-operators e/o #{:eq :le :lt :ge :gt :begins-with :between}."
[client-opts table prim-kvs update-map & [{:keys [return expected return-cc?]
:as opts}]]
(as-map
(.updateItem (db-client client-opts)
(update-item-request table prim-kvs update-map opts))))
:condition-expression - \"attribute_exists(attr_name) AND|OR ...\"
:update-expression - \"SET #attr_name = :attr_value\"
:expression-attr-names - {\"#attr_name\" \"name\"}
:expression-attr-values - {\":attr_value\" \"value\"}
:return - e/o #{:none :all-old :updated-old :all-new :updated-new}."
([client-opts table prim-kvs update-map & [{:keys [return expected return-cc?
condition-expression update-expression]
:as opts}]]
(assert (not (and expected
condition-expression))
"Only one of :expected or :condition-expression should be provided.")
(assert (not (and (not (empty? update-map))
update-expression))
"Only one of 'update-map' or :update-expression should be provided.")
(when expected
(prn "WARNING - :expected is a legacy option and has been deprecated. Please use :condition-expression instead"))
(when update-map
(prn "WARNING - update-map is a legacy option and has been deprecated. Please use :condition-expression instead"))
(as-map
(.updateItem (db-client client-opts)
(update-item-request table prim-kvs update-map opts)))))

(defn delete-item-request "Implementation detail."
[table prim-kvs & [{:keys [return expected return-cc?]
Expand Down
42 changes: 40 additions & 2 deletions test/taoensso/faraday/tests/main.clj
Expand Up @@ -97,8 +97,46 @@
(expect
#= (far/ex :conditional-check-failed)
(far/update-item *client-opts* ttable
{:id 10} {:name [:put "baz"]}
{:expected {:name "garbage"}})))
{:id 10} {:name [:put "baz"]}
{:expected {:name "garbage"}})))



;; Expressions support
(let [i {:id 10 :name "update me"}]
(after-setup!
#(far/delete-item *client-opts* ttable {:id 10}))

(expect ; Update expression support in update-item
{:id 10 :name "foo"}
(do
(far/update-item *client-opts* ttable
{:id 10}
{}
{:update-expression "SET #name = :name"
:expression-attr-names {"#name" "name"}
:expression-attr-values {":name" "foo"}
:return :all-new})))

(expect ; Condition expression support in update-item
#= (far/ex :conditional-check-failed)
(do
(far/update-item *client-opts* ttable
{:id 10}
{}
{:update-expression "SET #name = :name"
:expression-attr-names {"#name" "name"}
:expression-attr-values {":name" "foo"}
:condition-expression "#name <> :name"
:return :all-new})))

(expect ; Condition expression support in put-item
#= (far/ex :conditional-check-failed)
(do
(far/put-item *client-opts* ttable i
{:condition-expression "attribute_not_exists(id) AND #name <> :name"
:expression-attr-names {"#name" "name"}
:expression-attr-values {":name" "foo"}}))))

(let [items [{:id 11 :name "eleven" :test "batch"}
{:id 12 :name "twelve" :test "batch"}
Expand Down