You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by bd...@apache.org on 2022/09/07 23:15:48 UTC
[directory-scimple] 01/01: DRY out BaseResourceTypeResourceImpl
This is an automated email from the ASF dual-hosted git repository.
bdemers pushed a commit to branch dry-base-resource
in repository https://gitbox.apache.org/repos/asf/directory-scimple.git
commit 4e39a057be1daacedce98ab350dd02fc8e225965
Author: Brian Demers <bd...@apache.org>
AuthorDate: Wed Sep 7 19:15:41 2022 -0400
DRY out BaseResourceTypeResourceImpl
(and SelfResourceImpl)
Adds new exception mappers
---
.../server/exception/GenericExceptionMapper.java | 44 ++
.../ResourceExceptionMapper.java} | 30 +-
.../ScimExceptionMapper.java} | 14 +-
.../scim/server/exception/ScimServerException.java | 61 --
.../UnsupportedOperationExceptionMapper.java | 44 ++
.../WebApplicationExceptionMapper.java | 2 +-
.../server/rest/BaseResourceTypeResourceImpl.java | 711 ++++++---------------
.../scim/server/rest/ScimResourceHelper.java | 8 +-
.../scim/server/rest/SelfResourceImpl.java | 60 +-
.../rest/BaseResourceTypeResourceImplTest.java | 123 +---
.../scim/server/rest/SelfResourceImplTest.java | 15 +-
.../scim/protocol/BaseResourceTypeResource.java | 15 +-
.../directory/scim/protocol/SelfResource.java | 12 +-
.../scim/protocol/exception/ScimException.java | 10 +
.../attribute/AttributeReferenceListWrapper.java | 8 +
15 files changed, 405 insertions(+), 752 deletions(-)
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/exception/GenericExceptionMapper.java b/scim-server/src/main/java/org/apache/directory/scim/server/exception/GenericExceptionMapper.java
new file mode 100644
index 00000000..ef0c24f9
--- /dev/null
+++ b/scim-server/src/main/java/org/apache/directory/scim/server/exception/GenericExceptionMapper.java
@@ -0,0 +1,44 @@
+/*
+ * 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.server.exception;
+
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ExceptionMapper;
+import jakarta.ws.rs.ext.Provider;
+import org.apache.directory.scim.protocol.Constants;
+import org.apache.directory.scim.protocol.data.ErrorResponse;
+
+@Provider
+@Produces({Constants.SCIM_CONTENT_TYPE, MediaType.APPLICATION_JSON})
+public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
+
+ @Override
+ public Response toResponse(Throwable throwable) {
+ ErrorResponse em = new ErrorResponse(Response.Status.INTERNAL_SERVER_ERROR, throwable.getMessage());
+
+ Response response = em.toResponse();
+ response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, Constants.SCIM_CONTENT_TYPE);
+
+ return response;
+ }
+}
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/rest/WebApplicationExceptionMapper.java b/scim-server/src/main/java/org/apache/directory/scim/server/exception/ResourceExceptionMapper.java
similarity index 59%
copy from scim-server/src/main/java/org/apache/directory/scim/server/rest/WebApplicationExceptionMapper.java
copy to scim-server/src/main/java/org/apache/directory/scim/server/exception/ResourceExceptionMapper.java
index cd96d801..cc49b4b7 100644
--- a/scim-server/src/main/java/org/apache/directory/scim/server/rest/WebApplicationExceptionMapper.java
+++ b/scim-server/src/main/java/org/apache/directory/scim/server/exception/ResourceExceptionMapper.java
@@ -17,29 +17,43 @@
* under the License.
*/
-package org.apache.directory.scim.server.rest;
+package org.apache.directory.scim.server.exception;
import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
-
import org.apache.directory.scim.protocol.Constants;
+import org.apache.directory.scim.protocol.ErrorMessageType;
import org.apache.directory.scim.protocol.data.ErrorResponse;
+import org.apache.directory.scim.spec.exception.ResourceException;
@Provider
@Produces({Constants.SCIM_CONTENT_TYPE, MediaType.APPLICATION_JSON})
-public class WebApplicationExceptionMapper implements ExceptionMapper<WebApplicationException> {
+public class ResourceExceptionMapper implements ExceptionMapper<ResourceException> {
@Override
- public Response toResponse(WebApplicationException e) {
- ErrorResponse em = new ErrorResponse(Status.fromStatusCode(e.getResponse().getStatus()), e.getMessage());
-
- Response response = em.toResponse();
+ public Response toResponse(ResourceException e) {
+ Status status = Status.fromStatusCode(e.getStatus());
+ ErrorResponse errorResponse = new ErrorResponse(status, e.getMessage());
+
+ if (status == Status.CONFLICT) {
+ errorResponse.setScimType(ErrorMessageType.UNIQUENESS);
+
+ //only use default error message if the ErrorResponse does not already contain a message
+ if (e.getMessage() == null) {
+ errorResponse.setDetail(ErrorMessageType.UNIQUENESS.getDetail());
+ } else {
+ errorResponse.setDetail(e.getMessage());
+ }
+ } else {
+ errorResponse.setDetail(e.getMessage());
+ }
+
+ Response response = errorResponse.toResponse();
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, Constants.SCIM_CONTENT_TYPE);
return response;
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/rest/WebApplicationExceptionMapper.java b/scim-server/src/main/java/org/apache/directory/scim/server/exception/ScimExceptionMapper.java
similarity index 75%
copy from scim-server/src/main/java/org/apache/directory/scim/server/rest/WebApplicationExceptionMapper.java
copy to scim-server/src/main/java/org/apache/directory/scim/server/exception/ScimExceptionMapper.java
index cd96d801..e8454237 100644
--- a/scim-server/src/main/java/org/apache/directory/scim/server/rest/WebApplicationExceptionMapper.java
+++ b/scim-server/src/main/java/org/apache/directory/scim/server/exception/ScimExceptionMapper.java
@@ -17,29 +17,27 @@
* under the License.
*/
-package org.apache.directory.scim.server.rest;
+package org.apache.directory.scim.server.exception;
import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
-
import org.apache.directory.scim.protocol.Constants;
import org.apache.directory.scim.protocol.data.ErrorResponse;
+import org.apache.directory.scim.protocol.exception.ScimException;
@Provider
@Produces({Constants.SCIM_CONTENT_TYPE, MediaType.APPLICATION_JSON})
-public class WebApplicationExceptionMapper implements ExceptionMapper<WebApplicationException> {
+public class ScimExceptionMapper implements ExceptionMapper<ScimException> {
@Override
- public Response toResponse(WebApplicationException e) {
- ErrorResponse em = new ErrorResponse(Status.fromStatusCode(e.getResponse().getStatus()), e.getMessage());
+ public Response toResponse(ScimException e) {
+ ErrorResponse errorResponse = new ErrorResponse(e.getStatus(), e.getMessage());
- Response response = em.toResponse();
+ Response response = errorResponse.toResponse();
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, Constants.SCIM_CONTENT_TYPE);
return response;
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/exception/ScimServerException.java b/scim-server/src/main/java/org/apache/directory/scim/server/exception/ScimServerException.java
deleted file mode 100644
index af1804e0..00000000
--- a/scim-server/src/main/java/org/apache/directory/scim/server/exception/ScimServerException.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-* 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.server.exception;
-
-import jakarta.ws.rs.core.Response.Status;
-
-import org.apache.directory.scim.protocol.ErrorMessageType;
-import org.apache.directory.scim.protocol.data.ErrorResponse;
-import lombok.Getter;
-
-public class ScimServerException extends Exception {
-
- private static final long serialVersionUID = -3803568677019909403L;
-
- @Getter
- private final ErrorResponse errorResponse;
-
- public ScimServerException(Status status, String detail) {
- super(formatMessage(status, null, detail));
- this.errorResponse = new ErrorResponse(status, detail);
- }
-
- public ScimServerException(Status status, String detail, Exception e) {
- super(formatMessage(status, null, detail), e);
- this.errorResponse = new ErrorResponse(status, detail);
- }
-
- public ScimServerException(Status status, ErrorMessageType errorMessageType, String detail) {
- super(formatMessage(status, errorMessageType, detail));
- this.errorResponse = new ErrorResponse(status, detail);
- this.errorResponse.setScimType(errorMessageType);
- }
-
- public ScimServerException(Status status, ErrorMessageType errorMessageType, String detail, Exception e) {
- super(formatMessage(status, errorMessageType, detail), e);
- this.errorResponse = new ErrorResponse(status, detail);
- this.errorResponse.setScimType(errorMessageType);
- }
-
- private static String formatMessage(Status status, ErrorMessageType errorMessageType, String detail) {
- return "Scim Error: " + status + (errorMessageType != null ? " (" + errorMessageType + ")" : "") + ", " + detail;
- }
-
-}
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/exception/UnsupportedOperationExceptionMapper.java b/scim-server/src/main/java/org/apache/directory/scim/server/exception/UnsupportedOperationExceptionMapper.java
new file mode 100644
index 00000000..c3e71ba5
--- /dev/null
+++ b/scim-server/src/main/java/org/apache/directory/scim/server/exception/UnsupportedOperationExceptionMapper.java
@@ -0,0 +1,44 @@
+/*
+ * 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.server.exception;
+
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ExceptionMapper;
+import jakarta.ws.rs.ext.Provider;
+import org.apache.directory.scim.protocol.Constants;
+import org.apache.directory.scim.protocol.data.ErrorResponse;
+
+@Provider
+@Produces({Constants.SCIM_CONTENT_TYPE, MediaType.APPLICATION_JSON})
+public class UnsupportedOperationExceptionMapper implements ExceptionMapper<UnsupportedOperationException> {
+
+ @Override
+ public Response toResponse(UnsupportedOperationException throwable) {
+ ErrorResponse em = new ErrorResponse(Response.Status.NOT_IMPLEMENTED, throwable.getMessage());
+
+ Response response = em.toResponse();
+ response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, Constants.SCIM_CONTENT_TYPE);
+
+ return response;
+ }
+}
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/rest/WebApplicationExceptionMapper.java b/scim-server/src/main/java/org/apache/directory/scim/server/exception/WebApplicationExceptionMapper.java
similarity index 97%
rename from scim-server/src/main/java/org/apache/directory/scim/server/rest/WebApplicationExceptionMapper.java
rename to scim-server/src/main/java/org/apache/directory/scim/server/exception/WebApplicationExceptionMapper.java
index cd96d801..fb11df7e 100644
--- a/scim-server/src/main/java/org/apache/directory/scim/server/rest/WebApplicationExceptionMapper.java
+++ b/scim-server/src/main/java/org/apache/directory/scim/server/exception/WebApplicationExceptionMapper.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.directory.scim.server.rest;
+package org.apache.directory.scim.server.exception;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException;
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/rest/BaseResourceTypeResourceImpl.java b/scim-server/src/main/java/org/apache/directory/scim/server/rest/BaseResourceTypeResourceImpl.java
index 332aae1f..21ff4267 100644
--- a/scim-server/src/main/java/org/apache/directory/scim/server/rest/BaseResourceTypeResourceImpl.java
+++ b/scim-server/src/main/java/org/apache/directory/scim/server/rest/BaseResourceTypeResourceImpl.java
@@ -27,6 +27,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Function;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.ws.rs.WebApplicationException;
@@ -36,10 +37,12 @@ import jakarta.ws.rs.core.Response.ResponseBuilder;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.Response.Status.Family;
+import org.apache.directory.scim.protocol.exception.ScimException;
import org.apache.directory.scim.server.exception.*;
import org.apache.directory.scim.core.repository.RepositoryRegistry;
import org.apache.directory.scim.core.repository.Repository;
import org.apache.directory.scim.core.schema.SchemaRegistry;
+import org.apache.directory.scim.spec.exception.ResourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -53,7 +56,6 @@ import org.apache.directory.scim.spec.filter.attribute.ScimRequestContext;
import org.apache.directory.scim.core.repository.extensions.ClientFilterException;
import org.apache.directory.scim.protocol.adapter.FilterWrapper;
import org.apache.directory.scim.protocol.BaseResourceTypeResource;
-import org.apache.directory.scim.protocol.ErrorMessageType;
import org.apache.directory.scim.spec.filter.attribute.AttributeReference;
import org.apache.directory.scim.spec.filter.attribute.AttributeReferenceListWrapper;
import org.apache.directory.scim.protocol.data.ErrorResponse;
@@ -98,118 +100,66 @@ public abstract class BaseResourceTypeResourceImpl<T extends ScimResource> imple
return repositoryRegistry.getRepository(resourceClass);
}
- Repository<T> getRepositoryInternal() throws ScimServerException {
+ Repository<T> getRepositoryInternal() throws ScimException {
Repository<T> repository = getRepository();
if (repository == null) {
- throw new ScimServerException(Status.INTERNAL_SERVER_ERROR, "Provider not defined");
+ throw new ScimException(Status.INTERNAL_SERVER_ERROR, "Provider not defined");
}
return repository;
}
@Override
- public Response getById(String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) {
+ public Response getById(String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
if (requestContext.getUriInfo().getQueryParameters().getFirst("filter") != null) {
- return Response.status(Status.FORBIDDEN)
- .build();
+ return Response.status(Status.FORBIDDEN).build();
}
- try {
- Repository<T> repository = getRepositoryInternal();
-
- T resource = null;
- try {
- resource = repository.get(id);
- } catch (UnableToRetrieveResourceException e2) {
- Status status = Status.fromStatusCode(e2.getStatus());
- if (status.getFamily()
- .equals(Family.SERVER_ERROR)) {
- return createGenericExceptionResponse(e2, status);
- }
- } catch (Exception e) {
- log.error("Uncaught repository exception", e);
-
- return handleException(e);
- }
-
- if (resource != null) {
- EntityTag backingETag = null;
- try {
- backingETag = etagGenerator.generateEtag(resource);
- } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e1) {
- return createETagErrorResponse();
- }
-
- ResponseBuilder evaluatePreconditionsResponse = requestContext.getRequest().evaluatePreconditions(backingETag);
-
- if (evaluatePreconditionsResponse != null) {
- return Response.status(Status.NOT_MODIFIED)
- .build();
- }
- }
-
- Set<AttributeReference> attributeReferences = Optional.ofNullable(attributes)
- .map(wrapper -> wrapper.getAttributeReferences())
- .orElse(Collections.emptySet());
- Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(excludedAttributes)
- .map(wrapper -> wrapper.getAttributeReferences())
- .orElse(Collections.emptySet());
-
- if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) {
- return createAmbiguousAttributeParametersResponse();
- }
+ Repository<T> repository = getRepositoryInternal();
- if (resource == null) {
- return createNotFoundResponse(id);
+ T resource = null;
+ try {
+ resource = repository.get(id);
+ } catch (UnableToRetrieveResourceException e2) {
+ Status status = Status.fromStatusCode(e2.getStatus());
+ if (status.getFamily().equals(Family.SERVER_ERROR)) {
+ throw e2;
}
+ }
- EntityTag etag = null;
-
- try {
- etag = etagGenerator.generateEtag(resource);
- } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
- return createETagErrorResponse();
- }
+ if (resource != null) {
+ EntityTag backingETag = requireEtag(resource);
+ ResponseBuilder evaluatePreconditionsResponse = requestContext.getRequest().evaluatePreconditions(backingETag);
- // Process Attributes
- try {
- resource = processFilterAttributeExtensions(repository, resource, attributeReferences, excludedAttributeReferences);
- } catch (ClientFilterException e1) {
- ErrorResponse er = new ErrorResponse(e1.getStatus(), e1.getMessage());
- return er.toResponse();
+ if (evaluatePreconditionsResponse != null) {
+ return Response.status(Status.NOT_MODIFIED).build();
}
+ }
- try {
- if (!excludedAttributeReferences.isEmpty()) {
- resource = attributeUtil.setExcludedAttributesForDisplay(resource, excludedAttributeReferences);
- } else {
- resource = attributeUtil.setAttributesForDisplay(resource, attributeReferences);
- }
+ Set<AttributeReference> attributeReferences = AttributeReferenceListWrapper.getAttributeReferences(attributes);
+ Set<AttributeReference> excludedAttributeReferences = AttributeReferenceListWrapper.getAttributeReferences(excludedAttributes);
+ validateAttributes(attributeReferences, excludedAttributeReferences);
- return Response.ok()
- .entity(resource)
- .location(buildLocationTag(resource))
- .tag(etag)
- .build();
- } catch (AttributeException e) {
- return createAttributeProcessingErrorResponse(e);
- }
- } catch (ScimServerException sse) {
- LOG.error("Error Processing SCIM Request", sse);
- return sse.getErrorResponse()
- .toResponse();
+ if (resource == null) {
+ throw notFoundException(id);
}
+ EntityTag etag = requireEtag(resource);
+
+ // Process Attributes
+ resource = processFilterAttributeExtensions(repository, resource, attributeReferences, excludedAttributeReferences);
+ resource = attributesForDisplayThrowOnError(resource, attributeReferences, excludedAttributeReferences);
+ return Response.ok()
+ .entity(resource)
+ .location(buildLocationTag(resource))
+ .tag(etag)
+ .build();
}
@Override
- public Response query(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes, FilterWrapper filter, AttributeReference sortBy, SortOrder sortOrder, Integer startIndex, Integer count) {
+ public Response query(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes, FilterWrapper filter, AttributeReference sortBy, SortOrder sortOrder, Integer startIndex, Integer count) throws ScimException, ResourceException {
SearchRequest searchRequest = new SearchRequest();
- searchRequest.setAttributes(Optional.ofNullable(attributes)
- .map(wrapper -> wrapper.getAttributeReferences())
- .orElse(Collections.emptySet()));
- searchRequest.setExcludedAttributes(Optional.ofNullable(excludedAttributes)
- .map(wrapper -> wrapper.getAttributeReferences())
- .orElse(Collections.emptySet()));
+ searchRequest.setAttributes(AttributeReferenceListWrapper.getAttributeReferences(attributes));
+ searchRequest.setExcludedAttributes(AttributeReferenceListWrapper.getAttributeReferences(excludedAttributes));
if (filter != null) {
searchRequest.setFilter(filter.getFilter());
@@ -227,428 +177,143 @@ public abstract class BaseResourceTypeResourceImpl<T extends ScimResource> imple
}
@Override
- public Response create(T resource, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) {
- try {
- Repository<T> repository = getRepositoryInternal();
+ public Response create(T resource, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
+ Repository<T> repository = getRepositoryInternal();
- Set<AttributeReference> attributeReferences = Optional.ofNullable(attributes)
- .map(wrapper -> wrapper.getAttributeReferences())
- .orElse(Collections.emptySet());
- Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(excludedAttributes)
- .map(wrapper -> wrapper.getAttributeReferences())
- .orElse(Collections.emptySet());
+ Set<AttributeReference> attributeReferences = AttributeReferenceListWrapper.getAttributeReferences(attributes);
+ Set<AttributeReference> excludedAttributeReferences = AttributeReferenceListWrapper.getAttributeReferences(excludedAttributes);
+ validateAttributes(attributeReferences, excludedAttributeReferences);
- if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) {
- return createAmbiguousAttributeParametersResponse();
- }
+ T created = repository.create(resource);
- T created;
- try {
- created = repository.create(resource);
- } catch (UnableToCreateResourceException e1) {
- Status status = Status.fromStatusCode(e1.getStatus());
- ErrorResponse er = new ErrorResponse(status, "Error");
-
- if (status == Status.CONFLICT) {
- er.setScimType(ErrorMessageType.UNIQUENESS);
-
- //only use default error message if the ErrorResponse does not already contain a message
- if (e1.getMessage() == null) {
- er.setDetail(ErrorMessageType.UNIQUENESS.getDetail());
- } else {
- er.setDetail(e1.getMessage());
- }
- } else {
- er.setDetail(e1.getMessage());
- }
-
- return er.toResponse();
- } catch (Exception e) {
- log.error("Uncaught repository exception", e);
+ EntityTag etag = etag(created);
- return handleException(e);
- }
-
- EntityTag etag = null;
- try {
- etag = etagGenerator.generateEtag(created);
- } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
- log.error("Failed to generate etag for newly created entity " + e.getMessage());
- }
+ // Process Attributes
+ created = processFilterAttributeExtensions(repository, created, attributeReferences, excludedAttributeReferences);
- // Process Attributes
- try {
- created = processFilterAttributeExtensions(repository, created, attributeReferences, excludedAttributeReferences);
- } catch (ClientFilterException e1) {
- ErrorResponse er = new ErrorResponse(e1.getStatus(), e1.getMessage());
- return er.toResponse();
- }
-
- try {
- if (!excludedAttributeReferences.isEmpty()) {
- created = attributeUtil.setExcludedAttributesForDisplay(created, excludedAttributeReferences);
- } else {
- created = attributeUtil.setAttributesForDisplay(created, attributeReferences);
- }
- } catch (AttributeException e) {
- if (etag == null) {
- return Response.status(Status.CREATED)
- .location(buildLocationTag(created))
- .build();
- } else {
- Response.status(Status.CREATED)
- .location(buildLocationTag(created))
- .tag(etag)
- .build();
- }
- }
-
- // TODO - Is this the right behavior?
- if (etag == null) {
+ try {
+ created = attributesForDisplay(created, attributeReferences, excludedAttributeReferences);
+ } catch (AttributeException e) {
+ log.debug("Exception thrown while processing attributes", e);
return Response.status(Status.CREATED)
- .location(buildLocationTag(created))
- .entity(created)
- .build();
- }
-
- return Response.status(Status.CREATED)
- .location(buildLocationTag(created))
- .tag(etag)
- .entity(created)
- .build();
- } catch (ScimServerException sse) {
- LOG.error("Error Processing SCIM Request", sse);
- return sse.getErrorResponse()
- .toResponse();
+ .location(buildLocationTag(created))
+ .tag(etag)
+ .build();
}
+
+ return Response.status(Status.CREATED)
+ .location(buildLocationTag(created))
+ .tag(etag)
+ .entity(created)
+ .build();
}
@Override
- public Response find(SearchRequest request) {
- try {
- Repository<T> repository = getRepositoryInternal();
-
- Set<AttributeReference> attributeReferences = Optional.ofNullable(request.getAttributes())
- .orElse(Collections.emptySet());
- Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(request.getExcludedAttributes())
- .orElse(Collections.emptySet());
- if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) {
- return createAmbiguousAttributeParametersResponse();
- }
-
- Filter filter = request.getFilter();
- PageRequest pageRequest = request.getPageRequest();
- SortRequest sortRequest = request.getSortRequest();
-
- ListResponse<T> listResponse = new ListResponse<>();
-
- FilterResponse<T> filterResp = null;
- try {
- filterResp = repository.find(filter, pageRequest, sortRequest);
- } catch (UnableToRetrieveResourceException e1) {
- log.info("Caught an UnableToRetrieveResourceException " + e1.getMessage() + " : " + e1.getStatus());
- return createGenericExceptionResponse(e1, e1.getStatus());
- } catch (Exception e) {
- log.error("Uncaught repository exception", e);
-
- return handleException(e);
- }
-
- // If no resources are found, we should still return a ListResponse with
- // the totalResults set to 0;
- // (https://tools.ietf.org/html/rfc7644#section-3.4.2)
- if (filterResp == null || filterResp.getResources() == null || filterResp.getResources()
- .isEmpty()) {
- listResponse.setTotalResults(0);
- } else {
- log.info("Find returned " + filterResp.getResources()
- .size());
- listResponse.setItemsPerPage(filterResp.getResources()
- .size());
- listResponse.setStartIndex(1);
- listResponse.setTotalResults(filterResp.getResources()
- .size());
-
- List<T> results = new ArrayList<>();
-
- for (T resource : filterResp.getResources()) {
- EntityTag etag = null;
-
- try {
- etag = etagGenerator.generateEtag(resource);
- } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
- return createETagErrorResponse();
- }
-
- // Process Attributes
- try {
- log.info("=== Calling processFilterAttributeExtensions");
- resource = processFilterAttributeExtensions(repository, resource, attributeReferences, excludedAttributeReferences);
- } catch (ClientFilterException e1) {
- ErrorResponse er = new ErrorResponse(e1.getStatus(), e1.getMessage());
- return er.toResponse();
- }
-
- try {
- if (!excludedAttributeReferences.isEmpty()) {
- resource = attributeUtil.setExcludedAttributesForDisplay(resource, excludedAttributeReferences);
- } else {
- resource = attributeUtil.setAttributesForDisplay(resource, attributeReferences);
- }
-
- results.add(resource);
- } catch (AttributeException e) {
- return createAttributeProcessingErrorResponse(e);
- }
- }
-
- listResponse.setResources(results);
+ public Response find(SearchRequest request) throws ScimException, ResourceException {
+ Repository<T> repository = getRepositoryInternal();
+
+ Set<AttributeReference> attributeReferences = Optional.ofNullable(request.getAttributes())
+ .orElse(Collections.emptySet());
+ Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(request.getExcludedAttributes())
+ .orElse(Collections.emptySet());
+ validateAttributes(attributeReferences, excludedAttributeReferences);
+
+ Filter filter = request.getFilter();
+ PageRequest pageRequest = request.getPageRequest();
+ SortRequest sortRequest = request.getSortRequest();
+
+ ListResponse<T> listResponse = new ListResponse<>();
+
+ FilterResponse<T> filterResp = repository.find(filter, pageRequest, sortRequest);
+
+ // If no resources are found, we should still return a ListResponse with
+ // the totalResults set to 0;
+ // (https://tools.ietf.org/html/rfc7644#section-3.4.2)
+ if (filterResp == null || filterResp.getResources() == null || filterResp.getResources()
+ .isEmpty()) {
+ listResponse.setTotalResults(0);
+ } else {
+ log.info("Find returned " + filterResp.getResources()
+ .size());
+ listResponse.setItemsPerPage(filterResp.getResources()
+ .size());
+ listResponse.setStartIndex(1);
+ listResponse.setTotalResults(filterResp.getResources()
+ .size());
+
+ List<T> results = new ArrayList<>();
+
+ for (T resource : filterResp.getResources()) {
+ EntityTag etag = requireEtag(resource);
+
+ // Process Attributes
+ resource = processFilterAttributeExtensions(repository, resource, attributeReferences, excludedAttributeReferences);
+ resource = attributesForDisplayThrowOnError(resource, attributeReferences, excludedAttributeReferences);
+ results.add(resource);
}
- return Response.ok()
- .entity(listResponse)
- .build();
- } catch (ScimServerException sse) {
- LOG.error("Error Processing SCIM Request", sse);
- return sse.getErrorResponse()
- .toResponse();
+ listResponse.setResources(results);
}
+
+ return Response.ok()
+ .entity(listResponse)
+ .build();
}
@Override
- public Response update(T resource, String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) {
- try {
- Repository<T> repository = getRepositoryInternal();
-
- Set<AttributeReference> attributeReferences = Optional.ofNullable(attributes)
- .map(wrapper -> wrapper.getAttributeReferences())
- .orElse(Collections.emptySet());
- Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(excludedAttributes)
- .map(wrapper -> wrapper.getAttributeReferences())
- .orElse(Collections.emptySet());
-
- if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) {
- return createAmbiguousAttributeParametersResponse();
- }
-
- T stored;
- try {
- stored = repository.get(id);
- } catch (UnableToRetrieveResourceException e2) {
- log.error("Unable to retrieve resource with id: {}", id, e2);
- return createGenericExceptionResponse(e2, e2.getStatus());
- } catch (Exception e) {
- log.error("Uncaught repository exception", e);
-
- return handleException(e);
- }
-
- if (stored == null) {
- return createNotFoundResponse(id);
- }
-
- EntityTag backingETag = null;
- try {
- backingETag = etagGenerator.generateEtag(stored);
- } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e1) {
- return createETagErrorResponse();
- }
-
- ResponseBuilder evaluatePreconditionsResponse = requestContext.getRequest().evaluatePreconditions(backingETag);
-
- if (evaluatePreconditionsResponse != null) {
- return createPreconditionFailedResponse(id, evaluatePreconditionsResponse);
- }
-
- T updated;
- try {
- UpdateRequest<T> updateRequest = new UpdateRequest<>(id, stored, resource, schemaRegistry);
- updated = repository.update(updateRequest);
- } catch (UnableToUpdateResourceException e1) {
- return createGenericExceptionResponse(e1, e1.getStatus());
- } catch (Exception e1) {
- log.error("Uncaught repository exception", e1);
-
- return handleException(e1);
- }
-
- // Process Attributes
- try {
- updated = processFilterAttributeExtensions(repository, updated, attributeReferences, excludedAttributeReferences);
- } catch (ClientFilterException e1) {
- ErrorResponse er = new ErrorResponse(e1.getStatus(), e1.getMessage());
- return er.toResponse();
- }
-
- try {
- if (!excludedAttributeReferences.isEmpty()) {
- updated = attributeUtil.setExcludedAttributesForDisplay(updated, excludedAttributeReferences);
- } else {
- updated = attributeUtil.setAttributesForDisplay(updated, attributeReferences);
- }
- } catch (AttributeException e) {
- log.error("Failed to handle attribute processing in update " + e.getMessage());
- }
-
- EntityTag etag = null;
- try {
- etag = etagGenerator.generateEtag(updated);
- } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
- log.error("Failed to generate etag for newly created entity " + e.getMessage());
- }
+ public Response update(T resource, String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
+ return update(id, attributes, excludedAttributes, (stored) ->
+ new UpdateRequest<>(id, stored, resource, schemaRegistry));
+ }
- // TODO - Is this correct or should we support roll back semantics
- if (etag == null) {
- return Response.ok(updated)
- .location(buildLocationTag(updated))
- .build();
- }
+ @Override
+ public Response patch(PatchRequest patchRequest, String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
- return Response.ok(updated)
- .location(buildLocationTag(updated))
- .tag(etag)
- .build();
- } catch (ScimServerException sse) {
- LOG.error("Error Processing SCIM Request", sse);
- return sse.getErrorResponse()
- .toResponse();
- }
+ return update(id, attributes, excludedAttributes, (stored) ->
+ new UpdateRequest<>(id, stored, patchRequest.getPatchOperationList(), schemaRegistry));
}
@Override
- public Response patch(PatchRequest patchRequest, String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) {
- try {
+ public Response delete(String id) throws ScimException, ResourceException {
Repository<T> repository = getRepositoryInternal();
+ repository.delete(id);
+ return Response.noContent()
+ .build();
+ }
- Set<AttributeReference> attributeReferences = Optional.ofNullable(attributes)
- .map(wrapper -> wrapper.getAttributeReferences())
- .orElse(Collections.emptySet());
- Set<AttributeReference> excludedAttributeReferences = Optional.ofNullable(excludedAttributes)
- .map(wrapper -> wrapper.getAttributeReferences())
- .orElse(Collections.emptySet());
-
- if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) {
- return createAmbiguousAttributeParametersResponse();
- }
-
- T stored;
- try {
- stored = repository.get(id);
- } catch (UnableToRetrieveResourceException e2) {
- log.error("Unable to retrieve resource with id: {}", id, e2);
- return createGenericExceptionResponse(e2, e2.getStatus());
- } catch (Exception e) {
- log.error("Uncaught repository exception", e);
-
- return handleException(e);
- }
-
- if (stored == null) {
- return createNotFoundResponse(id);
- }
-
- EntityTag backingETag = null;
- try {
- backingETag = etagGenerator.generateEtag(stored);
- } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e1) {
- return createETagErrorResponse();
- }
-
- ResponseBuilder evaluatePreconditionsResponse = requestContext.getRequest().evaluatePreconditions(backingETag);
-
- if (evaluatePreconditionsResponse != null) {
- return createPreconditionFailedResponse(id, evaluatePreconditionsResponse);
- }
-
- T updated;
- try {
- UpdateRequest<T> updateRequest = new UpdateRequest<>(id, stored, patchRequest.getPatchOperationList(), schemaRegistry);
- updated = repository.update(updateRequest);
- } catch (UnableToUpdateResourceException e1) {
- return createGenericExceptionResponse(e1, e1.getStatus());
- } catch (UnsupportedOperationException e2) {
- return createGenericExceptionResponse(e2, Status.NOT_IMPLEMENTED);
- } catch (Exception e1) {
- log.error("Uncaught repository exception", e1);
-
- return handleException(e1);
- }
+ private Response update(String id, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes, Function<T, UpdateRequest<T>> updateRequestFunction) throws ScimException, ResourceException {
- // Process Attributes
- try {
- updated = processFilterAttributeExtensions(repository, updated, attributeReferences, excludedAttributeReferences);
- } catch (ClientFilterException e1) {
- ErrorResponse er = new ErrorResponse(e1.getStatus(), e1.getMessage());
- return er.toResponse();
- }
+ Repository<T> repository = getRepositoryInternal();
- try {
- if (!excludedAttributeReferences.isEmpty()) {
- updated = attributeUtil.setExcludedAttributesForDisplay(updated, excludedAttributeReferences);
- } else {
- updated = attributeUtil.setAttributesForDisplay(updated, attributeReferences);
- }
- } catch (AttributeException e) {
- log.error("Failed to handle attribute processing in update " + e.getMessage());
- }
+ Set<AttributeReference> attributeReferences = AttributeReferenceListWrapper.getAttributeReferences(attributes);
+ Set<AttributeReference> excludedAttributeReferences = AttributeReferenceListWrapper.getAttributeReferences(excludedAttributes);
+ validateAttributes(attributeReferences, excludedAttributeReferences);
- EntityTag etag = null;
- try {
- etag = etagGenerator.generateEtag(updated);
- } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
- log.error("Failed to generate etag for newly created entity " + e.getMessage());
- }
+ T stored = repository.get(id);
- // TODO - Is this correct or should we support roll back semantics
- if (etag == null) {
- return Response.ok(updated)
- .location(buildLocationTag(updated))
- .build();
- }
-
- return Response.ok(updated)
- .location(buildLocationTag(updated))
- .tag(etag)
- .build();
- } catch (ScimServerException sse) {
- LOG.error("Error Processing SCIM Request", sse);
- return sse.getErrorResponse()
- .toResponse();
+ if (stored == null) {
+ throw notFoundException(id);
}
- }
+ EntityTag backingETag = requireEtag(stored);
+ validatePreconditions(id, backingETag);
- @Override
- public Response delete(String id) {
- Response response;
- try {
- Repository<T> repository = getRepositoryInternal();
-
- try {
- response = Response.noContent()
- .build();
+ UpdateRequest<T> updateRequest = updateRequestFunction.apply(stored);
+ T updated = repository.update(updateRequest);
- repository.delete(id);
- return response;
- } catch (UnableToDeleteResourceException e) {
- response = Response.status(e.getStatus()).build();
- log.error("Unable to delete resource", e);
+ // Process Attributes
+ updated = processFilterAttributeExtensions(repository, updated, attributeReferences, excludedAttributeReferences);
+ updated = attributesForDisplayIgnoreErrors(updated, attributeReferences, excludedAttributeReferences);
- return response;
- } catch (Exception e) {
- log.error("Uncaught repository exception", e);
-
- return handleException(e);
- }
- } catch (ScimServerException sse) {
- LOG.error("Error Processing SCIM Request", sse);
- return sse.getErrorResponse()
- .toResponse();
- }
+ EntityTag etag = etag(updated);
+ return Response.ok(updated)
+ .location(buildLocationTag(updated))
+ .tag(etag)
+ .build();
}
@SuppressWarnings("unchecked")
- private T processFilterAttributeExtensions(Repository<T> repository, T resource, Set<AttributeReference> attributeReferences, Set<AttributeReference> excludedAttributeReferences) throws ClientFilterException {
+ private T processFilterAttributeExtensions(Repository<T> repository, T resource, Set<AttributeReference> attributeReferences, Set<AttributeReference> excludedAttributeReferences) throws ScimException {
ScimProcessingExtension annotation = repository.getClass()
.getAnnotation(ScimProcessingExtension.class);
if (annotation != null) {
@@ -659,8 +324,12 @@ public abstract class BaseResourceTypeResourceImpl<T extends ScimResource> imple
AttributeFilterExtension attributeFilterExtension = (AttributeFilterExtension) processingExtension;
ScimRequestContext scimRequestContext = new ScimRequestContext(attributeReferences, excludedAttributeReferences);
- resource = (T) attributeFilterExtension.filterAttributes(resource, scimRequestContext);
- log.info("Resource now - " + resource.toString());
+ try {
+ resource = (T) attributeFilterExtension.filterAttributes(resource, scimRequestContext);
+ log.debug("Resource now - " + resource.toString());
+ } catch (ClientFilterException e) {
+ throw new ScimException(Status.fromStatusCode(e.getStatus()), e.getMessage(), e);
+ }
}
}
}
@@ -679,57 +348,69 @@ public abstract class BaseResourceTypeResourceImpl<T extends ScimResource> imple
.build();
}
- static Response createGenericExceptionResponse(Throwable e1, int statusCode) {
- return createGenericExceptionResponse(e1, Status.fromStatusCode(statusCode));
- }
-
- public static Response createGenericExceptionResponse(Throwable e1, Status status) {
- Status myStatus = status;
- if (myStatus == null) {
- myStatus = Status.INTERNAL_SERVER_ERROR;
+ private T attributesForDisplay(T resource, Set<AttributeReference> includedAttributes, Set<AttributeReference> excludedAttributes) throws AttributeException {
+ if (!excludedAttributes.isEmpty()) {
+ resource = attributeUtil.setExcludedAttributesForDisplay(resource, excludedAttributes);
+ } else {
+ resource = attributeUtil.setAttributesForDisplay(resource, includedAttributes);
}
-
- ErrorResponse er = new ErrorResponse(myStatus, e1 != null ? e1.getMessage() : "Unknown Server Error");
- return er.toResponse();
+ return resource;
}
- private Response createAmbiguousAttributeParametersResponse() {
- ErrorResponse er = new ErrorResponse(Status.BAD_REQUEST, "Cannot include both attributes and excluded attributes in a single request");
- return er.toResponse();
+ private T attributesForDisplayIgnoreErrors(T resource, Set<AttributeReference> includedAttributes, Set<AttributeReference> excludedAttributes) {
+ try {
+ return attributesForDisplay(resource, includedAttributes, excludedAttributes);
+ } catch (AttributeException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to handle attribute processing in update " + e.getMessage(), e);
+ } else {
+ log.warn("Failed to handle attribute processing in update " + e.getMessage());
+ }
+ }
+ return resource;
}
- private Response createNotFoundResponse(String id) {
- ErrorResponse er = new ErrorResponse(Status.NOT_FOUND, "Resource " + id + " not found");
- return er.toResponse();
+ private T attributesForDisplayThrowOnError(T resource, Set<AttributeReference> includedAttributes, Set<AttributeReference> excludedAttributes) throws ScimException {
+ try {
+ return attributesForDisplay(resource, includedAttributes, excludedAttributes);
+ } catch (AttributeException e) {
+ throw new ScimException(Status.INTERNAL_SERVER_ERROR, "Failed to parse the attribute query value " + e.getMessage(), e);
+ }
}
- private Response createETagErrorResponse() {
- ErrorResponse er = new ErrorResponse(Status.INTERNAL_SERVER_ERROR, "Failed to generate the etag");
- return er.toResponse();
+ private ScimException notFoundException(String id) {
+ return new ScimException(Status.NOT_FOUND, "Resource " + id + " not found");
}
- private Response createAttributeProcessingErrorResponse(Exception e) {
- ErrorResponse er = new ErrorResponse(Status.INTERNAL_SERVER_ERROR, "Failed to parse the attribute query value " + e.getMessage());
- return er.toResponse();
+ private void validatePreconditions(String id, EntityTag etag) {
+ ResponseBuilder response = requestContext.getRequest().evaluatePreconditions(etag);
+ if (response != null) {
+ throw new WebApplicationException(response
+ .entity(new ErrorResponse(Status.PRECONDITION_FAILED, "Failed to update record, backing record has changed - " + id))
+ .build());
+ }
}
- private Response createNoRepositoryException() {
- ErrorResponse er = new ErrorResponse(Status.INTERNAL_SERVER_ERROR, "Repository not defined");
- return er.toResponse();
+ private EntityTag requireEtag(ScimResource resource) throws ScimException {
+ try {
+ return etagGenerator.generateEtag(resource);
+ } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
+ throw new ScimException(Status.INTERNAL_SERVER_ERROR, "Failed to generate the etag", e);
+ }
}
- private Response createPreconditionFailedResponse(String id, ResponseBuilder evaluatePreconditionsResponse) {
- ErrorResponse er = new ErrorResponse(Status.PRECONDITION_FAILED, "Failed to update record, backing record has changed - " + id);
- log.warn("Failed to update record, backing record has changed - " + id);
- return evaluatePreconditionsResponse.entity(er)
- .build();
+ private EntityTag etag(ScimResource resource) {
+ try {
+ return etagGenerator.generateEtag(resource);
+ } catch (JsonProcessingException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
+ log.warn("Failed to generate etag for resource", e);
+ return null;
+ }
}
- Response handleException(Throwable unhandled) {
- // Allow for ErrorMessageViolationExceptionMapper to handle JAX-RS exceptions by default
- if (unhandled instanceof WebApplicationException) {
- throw (WebApplicationException) unhandled;
+ private void validateAttributes(Set<AttributeReference> attributeReferences, Set<AttributeReference> excludedAttributeReferences) throws ScimException {
+ if (!attributeReferences.isEmpty() && !excludedAttributeReferences.isEmpty()) {
+ throw new ScimException(Status.BAD_REQUEST, "Cannot include both attributes and excluded attributes in a single request");
}
- return BaseResourceTypeResourceImpl.createGenericExceptionResponse(unhandled, Status.INTERNAL_SERVER_ERROR);
}
}
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/rest/ScimResourceHelper.java b/scim-server/src/main/java/org/apache/directory/scim/server/rest/ScimResourceHelper.java
index 26f52bfd..ee4e0b6b 100644
--- a/scim-server/src/main/java/org/apache/directory/scim/server/rest/ScimResourceHelper.java
+++ b/scim-server/src/main/java/org/apache/directory/scim/server/rest/ScimResourceHelper.java
@@ -21,7 +21,7 @@ package org.apache.directory.scim.server.rest;
import java.util.Set;
-import org.apache.directory.scim.server.exception.FilterParseExceptionMapper;
+import org.apache.directory.scim.server.exception.*;
/**
* Provides the SCIM defined set of end-points and resources without declaring a
@@ -54,8 +54,14 @@ public final class ScimResourceHelper {
SelfResourceImpl.class,
ServiceProviderConfigResourceImpl.class,
UserResourceImpl.class,
+
+ // exception mappers
+ ResourceExceptionMapper.class,
+ ScimExceptionMapper.class,
FilterParseExceptionMapper.class,
WebApplicationExceptionMapper.class,
+ UnsupportedOperationExceptionMapper.class,
+ GenericExceptionMapper.class,
// handle MediaType of application/scim+json
ScimJacksonXmlBindJsonProvider.class);
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 32db8deb..cfabb667 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
@@ -61,15 +61,9 @@ public class SelfResourceImpl implements SelfResource {
}
@Override
- public Response getSelf(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) {
- try {
- String internalId = getInternalId();
- return userResource.getById(internalId, attributes, excludedAttributes);
- } catch (ResourceException e) {
- return createErrorResponse(e);
- } catch (ScimException e) {
- return createErrorResponse(e);
- }
+ public Response getSelf(AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
+ String internalId = getInternalId();
+ return userResource.getById(internalId, attributes, excludedAttributes);
}
// @Override
@@ -81,51 +75,21 @@ public class SelfResourceImpl implements SelfResource {
// }
@Override
- public Response update(ScimUser resource, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) {
- try {
- String internalId = getInternalId();
- return userResource.update(resource, internalId, attributes, excludedAttributes);
- } catch (ResourceException e) {
- return createErrorResponse(e);
- } catch (ScimException e) {
- return createErrorResponse(e);
- }
+ public Response update(ScimUser resource, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
+ String internalId = getInternalId();
+ return userResource.update(resource, internalId, attributes, excludedAttributes);
}
@Override
- public Response patch(PatchRequest patchRequest, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) {
- try {
- String internalId = getInternalId();
- return userResource.patch(patchRequest, internalId, attributes, excludedAttributes);
- } catch (ResourceException e) {
- return createErrorResponse(e);
- } catch (ScimException e) {
- return createErrorResponse(e);
- }
+ public Response patch(PatchRequest patchRequest, AttributeReferenceListWrapper attributes, AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
+ String internalId = getInternalId();
+ return userResource.patch(patchRequest, internalId, attributes, excludedAttributes);
}
@Override
- public Response delete() {
- try {
- String internalId = getInternalId();
- return userResource.delete(internalId);
- } catch (ResourceException e) {
- return createErrorResponse(e);
- } catch (ScimException e) {
- return createErrorResponse(e);
- }
- }
-
- private Response createErrorResponse(ScimException e) {
- ErrorResponse er = new ErrorResponse(e.getStatus(), "Error");
- er.addErrorMessage(e.getMessage());
- return er.toResponse();
- }
-
- private Response createErrorResponse(ResourceException e) {
- ErrorResponse er = new ErrorResponse(e.getStatus(), "Error");
- er.addErrorMessage(e.getMessage());
- return er.toResponse();
+ public Response delete() throws ScimException, ResourceException {
+ String internalId = getInternalId();
+ return userResource.delete(internalId);
}
private String getInternalId() throws ResourceException {
diff --git a/scim-server/src/test/java/org/apache/directory/scim/server/rest/BaseResourceTypeResourceImplTest.java b/scim-server/src/test/java/org/apache/directory/scim/server/rest/BaseResourceTypeResourceImplTest.java
index f2996b62..5b2d04ec 100644
--- a/scim-server/src/test/java/org/apache/directory/scim/server/rest/BaseResourceTypeResourceImplTest.java
+++ b/scim-server/src/test/java/org/apache/directory/scim/server/rest/BaseResourceTypeResourceImplTest.java
@@ -19,32 +19,27 @@
package org.apache.directory.scim.server.rest;
-import static com.googlecode.catchexception.CatchException.catchException;
-import static com.googlecode.catchexception.CatchException.caughtException;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.sameInstance;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.hamcrest.Matchers.*;
+import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.UriInfo;
+import org.apache.directory.scim.protocol.exception.ScimException;
+import org.apache.directory.scim.spec.exception.ResourceException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.apache.directory.scim.server.exception.ScimServerException;
import org.apache.directory.scim.core.repository.Repository;
import org.apache.directory.scim.server.utility.ExampleObjectExtension;
import org.apache.directory.scim.server.utility.ExampleObjectExtension.ComplexObject;
@@ -52,7 +47,6 @@ import org.apache.directory.scim.spec.extension.EnterpriseExtension;
import org.apache.directory.scim.spec.extension.EnterpriseExtension.Manager;
import org.apache.directory.scim.spec.phonenumber.PhoneNumberParseException;
import org.apache.directory.scim.spec.filter.attribute.AttributeReferenceListWrapper;
-import org.apache.directory.scim.protocol.data.ErrorResponse;
import org.apache.directory.scim.protocol.data.PatchRequest;
import org.apache.directory.scim.protocol.data.SearchRequest;
import org.apache.directory.scim.spec.resources.Address;
@@ -73,7 +67,7 @@ public class BaseResourceTypeResourceImplTest {
AttributeReferenceListWrapper excludedAttributeList = new AttributeReferenceListWrapper("emails, phoneNumbers");
@Test
- public void testGetProviderInternal_ScimServerExceptionThrownWhenNoProvider() throws ScimServerException {
+ public void testGetProviderInternal_ScimServerExceptionThrownWhenNoProvider() throws ScimException {
// given
@SuppressWarnings("rawtypes")
BaseResourceTypeResourceImpl baseResourceImpl = Mockito.mock(BaseResourceTypeResourceImpl.class);
@@ -81,12 +75,12 @@ public class BaseResourceTypeResourceImplTest {
when(baseResourceImpl.getRepositoryInternal()).thenCallRealMethod();
// when
- assertThrows(ScimServerException.class, () -> baseResourceImpl.getRepositoryInternal());
+ assertThrows(ScimException.class, () -> baseResourceImpl.getRepositoryInternal());
}
@SuppressWarnings("unchecked")
@Test
- public void testGetById_ForbiddenIfNoFilter() {
+ public void testGetById_ForbiddenIfNoFilter() throws ScimException, ResourceException {
// given
@SuppressWarnings("rawtypes")
BaseResourceTypeResourceImpl baseResourceImpl = Mockito.mock(BaseResourceTypeResourceImpl.class);
@@ -102,12 +96,12 @@ public class BaseResourceTypeResourceImplTest {
Response response = baseResourceImpl.getById("1", includedAttributeList, excludedAttributeList);
// then
- assertTrue(response != null);
- assertTrue(response.getStatus() == Status.FORBIDDEN.getStatusCode());
+ assertNotNull(response);
+ assertEquals(response.getStatus(), Status.FORBIDDEN.getStatusCode());
}
@Test
- public void testQuery_NullParametersValid() {
+ public void testQuery_NullParametersValid() throws ScimException, ResourceException {
// given
@SuppressWarnings("rawtypes")
BaseResourceTypeResourceImpl baseResourceImpl = Mockito.mock(BaseResourceTypeResourceImpl.class);
@@ -124,38 +118,31 @@ public class BaseResourceTypeResourceImplTest {
Response response = baseResourceImpl.query(null, null, null, null, null, null, null);
// then
- verify(baseResourceImpl, times(1)).find(searchRequest);
- assertTrue(response != null);
- assertTrue(response.getStatus() == Status.OK.getStatusCode());
+ verify(baseResourceImpl, times(1)).find(searchRequest);
+ assertNotNull(response);
+ assertEquals(response.getStatus(), Status.OK.getStatusCode());
}
@Test
- public void testCreate_ErrorIfBothAttributesAndExcludedAttributesExist() {
+ public void testCreate_ErrorIfBothAttributesAndExcludedAttributesExist() throws ScimException, ResourceException, PhoneNumberParseException {
// given
@SuppressWarnings("unchecked")
BaseResourceTypeResourceImpl<ScimUser> baseResourceImpl = Mockito.mock(BaseResourceTypeResourceImpl.class);
- ScimUser scimUser = null;
- try {
- scimUser = getScimUser();
- } catch (PhoneNumberParseException e) {
- fail("Parsing phone number in getScimUser failed");
- }
+ ScimUser scimUser = getScimUser();
when(baseResourceImpl.create(scimUser, includedAttributeList, excludedAttributeList)).thenCallRealMethod();
// when
- Response response = baseResourceImpl.create(scimUser, includedAttributeList, excludedAttributeList);
-
+ ScimException exception = assertThrows(ScimException.class, () -> baseResourceImpl.create(scimUser, includedAttributeList, excludedAttributeList));
+
// then
- assertTrue(response != null);
- assertTrue(response.getStatus() == Status.BAD_REQUEST.getStatusCode());
- assertTrue(response.getEntity() instanceof ErrorResponse);
- assertTrue(((ErrorResponse)response.getEntity()).getDetail().equals("Cannot include both attributes and excluded attributes in a single request"));
+ assertEquals(exception.getStatus(), Status.BAD_REQUEST);
+ assertThat(exception.getError().getDetail(), is("Cannot include both attributes and excluded attributes in a single request"));
}
@Test
- public void testFind_ErrorIfBothAttributesAndExcludedAttributesExist() {
+ public void testFind_ErrorIfBothAttributesAndExcludedAttributesExist() throws ScimException, ResourceException {
// given
@SuppressWarnings("rawtypes")
BaseResourceTypeResourceImpl baseResourceImpl = Mockito.mock(BaseResourceTypeResourceImpl.class);
@@ -167,38 +154,29 @@ public class BaseResourceTypeResourceImplTest {
when(baseResourceImpl.find(searchRequest)).thenCallRealMethod();
// when
- Response response = baseResourceImpl.find(searchRequest);
-
+ ScimException exception = assertThrows(ScimException.class, () -> baseResourceImpl.find(searchRequest));
+
// then
- assertTrue(response != null);
- assertTrue(response.getStatus() == Status.BAD_REQUEST.getStatusCode());
- assertTrue(response.getEntity() instanceof ErrorResponse);
- assertTrue(((ErrorResponse)response.getEntity()).getDetail().equals("Cannot include both attributes and excluded attributes in a single request"));
+ assertEquals(exception.getStatus(), Status.BAD_REQUEST);
+ assertThat(exception.getError().getDetail(), is("Cannot include both attributes and excluded attributes in a single request"));
}
@Test
- public void testUpdate_ErrorIfBothAttributesAndExcludedAttributesExist() {
+ public void testUpdate_ErrorIfBothAttributesAndExcludedAttributesExist() throws ScimException, ResourceException, PhoneNumberParseException {
// given
@SuppressWarnings("unchecked")
BaseResourceTypeResourceImpl<ScimUser> baseResourceImpl = Mockito.mock(BaseResourceTypeResourceImpl.class);
- ScimUser scimUser = null;
- try {
- scimUser = getScimUser();
- } catch (PhoneNumberParseException e) {
- fail("Parsing phone number in getScimUser failed");
- }
+ ScimUser scimUser = getScimUser();
when(baseResourceImpl.update(scimUser, "1", includedAttributeList, excludedAttributeList)).thenCallRealMethod();
// when
- Response response = baseResourceImpl.update(scimUser, "1", includedAttributeList, excludedAttributeList);
-
+ ScimException exception = assertThrows(ScimException.class, () -> baseResourceImpl.update(scimUser, "1", includedAttributeList, excludedAttributeList));
+
// then
- assertTrue(response != null);
- assertTrue(response.getStatus() == Status.BAD_REQUEST.getStatusCode());
- assertTrue(response.getEntity() instanceof ErrorResponse);
- assertTrue(((ErrorResponse)response.getEntity()).getDetail().equals("Cannot include both attributes and excluded attributes in a single request"));
+ assertEquals(exception.getStatus(), Status.BAD_REQUEST);
+ assertThat(exception.getError().getDetail(), is("Cannot include both attributes and excluded attributes in a single request"));
}
@Test
@@ -212,46 +190,13 @@ public class BaseResourceTypeResourceImplTest {
when(baseResourceImpl.patch(patchRequest, "1", includedAttributeList, excludedAttributeList)).thenCallRealMethod();
// when
- Response response = baseResourceImpl.patch(patchRequest, "1", includedAttributeList, excludedAttributeList);
-
- // then
- assertTrue(response != null);
- assertTrue(response.getStatus() == Status.BAD_REQUEST.getStatusCode());
- assertTrue(response.getEntity() instanceof ErrorResponse);
- assertTrue(((ErrorResponse)response.getEntity()).getDetail().equals("Cannot include both attributes and excluded attributes in a single request"));
- }
-
- @Test
- public void handleException_jaxrsExceptionTest() {
- BaseResourceTypeResourceImpl<ScimUser> baseResourceImpl = mock(BaseResourceTypeResourceImpl.class);
- when(baseResourceImpl.handleException(any())).thenCallRealMethod();
-
- Exception e = new WebApplicationException();
- catchException(() -> baseResourceImpl.handleException(e));
- assertThat(caughtException(), sameInstance(e));
- }
-
- @Test
- public void handleException_runtimeExceptionTest() {
- BaseResourceTypeResourceImpl<ScimUser> baseResourceImpl = mock(BaseResourceTypeResourceImpl.class);
- when(baseResourceImpl.handleException(any())).thenCallRealMethod();
+ ScimException exception = assertThrows(ScimException.class, () -> baseResourceImpl.patch(patchRequest, "1", includedAttributeList, excludedAttributeList));
- Exception e = new RuntimeException("fake test exception");
- Response response = baseResourceImpl.handleException(e);
- assertThat(response.getStatus(), is(500));
- assertThat(((ErrorResponse)response.getEntity()).getDetail(), is("fake test exception"));
+ // then
+ assertEquals(exception.getStatus(), Status.BAD_REQUEST);
+ assertThat(exception.getError().getDetail(), is("Cannot include both attributes and excluded attributes in a single request"));
}
- @Test
- public void handleException_nullExceptionTest() {
- BaseResourceTypeResourceImpl<ScimUser> baseResourceImpl = mock(BaseResourceTypeResourceImpl.class);
- when(baseResourceImpl.handleException(any())).thenCallRealMethod();
-
- Response response = baseResourceImpl.handleException(null);
- assertThat(response.getStatus(), is(500));
- assertThat(((ErrorResponse)response.getEntity()).getDetail(), is("Unknown Server Error"));
- }
-
private ScimUser getScimUser() throws PhoneNumberParseException {
ScimUser user = new ScimUser();
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 619c751b..982a59be 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
@@ -19,10 +19,8 @@
package org.apache.directory.scim.server.rest;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.hasItem;
-import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.hamcrest.Matchers.sameInstance;
+import static org.hamcrest.Matchers.*;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -33,6 +31,7 @@ import jakarta.enterprise.inject.Instance;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.SecurityContext;
+import org.apache.directory.scim.server.exception.UnableToResolveIdResourceException;
import org.apache.directory.scim.spec.exception.ResourceException;
import org.apache.directory.scim.core.repository.SelfIdResolver;
import org.apache.directory.scim.protocol.UserResource;
@@ -56,11 +55,9 @@ public class SelfResourceImplTest {
SelfResourceImpl selfResource = new SelfResourceImpl(null, selfIdResolverInstance, new RequestContext().setSecurityContext(securityContext));
- Response response = selfResource.getSelf(null, null);
- assertThat(response.getEntity(), instanceOf(ErrorResponse.class));
- List<String> messages = ((ErrorResponse)response.getEntity()).getErrorMessageList();
- assertThat(messages, hasItem("Caller SelfIdResolver not available"));
- assertThat(messages, hasSize(1));
+ UnableToResolveIdResourceException exception = assertThrows(UnableToResolveIdResourceException.class, () -> selfResource.getSelf(null, null));
+
+ assertThat(exception.getMessage(), is("Caller SelfIdResolver not available"));
}
@Test
diff --git a/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/BaseResourceTypeResource.java b/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/BaseResourceTypeResource.java
index 2278ea11..806a8947 100644
--- a/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/BaseResourceTypeResource.java
+++ b/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/BaseResourceTypeResource.java
@@ -45,6 +45,7 @@ import jakarta.ws.rs.core.Response.Status;
import org.apache.directory.scim.protocol.data.PatchRequest;
import org.apache.directory.scim.protocol.exception.ScimException;
import org.apache.directory.scim.protocol.adapter.FilterWrapper;
+import org.apache.directory.scim.spec.exception.ResourceException;
import org.apache.directory.scim.spec.filter.attribute.AttributeReference;
import org.apache.directory.scim.spec.filter.attribute.AttributeReferenceListWrapper;
import org.apache.directory.scim.protocol.data.SearchRequest;
@@ -76,7 +77,7 @@ public interface BaseResourceTypeResource<T> {
})
default Response getById(@Parameter(name="id", required=true) @PathParam("id") String id,
@Parameter(name="attributes") @QueryParam("attributes") AttributeReferenceListWrapper attributes,
- @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException {
+ @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
@@ -102,7 +103,7 @@ public interface BaseResourceTypeResource<T> {
@Parameter(name="sortBy") @QueryParam("sortBy") AttributeReference sortBy,
@Parameter(name="sortOrder") @QueryParam("sortOrder") SortOrder sortOrder,
@Parameter(name="startIndex") @QueryParam("startIndex") Integer startIndex,
- @Parameter(name="count") @QueryParam("count") Integer count) throws ScimException {
+ @Parameter(name="count") @QueryParam("count") Integer count) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
@@ -127,7 +128,7 @@ public interface BaseResourceTypeResource<T> {
schema = @Schema(implementation = ScimResource.class)),
required = true) T resource,
@Parameter(name="attributes") @QueryParam("attributes") AttributeReferenceListWrapper attributes,
- @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException {
+ @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
@@ -148,7 +149,7 @@ public interface BaseResourceTypeResource<T> {
@ApiResponse(responseCode = "501", description = "Not Implemented") })
default Response find(@RequestBody(content = @Content(mediaType = Constants.SCIM_CONTENT_TYPE,
schema = @Schema(implementation = SearchRequest.class)),
- required = true) SearchRequest request) throws ScimException {
+ required = true) SearchRequest request) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
@@ -173,7 +174,7 @@ public interface BaseResourceTypeResource<T> {
required = true) T resource,
@PathParam("id") String id,
@Parameter(name="attributes") @QueryParam("attributes") AttributeReferenceListWrapper attributes,
- @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException {
+ @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
@@ -193,7 +194,7 @@ public interface BaseResourceTypeResource<T> {
required = true) PatchRequest patchRequest,
@PathParam("id") String id,
@Parameter(name="attributes") @QueryParam("attributes") AttributeReferenceListWrapper attributes,
- @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException {
+ @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
@@ -206,7 +207,7 @@ public interface BaseResourceTypeResource<T> {
@ApiResponse(responseCode = "404", description = "Not found"),
@ApiResponse(responseCode = "500", description = "Internal Server Error"),
@ApiResponse(responseCode = "501", description = "Not Implemented") })
- default Response delete(@Parameter(name = "id", required = true) @PathParam("id") String id) throws ScimException {
+ default Response delete(@Parameter(name = "id", required = true) @PathParam("id") String id) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
}
diff --git a/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/SelfResource.java b/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/SelfResource.java
index 201fd443..3f415f9f 100644
--- a/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/SelfResource.java
+++ b/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/SelfResource.java
@@ -41,6 +41,8 @@ import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import org.apache.directory.scim.protocol.data.PatchRequest;
+import org.apache.directory.scim.protocol.exception.ScimException;
+import org.apache.directory.scim.spec.exception.ResourceException;
import org.apache.directory.scim.spec.filter.attribute.AttributeReferenceListWrapper;
import org.apache.directory.scim.spec.resources.ScimResource;
import org.apache.directory.scim.spec.resources.ScimUser;
@@ -91,7 +93,7 @@ public interface SelfResource {
@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 {
+ @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
@@ -116,7 +118,7 @@ public interface SelfResource {
schema = @Schema(implementation = ScimResource.class)),
required = true) ScimUser resource,
@Parameter(name="attributes") @QueryParam("attributes") AttributeReferenceListWrapper attributes,
- @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws Exception {
+ @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
@@ -139,7 +141,7 @@ public interface SelfResource {
schema = @Schema(implementation = ScimUser.class)),
required = true) ScimUser resource,
@Parameter(name="attributes") @QueryParam("attributes") AttributeReferenceListWrapper attributes,
- @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws Exception {
+ @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
@@ -159,7 +161,7 @@ public interface SelfResource {
schema = @Schema(implementation = PatchRequest.class)),
required = true) PatchRequest patchRequest,
@Parameter(name="attributes") @QueryParam("attributes") AttributeReferenceListWrapper attributes,
- @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws Exception {
+ @Parameter(name="excludedAttributes") @QueryParam("excludedAttributes") AttributeReferenceListWrapper excludedAttributes) throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
@@ -171,7 +173,7 @@ public interface SelfResource {
@ApiResponse(responseCode = "404", description = "Not found"),
@ApiResponse(responseCode = "500", description = "Internal Server Error"),
@ApiResponse(responseCode = "501", description = "Not Implemented") })
- default Response delete() throws Exception {
+ default Response delete() throws ScimException, ResourceException {
return Response.status(Status.NOT_IMPLEMENTED).build();
}
}
diff --git a/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/exception/ScimException.java b/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/exception/ScimException.java
index 48ce8829..63cec55a 100644
--- a/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/exception/ScimException.java
+++ b/scim-spec/scim-spec-protocol/src/main/java/org/apache/directory/scim/protocol/exception/ScimException.java
@@ -33,6 +33,16 @@ public class ScimException extends Exception {
private ErrorResponse error;
private Status status;
+ public ScimException(Status status, String message, Throwable cause) {
+ super(message, cause);
+ this.error = new ErrorResponse(status, message);
+ this.status = status;
+ }
+
+ public ScimException(Status status, String message) {
+ this(new ErrorResponse(status, message), status);
+ }
+
public ScimException(ErrorResponse error, Status status) {
this.error = error;
this.status = status;
diff --git a/scim-spec/scim-spec-schema/src/main/java/org/apache/directory/scim/spec/filter/attribute/AttributeReferenceListWrapper.java b/scim-spec/scim-spec-schema/src/main/java/org/apache/directory/scim/spec/filter/attribute/AttributeReferenceListWrapper.java
index f2819a6b..b2d39e8c 100644
--- a/scim-spec/scim-spec-schema/src/main/java/org/apache/directory/scim/spec/filter/attribute/AttributeReferenceListWrapper.java
+++ b/scim-spec/scim-spec-schema/src/main/java/org/apache/directory/scim/spec/filter/attribute/AttributeReferenceListWrapper.java
@@ -19,7 +19,9 @@
package org.apache.directory.scim.spec.filter.attribute;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -54,6 +56,12 @@ public class AttributeReferenceListWrapper {
return wrapper;
}
+ public static Set<AttributeReference> getAttributeReferences(AttributeReferenceListWrapper attributeReferenceListWrapper) {
+ return Optional.ofNullable(attributeReferenceListWrapper)
+ .map(wrapper -> wrapper.getAttributeReferences())
+ .orElse(Collections.emptySet());
+ }
+
public String toString() {
if (attributeReferences == null || attributeReferences.isEmpty()) {
return "";