Skip to content
This repository has been archived by the owner on Jul 27, 2023. It is now read-only.

Commit

Permalink
Add support for Check-And-Set option to DELETE operation (#178)
Browse files Browse the repository at this point in the history
* Add eclipse project files to .gitignore

* Add support for Check-And-Set option to DELETE operation.
  • Loading branch information
bmahe authored and rickfast committed Oct 23, 2016
1 parent 3b02b5c commit ec8b90e
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ gradle/gradlew
gradle/gradlew.bat
gradle/gradle
src/gradle
.classpath
.project
.settings
25 changes: 19 additions & 6 deletions src/main/java/com/orbitz/consul/KeyValueClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.orbitz.consul.model.ConsulResponse;
import com.orbitz.consul.model.kv.Value;
import com.orbitz.consul.model.session.SessionInfo;
import com.orbitz.consul.option.DeleteOptions;
import com.orbitz.consul.option.ImmutablePutOptions;
import com.orbitz.consul.option.PutOptions;
import com.orbitz.consul.option.QueryOptions;
Expand Down Expand Up @@ -275,7 +276,7 @@ public List<String> getKeys(String key) {
* @param key The key to delete.
*/
public void deleteKey(String key) {
handle(api.deleteValue(trimLeadingSlash(key)));
deleteKey(key, DeleteOptions.BLANK);
}

/**
Expand All @@ -286,7 +287,22 @@ public void deleteKey(String key) {
* @param key The key to delete.
*/
public void deleteKeys(String key) {
handle(api.deleteValues(trimLeadingSlash(key), ImmutableMap.of("recurse", "true")));
deleteKey(key, DeleteOptions.RECURSE);
}

/**
* Deletes a specified key.
*
* DELETE /v1/kv/{key}
*
* @param key The key to delete.
* @param deleteOptions DELETE options (e.g. recurse, cas)
*/
public void deleteKey(String key, DeleteOptions deleteOptions) {
checkArgument(StringUtils.isNotEmpty(key), "Key must be defined");
Map<String, Object> query = deleteOptions.toQuery();

handle(api.deleteValues(trimLeadingSlash(key), query));
}

/**
Expand Down Expand Up @@ -365,11 +381,8 @@ Call<Boolean> putValue(@Path("key") String key,
@Body RequestBody data,
@QueryMap Map<String, Object> query);

@DELETE("kv/{key}")
Call<Void> deleteValue(@Path("key") String key);

@DELETE("kv/{key}")
Call<Void> deleteValues(@Path("key") String key,
@QueryMap Map<String, String> query);
@QueryMap Map<String, Object> query);
}
}
39 changes: 39 additions & 0 deletions src/main/java/com/orbitz/consul/option/DeleteOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.orbitz.consul.option;

import static com.orbitz.consul.option.Options.optionallyAdd;

import java.util.HashMap;
import java.util.Map;

import org.immutables.value.Value;

import com.google.common.base.Optional;

@Value.Immutable
public abstract class DeleteOptions implements ParamAdder {

public static final DeleteOptions BLANK = ImmutableDeleteOptions.builder().build();
public static final DeleteOptions RECURSE = ImmutableDeleteOptions.builder().recurse(true).build();

public abstract Optional<Long> getCas();

public abstract Optional<Boolean> getRecurse();

@Value.Derived
public boolean isRecurse() {
return getRecurse().isPresent();
}

@Override
public Map<String, Object> toQuery() {
final Map<String, Object> result = new HashMap<>();

if (isRecurse()) {
result.put("recurse", "");
}

optionallyAdd(result, "cas", getCas());

return result;
}
}
47 changes: 47 additions & 0 deletions src/test/java/com/orbitz/consul/KeyValueTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import com.orbitz.consul.model.kv.Value;
import com.orbitz.consul.model.session.ImmutableSession;
import com.orbitz.consul.model.session.SessionCreatedResponse;
import com.orbitz.consul.option.ImmutableDeleteOptions;
import com.orbitz.consul.option.ImmutableDeleteOptions.Builder;
import com.orbitz.consul.option.QueryOptions;
import org.junit.Test;

Expand Down Expand Up @@ -125,6 +127,51 @@ public void shouldDeleteRecursively() throws Exception {

}

@Test
public void shouldDeleteCas() throws Exception {
KeyValueClient keyValueClient = client.keyValueClient();
String key = UUID.randomUUID().toString();
final String value = UUID.randomUUID().toString();

/**
* Update the value twice and remember the value at each step
*/
keyValueClient.putValue(key, value);

final Optional<Value> valueAfter1stPut = keyValueClient.getValue(key);
assertTrue(valueAfter1stPut.isPresent());
assertTrue(valueAfter1stPut.get().getValueAsString().isPresent());

keyValueClient.putValue(key, UUID.randomUUID().toString());

final Optional<Value> valueAfter2ndPut = keyValueClient.getValue(key);
assertTrue(valueAfter2ndPut.isPresent());
assertTrue(valueAfter2ndPut.get().getValueAsString().isPresent());

/**
* Trying to delete the key once with the older lock, which should not
* work
*/
final Builder deleteOptionsBuilderWithOlderLock = ImmutableDeleteOptions.builder();
deleteOptionsBuilderWithOlderLock.cas(valueAfter1stPut.get().getModifyIndex());
final ImmutableDeleteOptions deleteOptionsWithOlderLock = deleteOptionsBuilderWithOlderLock.build();

keyValueClient.deleteKey(key, deleteOptionsWithOlderLock);

assertTrue(keyValueClient.getValueAsString(key).isPresent());

/**
* Deleting the key with the most recent lock, which should work
*/
final Builder deleteOptionsBuilderWithLatestLock = ImmutableDeleteOptions.builder();
deleteOptionsBuilderWithLatestLock.cas(valueAfter2ndPut.get().getModifyIndex());
final ImmutableDeleteOptions deleteOptionsWithLatestLock = deleteOptionsBuilderWithLatestLock.build();

keyValueClient.deleteKey(key, deleteOptionsWithLatestLock);

assertFalse(keyValueClient.getValueAsString(key).isPresent());
}

@Test
public void acquireAndReleaseLock() throws Exception {
KeyValueClient keyValueClient = client.keyValueClient();
Expand Down

0 comments on commit ec8b90e

Please sign in to comment.