@@ -183,7 +183,7 @@ module internal Provisioning =
183
183
run ( client, tableName, template) None
184
184
185
185
/// Represents the operation performed on the table, for metrics collection purposes
186
- type Operation = GetItem | PutItem | UpdateItem | DeleteItem | BatchGetItems | BatchWriteItems | Scan | Query
186
+ type Operation = GetItem | PutItem | UpdateItem | DeleteItem | BatchGetItems | BatchWriteItems | TransactWriteItems | Scan | Query
187
187
188
188
/// Represents metrics returned by the table operation, for plugging in to an observability framework
189
189
type RequestMetrics =
@@ -209,6 +209,57 @@ type private LimitType = All | Default | Count of int
209
209
static member AllOrCount ( l : int option ) = l |> Option.map Count |> Option.defaultValue All
210
210
static member DefaultOrCount ( l : int option ) = l |> Option.map Count |> Option.defaultValue Default
211
211
212
+ /// <summary>Represents an individual request that can be included in the <c>TransactItems</c> of a <c>TransactWriteItems</c> call.</summary>
213
+ [<RequireQualifiedAccess>]
214
+ type TransactWrite < 'TRecord > =
215
+ /// Specify a Check to be run on a specified item.
216
+ /// If the condition does not hold, the overall TransactWriteItems request will be Canceled.
217
+ | Check of key : TableKey * condition : ConditionExpression<'TRecord>
218
+ /// Specify a PutItem operation to be performed, inserting or replacing an item in the Table.
219
+ /// If the (optional) precondition does not hold, the overall TransactWriteItems request will be Canceled.
220
+ | Put of item : 'TRecord * precondition : ConditionExpression<'TRecord> option
221
+ /// Specify an UpdateItem operation to be performed, applying an updater expression on the item identified by the specified `key`, if it exists.
222
+ /// If the item exists and the (optional) precondition does not hold, the overall TransactWriteItems request will be Canceled.
223
+ | Update of key : TableKey * precondition : ConditionExpression<'TRecord> option * updater : UpdateExpression<'TRecord>
224
+ /// Specify a DeleteItem operation to be performed, removing the item identified by the specified `key` if it exists.
225
+ /// If the item exists and the (optional) precondition does not hold, the overall TransactWriteItems request will be Canceled.
226
+ | Delete of key : TableKey * precondition : ConditionExpression<'TRecord> option
227
+
228
+ /// Helpers for building a <c>TransactWriteItemsRequest</c> to supply to <c>TransactWriteItems</c>
229
+ module TransactWriteItemsRequest =
230
+
231
+ let private toTransactWriteItem < 'TRecord > tableName ( template : RecordTemplate < 'TRecord >) : TransactWrite < 'TRecord > -> TransactWriteItem = function
232
+ | TransactWrite.Check ( key, cond) ->
233
+ let req = ConditionCheck( TableName = tableName, Key = template.ToAttributeValues key)
234
+ let writer = AttributeWriter( req.ExpressionAttributeNames, req.ExpressionAttributeValues)
235
+ req.ConditionExpression <- cond.Conditional.Write writer
236
+ TransactWriteItem( ConditionCheck = req)
237
+ | TransactWrite.Put ( item, maybeCond) ->
238
+ let req = Put( TableName = tableName, Item = template.ToAttributeValues item)
239
+ maybeCond |> Option.iter ( fun cond ->
240
+ let writer = AttributeWriter( req.ExpressionAttributeNames, req.ExpressionAttributeValues)
241
+ req.ConditionExpression <- cond.Conditional.Write writer)
242
+ TransactWriteItem( Put = req)
243
+ | TransactWrite.Update ( key, maybeCond, updater) ->
244
+ let req = Update( TableName = tableName, Key = template.ToAttributeValues key)
245
+ let writer = AttributeWriter( req.ExpressionAttributeNames, req.ExpressionAttributeValues)
246
+ req.UpdateExpression <- updater.UpdateOps.Write( writer)
247
+ maybeCond |> Option.iter ( fun cond -> req.ConditionExpression <- cond.Conditional.Write writer)
248
+ TransactWriteItem( Update = req)
249
+ | TransactWrite.Delete ( key, maybeCond) ->
250
+ let req = Delete( TableName = tableName, Key = template.ToAttributeValues key)
251
+ maybeCond |> Option.iter ( fun cond ->
252
+ let writer = AttributeWriter( req.ExpressionAttributeNames, req.ExpressionAttributeValues)
253
+ req.ConditionExpression <- cond.Conditional.Write writer)
254
+ TransactWriteItem( Delete = req)
255
+ let internal toTransactItems < 'TRecord > tableName template items = Seq.map ( toTransactWriteItem< 'TRecord> tableName template) items |> rlist
256
+
257
+ /// <summary>Exception filter to identify whether a <c>TransactWriteItems</c> call has failed due to
258
+ /// one or more of the supplied <c>precondition</c> checks failing.</summary>
259
+ let (| TransactionCanceledConditionalCheckFailed | _ |) : exn -> unit option = function
260
+ | :? TransactionCanceledException as e when e.CancellationReasons.Exists( fun x -> x.Code = " ConditionalCheckFailed" ) -> Some ()
261
+ | _ -> None
262
+
212
263
/// DynamoDB client object for performing table operations in the context of given F# record representations
213
264
[<Sealed; AutoSerializable( false ) >]
214
265
type TableContext < 'TRecord > internal
@@ -744,6 +795,26 @@ type TableContext<'TRecord> internal
744
795
}
745
796
746
797
798
+ /// <summary>
799
+ /// Atomically applies a set of 1-25 write operations to the table.<br/>
800
+ /// NOTE requests are charged at twice the normal rate in Write Capacity Units.
801
+ /// See the DynamoDB <a href="https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html"><c>TransactWriteItems</c> API documentation</a> for full details of semantics and charges.<br/>
802
+ /// </summary>
803
+ /// <param name="items">Operations to be performed.<br/>
804
+ /// Use <c>TransactWriteItemsRequest.TransactionCanceledConditionalCheckFailed</c> to identify any Precondition Check failures.</param>
805
+ /// <param name="clientRequestToken">The <c>ClientRequestToken</c> to supply as an idempotency key (10 minute window).</param>
806
+ member _.TransactWriteItems ( items : seq < TransactWrite < 'TRecord >>, ? clientRequestToken ) : Async < unit > = async {
807
+ let reqs = TransactWriteItemsRequest.toTransactItems tableName template items
808
+ let req = TransactWriteItemsRequest( ReturnConsumedCapacity = returnConsumedCapacity, TransactItems = reqs)
809
+ clientRequestToken |> Option.iter ( fun x -> req.ClientRequestToken <- x)
810
+ let! ct = Async.CancellationToken
811
+ let! response = client.TransactWriteItemsAsync( req, ct) |> Async.AwaitTaskCorrect
812
+ maybeReport |> Option.iter ( fun r -> r TransactWriteItems ( Seq.toList response.ConsumedCapacity) reqs.Count)
813
+ if response.HttpStatusCode <> HttpStatusCode.OK then
814
+ failwithf " TransactWriteItems request returned error %O " response.HttpStatusCode
815
+ }
816
+
817
+
747
818
/// <summary>
748
819
/// Asynchronously queries table with given condition expressions.
749
820
/// </summary>
0 commit comments