You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2018/02/16 15:29:00 UTC

[2/2] syncope git commit: [SYNCOPE-1274] Add documentation for ETag, If-Match

[SYNCOPE-1274] Add documentation for ETag, If-Match


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/2c7138b2
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/2c7138b2
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/2c7138b2

Branch: refs/heads/2_0_X
Commit: 2c7138b2f9c6e1314112f618f083f0672136ac27
Parents: 66aac6a
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Feb 16 15:36:50 2018 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Feb 16 16:28:52 2018 +0100

----------------------------------------------------------------------
 .../rest/api/service/AnyObjectService.java      | 46 ++++++++----
 .../common/rest/api/service/AnyService.java     | 76 +++++++++++++++++---
 .../common/rest/api/service/GroupService.java   | 46 ++++++++----
 .../common/rest/api/service/UserService.java    | 65 ++++++++++++-----
 4 files changed, 181 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/2c7138b2/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyObjectService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyObjectService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyObjectService.java
index 9736860..8bd75cc 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyObjectService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyObjectService.java
@@ -51,6 +51,14 @@ import org.apache.syncope.common.rest.api.beans.AnyQuery;
 @Path("anyObjects")
 public interface AnyObjectService extends AnyService<AnyObjectTO> {
 
+    @ApiResponses(
+            @ApiResponse(code = 200,
+                    message =
+                    "Any object matching the provided key; if value looks like a UUID then it is interpreted as key,"
+                    + " otherwise as a name.", responseHeaders =
+                    @ResponseHeader(name = HttpHeaders.ETAG, response = String.class,
+                            description = "Opaque identifier for the latest modification made to the entity returned"
+                            + " by this endpoint")))
     @Override
     AnyObjectTO read(String key);
 
@@ -92,11 +100,15 @@ public interface AnyObjectService extends AnyService<AnyObjectTO> {
      * @param anyObjectPatch modification to be applied to any object matching the provided key
      * @return Response object featuring the updated any object enriched with propagation status information
      */
-    @ApiImplicitParams(
-            @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
-                    value = "Allows the client to specify a preference for the result to be returned from the server",
-                    defaultValue = "return-content", allowableValues = "return-content, return-no-content",
-                    allowEmptyValue = true))
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                value = "Allows the client to specify a preference for the result to be returned from the server",
+                defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                allowEmptyValue = true)
+        , @ApiImplicitParam(name = HttpHeaders.IF_MATCH, paramType = "header", dataType = "string",
+                value = "When the provided ETag value does not match the latest modification date of the entity, "
+                + "an error is reported and the requested operation is not performed.",
+                allowEmptyValue = true) })
     @ApiResponses({
         @ApiResponse(code = 200,
                 message = "Any object successfully updated enriched with propagation status information, as Entity",
@@ -105,7 +117,10 @@ public interface AnyObjectService extends AnyService<AnyObjectTO> {
                 message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
                 @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
                         description = "Allows the server to inform the "
-                        + "client about the fact that a specified preference was applied")) })
+                        + "client about the fact that a specified preference was applied"))
+        , @ApiResponse(code = 412,
+                message = "The ETag value provided via the 'If-Match' header does not match the latest modification "
+                + "date of the entity") })
     @PATCH
     @Path("{key}")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@@ -118,11 +133,15 @@ public interface AnyObjectService extends AnyService<AnyObjectTO> {
      * @param anyObjectTO complete update
      * @return Response object featuring the updated any object enriched with propagation status information
      */
-    @ApiImplicitParams(
-            @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
-                    value = "Allows the client to specify a preference for the result to be returned from the server",
-                    defaultValue = "return-content", allowableValues = "return-content, return-no-content",
-                    allowEmptyValue = true))
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                value = "Allows the client to specify a preference for the result to be returned from the server",
+                defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                allowEmptyValue = true)
+        , @ApiImplicitParam(name = HttpHeaders.IF_MATCH, paramType = "header", dataType = "string",
+                value = "When the provided ETag value does not match the latest modification date of the entity, "
+                + "an error is reported and the requested operation is not performed.",
+                allowEmptyValue = true) })
     @ApiResponses({
         @ApiResponse(code = 200,
                 message = "Any object successfully updated enriched with propagation status information, as Entity",
@@ -131,7 +150,10 @@ public interface AnyObjectService extends AnyService<AnyObjectTO> {
                 message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
                 @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
                         description = "Allows the server to inform the "
-                        + "client about the fact that a specified preference was applied")) })
+                        + "client about the fact that a specified preference was applied"))
+        , @ApiResponse(code = 412,
+                message = "The ETag value provided via the 'If-Match' header does not match the latest modification "
+                + "date of the entity") })
     @PUT
     @Path("{key}")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })

