Skip to content

Commit 8776c2a

Browse files
committed
Added check in DateTimeToEpochSeconds to inspect null value for Nullable DateTime property decorated with StoreAsEpoch attribute, where it was incorrectly relying on exception to return the entry.
1 parent 9d94bde commit 8776c2a

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"services": [
3+
{
4+
"serviceName": "DynamoDBv2",
5+
"type": "patch",
6+
"changeLogMessages": [
7+
"Added check in DateTimeToEpochSeconds to inspect null value for Nullable DateTime property decorated with StoreAsEpoch attribute, where it was incorrectly relying on exception to return the entry."
8+
]
9+
}
10+
]
11+
}

sdk/src/Services/DynamoDBv2/Custom/DocumentModel/Document.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,13 @@ internal static DynamoDBEntry DateTimeToEpochSeconds(DynamoDBEntry entry, string
290290
int? epochSeconds = null;
291291
try
292292
{
293+
var primitiveValue = entry.AsPrimitive();
294+
295+
// entry.AsPrimitive() could return null for UnconvertedDynamoDBEntry. UnconvertedDynamoDBEntry will always have a value due to check in it's constructor. Hence, we can process it further for epoch conversion.
296+
if (primitiveValue != null
297+
&& primitiveValue.Value == null)
298+
return entry;
299+
293300
var dateTime = entry.AsDateTime();
294301
epochSeconds = AWSSDKUtils.ConvertToUnixEpochSeconds(dateTime);
295302
}

sdk/test/Services/DynamoDBv2/IntegrationTests/DataModelTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ public void TestContext_DisableFetchingTableMetadata_DateTimeAsHashKey()
144144
EpochDate2 = EpochDate,
145145
NonEpochDate1 = EpochDate,
146146
NonEpochDate2 = EpochDate,
147+
NullableEpochDate1 = null,
148+
NullableEpochDate2 = EpochDate,
147149
LongEpochDate1 = LongEpochDate,
148150
LongEpochDate2 = LongEpochDate.AddDays(12),
149151
NullableLongEpochDate1 = null,
@@ -157,6 +159,8 @@ public void TestContext_DisableFetchingTableMetadata_DateTimeAsHashKey()
157159
ApproximatelyEqual(EpochDate, storedEmployee.EpochDate2);
158160
ApproximatelyEqual(EpochDate, storedEmployee.NonEpochDate1);
159161
ApproximatelyEqual(EpochDate, storedEmployee.NonEpochDate2);
162+
Assert.IsNull(storedEmployee.NullableEpochDate1);
163+
ApproximatelyEqual(EpochDate, storedEmployee.NullableEpochDate2.Value);
160164
ApproximatelyEqual(LongEpochDate, storedEmployee.LongEpochDate1);
161165
ApproximatelyEqual(LongEpochDate.AddDays(12), storedEmployee.LongEpochDate2);
162166
Assert.IsNull(storedEmployee.NullableLongEpochDate1);
@@ -293,6 +297,8 @@ public void TestContext_RetrieveDateTimeInUtc(bool retrieveDateTimeInUtc)
293297
EpochDate2 = currTime,
294298
NonEpochDate1 = currTime,
295299
NonEpochDate2 = currTime,
300+
NullableEpochDate1 = null,
301+
NullableEpochDate2 = currTime,
296302
LongEpochDate1 = longEpochTime,
297303
LongEpochDate2 = longEpochTimeBefore1970,
298304
NullableLongEpochDate1 = longEpochTime,
@@ -311,6 +317,8 @@ public void TestContext_RetrieveDateTimeInUtc(bool retrieveDateTimeInUtc)
311317
ApproximatelyEqual(expectedCurrTime, storedEmployee.EpochDate2);
312318
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate1);
313319
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate2);
320+
Assert.IsNull(storedEmployee.NullableEpochDate1);
321+
ApproximatelyEqual(expectedCurrTime, storedEmployee.NullableEpochDate2.Value);
314322
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.LongEpochDate1);
315323
ApproximatelyEqual(expectedLongEpochTimeBefore1970, storedEmployee.LongEpochDate2);
316324
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.NullableLongEpochDate1.Value);
@@ -327,6 +335,8 @@ public void TestContext_RetrieveDateTimeInUtc(bool retrieveDateTimeInUtc)
327335
ApproximatelyEqual(expectedCurrTime, storedEmployee.EpochDate2);
328336
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate1);
329337
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate2);
338+
Assert.IsNull(storedEmployee.NullableEpochDate1);
339+
ApproximatelyEqual(expectedCurrTime, storedEmployee.NullableEpochDate2.Value);
330340
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.LongEpochDate1);
331341
ApproximatelyEqual(expectedLongEpochTimeBefore1970, storedEmployee.LongEpochDate2);
332342
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.NullableLongEpochDate1.Value);
@@ -341,6 +351,8 @@ public void TestContext_RetrieveDateTimeInUtc(bool retrieveDateTimeInUtc)
341351
ApproximatelyEqual(expectedCurrTime, storedEmployee.EpochDate2);
342352
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate1);
343353
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate2);
354+
Assert.IsNull(storedEmployee.NullableEpochDate1);
355+
ApproximatelyEqual(expectedCurrTime, storedEmployee.NullableEpochDate2.Value);
344356
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.LongEpochDate1);
345357
ApproximatelyEqual(expectedLongEpochTimeBefore1970, storedEmployee.LongEpochDate2);
346358
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.NullableLongEpochDate1.Value);
@@ -386,6 +398,8 @@ public void TestContext_CustomDateTimeConverter(bool retrieveDateTimeInUtc)
386398
EpochDate2 = currTime,
387399
NonEpochDate1 = currTime,
388400
NonEpochDate2 = currTime,
401+
NullableEpochDate1 = null,
402+
NullableEpochDate2 = currTime,
389403
LongEpochDate1 = longEpochTime,
390404
LongEpochDate2 = longEpochTimeBefore1970,
391405
NullableLongEpochDate1 = longEpochTime,
@@ -406,6 +420,8 @@ public void TestContext_CustomDateTimeConverter(bool retrieveDateTimeInUtc)
406420
ApproximatelyEqual(expectedCurrTime, storedEmployee.EpochDate2);
407421
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate1);
408422
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate2);
423+
Assert.IsNull(storedEmployee.NullableEpochDate1);
424+
ApproximatelyEqual(expectedCurrTime, storedEmployee.NullableEpochDate2.Value);
409425
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.LongEpochDate1);
410426
ApproximatelyEqual(expectedLongEpochTimeBefore1970, storedEmployee.LongEpochDate2);
411427
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.NullableLongEpochDate1.Value);
@@ -422,6 +438,8 @@ public void TestContext_CustomDateTimeConverter(bool retrieveDateTimeInUtc)
422438
ApproximatelyEqual(expectedCurrTime, storedEmployee.EpochDate2);
423439
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate1);
424440
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate2);
441+
Assert.IsNull(storedEmployee.NullableEpochDate1);
442+
ApproximatelyEqual(expectedCurrTime, storedEmployee.NullableEpochDate2.Value);
425443
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.LongEpochDate1);
426444
ApproximatelyEqual(expectedLongEpochTimeBefore1970, storedEmployee.LongEpochDate2);
427445
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.NullableLongEpochDate1.Value);
@@ -436,6 +454,8 @@ public void TestContext_CustomDateTimeConverter(bool retrieveDateTimeInUtc)
436454
ApproximatelyEqual(expectedCurrTime, storedEmployee.EpochDate2);
437455
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate1);
438456
ApproximatelyEqual(expectedCurrTime, storedEmployee.NonEpochDate2);
457+
Assert.IsNull(storedEmployee.NullableEpochDate1);
458+
ApproximatelyEqual(expectedCurrTime, storedEmployee.NullableEpochDate2.Value);
439459
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.LongEpochDate1);
440460
ApproximatelyEqual(expectedLongEpochTimeBefore1970, storedEmployee.LongEpochDate2);
441461
ApproximatelyEqual(expectedLongEpochTime, storedEmployee.NullableLongEpochDate1.Value);
@@ -2133,6 +2153,12 @@ public class EpochEmployee : Employee
21332153

