From df38eb2ff437f173ad2adf208da699393e2bee8c Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 05 Jul 2013 21:41:32 +0000
Subject: [PATCH] Fix OPENDJ-1044: Doing a PUT with JSON data identical to what is on the server results in 400 status code
---
opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java | 295 +++++++++++++++++++++-----------
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java | 6
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java | 199 ++++++++++++++-------
3 files changed, 333 insertions(+), 167 deletions(-)
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
index ad470ef..80f0ef2 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
@@ -15,6 +15,7 @@
*/
package org.forgerock.opendj.rest2ldap;
+import static java.util.Arrays.asList;
import static org.forgerock.opendj.ldap.Filter.alwaysFalse;
import static org.forgerock.opendj.ldap.Filter.alwaysTrue;
import static org.forgerock.opendj.ldap.requests.Requests.newAddRequest;
@@ -211,69 +212,103 @@
final Context c = wrap(context);
final ResultHandler<Resource> h = wrap(c, handler);
- /*
- * Get the connection, search if needed, then determine modifications,
- * then perform modify.
- */
- c.run(h, doUpdate(c, resourceId, request.getRevision(), new ResultHandler<DN>() {
- @Override
- public void handleError(final ResourceException error) {
- h.handleError(error);
- }
-
- @Override
- public void handleResult(final DN dn) {
- // Convert the patch operations to LDAP modifications.
- final ResultHandler<List<Modification>> handler =
- accumulate(request.getPatchOperations().size(),
- new ResultHandler<List<List<Modification>>>() {
- @Override
- public void handleError(final ResourceException error) {
- h.handleError(error);
- }
-
- @Override
- public void handleResult(final List<List<Modification>> result) {
- // The patch operations have been converted successfully.
- try {
- final ModifyRequest modifyRequest =
- newModifyRequest(dn);
- if (config.readOnUpdatePolicy() == CONTROLS) {
- final String[] attributes =
- getLDAPAttributes(c, request.getFields());
- modifyRequest.addControl(PostReadRequestControl
- .newControl(false, attributes));
- }
- if (config.usePermissiveModify()) {
- modifyRequest
- .addControl(PermissiveModifyRequestControl
- .newControl(true));
- }
- addAssertionControl(modifyRequest, request
- .getRevision());
-
- // Add the modifications.
- for (final List<Modification> modifications : result) {
- if (modifications != null) {
- modifyRequest.getModifications().addAll(
- modifications);
- }
- }
-
- // Perform the modify request.
- c.getConnection().applyChangeAsync(modifyRequest, null,
- postUpdateHandler(c, h));
- } catch (final Exception e) {
- h.handleError(asResourceException(e));
- }
- }
- });
-
- for (final PatchOperation operation : request.getPatchOperations()) {
- attributeMapper.patch(c, new JsonPointer(), operation, handler);
+ if (request.getPatchOperations().isEmpty()) {
+ /*
+ * This patch is a no-op so just read the entry and check its
+ * version.
+ */
+ c.run(h, new Runnable() {
+ @Override
+ public void run() {
+ final String[] attributes = getLDAPAttributes(c, request.getFields());
+ final SearchRequest searchRequest =
+ nameStrategy.createSearchRequest(c, getBaseDN(c), resourceId)
+ .addAttribute(attributes);
+ c.getConnection().searchSingleEntryAsync(searchRequest,
+ postEmptyPatchHandler(c, request, h));
}
- }
- }));
+ });
+ } else {
+ /*
+ * Get the connection, search if needed, then determine
+ * modifications, then perform modify.
+ */
+ c.run(h, doUpdate(c, resourceId, request.getRevision(), new ResultHandler<DN>() {
+ @Override
+ public void handleError(final ResourceException error) {
+ h.handleError(error);
+ }
+
+ @Override
+ public void handleResult(final DN dn) {
+ // Convert the patch operations to LDAP modifications.
+ final ResultHandler<List<Modification>> handler =
+ accumulate(request.getPatchOperations().size(),
+ new ResultHandler<List<List<Modification>>>() {
+ @Override
+ public void handleError(final ResourceException error) {
+ h.handleError(error);
+ }
+
+ @Override
+ public void handleResult(
+ final List<List<Modification>> result) {
+ // The patch operations have been converted successfully.
+ try {
+ final ModifyRequest modifyRequest =
+ newModifyRequest(dn);
+
+ // Add the modifications.
+ for (final List<Modification> modifications : result) {
+ if (modifications != null) {
+ modifyRequest.getModifications().addAll(
+ modifications);
+ }
+ }
+
+ final List<String> attributes =
+ asList(getLDAPAttributes(c, request
+ .getFields()));
+ if (modifyRequest.getModifications().isEmpty()) {
+ /*
+ * This patch is a no-op so
+ * just read the entry and
+ * check its version.
+ */
+ c.getConnection().readEntryAsync(dn,
+ attributes,
+ postEmptyPatchHandler(c, request, h));
+ } else {
+ // Add controls and perform the modify request.
+ if (config.readOnUpdatePolicy() == CONTROLS) {
+ modifyRequest
+ .addControl(PostReadRequestControl
+ .newControl(false,
+ attributes));
+ }
+ if (config.usePermissiveModify()) {
+ modifyRequest
+ .addControl(PermissiveModifyRequestControl
+ .newControl(true));
+ }
+ addAssertionControl(modifyRequest, request
+ .getRevision());
+ c.getConnection().applyChangeAsync(
+ modifyRequest, null,
+ postUpdateHandler(c, h));
+ }
+ } catch (final Exception e) {
+ h.handleError(asResourceException(e));
+ }
+ }
+ });
+
+ for (final PatchOperation operation : request.getPatchOperations()) {
+ attributeMapper.patch(c, new JsonPointer(), operation, handler);
+ }
+ }
+ }));
+ }
}
@Override
@@ -545,10 +580,21 @@
public void handleResult(
final List<Modification> result) {
// Perform the modify operation.
- modifyRequest.getModifications().addAll(result);
- c.getConnection().applyChangeAsync(
- modifyRequest, null,
- postUpdateHandler(c, h));
+ if (result.isEmpty()) {
+ /*
+ * No changes to be
+ * performed, so just
+ * return the entry that
+ * we read.
+ */
+ adaptEntry(c, entry, h);
+ } else {
+ modifyRequest.getModifications().addAll(
+ result);
+ c.getConnection().applyChangeAsync(
+ modifyRequest, null,
+ postUpdateHandler(c, h));
+ }
}
});
} catch (final Exception e) {
@@ -871,6 +917,27 @@
return etagAttribute != null ? entry.parseAttribute(etagAttribute).asString() : null;
}
+ private org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry> postEmptyPatchHandler(
+ final Context c, final PatchRequest request, final ResultHandler<Resource> h) {
+ return new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
+ @Override
+ public void handleErrorResult(final ErrorResultException error) {
+ h.handleError(asResourceException(error));
+ }
+
+ @Override
+ public void handleResult(final SearchResultEntry entry) {
+ try {
+ // Fail if there is a version mismatch.
+ ensureMVCCVersionMatches(entry, request.getRevision());
+ adaptEntry(c, entry, h);
+ } catch (final Exception e) {
+ h.handleError(asResourceException(e));
+ }
+ }
+ };
+ }
+
private org.forgerock.opendj.ldap.ResultHandler<Result> postUpdateHandler(final Context c,
final ResultHandler<Resource> handler) {
// The handler which will be invoked for the LDAP add result.
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
index 3558ac3..987edf2 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
@@ -64,12 +64,16 @@
private final List<V> results;
private AccumulatingResultHandler(final int size, final ResultHandler<List<V>> handler) {
- if (size <= 0) {
+ if (size < 0) {
throw new IllegalStateException();
}
this.latch = new AtomicInteger(size);
this.results = new ArrayList<V>(size);
this.handler = handler;
+ if (size == 0) {
+ // Invoke immediately.
+ handler.handleResult(results);
+ }
}
@Override
diff --git a/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java b/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java
index 0bf227f..c195206 100644
--- a/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java
+++ b/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java
@@ -42,6 +42,8 @@
import static org.forgerock.opendj.rest2ldap.TestUtils.ctx;
import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.BadRequestException;
@@ -49,10 +51,27 @@
import org.forgerock.json.resource.NotFoundException;
import org.forgerock.json.resource.NotSupportedException;
import org.forgerock.json.resource.PreconditionFailedException;
-import org.forgerock.json.resource.RequestHandler;
import org.forgerock.json.resource.Resource;
import org.forgerock.opendj.ldap.ConnectionFactory;
+import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.MemoryBackend;
+import org.forgerock.opendj.ldap.RequestContext;
+import org.forgerock.opendj.ldap.RequestHandler;
+import org.forgerock.opendj.ldap.ResultHandler;
+import org.forgerock.opendj.ldap.SearchResultHandler;
+import org.forgerock.opendj.ldap.requests.AddRequest;
+import org.forgerock.opendj.ldap.requests.BindRequest;
+import org.forgerock.opendj.ldap.requests.CompareRequest;
+import org.forgerock.opendj.ldap.requests.DeleteRequest;
+import org.forgerock.opendj.ldap.requests.ExtendedRequest;
+import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
+import org.forgerock.opendj.ldap.requests.ModifyRequest;
+import org.forgerock.opendj.ldap.requests.Request;
+import org.forgerock.opendj.ldap.requests.SearchRequest;
+import org.forgerock.opendj.ldap.responses.BindResult;
+import org.forgerock.opendj.ldap.responses.CompareResult;
+import org.forgerock.opendj.ldap.responses.ExtendedResult;
+import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldif.LDIFEntryReader;
import org.forgerock.opendj.rest2ldap.Rest2LDAP.Builder;
import org.forgerock.testng.ForgeRockTestCase;
@@ -70,8 +89,7 @@
@Test
public void testDelete() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final Resource resource = connection.delete(ctx(), newDeleteRequest("/test1"));
checkResourcesAreEqual(resource, getTestUser1(12345));
try {
@@ -84,8 +102,7 @@
@Test
public void testDeleteMVCCMatch() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final Resource resource =
connection.delete(ctx(), newDeleteRequest("/test1").setRevision("12345"));
checkResourcesAreEqual(resource, getTestUser1(12345));
@@ -99,22 +116,19 @@
@Test(expectedExceptions = PreconditionFailedException.class)
public void testDeleteMVCCNoMatch() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.delete(ctx(), newDeleteRequest("/test1").setRevision("12346"));
}
@Test(expectedExceptions = NotFoundException.class)
public void testDeleteNotFound() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.delete(ctx(), newDeleteRequest("/missing"));
}
@Test
public void testPatch() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final Resource resource1 =
connection.patch(ctx(), newPatchRequest("/test1", add("/name/displayName",
"changed")));
@@ -124,9 +138,26 @@
}
@Test
+ public void testPatchEmpty() throws Exception {
+ final List<Request> requests = new LinkedList<Request>();
+ final Connection connection = newConnection(requests);
+ final Resource resource1 = connection.patch(ctx(), newPatchRequest("/test1"));
+ checkResourcesAreEqual(resource1, getTestUser1(12345));
+
+ /*
+ * Check that no modify operation was sent (only a single search should
+ * be sent in order to get the current resource).
+ */
+ assertThat(requests).hasSize(1);
+ assertThat(requests.get(0)).isInstanceOf(SearchRequest.class);
+
+ final Resource resource2 = connection.read(ctx(), newReadRequest("/test1"));
+ checkResourcesAreEqual(resource2, getTestUser1(12345));
+ }
+
+ @Test
public void testPatchAddOptionalAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent = getTestUser1(12345);
newContent.put("description", asList("one", "two"));
final Resource resource1 =
@@ -139,8 +170,7 @@
@Test
public void testPatchAddOptionalAttributeIndexAppend() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent = getTestUser1(12345);
newContent.put("description", asList("one", "two"));
final Resource resource1 =
@@ -153,15 +183,13 @@
@Test(expectedExceptions = BadRequestException.class)
public void testPatchConstantAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/schemas", asList("junk"))));
}
@Test
public void testPatchDeleteOptionalAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(),
newPatchRequest("/test1", add("/description", asList("one", "two"))));
final Resource resource1 =
@@ -173,8 +201,7 @@
@Test
public void testPatchIncrement() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent = getTestUser1(12345);
newContent.put("singleNumber", 100);
newContent.put("multiNumber", asList(200, 300));
@@ -190,15 +217,13 @@
@Test(expectedExceptions = BadRequestException.class)
public void testPatchMissingRequiredAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", remove("/name/surname")));
}
@Test
public void testPatchModifyOptionalAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(),
newPatchRequest("/test1", add("/description", asList("one", "two"))));
final Resource resource1 =
@@ -213,30 +238,26 @@
@Test(expectedExceptions = NotSupportedException.class)
public void testPatchMultiValuedAttributeIndexAppend() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/description/0", "junk")));
}
@Test(expectedExceptions = BadRequestException.class)
public void testPatchMultiValuedAttributeIndexAppendWithList() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/description/-",
asList("one", "two"))));
}
@Test(expectedExceptions = BadRequestException.class)
public void testPatchMultiValuedAttributeWithSingleValue() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/description", "one")));
}
@Test
public void testPatchMVCCMatch() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final Resource resource1 =
connection.patch(ctx(), newPatchRequest("/test1",
add("/name/displayName", "changed")).setRevision("12345"));
@@ -247,31 +268,27 @@
@Test(expectedExceptions = PreconditionFailedException.class)
public void testPatchMVCCNoMatch() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/name/displayName", "changed"))
.setRevision("12346"));
}
@Test(expectedExceptions = NotFoundException.class)
public void testPatchNotFound() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/missing", add("/name/displayName", "changed")));
}
@Test(expectedExceptions = BadRequestException.class)
public void testPatchReadOnlyAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
// Etag is read-only.
connection.patch(ctx(), newPatchRequest("/test1", add("_rev", "99999")));
}
@Test
public void testPatchReplacePartialObject() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue expected =
json(object(field("schemas", asList("urn:scim:schemas:core:1.0")), field("_id",
"test1"), field("_rev", "12345"), field("name", object(field("displayName",
@@ -286,8 +303,7 @@
@Test
public void testPatchReplaceWholeObject() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent =
json(object(field("name", object(field("displayName", "Humpty"), field("surname",
"Dumpty")))));
@@ -304,83 +320,70 @@
@Test(expectedExceptions = BadRequestException.class)
public void testPatchSingleValuedAttributeIndexAppend() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/name/surname/-", "junk")));
}
@Test(expectedExceptions = NotSupportedException.class)
public void testPatchSingleValuedAttributeIndexNumber() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/name/surname/0", "junk")));
}
@Test(expectedExceptions = BadRequestException.class)
public void testPatchSingleValuedAttributeWithMultipleValues() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/name/surname", asList("black",
"white"))));
}
@Test(expectedExceptions = BadRequestException.class)
public void testPatchUnknownAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/dummy", "junk")));
}
@Test(expectedExceptions = NotSupportedException.class)
public void testPatchUnknownOperation() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", operation("dummy", "/description",
asList("one", "two"))));
}
@Test(expectedExceptions = BadRequestException.class)
public void testPatchUnknownSubAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/description/dummy", "junk")));
}
@Test(expectedExceptions = BadRequestException.class)
public void testPatchUnknownSubSubAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.patch(ctx(), newPatchRequest("/test1", add("/description/dummy/dummy", "junk")));
}
@Test
public void testRead() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Resource resource =
- newInternalConnection(handler).read(ctx(), newReadRequest("/test1"));
+ final Resource resource = newConnection().read(ctx(), newReadRequest("/test1"));
checkResourcesAreEqual(resource, getTestUser1(12345));
}
@Test(expectedExceptions = NotFoundException.class)
public void testReadNotFound() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- newInternalConnection(handler).read(ctx(), newReadRequest("/missing"));
+ newConnection().read(ctx(), newReadRequest("/missing"));
}
@Test
public void testReadSelectAllFields() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
final Resource resource =
- newInternalConnection(handler).read(ctx(), newReadRequest("/test1").addField("/"));
+ newConnection().read(ctx(), newReadRequest("/test1").addField("/"));
checkResourcesAreEqual(resource, getTestUser1(12345));
}
@Test
public void testReadSelectPartial() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
final Resource resource =
- newInternalConnection(handler).read(ctx(),
- newReadRequest("/test1").addField("/name/surname"));
+ newConnection().read(ctx(), newReadRequest("/test1").addField("/name/surname"));
assertThat(resource.getId()).isEqualTo("test1");
assertThat(resource.getRevision()).isEqualTo("12345");
assertThat(resource.getContent().get("_id").asString()).isNull();
@@ -392,10 +395,8 @@
// Disabled - see CREST-86 (Should JSON resource fields be case insensitive?)
@Test(enabled = false)
public void testReadSelectPartialInsensitive() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
final Resource resource =
- newInternalConnection(handler).read(ctx(),
- newReadRequest("/test1").addField("/name/SURNAME"));
+ newConnection().read(ctx(), newReadRequest("/test1").addField("/name/SURNAME"));
assertThat(resource.getId()).isEqualTo("test1");
assertThat(resource.getRevision()).isEqualTo("12345");
assertThat(resource.getContent().get("_id").asString()).isNull();
@@ -406,8 +407,7 @@
@Test
public void testUpdate() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final Resource resource1 =
connection.update(ctx(), newUpdateRequest("/test1", getTestUser1Updated(12345)));
checkResourcesAreEqual(resource1, getTestUser1Updated(12345));
@@ -416,9 +416,27 @@
}
@Test
+ public void testUpdateNoChange() throws Exception {
+ final List<Request> requests = new LinkedList<Request>();
+ final Connection connection = newConnection(requests);
+ final Resource resource1 =
+ connection.update(ctx(), newUpdateRequest("/test1", getTestUser1(12345)));
+
+ /*
+ * Check that no modify operation was sent (only a single search should
+ * be sent in order to get the current resource).
+ */
+ assertThat(requests).hasSize(1);
+ assertThat(requests.get(0)).isInstanceOf(SearchRequest.class);
+
+ checkResourcesAreEqual(resource1, getTestUser1(12345));
+ final Resource resource2 = connection.read(ctx(), newReadRequest("/test1"));
+ checkResourcesAreEqual(resource2, getTestUser1(12345));
+ }
+
+ @Test
public void testUpdateAddOptionalAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent = getTestUser1Updated(12345);
newContent.put("description", asList("one", "two"));
final Resource resource1 = connection.update(ctx(), newUpdateRequest("/test1", newContent));
@@ -429,8 +447,7 @@
@Test(expectedExceptions = BadRequestException.class)
public void testUpdateConstantAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent = getTestUser1Updated(12345);
newContent.put("schemas", asList("junk"));
connection.update(ctx(), newUpdateRequest("/test1", newContent));
@@ -438,8 +455,7 @@
@Test
public void testUpdateDeleteOptionalAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent = getTestUser1Updated(12345);
newContent.put("description", asList("one", "two"));
connection.update(ctx(), newUpdateRequest("/test1", newContent));
@@ -452,8 +468,7 @@
@Test(expectedExceptions = BadRequestException.class)
public void testUpdateMissingRequiredAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent = getTestUser1Updated(12345);
newContent.get("name").remove("surname");
connection.update(ctx(), newUpdateRequest("/test1", newContent));
@@ -461,8 +476,7 @@
@Test
public void testUpdateModifyOptionalAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent = getTestUser1Updated(12345);
newContent.put("description", asList("one", "two"));
connection.update(ctx(), newUpdateRequest("/test1", newContent));
@@ -475,8 +489,7 @@
@Test
public void testUpdateMVCCMatch() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final Resource resource1 =
connection.update(ctx(), newUpdateRequest("/test1", getTestUser1Updated(12345))
.setRevision("12345"));
@@ -487,31 +500,27 @@
@Test(expectedExceptions = PreconditionFailedException.class)
public void testUpdateMVCCNoMatch() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.update(ctx(), newUpdateRequest("/test1", getTestUser1Updated(12345))
.setRevision("12346"));
}
@Test(expectedExceptions = NotFoundException.class)
public void testUpdateNotFound() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
connection.update(ctx(), newUpdateRequest("/missing", getTestUser1Updated(12345)));
}
@Test(expectedExceptions = BadRequestException.class)
public void testUpdateReadOnlyAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
// Etag is read-only.
connection.update(ctx(), newUpdateRequest("/test1", getTestUser1Updated(99999)));
}
@Test(expectedExceptions = BadRequestException.class)
public void testUpdateSingleValuedAttributeWithMultipleValues() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent = getTestUser1Updated(12345);
newContent.put("surname", asList("black", "white"));
connection.update(ctx(), newUpdateRequest("/test1", newContent));
@@ -519,17 +528,24 @@
@Test(expectedExceptions = BadRequestException.class)
public void testUpdateUnknownAttribute() throws Exception {
- final RequestHandler handler = newCollection(builder().build());
- final Connection connection = newInternalConnection(handler);
+ final Connection connection = newConnection();
final JsonValue newContent = getTestUser1Updated(12345);
newContent.add("dummy", "junk");
connection.update(ctx(), newUpdateRequest("/test1", newContent));
}
- private Builder builder() throws IOException {
- return Rest2LDAP.builder().ldapConnectionFactory(getConnectionFactory()).baseDN("dc=test")
- .useEtagAttribute().useClientDNNaming("uid").readOnUpdatePolicy(
- ReadOnUpdatePolicy.CONTROLS).authorizationPolicy(AuthorizationPolicy.NONE)
+ private Connection newConnection() throws IOException {
+ return newConnection(new LinkedList<Request>());
+ }
+
+ private Connection newConnection(final List<Request> requests) throws IOException {
+ return newInternalConnection(newCollection(builder(requests).build()));
+ }
+
+ private Builder builder(final List<Request> requests) throws IOException {
+ return Rest2LDAP.builder().ldapConnectionFactory(getConnectionFactory(requests)).baseDN(
+ "dc=test").useEtagAttribute().useClientDNNaming("uid").readOnUpdatePolicy(
+ ReadOnUpdatePolicy.CONTROLS).authorizationPolicy(AuthorizationPolicy.NONE)
.additionalLDAPAttribute("objectClass", "top", "person")
.mapper(object()
.attribute("schemas", constant(asList("urn:scim:schemas:core:1.0")))
@@ -560,7 +576,7 @@
expectedResource.getContent().getObject());
}
- private ConnectionFactory getConnectionFactory() throws IOException {
+ private ConnectionFactory getConnectionFactory(final List<Request> requests) throws IOException {
// @formatter:off
final MemoryBackend backend =
new MemoryBackend(new LDIFEntryReader(
@@ -589,7 +605,86 @@
));
// @formatter:on
- return newInternalConnectionFactory(backend);
+ return newInternalConnectionFactory(recordRequests(backend, requests));
+ }
+
+ private RequestHandler<RequestContext> recordRequests(
+ final RequestHandler<RequestContext> handler, final List<Request> requests) {
+ return new RequestHandler<RequestContext>() {
+ @Override
+ public void handleAdd(RequestContext requestContext, AddRequest request,
+ IntermediateResponseHandler intermediateResponseHandler,
+ ResultHandler<Result> resultHandler) {
+ requests.add(request);
+ handler.handleAdd(requestContext, request, intermediateResponseHandler,
+ resultHandler);
+ }
+
+ @Override
+ public void handleBind(RequestContext requestContext, int version, BindRequest request,
+ IntermediateResponseHandler intermediateResponseHandler,
+ ResultHandler<BindResult> resultHandler) {
+ requests.add(request);
+ handler.handleBind(requestContext, version, request, intermediateResponseHandler,
+ resultHandler);
+ }
+
+ @Override
+ public void handleCompare(RequestContext requestContext, CompareRequest request,
+ IntermediateResponseHandler intermediateResponseHandler,
+ ResultHandler<CompareResult> resultHandler) {
+ requests.add(request);
+ handler.handleCompare(requestContext, request, intermediateResponseHandler,
+ resultHandler);
+ }
+
+ @Override
+ public void handleDelete(RequestContext requestContext, DeleteRequest request,
+ IntermediateResponseHandler intermediateResponseHandler,
+ ResultHandler<Result> resultHandler) {
+ requests.add(request);
+ handler.handleDelete(requestContext, request, intermediateResponseHandler,
+ resultHandler);
+ }
+
+ @Override
+ public <R extends ExtendedResult> void handleExtendedRequest(
+ RequestContext requestContext, ExtendedRequest<R> request,
+ IntermediateResponseHandler intermediateResponseHandler,
+ ResultHandler<R> resultHandler) {
+ requests.add(request);
+ handler.handleExtendedRequest(requestContext, request, intermediateResponseHandler,
+ resultHandler);
+ }
+
+ @Override
+ public void handleModify(RequestContext requestContext, ModifyRequest request,
+ IntermediateResponseHandler intermediateResponseHandler,
+ ResultHandler<Result> resultHandler) {
+ requests.add(request);
+ handler.handleModify(requestContext, request, intermediateResponseHandler,
+ resultHandler);
+ }
+
+ @Override
+ public void handleModifyDN(RequestContext requestContext, ModifyDNRequest request,
+ IntermediateResponseHandler intermediateResponseHandler,
+ ResultHandler<Result> resultHandler) {
+ requests.add(request);
+ handler.handleModifyDN(requestContext, request, intermediateResponseHandler,
+ resultHandler);
+ }
+
+ @Override
+ public void handleSearch(RequestContext requestContext, SearchRequest request,
+ IntermediateResponseHandler intermediateResponseHandler,
+ SearchResultHandler resultHandler) {
+ requests.add(request);
+ handler.handleSearch(requestContext, request, intermediateResponseHandler,
+ resultHandler);
+ }
+
+ };
}
private JsonValue getTestUser1(final int rev) {
--
Gitblit v1.10.0