Skip to content

Fix TransactWrite<T>.AddSaveItem when T does NOT contain DynamoDbProperty Attributes #3614

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

Merged
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