21342154
public DateTime NonEpochDate2 { get; set; }
21352155

2156+
[DynamoDBProperty(StoreAsEpoch = true)]
2157+
public DateTime? NullableEpochDate1 { get; set; }
2158+
2159+
[DynamoDBProperty(StoreAsEpoch = true)]
2160+
public DateTime? NullableEpochDate2 { get; set; }
2161+
21362162
[DynamoDBProperty(StoreAsEpochLong = true)]
21372163
public DateTime LongEpochDate1 { get; set; }
21382164

sdk/test/Services/DynamoDBv2/IntegrationTests/TTLTests.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public void TestStoreAsEpoch()
4040
EpochDate2 = EpochDate,
4141
NonEpochDate1 = EpochDate,
4242
NonEpochDate2 = EpochDate,
43+
NullableEpochDate2 = EpochDate,
4344
LongEpochDate1 = LongEpochDate,
4445
LongEpochDate2 = LongEpochDate.AddMonths(3),
4546
NullableLongEpochDate2 = LongEpochDate.AddMonths(-3)
@@ -50,13 +51,17 @@ public void TestStoreAsEpoch()
5051
var storedEmployee = Context.Load<EpochEmployee>(employee.Name, employee.Age);
5152
Assert.IsNotNull(storedEmployee);
5253
ApproximatelyEqual(EpochDate, storedEmployee.CreationTime);
54+
Assert.IsNull(storedEmployee.NullableEpochDate1);
55+
ApproximatelyEqual(EpochDate, storedEmployee.NullableEpochDate2.Value);
5356
ApproximatelyEqual(LongEpochDate, storedEmployee.LongEpochDate1);
5457
ApproximatelyEqual(LongEpochDate.AddMonths(3), storedEmployee.LongEpochDate2);
5558
Assert.IsNull(storedEmployee.NullableLongEpochDate1);
5659
ApproximatelyEqual(LongEpochDate.AddMonths(-3), storedEmployee.NullableLongEpochDate2.Value);
5760
storedEmployee = Context.Load<EpochEmployee>(employee);
5861
Assert.IsNotNull(storedEmployee);
5962
ApproximatelyEqual(EpochDate, storedEmployee.CreationTime);
63+
Assert.IsNull(storedEmployee.NullableEpochDate1);
64+
ApproximatelyEqual(EpochDate, storedEmployee.NullableEpochDate2.Value);
6065
ApproximatelyEqual(LongEpochDate, storedEmployee.LongEpochDate1);
6166
ApproximatelyEqual(LongEpochDate.AddMonths(3), storedEmployee.LongEpochDate2);
6267
Assert.IsNull(storedEmployee.NullableLongEpochDate1);
@@ -66,6 +71,8 @@ public void TestStoreAsEpoch()
6671
var storedNumericEmployee = Context.Load<NumericEpochEmployee>(employee.CreationTime, employee.Name);
6772
Assert.IsNotNull(storedNumericEmployee);
6873
ApproximatelyEqual(EpochDate, storedNumericEmployee.CreationTime);
74+
Assert.IsNull(storedNumericEmployee.NullableEpochDate1);
75+
ApproximatelyEqual(EpochDate, storedNumericEmployee.NullableEpochDate2.Value);
6976
ApproximatelyEqual(LongEpochDate, storedNumericEmployee.LongEpochDate1);
7077
ApproximatelyEqual(LongEpochDate.AddMonths(3), storedNumericEmployee.LongEpochDate2);
7178
Assert.IsNull(storedEmployee.NullableLongEpochDate1);
@@ -74,6 +81,8 @@ public void TestStoreAsEpoch()
7481
storedNumericEmployee = Context.Load<NumericEpochEmployee>(numericEmployee);
7582
Assert.IsNotNull(storedNumericEmployee);
7683
ApproximatelyEqual(EpochDate, storedNumericEmployee.CreationTime);
84+
Assert.IsNull(storedNumericEmployee.NullableEpochDate1);
85+
ApproximatelyEqual(EpochDate, storedNumericEmployee.NullableEpochDate2.Value);
7786
ApproximatelyEqual(LongEpochDate, storedNumericEmployee.LongEpochDate1);
7887
ApproximatelyEqual(LongEpochDate.AddMonths(3), storedNumericEmployee.LongEpochDate2);
7988
Assert.IsNull(storedEmployee.NullableLongEpochDate1);
@@ -82,6 +91,8 @@ public void TestStoreAsEpoch()
8291
var doc = Context.ToDocument(employee);
8392
ApproximatelyEqual(EpochDate, doc["CreationTime"].AsDateTime());
8493
ApproximatelyEqual(EpochDate, doc["EpochDate2"].AsDateTime());
94+
Assert.IsNull(doc["NullableEpochDate1"].AsPrimitive().Value);
95+
ApproximatelyEqual(EpochDate, doc["NullableEpochDate2"].AsDateTime());
8596
ApproximatelyEqual(EpochDate, doc["NonEpochDate1"].AsDateTime());
8697
ApproximatelyEqual(EpochDate, doc["NonEpochDate1"].AsDateTime());
8798
ApproximatelyEqual(LongEpochDate, doc["LongEpochDate1"].AsDateTime());
@@ -92,6 +103,8 @@ public void TestStoreAsEpoch()
92103
var docV1 = doc.ForceConversion(DynamoDBEntryConversion.V1);
93104
ApproximatelyEqual(EpochDate, docV1["CreationTime"].AsDateTime());
94105
ApproximatelyEqual(EpochDate, docV1["EpochDate2"].AsDateTime());
106+
Assert.IsNull(docV1["NullableEpochDate1"].AsPrimitive().Value);
107+
ApproximatelyEqual(EpochDate, docV1["NullableEpochDate2"].AsDateTime());
95108
ApproximatelyEqual(EpochDate, docV1["NonEpochDate1"].AsDateTime());
96109
ApproximatelyEqual(EpochDate, docV1["NonEpochDate1"].AsDateTime());
97110
ApproximatelyEqual(LongEpochDate, docV1["LongEpochDate1"].AsDateTime());
@@ -102,6 +115,8 @@ public void TestStoreAsEpoch()
102115
var docV2 = doc.ForceConversion(DynamoDBEntryConversion.V2);
103116
ApproximatelyEqual(EpochDate, docV2["CreationTime"].AsDateTime());
104117
ApproximatelyEqual(EpochDate, docV2["EpochDate2"].AsDateTime());
118+
Assert.IsNull(docV2["NullableEpochDate1"].AsPrimitive().Value);
119+
ApproximatelyEqual(EpochDate, docV2["NullableEpochDate2"].AsDateTime());
105120
ApproximatelyEqual(EpochDate, docV2["NonEpochDate1"].AsDateTime());
106121
ApproximatelyEqual(EpochDate, docV2["NonEpochDate1"].AsDateTime());
107122
ApproximatelyEqual(LongEpochDate, docV2["LongEpochDate1"].AsDateTime());
@@ -118,6 +133,8 @@ public void TestStoreAsEpoch()
118133
var epochMap = epochTable.ToAttributeMap(doc);
119134
Assert.IsNotNull(epochMap["CreationTime"].N);
120135
Assert.IsNotNull(epochMap["EpochDate2"].N);
136+
Assert.IsFalse(epochMap.ContainsKey("NullableEpochDate1"));
137+
Assert.IsNotNull(epochMap["NullableEpochDate2"].N);
121138
Assert.IsNotNull(epochMap["NonEpochDate1"].S);
122139
Assert.IsNotNull(epochMap["NonEpochDate2"].S);
123140
Assert.IsNotNull(epochMap["LongEpochDate1"].N);
@@ -172,7 +189,7 @@ public void TestStoreAsEpoch(Table hashRangeTable, Table numericHashRangeTable)
172189
// construct tables with StoreAsEpoch and StoreAsEpochLong
173190
var config = new TableConfig(hashRangeTable.TableName)
174191
{
175-
AttributesToStoreAsEpoch = new List<string> { "CreationTime", "EpochDate2" },
192+
AttributesToStoreAsEpoch = new List<string> { "CreationTime", "EpochDate2", "NullableEpochDate1", "NullableEpochDate2" },
176193
AttributesToStoreAsEpochLong = new List<string> { "LongEpochDate", "NullableLongEpochDate1", "NullableLongEpochDate2" }
177194
};
178195
var epochTable = Table.LoadTable(Client, config);
@@ -181,7 +198,7 @@ public void TestStoreAsEpoch(Table hashRangeTable, Table numericHashRangeTable)
181198

