
software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils Maven / Gradle / Ivy
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package software.amazon.awssdk.enhanced.dynamodb.internal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClientExtension;
import software.amazon.awssdk.enhanced.dynamodb.Key;
import software.amazon.awssdk.enhanced.dynamodb.OperationContext;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.extensions.ReadModification;
import software.amazon.awssdk.enhanced.dynamodb.internal.extensions.DefaultDynamoDbExtensionContext;
import software.amazon.awssdk.enhanced.dynamodb.model.Page;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
@SdkInternalApi
public final class EnhancedClientUtils {
private static final Set SPECIAL_CHARACTERS = Stream.of(
'*', '.', '-', '#', '+', ':', '/', '(', ')',
'&', '<', '>', '?', '=', '!', '@', '%', '$', '|').collect(Collectors.toSet());
private EnhancedClientUtils() {
}
/** There is a divergence in what constitutes an acceptable attribute name versus a token used in expression
* names or values. Since the mapper translates one to the other, it is necessary to scrub out all these
* 'illegal' characters before adding them to expression values or expression names.
*
* @param key A key that may contain non alpha-numeric characters acceptable to a DynamoDb attribute name.
* @return A key that has all these characters scrubbed and overwritten with an underscore.
*/
public static String cleanAttributeName(String key) {
boolean somethingChanged = false;
char[] chars = key.toCharArray();
for (int i = 0; i < chars.length; ++i) {
if (SPECIAL_CHARACTERS.contains(chars[i])) {
chars[i] = '_';
somethingChanged = true;
}
}
return somethingChanged ? new String(chars) : key;
}
/**
* Creates a key token to be used with an ExpressionNames map.
*/
public static String keyRef(String key) {
return "#AMZN_MAPPED_" + cleanAttributeName(key);
}
/**
* Creates a value token to be used with an ExpressionValues map.
*/
public static String valueRef(String value) {
return ":AMZN_MAPPED_" + cleanAttributeName(value);
}
public static T readAndTransformSingleItem(Map itemMap,
TableSchema tableSchema,
OperationContext operationContext,
DynamoDbEnhancedClientExtension dynamoDbEnhancedClientExtension) {
if (itemMap == null || itemMap.isEmpty()) {
return null;
}
if (dynamoDbEnhancedClientExtension != null) {
ReadModification readModification = dynamoDbEnhancedClientExtension.afterRead(
DefaultDynamoDbExtensionContext.builder()
.items(itemMap)
.tableSchema(tableSchema)
.operationContext(operationContext)
.tableMetadata(tableSchema.tableMetadata())
.build());
if (readModification != null && readModification.transformedItem() != null) {
return tableSchema.mapToItem(readModification.transformedItem());
}
}
return tableSchema.mapToItem(itemMap);
}
public static Page readAndTransformPaginatedItems(
ResponseT response,
TableSchema tableSchema,
OperationContext operationContext,
DynamoDbEnhancedClientExtension dynamoDbEnhancedClientExtension,
Function>> getItems,
Function> getLastEvaluatedKey) {
if (getLastEvaluatedKey.apply(response) == null || getLastEvaluatedKey.apply(response).isEmpty()) {
// Last page
return Page.create(getItems.apply(response)
.stream()
.map(itemMap -> readAndTransformSingleItem(itemMap,
tableSchema,
operationContext,
dynamoDbEnhancedClientExtension))
.collect(Collectors.toList()));
} else {
// More pages to come; add the lastEvaluatedKey
return Page.create(getItems.apply(response)
.stream()
.map(itemMap -> readAndTransformSingleItem(itemMap,
tableSchema,
operationContext,
dynamoDbEnhancedClientExtension))
.collect(Collectors.toList()),
getLastEvaluatedKey.apply(response));
}
}
public static Key createKeyFromItem(T item, TableSchema tableSchema, String indexName) {
String partitionKeyName = tableSchema.tableMetadata().indexPartitionKey(indexName);
Optional sortKeyName = tableSchema.tableMetadata().indexSortKey(indexName);
AttributeValue partitionKeyValue = tableSchema.attributeValue(item, partitionKeyName);
Optional sortKeyValue = sortKeyName.map(key -> tableSchema.attributeValue(item, key));
return sortKeyValue.map(
attributeValue -> Key.builder()
.partitionValue(partitionKeyValue)
.sortValue(attributeValue)
.build())
.orElseGet(
() -> Key.builder()
.partitionValue(partitionKeyValue).build());
}
public static Key createKeyFromMap(Map itemMap,
TableSchema> tableSchema,
String indexName) {
String partitionKeyName = tableSchema.tableMetadata().indexPartitionKey(indexName);
Optional sortKeyName = tableSchema.tableMetadata().indexSortKey(indexName);
AttributeValue partitionKeyValue = itemMap.get(partitionKeyName);
Optional sortKeyValue = sortKeyName.map(itemMap::get);
return sortKeyValue.map(
attributeValue -> Key.builder()
.partitionValue(partitionKeyValue)
.sortValue(attributeValue)
.build())
.orElseGet(
() -> Key.builder()
.partitionValue(partitionKeyValue).build());
}
public static List getItemsFromSupplier(List> itemSupplierList) {
if (itemSupplierList == null || itemSupplierList.isEmpty()) {
return null;
}
return Collections.unmodifiableList(itemSupplierList.stream()
.map(Supplier::get)
.collect(Collectors.toList()));
}
/**
* A helper method to test if an {@link AttributeValue} is a 'null' constant. This will not test if the
* AttributeValue object is null itself, and in fact will throw a NullPointerException if you pass in null.
* @param attributeValue An {@link AttributeValue} to test for null.
* @return true if the supplied AttributeValue represents a null value, or false if it does not.
*/
public static boolean isNullAttributeValue(AttributeValue attributeValue) {
return attributeValue.nul() != null && attributeValue.nul();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy