diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/IdentityLinkEntity.java b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/IdentityLinkEntity.java
index f9479172662684b53360c55982aef7a178b80eb3..72f8ad9ccdd792833b8d9a73e9fb7693e62c0f0c 100644
--- a/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/IdentityLinkEntity.java
+++ b/modules/activiti-engine/src/main/java/org/activiti/engine/impl/persistence/entity/IdentityLinkEntity.java
@@ -202,6 +202,11 @@ public class IdentityLinkEntity implements Serializable, IdentityLink, Persisten
this.processDef = processDef;
this.processDefId = processDef.getId();
}
+
+ @Override
+ public String getProcessDefinitionId() {
+ return this.processDefId;
+ }
@Override
public String toString() {
diff --git a/modules/activiti-engine/src/main/java/org/activiti/engine/task/IdentityLink.java b/modules/activiti-engine/src/main/java/org/activiti/engine/task/IdentityLink.java
index 21b9f8becf41aaf37efc8e43a0fefc785b9c6b16..17f4476620c0441b954e0786400c4148cf7d9ecf 100644
--- a/modules/activiti-engine/src/main/java/org/activiti/engine/task/IdentityLink.java
+++ b/modules/activiti-engine/src/main/java/org/activiti/engine/task/IdentityLink.java
@@ -50,5 +50,15 @@ public interface IdentityLink {
* The id of the task associated with this identity link.
*/
String getTaskId();
+
+ /**
+ * The process definition id associated with this identity link.
+ */
+ String getProcessDefinitionId();
+
+ /**
+ * The process instance id associated with this identity link.
+ */
+ String getProcessInstanceId();
}
diff --git a/modules/activiti-engine/src/main/resources/org/activiti/db/mapping/entity/IdentityLink.xml b/modules/activiti-engine/src/main/resources/org/activiti/db/mapping/entity/IdentityLink.xml
index bfd57e2ce1372ebe607f07a23295277e217bb36f..b0b9f90549b1c59fce4d3721d4e0095315d9abab 100644
--- a/modules/activiti-engine/src/main/resources/org/activiti/db/mapping/entity/IdentityLink.xml
+++ b/modules/activiti-engine/src/main/resources/org/activiti/db/mapping/entity/IdentityLink.xml
@@ -39,7 +39,7 @@
-
+
diff --git a/modules/activiti-rest/src/main/java/org/activiti/rest/api/RestResponseFactory.java b/modules/activiti-rest/src/main/java/org/activiti/rest/api/RestResponseFactory.java
index b457c6ec72096a65905a19d039c219264680c492..d4654b7e5ad47f25de9e9fd509354be8f62231bd 100644
--- a/modules/activiti-rest/src/main/java/org/activiti/rest/api/RestResponseFactory.java
+++ b/modules/activiti-rest/src/main/java/org/activiti/rest/api/RestResponseFactory.java
@@ -43,6 +43,7 @@ import org.activiti.engine.task.Task;
import org.activiti.rest.api.engine.AttachmentResponse;
import org.activiti.rest.api.engine.CommentResponse;
import org.activiti.rest.api.engine.EventResponse;
+import org.activiti.rest.api.engine.RestIdentityLink;
import org.activiti.rest.api.engine.variable.BooleanRestVariableConverter;
import org.activiti.rest.api.engine.variable.DateRestVariableConverter;
import org.activiti.rest.api.engine.variable.DoubleRestVariableConverter;
@@ -63,7 +64,6 @@ import org.activiti.rest.api.identity.GroupResponse;
import org.activiti.rest.api.identity.MembershipResponse;
import org.activiti.rest.api.identity.UserInfoResponse;
import org.activiti.rest.api.identity.UserResponse;
-import org.activiti.rest.api.legacy.identity.LegacyRestIdentityLink;
import org.activiti.rest.api.management.JobResponse;
import org.activiti.rest.api.management.TableResponse;
import org.activiti.rest.api.repository.DeploymentResourceResponse;
@@ -300,12 +300,12 @@ public class RestResponseFactory {
return value;
}
- public LegacyRestIdentityLink createRestIdentityLink(SecuredResource securedResource, IdentityLink link) {
- return createRestIdentityLink(securedResource, link.getType(), link.getUserId(), link.getGroupId(), link.getTaskId());
+ public RestIdentityLink createRestIdentityLink(SecuredResource securedResource, IdentityLink link) {
+ return createRestIdentityLink(securedResource, link.getType(), link.getUserId(), link.getGroupId(), link.getTaskId(), link.getProcessDefinitionId());
}
- public LegacyRestIdentityLink createRestIdentityLink(SecuredResource securedResource, String type, String userId, String groupId, String taskId) {
- LegacyRestIdentityLink result = new LegacyRestIdentityLink();
+ public RestIdentityLink createRestIdentityLink(SecuredResource securedResource, String type, String userId, String groupId, String taskId, String processDefinitionId) {
+ RestIdentityLink result = new RestIdentityLink();
result.setUser(userId);
result.setGroup(groupId);
result.setType(type);
@@ -316,7 +316,11 @@ public class RestResponseFactory {
} else {
family = RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_GROUPS;
}
- result.setUrl(securedResource.createFullResourceUrl(RestUrls.URL_TASK_IDENTITYLINK, taskId, family, (userId != null ? userId : groupId), type));
+ if(processDefinitionId != null) {
+ result.setUrl(securedResource.createFullResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK, processDefinitionId, family, (userId != null ? userId : groupId)));
+ } else {
+ result.setUrl(securedResource.createFullResourceUrl(RestUrls.URL_TASK_IDENTITYLINK, taskId, family, (userId != null ? userId : groupId), type));
+ }
return result;
}
diff --git a/modules/activiti-rest/src/main/java/org/activiti/rest/api/RestUrls.java b/modules/activiti-rest/src/main/java/org/activiti/rest/api/RestUrls.java
index d3ecbe001d0dabb3f8156373f72ebeb755542009..72563fbac7e1953a6b540c5b5cd8bc5b59e45514 100644
--- a/modules/activiti-rest/src/main/java/org/activiti/rest/api/RestUrls.java
+++ b/modules/activiti-rest/src/main/java/org/activiti/rest/api/RestUrls.java
@@ -103,6 +103,15 @@ public final class RestUrls {
*/
public static final String[] URL_PROCESS_DEFINITION = {SEGMENT_REPOSITORY_RESOURCES, SEGMENT_PROCESS_DEFINITION_RESOURCE, "{0}"};
+ /**
+ * URL template for a process definition's identity links: repository/process-definitions/{0:processDefinitionId}/identitylinks
+ */
+ public static final String[] URL_PROCESS_DEFINITION_IDENTITYLINKS_COLLECTION = {SEGMENT_REPOSITORY_RESOURCES, SEGMENT_PROCESS_DEFINITION_RESOURCE, "{0}", SEGMENT_IDENTITYLINKS};
+
+ /**
+ * URL template for an identitylink on a process definition: repository/process-definitions/{0:processDefinitionId}/identitylinks/{1:family}/{2:identityId}
+ */
+ public static final String[] URL_PROCESS_DEFINITION_IDENTITYLINK = {SEGMENT_REPOSITORY_RESOURCES, SEGMENT_PROCESS_DEFINITION_RESOURCE, "{0}", SEGMENT_IDENTITYLINKS, "{1}", "{2}"};
/**
* URL template for task collection: runtime/tasks/{0:taskId}
diff --git a/modules/activiti-rest/src/main/java/org/activiti/rest/api/engine/RestIdentityLink.java b/modules/activiti-rest/src/main/java/org/activiti/rest/api/engine/RestIdentityLink.java
new file mode 100644
index 0000000000000000000000000000000000000000..87c815252505c09cf3d2adc0a9e2d2932f11a793
--- /dev/null
+++ b/modules/activiti-rest/src/main/java/org/activiti/rest/api/engine/RestIdentityLink.java
@@ -0,0 +1,51 @@
+/* Licensed 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.activiti.rest.api.engine;
+
+
+/**
+ * @author Frederik Heremans
+ */
+public class RestIdentityLink {
+
+ private String url;
+ private String user;
+ private String group;
+ private String type;
+
+ public String getUrl() {
+ return url;
+ }
+ public void setUrl(String url) {
+ this.url = url;
+ }
+ public String getUser() {
+ return user;
+ }
+ public void setUser(String user) {
+ this.user = user;
+ }
+ public String getGroup() {
+ return group;
+ }
+ public void setGroup(String group) {
+ this.group = group;
+ }
+ public String getType() {
+ return type;
+ }
+ public void setType(String type) {
+ this.type = type;
+ }
+}
diff --git a/modules/activiti-rest/src/main/java/org/activiti/rest/api/repository/ProcessDefinitionIdentityLinkCollectionResource.java b/modules/activiti-rest/src/main/java/org/activiti/rest/api/repository/ProcessDefinitionIdentityLinkCollectionResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2458b3df234616e2df0cd779dfc2bfd00b41597
--- /dev/null
+++ b/modules/activiti-rest/src/main/java/org/activiti/rest/api/repository/ProcessDefinitionIdentityLinkCollectionResource.java
@@ -0,0 +1,102 @@
+/* Licensed 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.activiti.rest.api.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.activiti.engine.ActivitiIllegalArgumentException;
+import org.activiti.engine.ActivitiObjectNotFoundException;
+import org.activiti.engine.repository.ProcessDefinition;
+import org.activiti.engine.task.IdentityLink;
+import org.activiti.engine.task.IdentityLinkType;
+import org.activiti.rest.api.ActivitiUtil;
+import org.activiti.rest.api.RestResponseFactory;
+import org.activiti.rest.api.SecuredResource;
+import org.activiti.rest.api.engine.RestIdentityLink;
+import org.activiti.rest.application.ActivitiRestServicesApplication;
+import org.restlet.data.Status;
+import org.restlet.resource.Get;
+import org.restlet.resource.Post;
+
+
+/**
+ * @author Frederik Heremans
+ */
+public class ProcessDefinitionIdentityLinkCollectionResource extends SecuredResource {
+
+ @Get
+ public List getIdentityLinks() {
+ if(!authenticate())
+ return null;
+
+ List result = new ArrayList();
+ ProcessDefinition processDefinition = getProcessDefinitionFromRequest();
+
+ List identityLinks = ActivitiUtil.getRepositoryService().getIdentityLinksForProcessDefinition(processDefinition.getId());
+ RestResponseFactory responseFactory = getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory();
+ for(IdentityLink link : identityLinks) {
+ result.add(responseFactory.createRestIdentityLink(this, link));
+ }
+ return result;
+ }
+
+ @Post
+ public RestIdentityLink createIdentityLink(RestIdentityLink identityLink) {
+ if(!authenticate())
+ return null;
+
+ ProcessDefinition processDefinition = getProcessDefinitionFromRequest();
+
+ if(identityLink.getGroup() == null && identityLink.getUser() == null) {
+ throw new ActivitiIllegalArgumentException("A group or a user is required to create an identity link.");
+ }
+
+ if(identityLink.getGroup() != null && identityLink.getUser() != null) {
+ throw new ActivitiIllegalArgumentException("Only one of user or group can be used to create an identity link.");
+ }
+
+ if(identityLink.getGroup() != null) {
+ ActivitiUtil.getRepositoryService().addCandidateStarterGroup(processDefinition.getId(), identityLink.getGroup());
+ } else {
+ ActivitiUtil.getRepositoryService().addCandidateStarterUser(processDefinition.getId(), identityLink.getUser());
+ }
+
+ // Always candidate for process-definition. User-provided value is ignored
+ identityLink.setType(IdentityLinkType.CANDIDATE);
+
+ setStatus(Status.SUCCESS_CREATED);
+ return getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
+ .createRestIdentityLink(this, identityLink.getType(), identityLink.getUser(), identityLink.getGroup(), null, processDefinition.getId());
+ }
+
+ /**
+ * Returns the {@link ProcessDefinition} that is requested. Throws the right exceptions
+ * when bad request was made or definition is not found.
+ */
+ protected ProcessDefinition getProcessDefinitionFromRequest() {
+ String processDefinitionId = getAttribute("processDefinitionId");
+ if(processDefinitionId == null) {
+ throw new ActivitiIllegalArgumentException("The processDefinitionId cannot be null");
+ }
+
+ ProcessDefinition processDefinition = ActivitiUtil.getRepositoryService().createProcessDefinitionQuery()
+ .processDefinitionId(processDefinitionId).singleResult();
+
+ if(processDefinition == null) {
+ throw new ActivitiObjectNotFoundException("Could not find a process definition with id '" + processDefinitionId + "'.", ProcessDefinition.class);
+ }
+ return processDefinition;
+ }
+}
diff --git a/modules/activiti-rest/src/main/java/org/activiti/rest/api/repository/ProcessDefinitionIdentityLinkResource.java b/modules/activiti-rest/src/main/java/org/activiti/rest/api/repository/ProcessDefinitionIdentityLinkResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..9d05c3f83de1ac9d05fc20186ffa6259821c49cd
--- /dev/null
+++ b/modules/activiti-rest/src/main/java/org/activiti/rest/api/repository/ProcessDefinitionIdentityLinkResource.java
@@ -0,0 +1,125 @@
+/* Licensed 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.activiti.rest.api.repository;
+
+import java.util.List;
+
+import org.activiti.engine.ActivitiIllegalArgumentException;
+import org.activiti.engine.ActivitiObjectNotFoundException;
+import org.activiti.engine.repository.ProcessDefinition;
+import org.activiti.engine.task.IdentityLink;
+import org.activiti.engine.task.IdentityLinkType;
+import org.activiti.rest.api.ActivitiUtil;
+import org.activiti.rest.api.RestUrls;
+import org.activiti.rest.api.SecuredResource;
+import org.activiti.rest.api.engine.RestIdentityLink;
+import org.activiti.rest.application.ActivitiRestServicesApplication;
+import org.restlet.data.Status;
+import org.restlet.resource.Delete;
+import org.restlet.resource.Get;
+
+/**
+ * @author Frederik Heremans
+ */
+public class ProcessDefinitionIdentityLinkResource extends SecuredResource {
+
+ @Get
+ public RestIdentityLink getIdentityLink() {
+ if (!authenticate())
+ return null;
+
+ ProcessDefinition processDefinition = getProcessDefinitionFromRequest();
+
+ // Extract and validate identity link from URL
+ String family = getAttribute("family");
+ String identityId = getAttribute("identityId");
+ validateIdentityLinkArguments(family, identityId);
+
+ // Check if identitylink to get exists
+ IdentityLink link = getIdentityLink(family, identityId, processDefinition.getId());
+ return getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory().createRestIdentityLink(this, link);
+ }
+
+ @Delete
+ public void deleteIdentityLink() {
+ if (!authenticate())
+ return;
+
+ ProcessDefinition processDefinition = getProcessDefinitionFromRequest();
+
+ // Extract and validate identity link from URL
+ String family = getAttribute("family");
+ String identityId = getAttribute("identityId");
+ validateIdentityLinkArguments(family, identityId);
+
+ // Check if identitylink to delete exists
+ IdentityLink link = getIdentityLink(family, identityId, processDefinition.getId());
+ if(link.getUserId() != null) {
+ ActivitiUtil.getRepositoryService().deleteCandidateStarterUser(processDefinition.getId(), link.getUserId());
+ } else {
+ ActivitiUtil.getRepositoryService().deleteCandidateStarterGroup(processDefinition.getId(), link.getGroupId());
+ }
+
+ setStatus(Status.SUCCESS_NO_CONTENT);
+ }
+
+ protected void validateIdentityLinkArguments(String family, String identityId) {
+ if (family == null || (!RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_GROUPS.equals(family) && !RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS.equals(family))) {
+ throw new ActivitiIllegalArgumentException("Identity link family should be 'users' or 'groups'.");
+ }
+ if (identityId == null) {
+ throw new ActivitiIllegalArgumentException("IdentityId is required.");
+ }
+ }
+
+ protected IdentityLink getIdentityLink(String family, String identityId, String processDefinitionId) {
+ boolean isUser = family.equals(RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS);
+
+ // Perhaps it would be better to offer getting a single identitylink from
+ // the API
+ List allLinks = ActivitiUtil.getRepositoryService().getIdentityLinksForProcessDefinition(processDefinitionId);
+ for (IdentityLink link : allLinks) {
+ boolean rightIdentity = false;
+ if (isUser) {
+ rightIdentity = identityId.equals(link.getUserId());
+ } else {
+ rightIdentity = identityId.equals(link.getGroupId());
+ }
+
+ if (rightIdentity && link.getType().equals(IdentityLinkType.CANDIDATE)) {
+ return link;
+ }
+ }
+ throw new ActivitiObjectNotFoundException("Could not find the requested identity link.", IdentityLink.class);
+ }
+
+ /**
+ * Returns the {@link ProcessDefinition} that is requested. Throws the right
+ * exceptions when bad request was made or definition is not found.
+ */
+ protected ProcessDefinition getProcessDefinitionFromRequest() {
+ String processDefinitionId = getAttribute("processDefinitionId");
+ if (processDefinitionId == null) {
+ throw new ActivitiIllegalArgumentException("The processDefinitionId cannot be null");
+ }
+
+ ProcessDefinition processDefinition = ActivitiUtil.getRepositoryService().createProcessDefinitionQuery().processDefinitionId(processDefinitionId)
+ .singleResult();
+
+ if (processDefinition == null) {
+ throw new ActivitiObjectNotFoundException("Could not find a process definition with id '" + processDefinitionId + "'.", ProcessDefinition.class);
+ }
+ return processDefinition;
+ }
+}
diff --git a/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkCollectionResource.java b/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkCollectionResource.java
index 90271dd3c55ad1faccebbb8366da41b34944a71c..57889279a3d9e079ece74bc3620e9a2aa4394de9 100644
--- a/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkCollectionResource.java
+++ b/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkCollectionResource.java
@@ -21,7 +21,7 @@ import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.RestResponseFactory;
-import org.activiti.rest.api.legacy.identity.LegacyRestIdentityLink;
+import org.activiti.rest.api.engine.RestIdentityLink;
import org.activiti.rest.application.ActivitiRestServicesApplication;
import org.restlet.data.Status;
import org.restlet.resource.Get;
@@ -34,11 +34,11 @@ import org.restlet.resource.Post;
public class TaskIdentityLinkCollectionResource extends TaskBaseResource {
@Get
- public List getIdentityLinks() {
+ public List getIdentityLinks() {
if(!authenticate())
return null;
- List result = new ArrayList();
+ List result = new ArrayList();
Task task = getTaskFromRequest();
List identityLinks = ActivitiUtil.getTaskService().getIdentityLinksForTask(task.getId());
@@ -50,7 +50,7 @@ public class TaskIdentityLinkCollectionResource extends TaskBaseResource {
}
@Post
- public LegacyRestIdentityLink createIdentityLink(LegacyRestIdentityLink identityLink) {
+ public RestIdentityLink createIdentityLink(RestIdentityLink identityLink) {
if(!authenticate())
return null;
@@ -76,6 +76,6 @@ public class TaskIdentityLinkCollectionResource extends TaskBaseResource {
setStatus(Status.SUCCESS_CREATED);
return getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
- .createRestIdentityLink(this, identityLink.getType(), identityLink.getUser(), identityLink.getGroup(), task.getId());
+ .createRestIdentityLink(this, identityLink.getType(), identityLink.getUser(), identityLink.getGroup(), task.getId(), null);
}
}
diff --git a/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkFamilyResource.java b/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkFamilyResource.java
index fc7ecba5bab0bb563bfde3354f0778f850f905bc..fdb03c7a77d1d8cf160a4eee3c13c3fdf49f32d9 100644
--- a/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkFamilyResource.java
+++ b/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkFamilyResource.java
@@ -23,7 +23,7 @@ import org.activiti.engine.task.Task;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.RestResponseFactory;
import org.activiti.rest.api.RestUrls;
-import org.activiti.rest.api.legacy.identity.LegacyRestIdentityLink;
+import org.activiti.rest.api.engine.RestIdentityLink;
import org.activiti.rest.application.ActivitiRestServicesApplication;
import org.restlet.data.Status;
import org.restlet.resource.Delete;
@@ -36,7 +36,7 @@ import org.restlet.resource.Get;
public class TaskIdentityLinkFamilyResource extends TaskBaseResource {
@Get
- public List getIdentityLinksForFamily() {
+ public List getIdentityLinksForFamily() {
if(!authenticate())
return null;
@@ -51,7 +51,7 @@ public class TaskIdentityLinkFamilyResource extends TaskBaseResource {
}
boolean isUser = family.equals(RestUrls.SEGMENT_IDENTITYLINKS_FAMILY_USERS);
- List results = new ArrayList();
+ List results = new ArrayList();
RestResponseFactory responseFactory = getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory();
List allLinks = ActivitiUtil.getTaskService().getIdentityLinksForTask(task.getId());
diff --git a/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkResource.java b/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkResource.java
index bb9b2e7892d35b4925dff2527a9e660a4b46d440..6515908d854cd092f0c05085d0358e44e6fa074a 100644
--- a/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkResource.java
+++ b/modules/activiti-rest/src/main/java/org/activiti/rest/api/runtime/task/TaskIdentityLinkResource.java
@@ -21,7 +21,7 @@ import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.RestUrls;
-import org.activiti.rest.api.legacy.identity.LegacyRestIdentityLink;
+import org.activiti.rest.api.engine.RestIdentityLink;
import org.activiti.rest.application.ActivitiRestServicesApplication;
import org.restlet.data.Status;
import org.restlet.resource.Delete;
@@ -34,7 +34,7 @@ import org.restlet.resource.Get;
public class TaskIdentityLinkResource extends TaskBaseResource {
@Get
- public LegacyRestIdentityLink getIdentityLink() {
+ public RestIdentityLink getIdentityLink() {
if(!authenticate())
return null;
diff --git a/modules/activiti-rest/src/main/java/org/activiti/rest/application/RestServicesInit.java b/modules/activiti-rest/src/main/java/org/activiti/rest/application/RestServicesInit.java
index 01da98e414502cb7d9669190d124ad80687a19c9..5de8ce316e6e55c2dcdff8ae90b5539b9783d4d5 100644
--- a/modules/activiti-rest/src/main/java/org/activiti/rest/application/RestServicesInit.java
+++ b/modules/activiti-rest/src/main/java/org/activiti/rest/application/RestServicesInit.java
@@ -74,6 +74,8 @@ import org.activiti.rest.api.repository.DeploymentResourceCollectionResource;
import org.activiti.rest.api.repository.DeploymentResourceDataResource;
import org.activiti.rest.api.repository.DeploymentResourceResource;
import org.activiti.rest.api.repository.ProcessDefinitionCollectionResource;
+import org.activiti.rest.api.repository.ProcessDefinitionIdentityLinkCollectionResource;
+import org.activiti.rest.api.repository.ProcessDefinitionIdentityLinkResource;
import org.activiti.rest.api.repository.ProcessDefinitionResource;
import org.activiti.rest.api.repository.SimpleWorkflowResource;
import org.activiti.rest.api.runtime.process.ExecutionCollectionResource;
@@ -128,6 +130,8 @@ public class RestServicesInit {
router.attach("/repository/process-definitions", ProcessDefinitionCollectionResource.class);
router.attach("/repository/process-definitions/{processDefinitionId}", ProcessDefinitionResource.class);
+ router.attach("/repository/process-definitions/{processDefinitionId}/identitylinks", ProcessDefinitionIdentityLinkCollectionResource.class);
+ router.attach("/repository/process-definitions/{processDefinitionId}/identitylinks/{family}/{identityId}", ProcessDefinitionIdentityLinkResource.class);
router.attach("/runtime/tasks", TaskCollectionResource.class);
router.attach("/runtime/tasks/{taskId}", TaskResource.class);
diff --git a/modules/activiti-rest/src/test/java/org/activiti/rest/api/repository/ProcessDefinitionIdentityLinksResourceTest.java b/modules/activiti-rest/src/test/java/org/activiti/rest/api/repository/ProcessDefinitionIdentityLinksResourceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6bbf3cb24031abdd1ecb65a9e84a9ce0298de98c
--- /dev/null
+++ b/modules/activiti-rest/src/test/java/org/activiti/rest/api/repository/ProcessDefinitionIdentityLinksResourceTest.java
@@ -0,0 +1,238 @@
+package org.activiti.rest.api.repository;
+
+import java.util.List;
+
+import org.activiti.engine.repository.ProcessDefinition;
+import org.activiti.engine.task.IdentityLink;
+import org.activiti.engine.test.Deployment;
+import org.activiti.rest.BaseRestTestCase;
+import org.activiti.rest.api.RestUrls;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.node.ObjectNode;
+import org.restlet.data.Status;
+import org.restlet.representation.Representation;
+import org.restlet.resource.ClientResource;
+import org.restlet.resource.ResourceException;
+
+/**
+ * Test for all REST-operations related to single a Process Definition resource.
+ *
+ * @author Frederik Heremans
+ */
+public class ProcessDefinitionIdentityLinksResourceTest extends BaseRestTestCase {
+
+ /**
+ * Test getting identitylinks for a process definition.
+ */
+ @Deployment(resources={"org/activiti/rest/api/repository/oneTaskProcess.bpmn20.xml"})
+ public void testGetIdentityLinksForProcessDefinition() throws Exception {
+
+ ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().singleResult();
+ repositoryService.addCandidateStarterGroup(processDefinition.getId(), "admin");
+ repositoryService.addCandidateStarterUser(processDefinition.getId(), "kermit");
+
+ ClientResource client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(
+ RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINKS_COLLECTION, processDefinition.getId()));
+ Representation response = client.get();
+
+ // Check "OK" status
+ assertEquals(Status.SUCCESS_OK, client.getResponse().getStatus());
+
+ JsonNode responseNode = objectMapper.readTree(response.getStream());
+ assertNotNull(responseNode);
+ assertTrue(responseNode.isArray());
+ assertEquals(2, responseNode.size());
+
+ boolean groupCandidateFound = false;
+ boolean userCandidateFound = false;
+
+ for(int i=0; i < responseNode.size(); i++) {
+ ObjectNode link = (ObjectNode) responseNode.get(i);
+ assertNotNull(link);
+ if(!link.get("user").isNull()) {
+ assertEquals("kermit", link.get("user").getTextValue());
+ assertEquals("candidate", link.get("type").getTextValue());
+ assertTrue(link.get("group").isNull());
+ assertTrue(link.get("url").getTextValue().endsWith(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK,
+ encode(processDefinition.getId()), "users", "kermit")));
+ userCandidateFound = true;
+ } else if(!link.get("group").isNull()) {
+ assertEquals("admin", link.get("group").getTextValue());
+ assertEquals("candidate", link.get("type").getTextValue());
+ assertTrue(link.get("user").isNull());
+ assertTrue(link.get("url").getTextValue().endsWith(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK,
+ encode(processDefinition.getId()), "groups", "admin")));
+ groupCandidateFound = true;
+ }
+ }
+ assertTrue(groupCandidateFound);
+ assertTrue(userCandidateFound);
+ }
+
+ public void testGetIdentityLinksForUnexistingProcessDefinition() throws Exception {
+ ClientResource client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINKS_COLLECTION, "unexisting"));
+ try {
+ client.get();
+ fail("404 expected, but was: " + client.getResponse().getStatus());
+ } catch(ResourceException expected) {
+ assertEquals(Status.CLIENT_ERROR_NOT_FOUND, client.getResponse().getStatus());
+ assertEquals("Could not find a process definition with id 'unexisting'.", client.getResponse().getStatus().getDescription());
+ }
+ }
+
+ @Deployment(resources={"org/activiti/rest/api/repository/oneTaskProcess.bpmn20.xml"})
+ public void testAddCandidateStarterToProcessDefinition() throws Exception {
+ ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().singleResult();
+ ClientResource client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINKS_COLLECTION, processDefinition.getId()));
+
+ // Create user candidate
+ ObjectNode requestNode = objectMapper.createObjectNode();
+ requestNode.put("user", "kermit");
+
+ Representation response = client.post(requestNode);
+
+ assertEquals(Status.SUCCESS_CREATED, client.getResponse().getStatus());
+
+ JsonNode responseNode = objectMapper.readTree(response.getStream());
+ assertNotNull(responseNode);
+ assertEquals("kermit", responseNode.get("user").getTextValue());
+ assertEquals("candidate", responseNode.get("type").getTextValue());
+ assertTrue(responseNode.get("group").isNull());
+ assertTrue(responseNode.get("url").getTextValue().endsWith(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK,
+ encode(processDefinition.getId()), "users", "kermit")));
+
+ List createdLinks = repositoryService.getIdentityLinksForProcessDefinition(processDefinition.getId());
+ assertEquals(1, createdLinks.size());
+ assertEquals("kermit", createdLinks.get(0).getUserId());
+ assertEquals("candidate", createdLinks.get(0).getType());
+ repositoryService.deleteCandidateStarterUser(processDefinition.getId(), "kermit");
+
+ // Create group candidate
+ requestNode = objectMapper.createObjectNode();
+ requestNode.put("group", "admin");
+
+ response = client.post(requestNode);
+
+ assertEquals(Status.SUCCESS_CREATED, client.getResponse().getStatus());
+
+ responseNode = objectMapper.readTree(response.getStream());
+ assertNotNull(responseNode);
+ assertEquals("admin", responseNode.get("group").getTextValue());
+ assertEquals("candidate", responseNode.get("type").getTextValue());
+ assertTrue(responseNode.get("user").isNull());
+ assertTrue(responseNode.get("url").getTextValue().endsWith(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK,
+ encode(processDefinition.getId()), "groups", "admin")));
+
+ createdLinks = repositoryService.getIdentityLinksForProcessDefinition(processDefinition.getId());
+ assertEquals(1, createdLinks.size());
+ assertEquals("admin", createdLinks.get(0).getGroupId());
+ assertEquals("candidate", createdLinks.get(0).getType());
+ repositoryService.deleteCandidateStarterUser(processDefinition.getId(), "admin");
+ }
+
+ public void testAddCandidateStarterToUnexistingProcessDefinition() throws Exception {
+ ClientResource client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINKS_COLLECTION, "unexisting"));
+
+ // Create user candidate
+ ObjectNode requestNode = objectMapper.createObjectNode();
+ requestNode.put("user", "kermit");
+
+ try {
+ client.post(requestNode);
+ fail("404 expected, but was: " + client.getResponse().getStatus());
+ } catch(ResourceException expected) {
+ assertEquals(Status.CLIENT_ERROR_NOT_FOUND, client.getResponse().getStatus());
+ assertEquals("Could not find a process definition with id 'unexisting'.", client.getResponse().getStatus().getDescription());
+ }
+ }
+
+ @Deployment(resources={"org/activiti/rest/api/repository/oneTaskProcess.bpmn20.xml"})
+ public void testGetCandidateStarterFromProcessDefinition() throws Exception {
+ ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().singleResult();
+ repositoryService.addCandidateStarterGroup(processDefinition.getId(), "admin");
+ repositoryService.addCandidateStarterUser(processDefinition.getId(), "kermit");
+
+ // Get user candidate
+ ClientResource client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK, processDefinition.getId(), "users", "kermit"));
+ Representation response = client.get();
+
+ assertEquals(Status.SUCCESS_OK, client.getResponse().getStatus());
+
+ JsonNode responseNode = objectMapper.readTree(response.getStream());
+ assertNotNull(responseNode);
+ assertEquals("kermit", responseNode.get("user").getTextValue());
+ assertEquals("candidate", responseNode.get("type").getTextValue());
+ assertTrue(responseNode.get("group").isNull());
+ assertTrue(responseNode.get("url").getTextValue().endsWith(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK,
+ encode(processDefinition.getId()), "users", "kermit")));
+
+ // Get group candidate
+ client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK, processDefinition.getId(), "groups", "admin"));
+ response = client.get();
+
+ assertEquals(Status.SUCCESS_OK, client.getResponse().getStatus());
+
+ responseNode = objectMapper.readTree(response.getStream());
+ assertNotNull(responseNode);
+ assertEquals("admin", responseNode.get("group").getTextValue());
+ assertEquals("candidate", responseNode.get("type").getTextValue());
+ assertTrue(responseNode.get("user").isNull());
+ assertTrue(responseNode.get("url").getTextValue().endsWith(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK,
+ encode(processDefinition.getId()), "groups", "admin")));
+ }
+
+ @Deployment(resources={"org/activiti/rest/api/repository/oneTaskProcess.bpmn20.xml"})
+ public void testDeleteCandidateStarterFromProcessDefinition() throws Exception {
+ ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().singleResult();
+ repositoryService.addCandidateStarterGroup(processDefinition.getId(), "admin");
+ repositoryService.addCandidateStarterUser(processDefinition.getId(), "kermit");
+
+ // Get user candidate
+ ClientResource client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK, processDefinition.getId(), "users", "kermit"));
+ Representation response = client.delete();
+
+ assertEquals(Status.SUCCESS_NO_CONTENT, client.getResponse().getStatus());
+ assertEquals(0, response.getSize());
+
+ // Check if group-link remains
+ List remainingLinks = repositoryService.getIdentityLinksForProcessDefinition(processDefinition.getId());
+ assertEquals(1, remainingLinks.size());
+ assertEquals("admin", remainingLinks.get(0).getGroupId());
+
+
+ // Delete group candidate
+ client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK, processDefinition.getId(), "groups", "admin"));
+ response = client.delete();
+
+ assertEquals(Status.SUCCESS_NO_CONTENT, client.getResponse().getStatus());
+ assertEquals(0, response.getSize());
+
+ // Check if all links are removed
+ remainingLinks = repositoryService.getIdentityLinksForProcessDefinition(processDefinition.getId());
+ assertEquals(0, remainingLinks.size());
+ }
+
+ public void testDeleteCandidateStarterFromUnexistingProcessDefinition() throws Exception {
+ ClientResource client = getAuthenticatedClient(
+ RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK, "unexisting", "groups", "admin"));
+ try {
+ client.delete();
+ fail("404 expected, but was: " + client.getResponse().getStatus());
+ } catch(ResourceException expected) {
+ assertEquals(Status.CLIENT_ERROR_NOT_FOUND, client.getResponse().getStatus());
+ assertEquals("Could not find a process definition with id 'unexisting'.", client.getResponse().getStatus().getDescription());
+ }
+ }
+
+ public void testGetCandidateStarterFromUnexistingProcessDefinition() throws Exception {
+ ClientResource client = getAuthenticatedClient(
+ RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_DEFINITION_IDENTITYLINK, "unexisting", "groups", "admin"));
+ try {
+ client.get();
+ fail("404 expected, but was: " + client.getResponse().getStatus());
+ } catch(ResourceException expected) {
+ assertEquals(Status.CLIENT_ERROR_NOT_FOUND, client.getResponse().getStatus());
+ assertEquals("Could not find a process definition with id 'unexisting'.", client.getResponse().getStatus().getDescription());
+ }
+ }
+}
diff --git a/userguide/src/en/chapters/ch14-REST.xml b/userguide/src/en/chapters/ch14-REST.xml
index c9643a15b03553b2848820d8d4dcc599bbc0bfa3..bd75bacd5c56b8368b15bfac2a549a2329ffaebb 100644
--- a/userguide/src/en/chapters/ch14-REST.xml
+++ b/userguide/src/en/chapters/ch14-REST.xml
@@ -1299,6 +1299,303 @@
+
+ Get all candidate starters for a process-definition
+
+ GET repository/process-definitions/{processDefinitionId}/identitylinks
+
+
+
+ URL parameters
+
+
+
+ Parameter
+ Required
+ Value
+ Description
+
+
+
+
+ processDefinitionId
+ Yes
+ String
+ The id of the process definition to get the identity links for.
+
+
+
+
+
+
+
+ Response codes
+
+
+
+ Response code
+ Description
+
+
+
+
+ 200
+ Indicates the process definition was found and the requested identity links are returned.
+
+
+ 404
+ Indicates the requested process definition was not found.
+
+
+
+
+
+
+ Success response body:
+
+[
+ {
+ "url":"http://localhost:8182/repository/process-definitions/oneTaskProcess%3A1%3A4/identitylinks/groups/admin",
+ "user":null,
+ "group":"admin",
+ "type":"candidate"
+ },
+ {
+ "url":"http://localhost:8182/repository/process-definitions/oneTaskProcess%3A1%3A4/identitylinks/users/kermit",
+ "user":"kermit",
+ "group":null,
+ "type":"candidate"
+ }
+]
+
+
+
+ Add a candidate starter to a process definition
+
+ POST repository/process-definitions/{processDefinitionId}/identitylinks
+
+
+
+ URL parameters
+
+
+
+ Parameter
+ Required
+ Value
+ Description
+
+
+
+
+ processDefinitionId
+ Yes
+ String
+ The id of the process definition.
+
+
+
+
+
+
+ Request body (user):
+
+{
+ "userId" : "kermit"
+}
+
+
+ Request body (group):
+
+{
+ "groupId" : "sales"
+}
+
+
+
+ Response codes
+
+
+
+ Response code
+ Description
+
+
+
+
+ 201
+ Indicates the process definition was found and the identity link was created.
+
+
+ 404
+ Indicates the requested process definition was not found.
+
+
+
+
+
+
+ Success response body:
+
+{
+ "url":"http://localhost:8182/repository/process-definitions/oneTaskProcess%3A1%3A4/identitylinks/users/kermit",
+ "user":"kermit",
+ "group":null,
+ "type":"candidate"
+}
+
+
+
+
+ Delete a candidate starter from a process definition
+
+ DELETE repository/process-definitions/{processDefinitionId}/identitylinks/{family}/{identityId}
+
+
+
+ URL parameters
+
+
+
+ Parameter
+ Required
+ Value
+ Description
+
+
+
+
+ processDefinitionId
+ Yes
+ String
+ The id of the process definition.
+
+
+ family
+ Yes
+ String
+ Either users or groups, depending on the type of identity link.
+
+
+ identityId
+ Yes
+ String
+ Either the userId or groupId of the identity to remove as candidate starter.
+
+
+
+
+
+
+
+ Response codes
+
+
+
+ Response code
+ Description
+
+
+
+
+ 204
+ Indicates the process definition was found and the identity link was removed. The response body is intentionally empty.
+
+
+ 404
+ Indicates the requested process definition was not found or the process definition doesn't have an identity-link that matches the url.
+
+
+
+
+
+
+ Success response body:
+
+{
+ "url":"http://localhost:8182/repository/process-definitions/oneTaskProcess%3A1%3A4/identitylinks/users/kermit",
+ "user":"kermit",
+ "group":null,
+ "type":"candidate"
+}
+
+
+
+
+ Get a candidate starter from a process definition
+
+ GET repository/process-definitions/{processDefinitionId}/identitylinks/{family}/{identityId}
+
+
+
+ URL parameters
+
+
+
+ Parameter
+ Required
+ Value
+ Description
+
+
+
+
+ processDefinitionId
+ Yes
+ String
+ The id of the process definition.
+
+
+ family
+ Yes
+ String
+ Either users or groups, depending on the type of identity link.
+
+
+ identityId
+ Yes
+ String
+ Either the userId or groupId of the identity to get as candidate starter.
+
+
+
+
+
+
+
+ Response codes
+
+
+
+ Response code
+ Description
+
+
+
+
+ 200
+ Indicates the process definition was found and the identity link was returned.
+
+
+ 404
+ Indicates the requested process definition was not found or the process definition doesn't have an identity-link that matches the url.
+
+
+
+
+
+
+ Success response body:
+
+{
+ "url":"http://localhost:8182/repository/process-definitions/oneTaskProcess%3A1%3A4/identitylinks/users/kermit",
+ "user":"kermit",
+ "group":null,
+ "type":"candidate"
+}
+
+
+
@@ -2785,6 +3082,85 @@ GET runtime/tasks/{taskId}/identitylinks/groups
"groupId" : "sales",
"type" : "candidate",
"url" : "http://localhost:8081/activiti-rest/service/runtime/tasks/100/identitylinks/groups/sales/candidate"
+}
+
+
+
+
+ Create a single identity link on a task
+
+ POST runtime/tasks/{taskId}/identitylinks
+
+
+
+ URL parameters
+
+
+
+ Parameter
+ Required
+ Value
+ Description
+
+
+
+
+ taskId
+ Yes
+ String
+ The id of the task .
+
+
+
+
+
+
+ Request body (user):
+
+{
+ "userId" : "kermit",
+ "type" : "candidate",
+}
+
+
+ Request body (group):
+
+{
+ "groupId" : "sales",
+ "type" : "candidate",
+}
+
+
+
+ Response codes
+
+
+
+ Response code
+ Description
+
+
+
+
+ 201
+ Indicates the task was found and the identity link was created.
+
+
+ 404
+ Indicates the requested task was not found or the task doesn't have the requested identityLink. The status contains additional information about this error.
+
+
+
+
+
+
+ Success response body:
+
+{
+ "userId" : null,
+ "groupId" : "sales",
+ "type" : "candidate",
+ "url" : "http://localhost:8081/activiti-rest/service/runtime/tasks/100/identitylinks/groups/sales/candidate"
}