182199
config = new TableConfig(numericHashRangeTable.TableName)
183200
{
184-
AttributesToStoreAsEpoch = new List<string> { "CreationTime", "EpochDate2" },
201+
AttributesToStoreAsEpoch = new List<string> { "CreationTime", "EpochDate2", "NullableEpochDate1", "NullableEpochDate2" },
185202
AttributesToStoreAsEpochLong = new List<string> { "LongEpochDate", "NullableLongEpochDate1", "NullableLongEpochDate2" }
186203
};
187204
var numericEpochTable = Table.LoadTable(Client, config);
@@ -193,6 +210,8 @@ public void TestStoreAsEpoch(Table hashRangeTable, Table numericHashRangeTable)
193210
Assert.IsNotNull(map["CreationTime"].S);
194211
Assert.IsNotNull(map["EpochDate2"].S);
195212
Assert.IsNotNull(map["NonEpochDate"].S);
213+
Assert.IsNotNull(map["NullableEpochDate1"].S);
214+
Assert.IsFalse(map.ContainsKey("NullableEpochDate2"));
196215
Assert.IsNotNull(map["LongEpochDate"].S);
197216
Assert.IsNotNull(map["NullableLongEpochDate1"].S);
198217
Assert.IsFalse(map.ContainsKey("NullableLongEpochDate2"));
@@ -201,7 +220,9 @@ public void TestStoreAsEpoch(Table hashRangeTable, Table numericHashRangeTable)
201220
Assert.IsNotNull(epochMap["CreationTime"].N);
202221
Assert.IsNotNull(epochMap["EpochDate2"].N);
203222
Assert.IsNotNull(epochMap["NonEpochDate"].S);
223+
Assert.IsNotNull(epochMap["NullableEpochDate1"].N);
204224
Assert.IsNotNull(epochMap["LongEpochDate"].N);
225+
Assert.IsNotNull(epochMap["NullableLongEpochDate1"].N);
205226

