hacking on client ideas
diff --git a/scim-client/pom.xml b/scim-client/pom.xml index 4f16089..6c9e684 100644 --- a/scim-client/pom.xml +++ b/scim-client/pom.xml
@@ -50,10 +50,25 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.jboss.resteasy</groupId> + <artifactId>resteasy-client</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project>
diff --git a/scim-client/src/main/java/org/apache/directory/scim/client/ScimClient.java b/scim-client/src/main/java/org/apache/directory/scim/client/ScimClient.java new file mode 100644 index 0000000..a0776f6 --- /dev/null +++ b/scim-client/src/main/java/org/apache/directory/scim/client/ScimClient.java
@@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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 org.apache.directory.scim.client; + +import jakarta.ws.rs.client.Client; +import org.apache.directory.scim.client.rest.*; + +/** + * The root client used to access a SCIM server. + * + */ +public class ScimClient { + + private final Client client; + private final String baseUrl; + + private final RestCall restCall; + + public ScimClient(Client client, String baseUrl) { + this(client, baseUrl, null); + } + + public ScimClient(Client client, String baseUrl, RestCall restCall) { + this.client = client; + this.baseUrl = baseUrl; + this.restCall = restCall; + } + + public ScimUserClient userClient() { + return new ScimUserClient(client, baseUrl, restCall); + } + + public ScimGroupClient groupClient() { + return new ScimGroupClient(client, baseUrl, restCall); + } + + public ScimSelfClient selfClient() { + return new ScimSelfClient(client, baseUrl, restCall); + } + + public ResourceTypesClient resourceTypesClient() { + return new ResourceTypesClient(client, baseUrl, restCall); + } +}
diff --git a/scim-client/src/main/java/org/apache/directory/scim/client/rest/BaseScimClient.java b/scim-client/src/main/java/org/apache/directory/scim/client/rest/BaseScimClient.java index 523b662..a1c9044 100644 --- a/scim-client/src/main/java/org/apache/directory/scim/client/rest/BaseScimClient.java +++ b/scim-client/src/main/java/org/apache/directory/scim/client/rest/BaseScimClient.java
@@ -57,9 +57,13 @@ private final GenericType<ListResponse<T>> scimResourceListResponseGenericType; private final WebTarget target; private final InternalScimClient scimClient; - private RestCall invoke = Invocation::invoke; + private final RestCall invoke; public BaseScimClient(Client client, String baseUrl, Class<T> scimResourceClass, GenericType<ListResponse<T>> scimResourceListGenericType) { + this(client, baseUrl, scimResourceClass, scimResourceListGenericType, null); + } + + public BaseScimClient(Client client, String baseUrl, Class<T> scimResourceClass, GenericType<ListResponse<T>> scimResourceListGenericType, RestCall invoke) { ScimResourceType scimResourceType = scimResourceClass.getAnnotation(ScimResourceType.class); String endpoint = scimResourceType != null ? scimResourceType.endpoint() : null; @@ -71,12 +75,7 @@ this.scimResourceListResponseGenericType = scimResourceListGenericType; this.target = this.client.target(baseUrl).path(endpoint); this.scimClient = new InternalScimClient(); - } - - public BaseScimClient(Client client, String baseUrl, Class<T> scimResourceClass, GenericType<ListResponse<T>> scimResourceListGenericType, RestCall invoke) { - this(client, baseUrl, scimResourceClass, scimResourceListGenericType); - - this.invoke = invoke; + this.invoke = invoke != null ? invoke : Invocation::invoke; } @Override @@ -199,14 +198,10 @@ return new ScimException(restException.getError(), restException.getStatus()); } - public RestCall getInvoke() { + protected RestCall getInvoke() { return this.invoke; } - public void setInvoke(RestCall invoke) { - this.invoke = invoke; - } - private class InternalScimClient implements BaseResourceTypeResource<T> { private static final String FILTER_QUERY_PARAM = "filter"; @@ -217,7 +212,6 @@ @Override public Response getById(String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { - Response response; Invocation request = BaseScimClient.this.target .path(id) .queryParam(ATTRIBUTES_QUERY_PARAM, nullOutQueryParamIfListIsNullOrEmpty(attributes)) @@ -225,18 +219,11 @@ .request(getContentType()) .buildGet(); - try { - response = BaseScimClient.this.invoke.apply(request); - - return response; - } catch (RestException restException) { - throw toScimException(restException); - } + return executeRequest(request); } @Override public Response query(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes, FilterWrapper filter, AttributeReference sortBy, SortOrder sortOrder, Integer startIndex, Integer count) throws ScimException { - Response response; Invocation request = BaseScimClient.this.target .queryParam(ATTRIBUTES_QUERY_PARAM, nullOutQueryParamIfListIsNullOrEmpty(attributes)) .queryParam(EXCLUDED_ATTRIBUTES_QUERY_PARAM, nullOutQueryParamIfListIsNullOrEmpty(excludedAttributes)) @@ -248,53 +235,32 @@ .request(getContentType()) .buildGet(); - try { - response = BaseScimClient.this.invoke.apply(request); - - return response; - } catch (RestException restException) { - throw toScimException(restException); - } + return executeRequest(request); } @Override public Response create(T resource, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { - Response response; Invocation request = BaseScimClient.this.target .queryParam(ATTRIBUTES_QUERY_PARAM, nullOutQueryParamIfListIsNullOrEmpty(attributes)) .queryParam(EXCLUDED_ATTRIBUTES_QUERY_PARAM, nullOutQueryParamIfListIsNullOrEmpty(excludedAttributes)) .request(getContentType()) .buildPost(Entity.entity(resource, getContentType())); - try { - response = BaseScimClient.this.invoke.apply(request); - - return response; - } catch (RestException restException) { - throw toScimException(restException); - } + return executeRequest(request); } @Override public Response find(SearchRequest searchRequest) throws ScimException { - Response response; Invocation request = BaseScimClient.this.target .path(".search") .request(getContentType()) .buildPost(Entity.entity(searchRequest, getContentType())); - try { - response = BaseScimClient.this.invoke.apply(request); - - return response; - } catch (RestException restException) { - throw toScimException(restException); - } + return executeRequest(request); } @Override public Response update(T resource, String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { - Response response; Invocation request = BaseScimClient.this.target .path(id) .queryParam(ATTRIBUTES_QUERY_PARAM, nullOutQueryParamIfListIsNullOrEmpty(attributes)) @@ -302,18 +268,11 @@ .request(getContentType()) .buildPut(Entity.entity(resource, getContentType())); - try { - response = BaseScimClient.this.invoke.apply(request); - - return response; - } catch (RestException restException) { - throw toScimException(restException); - } + return executeRequest(request); } @Override public Response patch(PatchRequest patchRequest, String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { - Response response; Invocation request = BaseScimClient.this.target .path(id) .queryParam(ATTRIBUTES_QUERY_PARAM, nullOutQueryParamIfListIsNullOrEmpty(attributes)) @@ -321,30 +280,17 @@ .request(getContentType()) .build("PATCH", Entity.entity(patchRequest, getContentType())); - try { - response = BaseScimClient.this.invoke.apply(request); - - return response; - } catch (RestException restException) { - throw toScimException(restException); - } + return executeRequest(request); } @Override public Response delete(String id) throws ScimException { - Response response; Invocation request = BaseScimClient.this.target .path(id) .request(getContentType()) .buildDelete(); - try { - response = BaseScimClient.this.invoke.apply(request); - - return response; - } catch (RestException restException) { - throw toScimException(restException); - } + return executeRequest(request); } private AttributeReferenceListWrapper nullOutQueryParamIfListIsNullOrEmpty(AttributeReferenceListWrapper wrapper) { @@ -358,6 +304,14 @@ return wrapper; } + + private Response executeRequest(Invocation invocation) throws ScimException { + try { + return BaseScimClient.this.invoke.apply(invocation); + } catch (RestException restClientException) { + throw toScimException(restClientException); + } + } } protected String getContentType() {
diff --git a/scim-client/src/main/java/org/apache/directory/scim/client/rest/ResourceTypesClient.java b/scim-client/src/main/java/org/apache/directory/scim/client/rest/ResourceTypesClient.java index a1f247f..deb1ab3 100644 --- a/scim-client/src/main/java/org/apache/directory/scim/client/rest/ResourceTypesClient.java +++ b/scim-client/src/main/java/org/apache/directory/scim/client/rest/ResourceTypesClient.java
@@ -24,6 +24,7 @@ import jakarta.ws.rs.ProcessingException; import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Invocation; import jakarta.ws.rs.client.WebTarget; import jakarta.ws.rs.core.GenericType; import jakarta.ws.rs.core.Response; @@ -37,14 +38,19 @@ private final Client client; private final WebTarget target; - private final ResourceTypesResourceClient resourceTypesResourceClient = new ResourceTypesResourceClient(); + private final ResourceTypesResourceClient resourceTypesResourceClient; public ResourceTypesClient(Client client, String baseUrl) { - this.client = client; - this.target = this.client.target(baseUrl).path("ResourceTypes"); + this(client, baseUrl, null); } - public List<ResourceType> getAllResourceTypes(String filter) throws RestException { + public ResourceTypesClient(Client client, String baseUrl, RestCall invoke) { + this.client = client; + this.target = this.client.target(baseUrl).path("ResourceTypes"); + this.resourceTypesResourceClient = new ResourceTypesResourceClient(invoke); + } + + public List<ResourceType> query(String filter) throws RestException { List<ResourceType> resourceTypes; Response response = this.resourceTypesResourceClient.getAllResourceTypes(filter); @@ -58,7 +64,7 @@ return resourceTypes; } - public Optional<ResourceType> getResourceType(String name) throws RestException, ProcessingException, IllegalStateException { + public Optional<ResourceType> get(String name) throws RestException, ProcessingException, IllegalStateException { Optional<ResourceType> resourceType; Response response = this.resourceTypesResourceClient.getResourceType(name); @@ -77,6 +83,12 @@ private class ResourceTypesResourceClient implements ResourceTypesResource { + private final RestCall invoke; + + private ResourceTypesResourceClient(RestCall invoke) { + this.invoke = invoke != null ? null : Invocation::invoke; + } + @Override public Response getAllResourceTypes(String filter) throws RestException { Response response = ResourceTypesClient.this.target
diff --git a/scim-client/src/main/java/org/apache/directory/scim/client/rest/ScimSelfClient.java b/scim-client/src/main/java/org/apache/directory/scim/client/rest/ScimSelfClient.java index 8580f12..6418f10 100644 --- a/scim-client/src/main/java/org/apache/directory/scim/client/rest/ScimSelfClient.java +++ b/scim-client/src/main/java/org/apache/directory/scim/client/rest/ScimSelfClient.java
@@ -53,41 +53,41 @@ this.invoke = invoke; } - public ScimUser getSelf(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { + public ScimUser get(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { ScimUser self; - Response response = this.selfResourceClient.getSelf(attributes, excludedAttributes); + Response response = this.selfResourceClient.get(attributes, excludedAttributes); self = BaseScimClient.handleResponse(response, ScimUser.class, response::readEntity); return self; } - public ScimUser getSelf() throws ScimException { - ScimUser self = this.getSelf(null, null); + public ScimUser get() throws ScimException { + ScimUser self = this.get(null, null); return self; } - public void updateSelf(ScimUser scimUser, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { + public void update(ScimUser scimUser, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { Response response = this.selfResourceClient.update(scimUser, attributes, excludedAttributes); BaseScimClient.handleResponse(response); } - public void updateSelf(ScimUser scimUser) throws ScimException { - this.updateSelf(scimUser, null, null); + public void update(ScimUser scimUser) throws ScimException { + this.update(scimUser, null, null); } - public void patchSelf(PatchRequest patchRequest, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { + public void patch(PatchRequest patchRequest, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { Response response = this.selfResourceClient.patch(patchRequest, attributes, excludedAttributes); BaseScimClient.handleResponse(response); } - public void patchSelf(PatchRequest patchRequest) throws ScimException { - this.patchSelf(patchRequest, null, null); + public void patch(PatchRequest patchRequest) throws ScimException { + this.patch(patchRequest, null, null); } - public void deleteSelf() throws ScimException { + public void delete() throws ScimException { Response response = this.selfResourceClient.delete(); BaseScimClient.handleResponse(response); @@ -97,10 +97,6 @@ return this.invoke; } - public void setInvoke(RestCall invoke) { - this.invoke = invoke; - } - @Override public void close() throws Exception { this.client.close(); @@ -109,7 +105,7 @@ private class SelfResourceClient implements SelfResource { @Override - public Response getSelf(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { + public Response get(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException { Response response; Invocation request = ScimSelfClient.this.target .queryParam(BaseScimClient.ATTRIBUTES_QUERY_PARAM, attributes)
diff --git a/scim-client/src/main/java/org/apache/directory/scim/client/rest/ScimUserClient.java b/scim-client/src/main/java/org/apache/directory/scim/client/rest/ScimUserClient.java index d5554e5..8defd5f 100644 --- a/scim-client/src/main/java/org/apache/directory/scim/client/rest/ScimUserClient.java +++ b/scim-client/src/main/java/org/apache/directory/scim/client/rest/ScimUserClient.java
@@ -20,6 +20,7 @@ package org.apache.directory.scim.client.rest; import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.WebTarget; import jakarta.ws.rs.core.GenericType; import org.apache.directory.scim.spec.protocol.data.ListResponse;
diff --git a/scim-client/src/test/java/org/apache/directory/scim/client/ScimClientTest.java b/scim-client/src/test/java/org/apache/directory/scim/client/ScimClientTest.java new file mode 100644 index 0000000..faefe34 --- /dev/null +++ b/scim-client/src/test/java/org/apache/directory/scim/client/ScimClientTest.java
@@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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 org.apache.directory.scim.client; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.WebTarget; +import org.apache.directory.scim.client.rest.ResourceTypesClient; +import org.apache.directory.scim.client.rest.ScimGroupClient; +import org.apache.directory.scim.client.rest.ScimSelfClient; +import org.apache.directory.scim.client.rest.ScimUserClient; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ScimClientTest { + + private static final String BASE_URL = "https://scim.example.com/test"; + + @Test + public void userClient() { + Client client = mock(Client.class); + ScimClient scimClient = new ScimClient(client, BASE_URL); + WebTarget webTarget = mock(WebTarget.class); + when(client.target(BASE_URL)).thenReturn(webTarget); + WebTarget usersTarget = mock(WebTarget.class); + ArgumentCaptor<String> pathCapture = ArgumentCaptor.forClass(String.class); + when(webTarget.path(pathCapture.capture())).thenReturn(usersTarget); + + ScimUserClient userClient = scimClient.userClient(); + + assertThat(userClient).extracting("client").isSameAs(client); + assertThat(userClient).extracting("target").isSameAs(usersTarget); + assertThat(pathCapture.getValue()).isEqualTo("/Users"); + } + + @Test + public void groupClient() { + Client client = mock(Client.class); + ScimClient scimClient = new ScimClient(client, BASE_URL); + WebTarget webTarget = mock(WebTarget.class); + when(client.target(BASE_URL)).thenReturn(webTarget); + WebTarget usersTarget = mock(WebTarget.class); + ArgumentCaptor<String> pathCapture = ArgumentCaptor.forClass(String.class); + when(webTarget.path(pathCapture.capture())).thenReturn(usersTarget); + + ScimGroupClient groupClient = scimClient.groupClient(); + + assertThat(groupClient).extracting("client").isSameAs(client); + assertThat(groupClient).extracting("target").isSameAs(usersTarget); + assertThat(pathCapture.getValue()).isEqualTo("/Groups"); + } + + @Test + public void typesClient() { + Client client = mock(Client.class); + ScimClient scimClient = new ScimClient(client, BASE_URL); + WebTarget webTarget = mock(WebTarget.class); + when(client.target(BASE_URL)).thenReturn(webTarget); + WebTarget usersTarget = mock(WebTarget.class); + ArgumentCaptor<String> pathCapture = ArgumentCaptor.forClass(String.class); + when(webTarget.path(pathCapture.capture())).thenReturn(usersTarget); + + ResourceTypesClient typesClient = scimClient.resourceTypesClient(); + + assertThat(typesClient).extracting("client").isSameAs(client); + assertThat(typesClient).extracting("target").isSameAs(usersTarget); + assertThat(pathCapture.getValue()).isEqualTo("ResourceTypes"); + } + + @Test + public void selfClient() { + Client client = mock(Client.class); + ScimClient scimClient = new ScimClient(client, BASE_URL); + WebTarget webTarget = mock(WebTarget.class); + when(client.target(BASE_URL)).thenReturn(webTarget); + WebTarget usersTarget = mock(WebTarget.class); + ArgumentCaptor<String> pathCapture = ArgumentCaptor.forClass(String.class); + when(webTarget.path(pathCapture.capture())).thenReturn(usersTarget); + + ScimSelfClient selfClient = scimClient.selfClient(); + + assertThat(selfClient).extracting("client").isSameAs(client); + assertThat(selfClient).extracting("target").isSameAs(usersTarget); + assertThat(pathCapture.getValue()).isEqualTo("Me"); + } +}
diff --git a/scim-client/src/test/java/org/apache/directory/scim/client/rest/ScimUserClientTest.java b/scim-client/src/test/java/org/apache/directory/scim/client/rest/ScimUserClientTest.java new file mode 100644 index 0000000..30df2fc --- /dev/null +++ b/scim-client/src/test/java/org/apache/directory/scim/client/rest/ScimUserClientTest.java
@@ -0,0 +1,317 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, + * software distributed under the License 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 org.apache.directory.scim.client.rest; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.GenericType; +import jakarta.ws.rs.core.Response; +import org.apache.directory.scim.spec.protocol.Constants; +import org.apache.directory.scim.spec.protocol.attribute.AttributeReference; +import org.apache.directory.scim.spec.protocol.attribute.AttributeReferenceListWrapper; +import org.apache.directory.scim.spec.protocol.data.ErrorResponse; +import org.apache.directory.scim.spec.protocol.data.ListResponse; +import org.apache.directory.scim.spec.protocol.data.PatchRequest; +import org.apache.directory.scim.spec.protocol.data.SearchRequest; +import org.apache.directory.scim.spec.protocol.exception.ScimException; +import org.apache.directory.scim.spec.protocol.search.Filter; +import org.apache.directory.scim.spec.protocol.search.SortOrder; +import org.apache.directory.scim.spec.resources.ScimUser; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import java.util.*; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +public class ScimUserClientTest { + + private static final String BASE_URL = "https://scim.example.com/test"; + + @Test + public void findTest() throws Exception { + + ListResponse<ScimUser> mockListResponse = mock(ListResponse.class); + MockClient mockClient = new MockClient(BASE_URL, "POST", ok(mockListResponse)); + SearchRequest searchRequest = new SearchRequest(); + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + ListResponse<ScimUser> listResponse = userClient.find(searchRequest); + + assertThat(listResponse).isSameAs(mockListResponse); + assertThat(mockClient.requestPath()).isEqualTo("/Users/.search"); + assertThat(mockClient.requestEntity()).isSameAs(searchRequest); + } + + @Test + public void queryTest() throws Exception { + + ListResponse<ScimUser> mockListResponse = mock(ListResponse.class); + MockClient mockClient = new MockClient(BASE_URL, "GET", ok(mockListResponse)); + AttributeReferenceListWrapper attributes = mock(AttributeReferenceListWrapper.class); + AttributeReferenceListWrapper excludedAttributes = mock(AttributeReferenceListWrapper.class); + Filter filter = mock(Filter.class); + AttributeReference sortBy = mock(AttributeReference.class); + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + ListResponse<ScimUser> listResponse = userClient.query(attributes, excludedAttributes, filter, sortBy, SortOrder.ASCENDING, 5, 42); + + Map<String, Object> expectedQueryParams = new HashMap<>(); + expectedQueryParams.put("attributes", null); + expectedQueryParams.put("count", 42); + expectedQueryParams.put("excludedAttributes", null); + expectedQueryParams.put("filter", filter); + expectedQueryParams.put("sortBy", sortBy); + expectedQueryParams.put("sortOrder", "ASCENDING"); + expectedQueryParams.put("startIndex", 5); + + assertThat(listResponse).isSameAs(mockListResponse); + assertThat(mockClient.requestPath()).isEqualTo("/Users"); + assertThat(mockClient.queryParams()).isEqualTo(expectedQueryParams); + } + + @Test + public void getById_found() throws Exception { + ScimUser entity = mock(ScimUser.class); + MockClient mockClient = new MockClient(BASE_URL, "GET", ok(entity)); + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + Optional<ScimUser> optionalResponse = userClient.getById("test-id"); + + assertThat(optionalResponse).isNotNull(); + assertThat(optionalResponse.get()).isSameAs(entity); + assertThat(mockClient.requestPath()).isEqualTo("/Users/test-id"); + } + + @Test + public void getById_notFound() throws Exception { + MockClient mockClient = new MockClient(BASE_URL, "GET", notFound()); + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + Optional<ScimUser> optionalResponse = userClient.getById("test-id-not-found"); + + assertThat(optionalResponse).isEmpty(); + assertThat(mockClient.requestPath()).isEqualTo("/Users/test-id-not-found"); + } + + @Test + public void getById_serverError() throws Exception { + MockClient mockClient = new MockClient(BASE_URL, "GET", error(Response.Status.INTERNAL_SERVER_ERROR)); + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + ScimException exception = expect(ScimException.class, () -> userClient.getById("test-id-error")); + + assertThat(exception.getStatus()).isEqualTo(Response.Status.INTERNAL_SERVER_ERROR); + assertThat(mockClient.requestPath()).isEqualTo("/Users/test-id-error"); + } + + @Test + public void create_success() throws Exception { + ScimUser entity = mock(ScimUser.class); + ScimUser user = mock(ScimUser.class); + MockClient mockClient = new MockClient(BASE_URL, "POST", ok(entity)); + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + ScimUser response = userClient.create(user); + + assertThat(response).isSameAs(entity); + assertThat(mockClient.requestEntity()).isSameAs(user); + assertThat(mockClient.requestPath()).isEqualTo("/Users"); + } + + @Test + public void create_fail() throws Exception { + ScimUser entity = mock(ScimUser.class); + MockClient mockClient = new MockClient(BASE_URL, "POST", error(Response.Status.INTERNAL_SERVER_ERROR)); + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + ScimException exception = expect(ScimException.class, () -> userClient.create(entity)); + + assertThat(exception.getStatus()).isEqualTo(Response.Status.INTERNAL_SERVER_ERROR); + assertThat(mockClient.requestEntity()).isSameAs(entity); + assertThat(mockClient.requestPath()).isEqualTo("/Users"); + } + + @Test + public void create_withRestCallFail() throws Exception { + ScimUser entity = mock(ScimUser.class); + MockClient mockClient = new MockClient(BASE_URL, "POST", error(Response.Status.INTERNAL_SERVER_ERROR)); + ErrorResponse error = new ErrorResponse(Response.Status.CONFLICT, "expected test exception in create_withRestCall"); + RestCall restCall = (invocation) -> { throw new RestException(409, error); }; + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL, restCall); + ScimException exception = expect(ScimException.class, () -> userClient.create(entity)); + + assertThat(exception.getStatus()).isEqualTo(Response.Status.CONFLICT); + assertThat(mockClient.requestEntity()).isSameAs(entity); + assertThat(mockClient.requestPath()).isEqualTo("/Users"); + } + + @Test + public void update_success() throws Exception { + ScimUser entity = mock(ScimUser.class); + ScimUser user = mock(ScimUser.class); + MockClient mockClient = new MockClient(BASE_URL, "PUT", ok(entity)); + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + ScimUser response = userClient.update("update-test", user); + + assertThat(response).isSameAs(entity); + assertThat(mockClient.requestEntity()).isSameAs(user); + assertThat(mockClient.requestPath()).isEqualTo("/Users/update-test"); + } + + @Test + public void patch_success() throws Exception { + PatchRequest patch = mock(PatchRequest.class); + ScimUser entity = mock(ScimUser.class); + MockClient mockClient = new MockClient(BASE_URL, "PATCH", ok(entity)); + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + ScimUser response = userClient.patch("update-test", patch); + + assertThat(response).isSameAs(entity); + assertThat(mockClient.requestEntity()).isSameAs(patch); + assertThat(mockClient.requestPath()).isEqualTo("/Users/update-test"); + } + + @Test + public void delete_success() throws Exception { + ScimUser entity = mock(ScimUser.class); + MockClient mockClient = new MockClient(BASE_URL, "DELETE", ok(entity)); + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + userClient.delete("test-delete"); + + assertThat(mockClient.requestPath()).isEqualTo("/Users/test-delete"); + } + + @Test + public void delete_error() throws Exception { + MockClient mockClient = new MockClient(BASE_URL, "DELETE", notFound()); + + ScimUserClient userClient = new ScimUserClient(mockClient.client, BASE_URL); + ScimException exception = expect(ScimException.class, () -> userClient.delete("test-delete-404")); + + assertThat(exception.getStatus()).isEqualTo(Response.Status.NOT_FOUND); + assertThat(mockClient.requestPath()).isEqualTo("/Users/test-delete-404"); + } + + static Response ok(Object entity) { + Response response = mock(Response.class); + when(response.readEntity(any(GenericType.class))).thenReturn(entity); // list response + when(response.readEntity(any(Class.class))).thenReturn(entity); // single entity + when(response.getStatusInfo()).thenReturn(Response.Status.OK); + when(response.getStatus()).thenReturn(Response.Status.OK.getStatusCode()); + return response; + } + + static Response notFound() { + return error(Response.Status.NOT_FOUND); + } + + static Response error(Response.Status status) { + Response response = mock(Response.class); + when(response.getStatusInfo()).thenReturn(status); + when(response.getStatus()).thenReturn(status.getStatusCode()); + return response; + } + + @FunctionalInterface + interface ThrowingRunnable { + void run() throws Exception; + } + + <T extends Exception> T expect(Class<T> exceptionType, ThrowingRunnable runnable) { + try { + runnable.run(); + } catch (Exception e) { + if (e.getClass().equals(exceptionType)) { + return (T) e; + } + throw new RuntimeException("Expected block to throw exception of type: " + exceptionType + " but was: " + e.getClass(), e); + } + throw new RuntimeException("Expected block to throw exception of type " + exceptionType); + } + + static class MockClient { + + Client client = mock(Client.class); + WebTarget webTarget = mock(WebTarget.class); + Invocation.Builder builder = mock(Invocation.Builder.class); + ArgumentCaptor<Entity> entityCaptor = ArgumentCaptor.forClass(Entity.class); + Invocation invocation = mock(Invocation.class); + ArgumentCaptor<String> pathCapture = ArgumentCaptor.forClass(String.class); + ArgumentCaptor<String> queryKeysCapture = ArgumentCaptor.forClass(String.class); + ArgumentCaptor<Object> queryValuesCapture = ArgumentCaptor.forClass(Object.class); + + MockClient(String baseUrl, String method, Response response) { + when(client.target(baseUrl)).thenReturn(webTarget); + when(webTarget.path(pathCapture.capture())).thenReturn(webTarget); + when(webTarget.request(Constants.SCIM_CONTENT_TYPE)).thenReturn(builder); + when(webTarget.queryParam(queryKeysCapture.capture(), queryValuesCapture.capture())).thenReturn(webTarget); + when(invocation.invoke()).thenReturn(response); + + switch(method){ + case "POST": + when(builder.buildPost(entityCaptor.capture())).thenReturn(invocation); + break; + case "PUT": + when(builder.buildPut(entityCaptor.capture())).thenReturn(invocation); + break; + case "PATCH": + when(builder.build(eq("PATCH"), entityCaptor.capture())).thenReturn(invocation); + break; + case "GET": + when(builder.buildGet()).thenReturn(invocation); + break; + case "DELETE": + when(builder.buildDelete()).thenReturn(invocation); + break; + default: + throw new IllegalStateException("Unsupported method type '" + method + "', an update to the `MockClient` is needed"); + } + } + + String requestPath() { + return String.join("/", pathCapture.getAllValues()); + } + + Map<String, Object> queryParams() { + List<String> keys = queryKeysCapture.getAllValues(); + List<Object> values = queryValuesCapture.getAllValues(); + + Map<String, Object> params = new LinkedHashMap<>(keys.size()); + IntStream + .range(0, keys.size()) + .forEach(i -> params.put(keys.get(i), values.get(i))); + + return params; + } + + Object requestEntity() { + return entityCaptor.getValue().getEntity(); + } + } +}
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/rest/SelfResourceImpl.java b/scim-server/src/main/java/org/apache/directory/scim/server/rest/SelfResourceImpl.java index 32c789e..51cfeb2 100644 --- a/scim-server/src/main/java/org/apache/directory/scim/server/rest/SelfResourceImpl.java +++ b/scim-server/src/main/java/org/apache/directory/scim/server/rest/SelfResourceImpl.java
@@ -54,7 +54,7 @@ SecurityContext securityContext; @Override - public Response getSelf(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) { + public Response get(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) { try { String internalId = getInternalId(); return userResource.getById(internalId, attributes, excludedAttributes);
diff --git a/scim-server/src/test/java/org/apache/directory/scim/server/rest/SelfResourceImplTest.java b/scim-server/src/test/java/org/apache/directory/scim/server/rest/SelfResourceImplTest.java index b5e8143..5b22241 100644 --- a/scim-server/src/test/java/org/apache/directory/scim/server/rest/SelfResourceImplTest.java +++ b/scim-server/src/test/java/org/apache/directory/scim/server/rest/SelfResourceImplTest.java
@@ -58,7 +58,7 @@ selfResource.selfIdResolver = selfIdResolverInstance; selfResource.securityContext = securityContext; - Response response = selfResource.getSelf(null, null); + Response response = selfResource.get(null, null); assertThat(response.getEntity(), instanceOf(ErrorResponse.class)); List<String> messages = ((ErrorResponse)response.getEntity()).getErrorMessageList(); assertThat(messages, hasItem("Caller SelfIdResolver not available")); @@ -90,6 +90,6 @@ selfResource.userResource = userResource; // the response is just a passed along from the UserResource, so just validate it is the same instance. - assertThat(selfResource.getSelf(null, null), sameInstance(mockResponse)); + assertThat(selfResource.get(null, null), sameInstance(mockResponse)); } }
diff --git a/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/spec/protocol/SelfResource.java b/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/spec/protocol/SelfResource.java index d040b41..1a40d48 100644 --- a/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/spec/protocol/SelfResource.java +++ b/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/spec/protocol/SelfResource.java
@@ -90,8 +90,8 @@ @ApiResponse(responseCode="500", description="Internal Server Error"), @ApiResponse(responseCode="501", description="Not Implemented") }) - default Response getSelf(@Parameter(name="attributes") @QueryParam("attributes") AttributeReferenceListWrapper attributes, - @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws Exception { + default Response get(@Parameter(name="attributes") @QueryParam("attributes") AttributeReferenceListWrapper attributes, + @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws Exception { return Response.status(Status.NOT_IMPLEMENTED).build(); }