Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Commit

Permalink
Merge ba33c73 into 400c58a
Browse files Browse the repository at this point in the history
  • Loading branch information
msgroi committed Aug 25, 2018
2 parents 400c58a + ba33c73 commit a380771
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,12 @@ public CreateTableResult createTable(CreateTableRequest createTableRequest) {
return super.createTable(createTableRequest);
}

/**
* deleteItem logging wrapper.
*/
public DeleteItemResult deleteItem(DeleteItemRequest deleteItemRequest) {
log("deleteItem", tableToString(deleteItemRequest.getTableName()), keyToString(deleteItemRequest.getKey()));
log("deleteItem", tableToString(deleteItemRequest.getTableName()),
deleteItemRequestToString(deleteItemRequest));
return super.deleteItem(deleteItemRequest);
}

Expand Down Expand Up @@ -208,6 +212,19 @@ private String updateItemRequestToString(UpdateItemRequest updateRequest) {
: "");
}

private String deleteItemRequestToString(DeleteItemRequest deleteItemRequest) {
return "key=" + deleteItemRequest.getKey()
+ (deleteItemRequest.getConditionExpression() != null
? ", conditionExpression=" + deleteItemRequest.getConditionExpression()
: "")
+ (deleteItemRequest.getExpressionAttributeNames() != null
? ", names=" + deleteItemRequest.getExpressionAttributeNames()
: "")
+ (deleteItemRequest.getExpressionAttributeValues() != null
? ", values=" + deleteItemRequest.getExpressionAttributeValues()
: "");
}

private void log(String method, String... messages) {
if (logAll || methodsToLog.contains(method)) {
String concatenatedMessage = "method=" + method + "(), " + Joiner.on(", ").join(messages);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;

/**
* Applies mapping and prefixing to condition query and conditional update expressions.
Expand Down Expand Up @@ -79,14 +79,18 @@ void applyKeyConditionToField(RequestWrapper request,
.filter(entry -> entry.getValue().equals(virtualAttrName)).map(Entry::getKey).findAny()
: Optional.empty(); // Optional[#field1]
if (keyFieldName.isPresent() && !keyFieldName.get().equals(NAME_PLACEHOLDER)) {
String virtualValuePlaceholder = findVirtualValuePlaceholder(primaryExpression, filterExpression,
keyFieldName.get()); // ":value"
AttributeValue virtualAttr =
request.getExpressionAttributeValues().get(virtualValuePlaceholder); // {S: hkvalue,}
AttributeValue physicalAttr = fieldMapping.isContextAware()
? fieldMapper.apply(fieldMapping, virtualAttr) // {S: ctx.virtualTable.hkvalue,}
: virtualAttr;
request.putExpressionAttributeValue(virtualValuePlaceholder, physicalAttr);
Optional<String> virtualValuePlaceholderOpt =
findVirtualValuePlaceholder(primaryExpression, filterExpression, keyFieldName.get()); // ":value"
if (virtualValuePlaceholderOpt.isPresent()) {
String virtualValuePlaceholder = virtualValuePlaceholderOpt.get();
AttributeValue virtualAttr =
request.getExpressionAttributeValues().get(virtualValuePlaceholder); // {S: hkvalue,}
AttributeValue physicalAttr =
fieldMapping.isContextAware()
? fieldMapper.apply(fieldMapping, virtualAttr) // {S: ctx.virtualTable.hkvalue,}
: virtualAttr;
request.putExpressionAttributeValue(virtualValuePlaceholder, physicalAttr);
}
request.putExpressionAttributeName(keyFieldName.get(), fieldMapping.getTarget().getName());
}
}
Expand Down Expand Up @@ -142,14 +146,15 @@ static String getNextFieldPlaceholder(Map<String, String> expressionAttributeNam
* primary expression, then in the filterExpression.
*/
@VisibleForTesting
static String findVirtualValuePlaceholder(String primaryExpression,
static Optional<String> findVirtualValuePlaceholder(String primaryExpression,
String filterExpression,
String keyFieldName) {
return findVirtualValuePlaceholder(primaryExpression, keyFieldName)
.orElseGet((Supplier<String>) () -> findVirtualValuePlaceholder(filterExpression, keyFieldName)
.orElseThrow((Supplier<IllegalArgumentException>) () ->
new IllegalArgumentException("field " + keyFieldName + " not found in either conditionExpression="
+ primaryExpression + ", or filterExpression=" + filterExpression)));
return Stream.of(
findVirtualValuePlaceholder(primaryExpression, keyFieldName),
findVirtualValuePlaceholder(filterExpression, keyFieldName))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
}

/**
Expand All @@ -158,11 +163,12 @@ static String findVirtualValuePlaceholder(String primaryExpression,
* <p>Comments show expected variable values with a sample set of inputs.
*/
@VisibleForTesting
static Optional<String> findVirtualValuePlaceholder(
String conditionExpression, // "#field1 = :value"
String keyFieldName) { // "#field1"
String toFind = keyFieldName + " = "; // "#field1 = "
int start = conditionExpression.indexOf(toFind); // 0
static Optional<String> findVirtualValuePlaceholder(String conditionExpression, String keyFieldName) {
if (conditionExpression == null) {
return Optional.empty();
}
String toFind = keyFieldName + " = ";
int start = conditionExpression.indexOf(toFind);
if (start == -1) {
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ public DeleteItemResult deleteItem(DeleteItemRequest deleteItemRequest) {
// map key
deleteItemRequest.setKey(tableMapping.getItemMapper().apply(deleteItemRequest.getKey()));

// map conditions
tableMapping.getConditionMapper().apply(new DeleteItemRequestWrapper(deleteItemRequest));

// delete
return getAmazonDynamoDb().deleteItem(deleteItemRequest);
}
Expand Down Expand Up @@ -448,56 +451,147 @@ private static Map<String, AttributeValue> getKeyFromItem(Map<String, AttributeV

private static class UpdateItemRequestWrapper implements RequestWrapper {

private final UpdateItemRequest updateItemRequestCloned;
private final UpdateItemRequest updateItemRequest;

UpdateItemRequestWrapper(UpdateItemRequest updateItemRequest) {
this.updateItemRequest = updateItemRequest;
}

@Override
public Map<String, String> getExpressionAttributeNames() {
return updateItemRequest.getExpressionAttributeNames();
}

@Override
public void putExpressionAttributeName(String key, String value) {
if (updateItemRequest.getExpressionAttributeNames() == null) {
updateItemRequest.setExpressionAttributeNames(new HashMap<>());
}
updateItemRequest.getExpressionAttributeNames().put(key, value);
}

@Override
public Map<String, AttributeValue> getExpressionAttributeValues() {
if (updateItemRequest.getExpressionAttributeValues() == null) {
updateItemRequest.setExpressionAttributeValues(new HashMap<>());
}
return updateItemRequest.getExpressionAttributeValues();
}

@Override
public void putExpressionAttributeValue(String key, AttributeValue value) {
updateItemRequest.getExpressionAttributeValues().put(key, value);
}

@Override
public String getPrimaryExpression() {
return updateItemRequest.getUpdateExpression();
}

@Override
public void setPrimaryExpression(String expression) {
updateItemRequest.setUpdateExpression(expression);
}

@Override
public String getFilterExpression() {
return updateItemRequest.getConditionExpression();
}

@Override
public void setFilterExpression(String conditionalExpression) {
updateItemRequest.setConditionExpression(conditionalExpression);
}

@Override
public String getIndexName() {
throw new UnsupportedOperationException();
}

UpdateItemRequestWrapper(UpdateItemRequest updateItemRequestCloned) {
this.updateItemRequestCloned = updateItemRequestCloned;
@Override
public void setIndexName(String indexName) {
throw new UnsupportedOperationException();
}

@Override
public Map<String, Condition> getLegacyExpression() {
throw new UnsupportedOperationException();
}

@Override
public void clearLegacyExpression() {
throw new UnsupportedOperationException();
}

@Override
public Map<String, AttributeValue> getExclusiveStartKey() {
throw new UnsupportedOperationException();
}

@Override
public void setExclusiveStartKey(Map<String, AttributeValue> exclusiveStartKey) {
throw new UnsupportedOperationException();
}

}

private static class DeleteItemRequestWrapper implements RequestWrapper {

private final DeleteItemRequest deleteItemRequest;

DeleteItemRequestWrapper(DeleteItemRequest deleteItemRequest) {
this.deleteItemRequest = deleteItemRequest;
if (this.deleteItemRequest.getExpressionAttributeNames() != null) {
this.deleteItemRequest.setExpressionAttributeNames(new HashMap<>(this.getExpressionAttributeNames()));
}
if (this.deleteItemRequest.getExpressionAttributeValues() != null) {
this.deleteItemRequest.setExpressionAttributeValues(new HashMap<>(this.getExpressionAttributeValues()));
}
}

@Override
public Map<String, String> getExpressionAttributeNames() {
return updateItemRequestCloned.getExpressionAttributeNames();
return deleteItemRequest.getExpressionAttributeNames();
}

@Override
public void putExpressionAttributeName(String key, String value) {
if (updateItemRequestCloned.getExpressionAttributeNames() == null) {
updateItemRequestCloned.setExpressionAttributeNames(new HashMap<>());
if (deleteItemRequest.getExpressionAttributeNames() == null) {
deleteItemRequest.setExpressionAttributeNames(new HashMap<>());
}
updateItemRequestCloned.getExpressionAttributeNames().put(key, value);
deleteItemRequest.getExpressionAttributeNames().put(key, value);
}

@Override
public Map<String, AttributeValue> getExpressionAttributeValues() {
if (updateItemRequestCloned.getExpressionAttributeValues() == null) {
updateItemRequestCloned.setExpressionAttributeValues(new HashMap<>());
if (deleteItemRequest.getExpressionAttributeValues() == null) {
deleteItemRequest.setExpressionAttributeValues(new HashMap<>());
}
return updateItemRequestCloned.getExpressionAttributeValues();
return deleteItemRequest.getExpressionAttributeValues();
}

@Override
public void putExpressionAttributeValue(String key, AttributeValue value) {
updateItemRequestCloned.getExpressionAttributeValues().put(key, value);
deleteItemRequest.getExpressionAttributeValues().put(key, value);
}

@Override
public String getPrimaryExpression() {
return updateItemRequestCloned.getUpdateExpression();
return deleteItemRequest.getConditionExpression();
}

@Override
public void setPrimaryExpression(String expression) {
updateItemRequestCloned.setUpdateExpression(expression);
deleteItemRequest.setConditionExpression(expression);
}

@Override
public String getFilterExpression() {
return updateItemRequestCloned.getConditionExpression();
return null;
}

@Override
public void setFilterExpression(String conditionalExpression) {
updateItemRequestCloned.setConditionExpression(conditionalExpression);
}

@Override
Expand Down
55 changes: 55 additions & 0 deletions src/test/java/com/salesforce/dynamodbv2/DeleteTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import static com.salesforce.dynamodbv2.testsupport.DefaultTestSetup.TABLE1;
import static com.salesforce.dynamodbv2.testsupport.DefaultTestSetup.TABLE2;
import static com.salesforce.dynamodbv2.testsupport.DefaultTestSetup.TABLE3;
import static com.salesforce.dynamodbv2.testsupport.TestSupport.HASH_KEY_FIELD;
import static com.salesforce.dynamodbv2.testsupport.TestSupport.HASH_KEY_VALUE;
import static com.salesforce.dynamodbv2.testsupport.TestSupport.RANGE_KEY_VALUE;
import static com.salesforce.dynamodbv2.testsupport.TestSupport.SOME_FIELD;
import static com.salesforce.dynamodbv2.testsupport.TestSupport.SOME_FIELD_VALUE;
import static com.salesforce.dynamodbv2.testsupport.TestSupport.buildHkRkItemWithSomeFieldValue;
import static com.salesforce.dynamodbv2.testsupport.TestSupport.buildHkRkKey;
Expand All @@ -16,9 +18,12 @@
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.fail;

import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException;
import com.amazonaws.services.dynamodbv2.model.DeleteItemRequest;
import com.google.common.collect.ImmutableMap;
import com.salesforce.dynamodbv2.mt.context.MtAmazonDynamoDbContextProvider;
import com.salesforce.dynamodbv2.testsupport.ArgumentBuilder;
import com.salesforce.dynamodbv2.testsupport.ArgumentBuilder.TestArgument;
Expand Down Expand Up @@ -65,6 +70,56 @@ void delete(TestArgument testArgument) {
});
}

@ParameterizedTest(name = "{arguments}")
@ArgumentsSource(DefaultArgumentProvider.class)
void deleteWithAttributeExists(TestArgument testArgument) {
MT_CONTEXT.setContext(testArgument.getOrgs().get(0));
testArgument.getAmazonDynamoDb().deleteItem(new DeleteItemRequest()
.withTableName(TABLE1).withKey(buildKey(testArgument.getHashKeyAttrType()))
.withConditionExpression("attribute_exists(#field)")
.withExpressionAttributeNames(ImmutableMap.of("#field", SOME_FIELD)));
assertNull(getItem(testArgument.getHashKeyAttrType(),
testArgument.getAmazonDynamoDb(), TABLE1, HASH_KEY_VALUE));
}

@ParameterizedTest(name = "{arguments}")
@ArgumentsSource(DefaultArgumentProvider.class)
void deleteWithAttributeExistsWithLiterals(TestArgument testArgument) {
MT_CONTEXT.setContext(testArgument.getOrgs().get(0));
testArgument.getAmazonDynamoDb().deleteItem(new DeleteItemRequest()
.withTableName(TABLE1).withKey(buildKey(testArgument.getHashKeyAttrType()))
.withConditionExpression("attribute_exists(" + SOME_FIELD + ")"));
assertNull(getItem(testArgument.getHashKeyAttrType(),
testArgument.getAmazonDynamoDb(), TABLE1, HASH_KEY_VALUE));
}

@ParameterizedTest(name = "{arguments}")
@ArgumentsSource(DefaultArgumentProvider.class)
void deleteWithAttributeExistsFails(TestArgument testArgument) {
MT_CONTEXT.setContext(testArgument.getOrgs().get(0));
try {
testArgument.getAmazonDynamoDb().deleteItem(new DeleteItemRequest()
.withTableName(TABLE1).withKey(buildKey(testArgument.getHashKeyAttrType()))
.withConditionExpression("attribute_exists(#field)")
.withExpressionAttributeNames(ImmutableMap.of("#field", "someNonExistentField")));
fail("expected ConditionalCheckFailedException not encountered");
} catch (ConditionalCheckFailedException ignore) {
// expected
}
}

@ParameterizedTest(name = "{arguments}")
@ArgumentsSource(DefaultArgumentProvider.class)
void deleteWithHkAttributeExists(TestArgument testArgument) {
MT_CONTEXT.setContext(testArgument.getOrgs().get(0));
testArgument.getAmazonDynamoDb().deleteItem(new DeleteItemRequest()
.withTableName(TABLE1).withKey(buildKey(testArgument.getHashKeyAttrType()))
.withConditionExpression("attribute_exists(#field)")
.withExpressionAttributeNames(ImmutableMap.of("#field", HASH_KEY_FIELD)));
assertNull(getItem(testArgument.getHashKeyAttrType(),
testArgument.getAmazonDynamoDb(), TABLE1, HASH_KEY_VALUE));
}

@ParameterizedTest(name = "{arguments}")
@ArgumentsSource(DefaultArgumentProvider.class)
void deleteHkRkTable(TestArgument testArgument) {
Expand Down

0 comments on commit a380771

Please sign in to comment.