206227
// put
207228
epochTable.PutItem(CreateTestDocument());
@@ -241,6 +262,8 @@ public void TestStoreAsEpoch(Table hashRangeTable, Table numericHashRangeTable)
241262
doc2["CreationTime"] = EpochDate;
242263
doc2["EpochDate2"] = EpochDate;
243264
doc2["NonEpochDate"] = EpochDate;
265+
doc2["NullableEpochDate1"] = (DateTime?) EpochDate;
266+
doc2["NullableEpochDate2"] = (DateTime?) null;
244267
doc2["LongEpochDate"] = LongEpochDate;
245268
doc2["NullableLongEpochDate1"] = (DateTime?) LongEpochDate;
246269
doc2["NullableLongEpochDate2"] = (DateTime?) null;
@@ -335,6 +358,8 @@ private static Document CreateTestDocument()
335358
doc["CreationTime"] = EpochDate;
336359
doc["EpochDate2"] = EpochDate;
337360
doc["NonEpochDate"] = EpochDate;
361+
doc["NullableEpochDate1"] = (DateTime?) EpochDate;
362+
doc["NullableEpochDate2"] = (DateTime?) null;
338363
doc["LongEpochDate"] = LongEpochDate;
339364
doc["NullableLongEpochDate1"] = (DateTime?) LongEpochDate;
340365
doc["NullableLongEpochDate2"] = (DateTime?) null;
@@ -348,6 +373,7 @@ private static void TestWrittenData(Primitive hash, Primitive range, Table hashR
348373
Assert.AreEqual(EpochSeconds, nonEpochDoc["CreationTime"].AsInt());
349374
Assert.AreEqual(EpochSeconds, nonEpochDoc["EpochDate2"].AsInt());
350375
ApproximatelyEqual(EpochDate, nonEpochDoc["NonEpochDate"].AsDateTime());
376+
Assert.AreEqual(EpochSeconds, nonEpochDoc["NullableEpochDate1"].AsInt());
351377
Assert.AreEqual(LongEpochSeconds, nonEpochDoc["LongEpochDate"].AsLong());
352378
Assert.AreEqual(LongEpochSeconds, nonEpochDoc["NullableLongEpochDate1"].AsLong());
353379
Assert.IsFalse(nonEpochDoc.ContainsKey("NullableLongEpochDate2"));
@@ -359,6 +385,7 @@ private static void TestWrittenData(Primitive hash, Primitive range, Table hashR
359385
ApproximatelyEqual(EpochDate, epochDoc["CreationTime"].AsDateTime());
360386
ApproximatelyEqual(EpochDate, epochDoc["EpochDate2"].AsDateTime());
361387
ApproximatelyEqual(EpochDate, epochDoc["NonEpochDate"].AsDateTime());
388+
ApproximatelyEqual(EpochDate, epochDoc["NullableEpochDate1"].AsDateTime());
362389
ApproximatelyEqual(LongEpochDate, epochDoc["LongEpochDate"].AsDateTime());
363390
ApproximatelyEqual(LongEpochDate, epochDoc["NullableLongEpochDate1"].AsDateTime());
364391
if (checkForConditionalUpdate)
@@ -374,6 +401,7 @@ private static void TestForInts(List<Document> nonEpochDocs)
374401
Assert.AreEqual(EpochSeconds, ed["CreationTime"].AsInt());
375402
Assert.AreEqual(EpochSeconds, ed["EpochDate2"].AsInt());
376403
ApproximatelyEqual(EpochDate, ed["NonEpochDate"].AsDateTime());
404+
Assert.AreEqual(EpochSeconds, ed["NullableEpochDate1"].AsInt());
377405
Assert.AreEqual(LongEpochSeconds, ed["LongEpochDate"].AsLong());
378406
Assert.AreEqual(LongEpochSeconds, ed["NullableLongEpochDate1"].AsLong());
379407
}
@@ -386,6 +414,7 @@ private static void TestForDateTime(List<Document> epochDocs)
386414
ApproximatelyEqual(EpochDate, ed["CreationTime"].AsDateTime());
387415
ApproximatelyEqual(EpochDate, ed["EpochDate2"].AsDateTime());
388416
ApproximatelyEqual(EpochDate, ed["NonEpochDate"].AsDateTime());
417+
ApproximatelyEqual(EpochDate, ed["NullableEpochDate1"].AsDateTime());
389418
ApproximatelyEqual(LongEpochDate, ed["LongEpochDate"].AsDateTime());
390419
ApproximatelyEqual(LongEpochDate, ed["NullableLongEpochDate1"].AsDateTime());
391420
}

0 commit comments

Comments
 (0)