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