http://git-wip-us.apache.org/repos/asf/syncope/blob/2c7138b2/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
index 4d4725d..2b9ff04 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
@@ -34,6 +34,7 @@ import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.patch.AssociationPatch;
@@ -41,6 +42,7 @@ import org.apache.syncope.common.lib.patch.DeassociationPatch;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.types.SchemaType;
@@ -86,10 +88,7 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
     /**
      * Reads the any object matching the provided key.
      *
-     * Note that for the UserService, GroupService and AnyObjectService subclasses, if the key parameter
-     * looks like a UUID then it is interpreted as as key, otherwise as a (user)name.
-     *
-     * @param key any object key or name
+     * @param key if value looks like a UUID then it is interpreted as key, otherwise as a (user)name
      * @return any object with matching key
      */
     @GET
@@ -146,11 +145,15 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
      * @param key any object key or name
      * @return Response object featuring the deleted any object enriched with propagation status information
      */
-    @ApiImplicitParams(
-            @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
-                    value = "Allows the client to specify a preference for the result to be returned from the server",
-                    defaultValue = "return-content", allowableValues = "return-content, return-no-content",
-                    allowEmptyValue = true))
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                value = "Allows the client to specify a preference for the result to be returned from the server",
+                defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                allowEmptyValue = true)
+        , @ApiImplicitParam(name = HttpHeaders.IF_MATCH, paramType = "header", dataType = "string",
+                value = "When the provided ETag value does not match the latest modification date of the entity, "
+                + "an error is reported and the requested operation is not performed.",
+                allowEmptyValue = true) })
     @ApiResponses({
         @ApiResponse(code = 200,
                 message = "User, Group or Any Object successfully deleted enriched with propagation status information,"
@@ -160,7 +163,10 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
                 message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
                 @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
                         description = "Allows the server to inform the "
-                        + "client about the fact that a specified preference was applied")) })
+                        + "client about the fact that a specified preference was applied"))
+        , @ApiResponse(code = 412,
+                message = "The ETag value provided via the 'If-Match' header does not match the latest modification "
+                + "date of the entity") })
     @DELETE
     @Path("{key}")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@@ -172,6 +178,25 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
      * @param patch external resources to be used for propagation-related operations
      * @return Response object featuring BulkActionResult as Entity
      */
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                value = "Allows the client to specify a preference for the result to be returned from the server",
+                defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                allowEmptyValue = true)
+        , @ApiImplicitParam(name = HttpHeaders.IF_MATCH, paramType = "header", dataType = "string",
+                value = "When the provided ETag value does not match the latest modification date of the entity, "
+                + "an error is reported and the requested operation is not performed.",
+                allowEmptyValue = true) })
+    @ApiResponses({
+        @ApiResponse(code = 200, message = "Bulk action result", response = BulkActionResult.class)
+        , @ApiResponse(code = 204,
+                message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
+                @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
+                        description = "Allows the server to inform the "
+                        + "client about the fact that a specified preference was applied"))
+        , @ApiResponse(code = 412,
+                message = "The ETag value provided via the 'If-Match' header does not match the latest modification "
+                + "date of the entity") })
     @POST
     @Path("{key}/deassociate/{action}")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@@ -184,6 +209,25 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
      * @param patch external resources to be used for propagation-related operations
      * @return Response object featuring BulkActionResult as Entity
      */
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                value = "Allows the client to specify a preference for the result to be returned from the server",
+                defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                allowEmptyValue = true)
+        , @ApiImplicitParam(name = HttpHeaders.IF_MATCH, paramType = "header", dataType = "string",
+                value = "When the provided ETag value does not match the latest modification date of the entity, "
+                + "an error is reported and the requested operation is not performed.",
+                allowEmptyValue = true) })
+    @ApiResponses({
+        @ApiResponse(code = 200, message = "Bulk action result", response = BulkActionResult.class)
+        , @ApiResponse(code = 204,
+                message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
+                @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
+                        description = "Allows the server to inform the "
+                        + "client about the fact that a specified preference was applied"))
+        , @ApiResponse(code = 412,
+                message = "The ETag value provided via the 'If-Match' header does not match the latest modification "
+                + "date of the entity") })
     @POST
     @Path("{key}/associate/{action}")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@@ -196,6 +240,18 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
      * @param bulkAction list of any object ids against which the bulk action will be performed.
      * @return Response object featuring BulkActionResult as Entity
      */
+    @ApiImplicitParams(
+            @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                    value = "Allows the client to specify a preference for the result to be returned from the server",
+                    defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                    allowEmptyValue = true))
+    @ApiResponses({
+        @ApiResponse(code = 200, message = "Bulk action result", response = BulkActionResult.class)
+        , @ApiResponse(code = 204,
+                message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
+                @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
+                        description = "Allows the server to inform the "
+                        + "client about the fact that a specified preference was applied")) })
     @POST
     @Path("bulk")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })

http://git-wip-us.apache.org/repos/asf/syncope/blob/2c7138b2/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
index 8764eec..197c755 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/GroupService.java
@@ -56,6 +56,14 @@ import org.apache.syncope.common.rest.api.beans.AnyQuery;
 @Path("groups")
 public interface GroupService extends AnyService<GroupTO> {
 
+    @ApiResponses(
+            @ApiResponse(code = 200,
+                    message =
+                    "Group matching the provided key; if value looks like a UUID then it is interpreted as key,"
+                    + " otherwise as a name.", responseHeaders =
+                    @ResponseHeader(name = HttpHeaders.ETAG, response = String.class,
+                            description = "Opaque identifier for the latest modification made to the entity returned"
+                            + " by this endpoint")))
     @Override
     GroupTO read(String key);
 
@@ -97,11 +105,15 @@ public interface GroupService extends AnyService<GroupTO> {
      * @param groupPatch modification to be applied to group matching the provided key
      * @return Response object featuring the updated group enriched with propagation status information
      */
-    @ApiImplicitParams(
-            @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
-                    value = "Allows the client to specify a preference for the result to be returned from the server",
-                    defaultValue = "return-content", allowableValues = "return-content, return-no-content",
-                    allowEmptyValue = true))
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                value = "Allows the client to specify a preference for the result to be returned from the server",
+                defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                allowEmptyValue = true)
+        , @ApiImplicitParam(name = HttpHeaders.IF_MATCH, paramType = "header", dataType = "string",
+                value = "When the provided ETag value does not match the latest modification date of the entity, "
+                + "an error is reported and the requested operation is not performed.",
+                allowEmptyValue = true) })
     @ApiResponses({
         @ApiResponse(code = 200,
                 message = "Group successfully updated enriched with propagation status information, as Entity",
@@ -110,7 +122,10 @@ public interface GroupService extends AnyService<GroupTO> {
                 message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
                 @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
                         description = "Allows the server to inform the "
-                        + "client about the fact that a specified preference was applied")) })
+                        + "client about the fact that a specified preference was applied"))
+        , @ApiResponse(code = 412,
+                message = "The ETag value provided via the 'If-Match' header does not match the latest modification "
+                + "date of the entity") })
     @PATCH
     @Path("{key}")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@@ -123,11 +138,15 @@ public interface GroupService extends AnyService<GroupTO> {
      * @param groupTO complete update
      * @return Response object featuring the updated group enriched with propagation status information
      */
-    @ApiImplicitParams(
-            @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
-                    value = "Allows the client to specify a preference for the result to be returned from the server",
-                    defaultValue = "return-content", allowableValues = "return-content, return-no-content",
-                    allowEmptyValue = true))
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                value = "Allows the client to specify a preference for the result to be returned from the server",
+                defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                allowEmptyValue = true)
+        , @ApiImplicitParam(name = HttpHeaders.IF_MATCH, paramType = "header", dataType = "string",
+                value = "When the provided ETag value does not match the latest modification date of the entity, "
+                + "an error is reported and the requested operation is not performed.",
+                allowEmptyValue = true) })
     @ApiResponses({
         @ApiResponse(code = 200,
                 message = "Group successfully updated enriched with propagation status information, as Entity",
@@ -136,7 +155,10 @@ public interface GroupService extends AnyService<GroupTO> {
                 message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
                 @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
                         description = "Allows the server to inform the "
-                        + "client about the fact that a specified preference was applied")) })
+                        + "client about the fact that a specified preference was applied"))
+        , @ApiResponse(code = 412,
+                message = "The ETag value provided via the 'If-Match' header does not match the latest modification "
+                + "date of the entity") })
     @PUT
     @Path("{key}")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })

