diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7e3b6cf..c8f2ffe 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,7 @@ * Moved Sync-over-Async versions of `TableContext` operations into `namespace FSharp.AWS.DynamoDB.Scripting` * Added `?collector` parameter to each operation on `TableContext` to enable separated collection for concurrent requests * Ensured metrics are reported even for failed requests +* Added `TryGetItemAsync` (same as `GetItemAsync`, but returns `None`, instead of throwing, if an item is not present) ### 0.9.3-beta * Added `RequestMetrics` record type diff --git a/src/FSharp.AWS.DynamoDB/TableContext.fs b/src/FSharp.AWS.DynamoDB/TableContext.fs index 8e6b66c..a7ebaac 100644 --- a/src/FSharp.AWS.DynamoDB/TableContext.fs +++ b/src/FSharp.AWS.DynamoDB/TableContext.fs @@ -62,7 +62,7 @@ type TableContext<'TRecord> internal | Some sink -> ReturnConsumedCapacity.INDEXES, Some (reportMetrics sink) | None -> ReturnConsumedCapacity.NONE, None - let getItemAsync (key : TableKey) (proj : ProjectionExpr.ProjectionExpr option) collector = async { + let tryGetItemAsync (key : TableKey) (proj : ProjectionExpr.ProjectionExpr option) collector = async { let kav = template.ToAttributeValues(key) let request = GetItemRequest(tableName, kav) match proj with @@ -80,11 +80,14 @@ type TableContext<'TRecord> internal if response.HttpStatusCode <> HttpStatusCode.OK then failwithf "GetItem request returned error %O" response.HttpStatusCode - if not response.IsItemSet then - let msg = sprintf "could not find item %O" key - raise <| ResourceNotFoundException(msg) + if response.IsItemSet then return Some response.Item + else return None + } - return response + let getItemAsync (key : TableKey) (proj : ProjectionExpr.ProjectionExpr option) collector = async { + match! tryGetItemAsync key proj collector with + | Some item -> return item + | None -> return raise <| ResourceNotFoundException(sprintf "could not find item %O" key) } let batchGetItemsAsync (keys : seq) (consistentRead : bool option) @@ -421,6 +424,17 @@ type TableContext<'TRecord> internal } + /// + /// Asynchronously attempts to fetch item with given key from table. Returns None if no item with that key is present. + /// + /// Key of item to be fetched. + /// Function to receive request metrics. + member __.TryGetItemAsync(key : TableKey, ?collector) : Async<'TRecord option> = async { + let! response = tryGetItemAsync key None collector + return response |> Option.map template.OfAttributeValues + } + + /// /// Asynchronously checks whether item of supplied key exists in table. /// @@ -446,8 +460,8 @@ type TableContext<'TRecord> internal /// Key of item to be fetched. /// Function to receive request metrics. member __.GetItemAsync(key : TableKey, ?collector) : Async<'TRecord> = async { - let! response = getItemAsync key None collector - return template.OfAttributeValues response.Item + let! item = getItemAsync key None collector + return template.OfAttributeValues item } @@ -460,8 +474,8 @@ type TableContext<'TRecord> internal /// Projection expression to be applied to item. /// Function to receive request metrics. member __.GetItemProjectedAsync(key : TableKey, projection : ProjectionExpression<'TRecord, 'TProjection>, ?collector) : Async<'TProjection> = async { - let! response = getItemAsync key (Some projection.ProjectionExpr) collector - return projection.UnPickle response.Item + let! item = getItemAsync key (Some projection.ProjectionExpr) collector + return projection.UnPickle item } /// diff --git a/tests/FSharp.AWS.DynamoDB.Tests/SimpleTableOperationTests.fs b/tests/FSharp.AWS.DynamoDB.Tests/SimpleTableOperationTests.fs index fba315e..9e9df08 100644 --- a/tests/FSharp.AWS.DynamoDB.Tests/SimpleTableOperationTests.fs +++ b/tests/FSharp.AWS.DynamoDB.Tests/SimpleTableOperationTests.fs @@ -78,6 +78,19 @@ type ``Simple Table Operation Tests`` (fixture : TableFixture) = let _ = table.DeleteItem key Expect.equal (table.ContainsKey key) false "ContainsKey should be false" + member this.``TryGet Operation`` () = + let value = mkItem() + let computedKey = table.Template.ExtractKey value + let get k = table.TryGetItemAsync k |> Async.RunSynchronously + let initialLoad = get computedKey + Expect.equal initialLoad None "TryGetItemAsync should yield None when not present" + let key = table.PutItem value + Expect.equal computedKey key "Local key computation should be same as Put result" + let loaded = get computedKey + Expect.equal (Some value) loaded "TryGetItemAsync should yield roundtripped value" + let _ = table.DeleteItem key + Expect.equal (get key |> Option.isSome) false "TryGetItemAsync should yield None" + member this.``Batch Put Operation`` () = let values = set [ for i in 1L .. 20L -> mkItem() ] let unprocessed = table.BatchPutItems values