1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.enhanced.dynamodb.document;
17 
18 import static java.time.Instant.ofEpochMilli;
19 import static software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider.defaultProvider;
20 import static software.amazon.awssdk.enhanced.dynamodb.document.TestData.TypeMap.typeMap;
21 import static software.amazon.awssdk.enhanced.dynamodb.document.TestData.dataBuilder;
22 
23 import java.math.BigDecimal;
24 import java.time.Instant;
25 import java.time.LocalDate;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.LinkedHashMap;
30 import java.util.LinkedHashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.function.Function;
34 import java.util.stream.Collectors;
35 import java.util.stream.IntStream;
36 import java.util.stream.Stream;
37 import org.junit.jupiter.api.extension.ExtensionContext;
38 import org.junit.jupiter.params.provider.Arguments;
39 import org.junit.jupiter.params.provider.ArgumentsProvider;
40 import software.amazon.awssdk.core.SdkBytes;
41 import software.amazon.awssdk.core.SdkNumber;
42 import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
43 import software.amazon.awssdk.enhanced.dynamodb.converters.document.CustomAttributeForDocumentConverterProvider;
44 import software.amazon.awssdk.enhanced.dynamodb.converters.document.CustomClassForDocumentAPI;
45 import software.amazon.awssdk.enhanced.dynamodb.internal.converter.ChainConverterProvider;
46 import software.amazon.awssdk.enhanced.dynamodb.internal.converter.StringConverter;
47 import software.amazon.awssdk.enhanced.dynamodb.internal.converter.string.CharSequenceStringConverter;
48 import software.amazon.awssdk.enhanced.dynamodb.internal.document.DefaultEnhancedDocument;
49 import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
50 import software.amazon.awssdk.utils.Pair;
51 
52 public final class EnhancedDocumentTestData implements ArgumentsProvider {
53 
54     private static long FIXED_INSTANT_TIME = 1677690845038L;
55 
56     public static final AttributeValue NUMBER_STRING_ARRAY_ATTRIBUTES_LISTS =
57         AttributeValue.fromL(Arrays.asList(AttributeValue.fromN("1"),
58                                            AttributeValue.fromN("2"),
59                                            AttributeValue.fromN("3")));
60 
61     private static final EnhancedDocumentTestData INSTANCE = new EnhancedDocumentTestData();
62 
63     private Map<String, TestData> testScenarioMap;
64     private List<TestData> testDataList;
65 
EnhancedDocumentTestData()66     public EnhancedDocumentTestData() {
67         initializeTestData();
68     }
69 
testDataInstance()70     public static EnhancedDocumentTestData testDataInstance() {
71         return new EnhancedDocumentTestData();
72     }
73 
defaultDocBuilder()74     public static EnhancedDocument.Builder defaultDocBuilder() {
75         EnhancedDocument.Builder defaultBuilder = DefaultEnhancedDocument.builder();
76         return defaultBuilder.addAttributeConverterProvider(defaultProvider());
77     }
78 
map()79     public static AttributeStringValueMap map() {
80         return new AttributeStringValueMap();
81     }
82 
initializeTestData()83     private void initializeTestData() {
84 
85         testDataList = new ArrayList<>();
86         testDataList.add(dataBuilder().scenario("nullKey")
87                                       .ddbItemMap(map().withKeyValue("nullKey", AttributeValue.fromNul(true)).get())
88                                       .enhancedDocument(defaultDocBuilder()
89                                                             .putNull("nullKey")
90                                                             .build())
91                                       .json("{\"nullKey\":null}")
92                                       .attributeConverterProvider(defaultProvider())
93                                       .build());
94 
95 
96         testDataList.add(dataBuilder().scenario("simpleString")
97                                       .ddbItemMap(map().withKeyValue("stringKey", AttributeValue.fromS("stringValue")).get())
98                                       .enhancedDocument(
99                                           ((DefaultEnhancedDocument.DefaultBuilder)
100                                               DefaultEnhancedDocument.builder()).putObject("stringKey", "stringValue")
101                                                                  .addAttributeConverterProvider(defaultProvider()).build())
102                                       .attributeConverterProvider(defaultProvider())
103                                       .json("{\"stringKey\":\"stringValue\"}")
104 
105                                       .build());
106 
107         testDataList.add(dataBuilder().scenario("record")
108 
109                                       .ddbItemMap(map().withKeyValue("uniqueId", AttributeValue.fromS("id-value"))
110                                                       .withKeyValue("sortKey",AttributeValue.fromS("sort-value"))
111                                                       .withKeyValue("attributeKey", AttributeValue.fromS("one"))
112                                                       .withKeyValue("attributeKey2", AttributeValue.fromS("two"))
113                                                       .withKeyValue("attributeKey3", AttributeValue.fromS("three")).get())
114                                       .enhancedDocument(
115                                             defaultDocBuilder()
116                                                 .putString("uniqueId","id-value")
117                                                 .putString("sortKey","sort-value")
118                                                 .putString("attributeKey","one")
119                                                 .putString("attributeKey2","two")
120                                                 .putString("attributeKey3","three")
121                                                 .build()
122                                       )
123 
124 
125                                       .attributeConverterProvider(defaultProvider())
126                                       .json("{\"uniqueId\":\"id-value\",\"sortKey\":\"sort-value\",\"attributeKey\":\"one\","
127                                             + "\"attributeKey2\":\"two\",\"attributeKey3\":\"three\"}")
128 
129                                       .build());
130 
131         testDataList.add(dataBuilder().scenario("differentNumberTypes")
132                                       .ddbItemMap(map()
133                                                       .withKeyValue("numberKey", AttributeValue.fromN("10"))
134                                                       .withKeyValue("bigDecimalNumberKey",
135                                                                     AttributeValue.fromN(new BigDecimal(10).toString())).get())
136                                       .enhancedDocument(
137                                           defaultDocBuilder()
138                                               .putNumber("numberKey", Integer.valueOf(10))
139                                               .putNumber("bigDecimalNumberKey", new BigDecimal(10))
140                                               .build())
141                                       .attributeConverterProvider(defaultProvider())
142                                       .json("{" + "\"numberKey\":10," + "\"bigDecimalNumberKey\":10" + "}")
143 
144                                       .build());
145 
146         testDataList.add(dataBuilder().scenario("allSimpleTypes")
147                                       .ddbItemMap(map()
148                                                       .withKeyValue("stringKey", AttributeValue.fromS("stringValue"))
149                                                       .withKeyValue("numberKey", AttributeValue.fromN("10"))
150                                                       .withKeyValue("boolKey", AttributeValue.fromBool(true))
151                                                       .withKeyValue("nullKey", AttributeValue.fromNul(true))
152                                                       .withKeyValue("numberSet", AttributeValue.fromNs(Arrays.asList("1", "2", "3")))
153                                                       .withKeyValue("sdkBytesSet",
154                                                                     AttributeValue.fromBs(Arrays.asList(SdkBytes.fromUtf8String("a")
155                                                                         ,SdkBytes.fromUtf8String("b")
156                                                                         ,SdkBytes.fromUtf8String("c"))))
157                                                       .withKeyValue("stringSet",
158                                                                     AttributeValue.fromSs(Arrays.asList("a", "b", "c"))).get())
159                                       .enhancedDocument(defaultDocBuilder()
160                                                             .putString("stringKey", "stringValue")
161                                                             .putNumber("numberKey", 10)
162                                                             .putBoolean("boolKey", true)
163                                                             .putNull("nullKey")
164                                                             .putNumberSet("numberSet", Stream.of(1, 2, 3).collect(Collectors.toSet()))
165                                                             .putBytesSet("sdkBytesSet", Stream.of(SdkBytes.fromUtf8String("a"),
166                                                                                                   SdkBytes.fromUtf8String("b"),
167                                                                                                   SdkBytes.fromUtf8String("c"))
168                                                                                               .collect(Collectors.toSet()))
169                                                             .putStringSet("stringSet", Stream.of("a", "b", "c").collect(Collectors.toSet()))
170                                                             .build())
171                                       .json("{\"stringKey\":\"stringValue\",\"numberKey\":10,\"boolKey\":true,\"nullKey\":null,"
172                                             + "\"numberSet\":[1,2,3],\"sdkBytesSet\":[\"YQ==\",\"Yg==\",\"Yw==\"],\"stringSet\":[\"a\","
173                                             + "\"b\",\"c\"]}")
174                              .attributeConverterProvider(defaultProvider())
175                                       .build());
176 
177         testDataList.add(dataBuilder().scenario("differentNumberSets")
178                              .isGeneric(false)
179                                       .ddbItemMap(map()
180                                                       .withKeyValue("floatSet", AttributeValue.fromNs(Arrays.asList("2.0", "3.0")))
181                                                       .withKeyValue("integerSet", AttributeValue.fromNs(Arrays.asList("-1", "0", "1")))
182                                                       .withKeyValue("bigDecimal", AttributeValue.fromNs(Arrays.asList("1000.002", "2000.003")))
183                                                       .withKeyValue("sdkNumberSet", AttributeValue.fromNs(Arrays.asList("1", "2", "3"))).get())
184                                       .enhancedDocument(defaultDocBuilder()
185                                                             .putNumberSet("floatSet", Stream.of( Float.parseFloat("2.0"),
186                                                                                                  Float.parseFloat("3.0") ).collect(Collectors.toCollection(LinkedHashSet::new)))
187                                                             .putNumberSet("integerSet",Arrays.asList(-1,0, 1).stream().collect(Collectors.toCollection(LinkedHashSet::new)))
188                                                             .putNumberSet("bigDecimal", Stream.of(BigDecimal.valueOf(1000.002), BigDecimal.valueOf(2000.003) ).collect(Collectors.toCollection(LinkedHashSet::new)))
189                                                             .putNumberSet("sdkNumberSet", Stream.of(SdkNumber.fromInteger(1), SdkNumber.fromInteger(2), SdkNumber.fromInteger(3) ).collect(Collectors.toSet()))
190                                                             .build())
191                                       .json("{\"floatSet\": [2.0, 3.0],\"integerSet\": [-1, 0, 1],\"bigDecimal\": [1000.002, 2000.003],\"sdkNumberSet\": [1,2, 3]}")
192                              .attributeConverterProvider(defaultProvider())
193                                       .build());
194 
195         testDataList.add(dataBuilder().scenario("simpleListExcludingBytes")
196                                       .ddbItemMap(map()
197                                                       .withKeyValue("numberList",
198                                                                     AttributeValue.fromL(
199                                                                         Arrays.asList(AttributeValue.fromN("1"),
200                                                                                       AttributeValue.fromN("2"))))
201                                                       .withKeyValue("stringList",
202                                                                     AttributeValue.fromL(
203                                                                         Arrays.asList(
204                                                                             AttributeValue.fromS("one"),
205                                                                             AttributeValue.fromS("two")))).get())
206                                       .enhancedDocument(
207                                           defaultDocBuilder()
208                                               .putList("numberList", Arrays.asList(1,2), EnhancedType.of(Integer.class))
209                                               .putList("stringList", Arrays.asList("one","two"), EnhancedType.of(String.class))
210                                               .build()
211                                       )
212                              .typeMap(typeMap()
213                                           .addAttribute("numberList", EnhancedType.of(Integer.class))
214                                           .addAttribute("stringList", EnhancedType.of(String.class)))
215                                       .attributeConverterProvider(defaultProvider())
216                                       .json("{\"numberList\":[1,2],\"stringList\":[\"one\",\"two\"]}")
217                                       .build());
218 
219         testDataList.add(dataBuilder().scenario("customList")
220                                       .ddbItemMap(
221                                           map().withKeyValue("customClassForDocumentAPI", AttributeValue.fromL(
222                                               Arrays.asList(
223                                                   AttributeValue.fromM(getAttributeValueMapForCustomClassWithPrefix(1,10, false))
224                                                   ,
225                                                   AttributeValue.fromM(getAttributeValueMapForCustomClassWithPrefix(2,200, false))))).get())
226                                       .enhancedDocument(
227                                           defaultDocBuilder()
228                                               .attributeConverterProviders(CustomAttributeForDocumentConverterProvider.create()
229                                                   ,defaultProvider())
230                                       .putList("customClassForDocumentAPI"
231                                           , Arrays.asList(getCustomClassForDocumentAPIWithBaseAndOffset(1,10)
232                                               ,getCustomClassForDocumentAPIWithBaseAndOffset(2,200)),
233                                                EnhancedType.of(CustomClassForDocumentAPI.class))
234                                       .build()
235                                       )
236                              .typeMap(typeMap()
237                                           .addAttribute("instantList", EnhancedType.of(Instant.class))
238                                           .addAttribute("customClassForDocumentAPI", EnhancedType.of(CustomClassForDocumentAPI.class)))
239                                       .attributeConverterProvider(ChainConverterProvider.create(CustomAttributeForDocumentConverterProvider.create(),
240                                                                                                 defaultProvider()))
241                                       .json("{\"customClassForDocumentAPI\":[{\"instantList\":[\"2023-03-01T17:14:05.049Z\","
242                                             + "\"2023-03-01T17:14:05.049Z\",\"2023-03-01T17:14:05.049Z\"],\"longNumber\":11,"
243                                             + "\"string\":\"11\",\"stringSet\":[\"12\",\"13\",\"14\"]},"
244                                             + "{\"instantList\":[\"2023-03-01T17:14:05.240Z\",\"2023-03-01T17:14:05.240Z\","
245                                             + "\"2023-03-01T17:14:05.240Z\"],\"longNumber\":202,\"string\":\"202\","
246                                             + "\"stringSet\":[\"203\",\"204\",\"205\"]}]}")
247                                       .build());
248 
249         testDataList.add(dataBuilder().scenario("ThreeLevelNestedList")
250                                       .ddbItemMap(
251                                           map().withKeyValue("threeLevelList",
252                                                              AttributeValue.fromL(Arrays.asList(
253                                                                  AttributeValue.fromL(Arrays.asList(
254                                                                      AttributeValue.fromL(Arrays.asList(AttributeValue.fromS("l1_0"),
255                                                                                                         AttributeValue.fromS("l1_1"))),
256                                                                      AttributeValue.fromL(Arrays.asList(AttributeValue.fromS("l2_0"),
257                                                                                                         AttributeValue.fromS("l2_1")))
258                                                                  ))
259                                                                  ,
260                                                                  AttributeValue.fromL(Arrays.asList(
261                                                                      AttributeValue.fromL(Arrays.asList(AttributeValue.fromS("l3_0"),
262                                                                                                         AttributeValue.fromS("l3_1"))),
263                                                                      AttributeValue.fromL(Arrays.asList(AttributeValue.fromS("l4_0"),
264                                                                                                         AttributeValue.fromS("l4_1")))
265                                                                  ))))).get()
266                                       )
267                                       .enhancedDocument(
268                                           defaultDocBuilder()
269                                               .putList("threeLevelList"
270                                                   , Arrays.asList(
271                                                       Arrays.asList(
272                                                           Arrays.asList("l1_0", "l1_1"), Arrays.asList("l2_0", "l2_1")
273                                                       ),
274                                                       Arrays.asList(
275                                                           Arrays.asList("l3_0", "l3_1"), Arrays.asList("l4_0", "l4_1")
276                                                       )
277                                                   )
278                                                   , new EnhancedType<List<List<String>>>() {
279                                                   }
280                                               )
281 
282                                               .build()
283                                       )
284                                       .attributeConverterProvider(defaultProvider())
285                                       .json("{\"threeLevelList\":[[[\"l1_0\",\"l1_1\"],[\"l2_0\",\"l2_1\"]],[[\"l3_0\","
286                                             + "\"l3_1\"],[\"l4_0\",\"l4_1\"]]]}")
287                                       .typeMap(typeMap()
288                                                    .addAttribute("threeLevelList", new EnhancedType<List<List<String>>>() {
289                                                    }))
290                                       .build());
291 
292         // Test case for Nested List with Maps List<Map<String, List<String>>
293         testDataList.add(dataBuilder().scenario("listOfMapOfListValues")
294                                       .ddbItemMap(
295                                           map()
296                                               .withKeyValue("listOfListOfMaps",
297                                                             AttributeValue.fromL(
298                                                                 Arrays.asList(
299                                                                     AttributeValue.fromM(getStringListAttributeValueMap("a", 2, 2)),
300                                                                     AttributeValue.fromM(getStringListAttributeValueMap("b", 1, 1))
301                                                                 )
302                                                             )
303                                               ).get())
304                                       .enhancedDocument(
305                                           defaultDocBuilder()
306                                               .putList("listOfListOfMaps"
307                                                   , Arrays.asList(
308                                                       getStringListObjectMap("a", 2, 2),
309                                                       getStringListObjectMap("b", 1, 1)
310                                                   )
311                                                   , new EnhancedType<Map<String, List<Integer>>>() {
312                                                   }
313                                               )
314                                               .build()
315                                       )
316                                       .json("{\"listOfListOfMaps\":[{\"key_a_1\":[1,2],\"key_a_2\":[1,2]},{\"key_b_1\":[1]}]}")
317                                       .attributeConverterProvider(defaultProvider())
318                                       .typeMap(typeMap()
319                                                    .addAttribute("listOfListOfMaps", new EnhancedType<Map<String, List<Integer>>>() {
320                                                    }))
321                                       .build());
322 
323         testDataList.add(dataBuilder().scenario("simpleMap")
324                                       .ddbItemMap(
325                                           map()
326                                               .withKeyValue("simpleMap", AttributeValue.fromM(getStringSimpleAttributeValueMap(
327                                                   "suffix", 7)))
328                                               .get())
329                                       .enhancedDocument(
330                                           defaultDocBuilder()
331                                               .putMap("simpleMap", getStringSimpleMap("suffix", 7, CharSequenceStringConverter.create()),
332                                                       EnhancedType.of(CharSequence.class), EnhancedType.of(String.class))
333                                               .build()
334                                       )
335                                       .attributeConverterProvider(defaultProvider())
336                                       .typeMap(typeMap()
337                                                    .addAttribute("simpleMap", EnhancedType.of(CharSequence.class),
338                                                                  EnhancedType.of(String.class)))
339                                       .json("{\"simpleMap\":{\"key_suffix_1\":\"1\",\"key_suffix_2\":\"2\","
340                                             + "\"key_suffix_3\":\"3\",\"key_suffix_4\":\"4\",\"key_suffix_5\":\"5\","
341                                             + "\"key_suffix_6\":\"6\",\"key_suffix_7\":\"7\"}}")
342                                       .build());
343 
344         testDataList.add(dataBuilder().scenario("ElementsOfCustomType")
345                                       .ddbItemMap(
346                                           map().withKeyValue("customMapValue",
347                                                              AttributeValue.fromM(
348                                                                  map().withKeyValue("entryOne",
349                                                                                     AttributeValue.fromM(getAttributeValueMapForCustomClassWithPrefix(2, 10, false)))
350                                                                       .get()))
351                                                .get()
352                                       )
353                                       .enhancedDocument(
354                                           defaultDocBuilder()
355                                               .attributeConverterProviders(CustomAttributeForDocumentConverterProvider.create()
356                                                   , defaultProvider())
357                                               .putMap("customMapValue",
358                                                       Stream.of(Pair.of("entryOne", customValueWithBaseAndOffset(2, 10)))
359                                                                   .collect(Collectors.toMap(p -> CharSequenceStringConverter.create().fromString(p.left()), p -> p.right(),
360                                                                                             (oldValue, newValue) -> oldValue,
361                                                                                             LinkedHashMap::new))
362                                                   , EnhancedType.of(CharSequence.class),
363                                                       EnhancedType.of(CustomClassForDocumentAPI.class))
364                                               .build()
365                                       )
366                                       .json("{\"customMapValue\":{\"entryOne\":{\"instantList\":[\"2023-03-01T17:14:05.050Z\","
367                                             + "\"2023-03-01T17:14:05.050Z\",\"2023-03-01T17:14:05.050Z\"],\"longNumber\":12,"
368                                             + "\"string\":\"12\",\"stringSet\":[\"13\",\"14\",\"15\"]}}}")
369                                       .typeMap(typeMap()
370                                                    .addAttribute("customMapValue", EnhancedType.of(CharSequence.class),
371                                                                  EnhancedType.of(CustomClassForDocumentAPI.class)))
372                                       .attributeConverterProvider(ChainConverterProvider.create(CustomAttributeForDocumentConverterProvider.create(),
373                                                                                                 defaultProvider()))
374                                       .build());
375 
376         testDataList.add(dataBuilder().scenario("complexDocWithSdkBytesAndMapArrays_And_PutOverWritten")
377                                       .ddbItemMap(map().withKeyValue("nullKey",AttributeValue.fromNul(true)).get())
378                                       .enhancedDocument(
379                                           defaultDocBuilder()
380                                               .putNull("nullKey")
381                                               .putNumber("numberKey", 1)
382                                               .putString("stringKey", "stringValue")
383                                               .putList("numberList", Arrays.asList(1, 2, 3), EnhancedType.of(Integer.class))
384                                               .put("simpleDate", LocalDate.MIN, EnhancedType.of(LocalDate.class))
385                                               .putStringSet("stringSet", Stream.of("one", "two").collect(Collectors.toSet()))
386                                               .putBytes("sdkByteKey", SdkBytes.fromUtf8String("a"))
387                                               .putBytesSet("sdkByteSet",
388                                                            Stream.of(SdkBytes.fromUtf8String("a"),
389                                                                      SdkBytes.fromUtf8String("b")).collect(Collectors.toSet()))
390                                               .putNumberSet("numberSetSet", Stream.of(1, 2).collect(Collectors.toSet()))
391                                               .putList("numberList", Arrays.asList(4, 5, 6), EnhancedType.of(Integer.class))
392                                               .putMap("simpleMap",
393                                                       mapFromSimpleKeyValue(Pair.of("78b3522c-2ab3-4162-8c5d"
394                                                                                                        + "-f093fa76e68c", 3),
395                                                                                                Pair.of("4ae1f694-52ce-4cf6-8211"
396                                                                                                        + "-232ccf780da8", 9)),
397                                                       EnhancedType.of(String.class), EnhancedType.of(Integer.class))
398                                               .putMap("mapKey", mapFromSimpleKeyValue(Pair.of("1", Arrays.asList("a", "b"
399                                                                 , "c")), Pair.of("2",
400                                                                                  Collections.singletonList("1"))),
401                                                       EnhancedType.of(String.class), EnhancedType.listOf(String.class))
402                                               .build()
403                                       )
404                                       .json("{\"nullKey\": null, \"numberKey\": 1, \"stringKey\":\"stringValue\", "
405                                             + "\"simpleDate\":\"-999999999-01-01\",\"stringSet\": "
406                                             + "[\"one\",\"two\"],\"sdkByteKey\":\"a\",\"sdkByteSet\":[\"a\",\"b\"], "
407                                             + "\"numberSetSet\": [1,2], "
408                                             + "\"numberList\": [4, 5, 6], "
409                                             + "\"simpleMap\": {\"78b3522c-2ab3-4162-8c5d-f093fa76e68c\": 3,"
410                                             + "\"4ae1f694-52ce-4cf6-8211-232ccf780da8\": 9}, \"mapKey\": {\"1\":[\"a\",\"b\","
411                                             + " \"c\"],\"2\":[\"1\"]}}")
412                                       .attributeConverterProvider(defaultProvider())
413                              .isGeneric(false)
414                                       .build());
415 
416         testDataList.add(dataBuilder().scenario("insertUsingPutJson")
417                                       .ddbItemMap(
418                                           map().withKeyValue("customMapValue",
419                                                              AttributeValue.fromM(
420                                                                  map().withKeyValue("entryOne",
421                                                                                     AttributeValue.fromM(getAttributeValueMapForCustomClassWithPrefix(2, 10, true)))
422                                                                       .get()))
423                                                .get()
424                                       )
425                                       .enhancedDocument(
426                                           defaultDocBuilder()
427                                               .attributeConverterProviders(CustomAttributeForDocumentConverterProvider.create()
428                                                   ,defaultProvider())
429 
430                                               .putJson("customMapValue",
431                                                        "{\"entryOne\": "
432                                                        + "{"
433                                                        + "\"instantList\": [\"2023-03-01T17:14:05.050Z\", \"2023-03-01T17:14:05.050Z\", \"2023-03-01T17:14:05.050Z\"], "
434                                                        + "\"longNumber\": 12, "
435                                                        + "\"string\": \"12\""
436                                                        + "}"
437                                                        + "}")
438                                               .build()
439                                       )
440                                       .json("{\"customMapValue\":{\"entryOne\":{\"instantList\":[\"2023-03-01T17:14:05.050Z\","
441                                             + "\"2023-03-01T17:14:05.050Z\",\"2023-03-01T17:14:05.050Z\"],\"longNumber\":12,"
442                                             + "\"string\":\"12\"}}}")
443                                       .typeMap(typeMap()
444                                                    .addAttribute("customMapValue", EnhancedType.of(CharSequence.class),
445                                                                  EnhancedType.of(CustomClassForDocumentAPI.class)))
446                                       .attributeConverterProvider(ChainConverterProvider.create(CustomAttributeForDocumentConverterProvider.create(),
447                                                                                                 defaultProvider()))
448                                       .build());
449 
450         testDataList.add(dataBuilder().scenario("putJsonWithSimpleMapOfStrings")
451                                       .ddbItemMap(
452                                           map()
453                                               .withKeyValue("simpleMap", AttributeValue.fromM(getStringSimpleAttributeValueMap(
454                                                   "suffix", 7)))
455                                               .get())
456                                       .enhancedDocument(
457                                           defaultDocBuilder()
458                                               .putJson("simpleMap",
459                                                        "{\"key_suffix_1\":\"1\",\"key_suffix_2\":\"2\",\"key_suffix_3\":"
460                                                        + " \"3\",\"key_suffix_4\": "
461                                                        + "\"4\",\"key_suffix_5\":\"5\",\"key_suffix_6\":\"6\",\"key_suffix_7\":\"7\"}" )
462                                               .build()
463                                       )
464                                       .attributeConverterProvider(defaultProvider())
465                                       .typeMap(typeMap()
466                                                    .addAttribute("simpleMap", EnhancedType.of(String.class),
467                                                                  EnhancedType.of(String.class)))
468                                       .json("{\"simpleMap\":{\"key_suffix_1\":\"1\",\"key_suffix_2\":\"2\","
469                                             + "\"key_suffix_3\":\"3\",\"key_suffix_4\":\"4\",\"key_suffix_5\":\"5\","
470                                             + "\"key_suffix_6\":\"6\",\"key_suffix_7\":\"7\"}}")
471 
472                                       .build());
473 
474         // singleSdkByte SetOfSdkBytes ListOfSdkBytes and Map of SdkBytes
475         testDataList.add(dataBuilder().scenario("bytesSet")
476                              .isGeneric(false)
477                                       .ddbItemMap(
478                                           map()
479                                               .withKeyValue("bytes", AttributeValue.fromB(SdkBytes.fromUtf8String("HelloWorld")))
480                                               .withKeyValue("setOfBytes", AttributeValue.fromBs(
481                                                                 Arrays.asList(SdkBytes.fromUtf8String("one"),
482                                                                               SdkBytes.fromUtf8String("two"),
483                                                                               SdkBytes.fromUtf8String("three"))))
484                                               .withKeyValue("listOfBytes", AttributeValue.fromL(
485                                                   Arrays.asList(SdkBytes.fromUtf8String("i1"),
486                                                                 SdkBytes.fromUtf8String("i2"),
487                                                                 SdkBytes.fromUtf8String("i3")).stream().map(
488                                                                     s -> AttributeValue.fromB(s)).collect(Collectors.toList())))
489                                               .withKeyValue("mapOfBytes", AttributeValue.fromM(
490                                                   Stream.of(Pair.of("k1", AttributeValue.fromB(SdkBytes.fromUtf8String("v1")))
491                                                             ,Pair.of("k2", AttributeValue.fromB(SdkBytes.fromUtf8String("v2"))))
492                                                         .collect(Collectors.toMap(k->k.left(),
493                                                                                   r ->r.right(),
494                                                                                   (oldV, newV)-> oldV,
495                                                                                   LinkedHashMap::new)
496 
497 
498                                               ))).get())
499                                       .enhancedDocument(
500                                           defaultDocBuilder()
501                                               .putBytes("bytes", SdkBytes.fromUtf8String("HelloWorld"))
502                                               .putBytesSet("setOfBytes",
503                                                   Arrays.asList(SdkBytes.fromUtf8String("one"),
504                                                             SdkBytes.fromUtf8String("two"),
505                                                             SdkBytes.fromUtf8String("three")).stream()
506                                                       .collect(Collectors.toCollection(LinkedHashSet::new))
507                                               )
508                                               .putList("listOfBytes",
509                                                        Arrays.asList(SdkBytes.fromUtf8String("i1"),
510                                                                  SdkBytes.fromUtf8String("i2"),
511                                                                  SdkBytes.fromUtf8String("i3"))
512                                                        ,EnhancedType.of(SdkBytes.class)
513                                               )
514                                               .putMap("mapOfBytes"
515                                                   , Stream.of(Pair.of("k1", SdkBytes.fromUtf8String("v1"))
516                                                             ,Pair.of("k2", SdkBytes.fromUtf8String("v2")))
517                                                                 .collect(Collectors.toMap(k->k.left(),
518                                                                                           r ->r.right(),
519                                                                                           (oldV, newV)-> oldV,
520                                                                          LinkedHashMap::new)
521 
522                                               ), EnhancedType.of(String.class), EnhancedType.of(SdkBytes.class))
523 
524                                               .build()
525                                       )
526                                       .json("{\"bytes\":\"HelloWorld\",\"setOfBytes\":[\"one\",\"two\",\"three\"], "
527                                             + "\"listOfBytes\":[\"i1\",\"i2\",\"i3\"],\"mapOfBytes\": {\"k1\":\"v1\","
528                                             + "\"k2\":\"v2\"}}")
529                              .attributeConverterProvider(defaultProvider())
530                              .typeMap(typeMap()
531                                           .addAttribute("listOfBytes", EnhancedType.of(SdkBytes.class))
532                                           .addAttribute("mapOfBytes", EnhancedType.of(String.class),
533                                                         EnhancedType.of(SdkBytes.class)))
534                                       .build());
535         testScenarioMap = testDataList.stream().collect(Collectors.toMap(TestData::getScenario, Function.identity()));
536 
537         // testScenarioMap = testDataList.stream().collect(Collectors.toMap(k->k.getScenario(), Function.identity()));
538     }
539 
dataForScenario(String scenario)540     public TestData dataForScenario(String scenario) {
541         return testScenarioMap.get(scenario);
542     }
543 
getAllGenericScenarios()544     public List<TestData> getAllGenericScenarios() {
545         return testScenarioMap.values().stream().filter(testData -> testData.isGeneric()).collect(Collectors.toList());
546     }
547 
548     public static class AttributeStringValueMap {
549         private Map<String, AttributeValue> attributeValueMap = new LinkedHashMap<>();
550 
get()551         public Map<String, AttributeValue> get() {
552             return attributeValueMap;
553         }
554 
withKeyValue(String key, AttributeValue value)555         public AttributeStringValueMap withKeyValue(String key, AttributeValue value) {
556             attributeValueMap.put(key, value);
557             return this;
558         }
559     }
560 
561     /**
562      *
563      * @param offset from base elements to differentiate the subsequent elements
564      * @param base Start of the foest element
565      * @param includeSets While testing FromJson, sets are excluded since Json treats sets as lists
566      * @return Map with Key - value as String, AttributeValue>
567      */
568     /**
569      * Creates a map of attribute values for a custom class with a prefix added to each key.
570      *
571      * @param offset The offset from the base element to differentiate subsequent elements.
572      * @param base The start index of the first element.
573      * @param excludeSetsInMap Whether to exclude sets when creating the map. (Json treats sets as lists.)
574      * @return A map with key-value pairs of String and AttributeValue.
575      */
getAttributeValueMapForCustomClassWithPrefix(int offset, int base, boolean excludeSetsInMap)576     private static Map<String, AttributeValue> getAttributeValueMapForCustomClassWithPrefix(int offset, int base,
577                                                                                             boolean excludeSetsInMap) {
578 
579         Map<String, AttributeValue> map = new LinkedHashMap<>();
580         map.put("instantList",
581                 AttributeValue.fromL(Stream.of(
582                                                ofEpochMilli(FIXED_INSTANT_TIME + base +offset) ,ofEpochMilli(FIXED_INSTANT_TIME + base + offset),
583                                                ofEpochMilli(FIXED_INSTANT_TIME + base + offset))
584                                            .map(r -> AttributeValue.fromS(String.valueOf(r))).collect(Collectors.toList())));
585         map.put("longNumber", AttributeValue.fromN(String.valueOf(base + offset)));
586         map.put("string", AttributeValue.fromS(String.valueOf(base + offset)));
587         if(! excludeSetsInMap){
588             map.put("stringSet",
589                     AttributeValue.fromSs(Stream.of(1 + base + offset,2 + base +offset, 3 + base +offset).map(r -> String.valueOf(r)).collect(Collectors.toList())));
590 
591         }
592         return map;
593     }
594 
getCustomClassForDocumentAPIWithBaseAndOffset(int offset, int base)595     private static CustomClassForDocumentAPI getCustomClassForDocumentAPIWithBaseAndOffset(int offset, int base) {
596 
597         return CustomClassForDocumentAPI.builder()
598                                         .instantList(Stream.of(
599                                                                ofEpochMilli(FIXED_INSTANT_TIME + base + offset),
600                                                                ofEpochMilli(FIXED_INSTANT_TIME + base + offset),
601                                                                ofEpochMilli(FIXED_INSTANT_TIME + base + offset))
602                                                            .collect(Collectors.toList()))
603                                         .longNumber(Long.valueOf(base + offset))
604                                         .string(String.valueOf(base + offset))
605                                         .stringSet(Stream.of(1+ base + offset, 2 +base + offset, 3 + base  + offset).map(String::valueOf).collect(Collectors.toCollection(LinkedHashSet::new)))
606                                         .build();
607 
608     }
609 
610     /**
611      * getStringListAttributeValueMap("lvl_1", 3, 2)
612      * {
613      *          key_lvl_1_1=AttributeValue(L=[AttributeValue(N=1), AttributeValue(N=2)]),
614      *          key_lvl_1_2=AttributeValue(L=[AttributeValue(N=1), AttributeValue(N=2)]),
615      *          key_lvl_1_3=AttributeValue(L=[AttributeValue(N=1), AttributeValue(N=2)])
616      * }
617      */
getStringListAttributeValueMap(String suffixKey, int numberOfKeys , int nestedListLength )618     private static Map<String, AttributeValue> getStringListAttributeValueMap(String suffixKey, int numberOfKeys ,
619                                                                               int nestedListLength ) {
620 
621         Map<String, AttributeValue> result = new LinkedHashMap<>();
622         IntStream.range(1, numberOfKeys + 1).forEach(n->
623                                                          result.put(String.format("key_%s_%d",suffixKey,n),
624                                                                     AttributeValue.fromL(IntStream.range(1, nestedListLength+1).mapToObj(numb -> AttributeValue.fromN(String.valueOf(numb))).collect(Collectors.toList())))
625         );
626         return result;
627     }
628 
629     /**
630      * getStringListObjectMap("lvl_1", 3, 2)
631      * {
632      *  key_lvl_1_1=[1,2],
633      *  key_lvl_1_2=[1,2],
634      *  key_lvl_1_3=[1,2]
635      * }
636      */
getStringListObjectMap(String suffixKey, int numberOfKeys , int nestedListLength )637     private static Map<String, List<Integer>> getStringListObjectMap(String suffixKey, int numberOfKeys ,
638                                                                      int nestedListLength )  {
639         Map<String, List<Integer>> result = new LinkedHashMap<>();
640         IntStream.range(1, numberOfKeys + 1).forEach( n->
641                                                           result.put(String.format("key_%s_%d",suffixKey,n),
642                                                                      IntStream.range(1, nestedListLength+1).mapToObj(numb -> Integer.valueOf(numb)).collect(Collectors.toList()))
643         );
644         return result;
645     }
646 
647     @Override
provideArguments(ExtensionContext context)648     public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
649         return testDataInstance().getAllGenericScenarios().stream().map(Arguments::of);
650     }
651 
652     /**
653      * {
654      *          key_suffix_1=AttributeValue(S=1),
655      *          key_suffix_2=AttributeValue(S=2),
656      *          key_suffix_3=AttributeValue(S=3)
657      * }
658      */
getStringSimpleAttributeValueMap(String suffix, int numberOfElements)659     private static Map<String, AttributeValue> getStringSimpleAttributeValueMap(String suffix, int numberOfElements) {
660 
661         return IntStream.range(1, numberOfElements + 1)
662                         .boxed()
663                         .collect(Collectors.toMap(n -> String.format("key_%s_%d", suffix, n),
664                                                   n -> AttributeValue.fromS(String.valueOf(n))
665                             , (oldValue, newValue) -> oldValue, LinkedHashMap::new));
666 
667     }
668 
customValueWithBaseAndOffset(int offset, int base)669     private static CustomClassForDocumentAPI customValueWithBaseAndOffset(int offset, int base) {
670 
671         return CustomClassForDocumentAPI.builder()
672                                         .instantList(Stream.of(
673                                             ofEpochMilli(FIXED_INSTANT_TIME + base +offset) ,ofEpochMilli(FIXED_INSTANT_TIME + base + offset),
674                                             ofEpochMilli(FIXED_INSTANT_TIME + base + offset)).collect(Collectors.toList()))
675                                         .longNumber(Long.valueOf(base + offset))
676                                         .string(String.valueOf(base + offset))
677                                         .stringSet(Stream.of(1 + base + offset,2 + base +offset, 3 + base +offset).map(r -> String.valueOf(r)).collect(Collectors.toCollection(LinkedHashSet::new)))
678 
679                                         .build();
680 
681     }
682 
683     /**
684      * getStringSimpleMap("suffix", 2, CharSequenceStringConverter.create()))
685      *{
686      *      key_suffix_1=1,
687      *      key_suffix_2=2
688      *}
689      */
getStringSimpleMap(String suffix, int numberOfElements, StringConverter<T> stringConverter)690     private static <T>Map<T, String> getStringSimpleMap(String suffix, int numberOfElements, StringConverter<T> stringConverter) {
691         return IntStream.range(1, numberOfElements + 1)
692                         .boxed()
693                         .collect(Collectors.toMap(
694                             n ->  stringConverter.fromString(String.format("key_%s_%d", suffix, n)),
695                             n -> String.valueOf(n),
696                             (key, value) -> key, // merge function to handle collisions
697                             LinkedHashMap::new
698                         ));
699     }
700 
mapFromSimpleKeyValue(Pair<String, T>....keyValuePair)701     private <T> Map<String, T> mapFromSimpleKeyValue(Pair<String, T>...keyValuePair) {
702         return Stream.of(keyValuePair)
703                      .collect(Collectors.toMap(k ->k.left(),
704                                                v ->v.right(),
705                                                (oldValue, newValue) -> oldValue,
706                                                LinkedHashMap::new));
707     }
708 }
709