http://git-wip-us.apache.org/repos/asf/syncope/blob/2c7138b2/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
index 5e9c155..86bbb73 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserService.java
@@ -54,6 +54,14 @@ import org.apache.syncope.common.rest.api.beans.AnyQuery;
 @Path("users")
 public interface UserService extends AnyService<UserTO> {
 
+    @ApiResponses(
+            @ApiResponse(code = 200,
+                    message =
+                    "User matching the provided key; if value looks like a UUID then it is interpreted as key,"
+                    + " otherwise as a username.", responseHeaders =
+                    @ResponseHeader(name = HttpHeaders.ETAG, response = String.class,
+                            description = "Opaque identifier for the latest modification made to the entity returned"
+                            + " by this endpoint")))
     @Override
     UserTO read(String key);
 
@@ -98,11 +106,15 @@ public interface UserService extends AnyService<UserTO> {
      * @param userPatch modification to be applied to user matching the provided key
      * @return Response object featuring the updated user enriched with propagation status information
      */
-    @ApiImplicitParams(
-            @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
-                    value = "Allows the client to specify a preference for the result to be returned from the server",
-                    defaultValue = "return-content", allowableValues = "return-content, return-no-content",
-                    allowEmptyValue = true))
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                value = "Allows the client to specify a preference for the result to be returned from the server",
+                defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                allowEmptyValue = true)
+        , @ApiImplicitParam(name = HttpHeaders.IF_MATCH, paramType = "header", dataType = "string",
+                value = "When the provided ETag value does not match the latest modification date of the entity, "
+                + "an error is reported and the requested operation is not performed.",
+                allowEmptyValue = true) })
     @ApiResponses({
         @ApiResponse(code = 200,
                 message = "User successfully updated enriched with propagation status information, as Entity",
@@ -111,7 +123,10 @@ public interface UserService extends AnyService<UserTO> {
                 message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
                 @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
                         description = "Allows the server to inform the "
-                        + "client about the fact that a specified preference was applied")) })
+                        + "client about the fact that a specified preference was applied"))
+        , @ApiResponse(code = 412,
+                message = "The ETag value provided via the 'If-Match' header does not match the latest modification "
+                + "date of the entity") })
     @PATCH
     @Path("{key}")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@@ -124,11 +139,15 @@ public interface UserService extends AnyService<UserTO> {
      * @param userTO complete update
      * @return Response object featuring the updated user enriched with propagation status information
      */
-    @ApiImplicitParams(
-            @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
-                    value = "Allows the client to specify a preference for the result to be returned from the server",
-                    defaultValue = "return-content", allowableValues = "return-content, return-no-content",
-                    allowEmptyValue = true))
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                value = "Allows the client to specify a preference for the result to be returned from the server",
+                defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                allowEmptyValue = true)
+        , @ApiImplicitParam(name = HttpHeaders.IF_MATCH, paramType = "header", dataType = "string",
+                value = "When the provided ETag value does not match the latest modification date of the entity, "
+                + "an error is reported and the requested operation is not performed.",
+                allowEmptyValue = true) })
     @ApiResponses({
         @ApiResponse(code = 200,
                 message = "User successfully updated enriched with propagation status information, as Entity",
@@ -137,7 +156,10 @@ public interface UserService extends AnyService<UserTO> {
                 message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
                 @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
                         description = "Allows the server to inform the "
-                        + "client about the fact that a specified preference was applied")) })
+                        + "client about the fact that a specified preference was applied"))
+        , @ApiResponse(code = 412,
+                message = "The ETag value provided via the 'If-Match' header does not match the latest modification "
+                + "date of the entity") })
     @PUT
     @Path("{key}")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@@ -150,11 +172,15 @@ public interface UserService extends AnyService<UserTO> {
      * @param statusPatch status update details
      * @return Response object featuring the updated user enriched with propagation status information
      */
-    @ApiImplicitParams(
-            @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
-                    value = "Allows the client to specify a preference for the result to be returned from the server",
-                    defaultValue = "return-content", allowableValues = "return-content, return-no-content",
-                    allowEmptyValue = true))
+    @ApiImplicitParams({
+        @ApiImplicitParam(name = RESTHeaders.PREFER, paramType = "header", dataType = "string",
+                value = "Allows the client to specify a preference for the result to be returned from the server",
+                defaultValue = "return-content", allowableValues = "return-content, return-no-content",
+                allowEmptyValue = true)
+        , @ApiImplicitParam(name = HttpHeaders.IF_MATCH, paramType = "header", dataType = "string",
+                value = "When the provided ETag value does not match the latest modification date of the entity, "
+                + "an error is reported and the requested operation is not performed.",
+                allowEmptyValue = true) })
     @ApiResponses({
         @ApiResponse(code = 200,
                 message = "User successfully updated enriched with propagation status information, as Entity",
@@ -163,7 +189,10 @@ public interface UserService extends AnyService<UserTO> {
                 message = "No content if 'Prefer: return-no-content' was specified", responseHeaders =
                 @ResponseHeader(name = RESTHeaders.PREFERENCE_APPLIED, response = String.class,
                         description = "Allows the server to inform the "
-                        + "client about the fact that a specified preference was applied")) })
+                        + "client about the fact that a specified preference was applied"))
+        , @ApiResponse(code = 412,
+                message = "The ETag value provided via the 'If-Match' header does not match the latest modification "
+                + "date of the entity") })
     @POST
     @Path("{key}/status")
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })