Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions generator/.DevConfigs/8e546afe-27ad-4b11-8400-e0f3c33f0a4a.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"services": [
{
"serviceName": "DynamoDBv2",
"type": "patch",
"changeLogMessages": [
"Fixed issue with TransactWrite in the DataModel where it wasn't correctly handling cases where only keys were being saved."
]
}
]
}
47 changes: 42 additions & 5 deletions sdk/src/Services/DynamoDBv2/Custom/DataModel/TransactWrite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
#if AWS_ASYNC_API
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -134,11 +135,8 @@ public void AddSaveItem(T item)
Expression conditionExpression = CreateConditionExpressionForVersion(storage);
SetNewVersion(storage);

DocumentTransaction.AddDocumentToUpdate(storage.Document, new TransactWriteItemOperationConfig
{
ConditionalExpression = conditionExpression,
ReturnValuesOnConditionCheckFailure = DocumentModel.ReturnValuesOnConditionCheckFailure.None
});
AddDocumentTransaction(storage, conditionExpression);

var objectItem = new DynamoDBContext.ObjectWithItemStorage
{
OriginalObject = item,
Expand Down Expand Up @@ -437,6 +435,45 @@ private Expression CreateConditionExpressionForVersion(ItemStorage storage)
DocumentTransaction.TargetTable.IsEmptyStringValueEnabled);
return DynamoDBContext.CreateConditionExpressionForVersion(storage, conversionConfig);
}


private void AddDocumentTransaction(ItemStorage storage, Expression conditionExpression)
{
var hashKeyPropertyNames = storage.Config.HashKeyPropertyNames;
var rangeKeyPropertyNames = storage.Config.RangeKeyPropertyNames;

var attributeNames = storage.Document.Keys.ToList();

foreach (var keyPropertyName in hashKeyPropertyNames)
{
attributeNames.Remove(keyPropertyName);
}

foreach (var rangeKeyPropertyName in rangeKeyPropertyNames)
{
attributeNames.Remove(rangeKeyPropertyName);
}

// If there are no attributes left, we need to use PutItem
// as UpdateItem requires at least one data attribute
if (attributeNames.Any())
{
DocumentTransaction.AddDocumentToUpdate(storage.Document, new TransactWriteItemOperationConfig
{
ConditionalExpression = conditionExpression,
ReturnValuesOnConditionCheckFailure = DocumentModel.ReturnValuesOnConditionCheckFailure.None
});
}
else
{

DocumentTransaction.AddDocumentToPut(storage.Document, new TransactWriteItemOperationConfig
{
ConditionalExpression = conditionExpression,
ReturnValuesOnConditionCheckFailure = DocumentModel.ReturnValuesOnConditionCheckFailure.None
});
}
}

private void SetNewVersion(ItemStorage storage)
{
Expand Down
98 changes: 98 additions & 0 deletions sdk/test/Services/DynamoDBv2/IntegrationTests/DataModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,76 @@ public void TestContext_DisableFetchingTableMetadata_KeyWithPropertyConverter()
Assert.AreEqual(employee.Name, storedEmployee.Name);
}


/// <summary>
/// Tests that disabling fetching table metadata works with a key that has a property converter.
/// </summary>
[TestMethod]
[TestCategory("DynamoDBv2")]
public void TestTransactWrite_AddSaveItem_DocumentTransaction()
{
TableCache.Clear();
CleanupTables();
TableCache.Clear();

CreateContext(DynamoDBEntryConversion.V2, true, true);

{

var hashRangeOnly = new AnnotatedRangeTable
{
Name = "Bob",
Age = 10
};

var transactWrite = Context.CreateTransactWrite<AnnotatedRangeTable>();
transactWrite.AddSaveItem(hashRangeOnly);
transactWrite.Execute();

var storedHashOnly = Context.Load<AnnotatedRangeTable>(hashRangeOnly.Name, hashRangeOnly.Age);
Assert.IsNotNull(storedHashOnly);
Assert.AreEqual(hashRangeOnly.Name, storedHashOnly.Name);
}

{
var hashRangeOnly = new IgnoreAnnotatedRangeTable
{
Name = "Bob",
Age = 10,
IgnoreAttribute = 100
};

var transactWrite = Context.CreateTransactWrite<IgnoreAnnotatedRangeTable>();
transactWrite.AddSaveItem(hashRangeOnly);
transactWrite.Execute();

var storedHashOnly = Context.Load<IgnoreAnnotatedRangeTable>(hashRangeOnly.Name, hashRangeOnly.Age);
Assert.IsNotNull(storedHashOnly);
Assert.AreEqual(hashRangeOnly.Name, storedHashOnly.Name);
Assert.AreEqual(hashRangeOnly.Age, storedHashOnly.Age);
}

{
var hashRangeOnly = new AnnotatedRangeTable2
{
Name = "Bob",
Age = 10,
NotAnnotatedAttribute = 100
};

var transactWrite = Context.CreateTransactWrite<AnnotatedRangeTable2>();
transactWrite.AddSaveItem(hashRangeOnly);
transactWrite.Execute();

var storedHashOnly = Context.Load<AnnotatedRangeTable2>(hashRangeOnly.Name, hashRangeOnly.Age);
Assert.IsNotNull(storedHashOnly);
Assert.AreEqual(hashRangeOnly.Name, storedHashOnly.Name);
Assert.AreEqual(hashRangeOnly.Age, storedHashOnly.Age);
Assert.AreEqual(hashRangeOnly.NotAnnotatedAttribute, storedHashOnly.NotAnnotatedAttribute);
}

}

/// <summary>
/// Tests that the DynamoDB operations can retrieve <see cref="DateTime"/> attributes in UTC and local timezone.
/// </summary>
Expand Down Expand Up @@ -2039,6 +2109,34 @@ public class PropertyConverterEmployee
public Status Name { get; set; }
}

[DynamoDBTable("HashRangeTable")]
public class AnnotatedRangeTable
{
// Hash key
[DynamoDBHashKey]
public string Name { get; set; }

// Range key
[DynamoDBRangeKey]
internal int Age { get; set; }
}

[DynamoDBTable("HashRangeTable")]
public class IgnoreAnnotatedRangeTable : AnnotatedRangeTable
{
[DynamoDBIgnore]
internal int IgnoreAttribute { get; set; }
}


[DynamoDBTable("HashRangeTable")]
public class AnnotatedRangeTable2 : AnnotatedRangeTable
{
internal int NotAnnotatedAttribute { get; set; }
}



public class DateTimeUtcConverter : IPropertyConverter
{
public DynamoDBEntry ToEntry(object value) => (DateTime)value;
Expand Down