Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(engine/engine-rest): like-queries for candidate groups #4543

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@
type = "string"
desc = "Only include tasks that are offered to the given group." />

<@lib.parameter name = "candidateGroupLike"
location = "query"
type = "string"
desc = "Only include tasks that are offered to groups that have the parameter value as a substring." />

<@lib.parameter name = "candidateGroupExpression"
location = "query"
type = "string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@
name = "candidateGroup"
type = "string"
desc = "Only include tasks that are offered to the given group." />

<@lib.property
name = "candidateGroupLike"
type = "string"
desc = "Only include tasks that are offered to groups that have the parameter value as a substring." />

<@lib.property
name = "candidateGroupExpression"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ public class TaskQueryDto extends AbstractQueryDto<TaskQuery> {
private String[] assigneeNotIn;
private String candidateGroup;
private String candidateGroupExpression;
private String candidateGroupLike;
private String candidateUser;
private String candidateUserExpression;
private Boolean includeAssignedTasks;
Expand Down Expand Up @@ -347,6 +348,11 @@ public void setCandidateGroupExpression(String candidateGroupExpression) {
this.candidateGroupExpression = candidateGroupExpression;
}

@CamundaQueryParam("candidateGroupLike")
joaquinfelici marked this conversation as resolved.
Show resolved Hide resolved
public void setCandidateGroupLike(String candidateGroupLike) {
this.candidateGroupLike = candidateGroupLike;
}

@CamundaQueryParam(value = "withCandidateGroups", converter = BooleanConverter.class)
public void setWithCandidateGroups(Boolean withCandidateGroups) {
this.withCandidateGroups = withCandidateGroups;
Expand Down Expand Up @@ -821,6 +827,10 @@ public String getCandidateGroupExpression() {
return candidateGroupExpression;
}

public String getCandidateGroupLike() {
return candidateGroupLike;
}

public String getCandidateUser() {
return candidateUser;
}
Expand Down Expand Up @@ -1169,6 +1179,9 @@ protected void applyFilters(TaskQuery query) {
if (candidateGroupExpression != null) {
query.taskCandidateGroupExpression(candidateGroupExpression);
}
if (candidateGroupLike != null) {
query.taskCandidateGroupLike(candidateGroupLike);
}
if (withCandidateGroups != null && withCandidateGroups) {
query.withCandidateGroups();
}
Expand Down Expand Up @@ -1570,6 +1583,7 @@ public static TaskQueryDto fromQuery(Query<?, ?> query, boolean isOrQueryActive)

dto.candidateUser = taskQuery.getCandidateUser();
dto.candidateGroup = taskQuery.getCandidateGroup();
dto.candidateGroupLike = taskQuery.getCandidateGroupLike();
dto.candidateGroups = taskQuery.getCandidateGroupsInternal();
dto.includeAssignedTasks = taskQuery.isIncludeAssignedTasksInternal();
dto.withCandidateGroups = taskQuery.isWithCandidateGroups();
Expand Down Expand Up @@ -1876,4 +1890,4 @@ else if (VariableInstanceQueryProperty.VARIABLE_TYPE.equals(property)) {
}
return parameters;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ private Map<String, String> getCompleteStringQueryParameters() {
parameters.put("assignee", "anAssignee");
parameters.put("assigneeLike", "anAssigneeLike");
parameters.put("candidateGroup", "aCandidateGroup");
parameters.put("candidateGroupLike", "aCandidateGroupLike");
parameters.put("candidateUser", "aCandidate");
parameters.put("includeAssignedTasks", "false");
parameters.put("taskId", "aTaskId");
Expand Down Expand Up @@ -566,6 +567,7 @@ private void verifyStringParameterQueryInvocations() {
verify(mockQuery).taskAssignee(stringQueryParameters.get("assignee"));
verify(mockQuery).taskAssigneeLike(stringQueryParameters.get("assigneeLike"));
verify(mockQuery).taskCandidateGroup(stringQueryParameters.get("candidateGroup"));
verify(mockQuery).taskCandidateGroupLike(stringQueryParameters.get("candidateGroupLike"));
verify(mockQuery).taskCandidateUser(stringQueryParameters.get("candidateUser"));
verify(mockQuery).taskDefinitionKey(stringQueryParameters.get("taskDefinitionKey"));
verify(mockQuery).taskDefinitionKeyLike(stringQueryParameters.get("taskDefinitionKeyLike"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ public class TaskQueryImpl extends AbstractQuery<TaskQuery, Task> implements Tas
protected DelegationState delegationState;
protected String candidateUser;
protected String candidateGroup;
protected String candidateGroupLike;
protected List<String> candidateGroups;
protected Boolean withCandidateGroups;
protected Boolean withoutCandidateGroups;
Expand Down Expand Up @@ -451,6 +452,18 @@ public TaskQuery taskCandidateGroupExpression(String candidateGroupExpression) {
return this;
}

@Override
public TaskQuery taskCandidateGroupLike(String candidateGroupLike) {
ensureNotNull("Candidate group like", candidateGroupLike);

if (!isOrQueryActive && (candidateUser != null || expressions.containsKey("taskCandidateUser"))) {
throw new ProcessEngineException("Invalid query usage: cannot set both candidateGroupLike and candidateUser");
}

this.candidateGroupLike = candidateGroupLike;
return this;
}

@Override
public TaskQuery taskCandidateGroupIn(List<String> candidateGroups) {
ensureNotEmpty("Candidate group list", candidateGroups);
Expand Down Expand Up @@ -482,10 +495,11 @@ public TaskQuery taskCandidateGroupInExpression(String candidateGroupsExpression

@Override
public TaskQuery includeAssignedTasks() {
if (candidateUser == null && candidateGroup == null && candidateGroups == null && !isWithCandidateGroups() && !isWithoutCandidateGroups() && !isWithCandidateUsers() && !isWithoutCandidateUsers()
if (candidateUser == null && candidateGroup == null && candidateGroupLike == null && candidateGroups == null
&& !isWithCandidateGroups() && !isWithoutCandidateGroups() && !isWithCandidateUsers() && !isWithoutCandidateUsers()
&& !expressions.containsKey("taskCandidateUser") && !expressions.containsKey("taskCandidateGroup")
&& !expressions.containsKey("taskCandidateGroupIn")) {
throw new ProcessEngineException("Invalid query usage: candidateUser, candidateGroup, candidateGroupIn, withCandidateGroups, withoutCandidateGroups, withCandidateUsers, withoutCandidateUsers has to be called before 'includeAssignedTasks'.");
throw new ProcessEngineException("Invalid query usage: candidateUser, candidateGroup, candidateGroupLike, candidateGroupIn, withCandidateGroups, withoutCandidateGroups, withCandidateUsers, withoutCandidateUsers has to be called before 'includeAssignedTasks'.");
}

includeAssignedTasks = true;
Expand Down Expand Up @@ -1578,6 +1592,10 @@ public String getCandidateGroup() {
return candidateGroup;
}

public String getCandidateGroupLike() {
return candidateGroupLike;
}

public boolean isIncludeAssignedTasks() {
return includeAssignedTasks != null ? includeAssignedTasks : false;
}
Expand Down Expand Up @@ -1929,6 +1947,13 @@ else if (this.getCandidateGroup() != null) {
extendedQuery.taskCandidateGroup(this.getCandidateGroup());
}

if (extendingQuery.getCandidateGroupLike() != null) {
extendedQuery.taskCandidateGroupLike(extendingQuery.getCandidateGroupLike());
}
else if (this.getCandidateGroupLike() != null) {
extendedQuery.taskCandidateGroupLike(this.getCandidateGroupLike());
}

if (extendingQuery.isWithCandidateGroups() || this.isWithCandidateGroups()) {
extendedQuery.withCandidateGroups();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public class JsonTaskQueryConverter extends JsonObjectConverter<TaskQuery> {
public static final String DELEGATION_STATE = "delegationState";
public static final String CANDIDATE_USER = "candidateUser";
public static final String CANDIDATE_GROUP = "candidateGroup";
public static final String CANDIDATE_GROUP_LIKE = "candidateGroupLike";
public static final String CANDIDATE_GROUPS = "candidateGroups";
public static final String WITH_CANDIDATE_GROUPS = "withCandidateGroups";
public static final String WITHOUT_CANDIDATE_GROUPS = "withoutCandidateGroups";
Expand Down Expand Up @@ -166,6 +167,7 @@ public JsonObject toJsonObject(TaskQuery taskQuery, boolean isOrQueryActive) {
JsonUtil.addField(json, DELEGATION_STATE, query.getDelegationStateString());
JsonUtil.addField(json, CANDIDATE_USER, query.getCandidateUser());
JsonUtil.addField(json, CANDIDATE_GROUP, query.getCandidateGroup());
JsonUtil.addField(json, CANDIDATE_GROUP_LIKE, query.getCandidateGroupLike());
JsonUtil.addListField(json, CANDIDATE_GROUPS, query.getCandidateGroupsInternal());
JsonUtil.addDefaultField(json, WITH_CANDIDATE_GROUPS, false, query.isWithCandidateGroups());
JsonUtil.addDefaultField(json, WITHOUT_CANDIDATE_GROUPS, false, query.isWithoutCandidateGroups());
Expand Down Expand Up @@ -363,6 +365,9 @@ protected TaskQuery toObject(JsonObject json, boolean isOrQuery) {
if (json.has(CANDIDATE_GROUP)) {
query.taskCandidateGroup(JsonUtil.getString(json, CANDIDATE_GROUP));
}
if (json.has(CANDIDATE_GROUP_LIKE)) {
query.taskCandidateGroupLike(JsonUtil.getString(json, CANDIDATE_GROUP_LIKE));
}
if (json.has(CANDIDATE_GROUPS) && !json.has(CANDIDATE_USER) && !json.has(CANDIDATE_GROUP)) {
query.taskCandidateGroupIn(getList(JsonUtil.getArray(json, CANDIDATE_GROUPS)));
}
Expand Down
19 changes: 19 additions & 0 deletions engine/src/main/java/org/camunda/bpm/engine/task/TaskQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,25 @@ public interface TaskQuery extends Query<TaskQuery, Task> {
*/
TaskQuery taskCandidateGroupExpression(String candidateGroupExpression);

/**
* Only select tasks whose candidate users belong to groups matching the given parameter.
* The syntax is that of SQL: for example usage: nameLike(%camunda%)
*
* <p>
* Per default it only selects tasks which are not already assigned
* to a user. To also include assigned task in the result specify
* {@link #includeAssignedTasks()} in your query.
* </p>
*
* @throws ProcessEngineException <ul><li>When query is executed and {@link #taskCandidateUser(String)} or
* {@link #taskCandidateUserExpression(String)} (List)} has been executed on the
* "and query" instance. <br>
* No exception is thrown when query is executed and {@link #taskCandidateUser(String)} or
* {@link #taskCandidateUserExpression(String)} has been executed on the "or query" instance.</li>
* <li>When passed group is <code>null</code>.</li></ul>
*/
TaskQuery taskCandidateGroupLike(String candidateGroupLike);

/**
* Only select tasks for which the 'candidateGroup' is one of the given groups.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@
<if test="query.isOrQueryActive">
<bind name="JOIN_TYPE" value="'left join'" />
</if>
<if test="query != null &amp;&amp; (query.candidateUser != null || query.candidateGroups != null || query.involvedUser != null || query.withCandidateGroups || query.withCandidateUsers)">
<if test="query != null &amp;&amp; (query.candidateUser != null || query.candidateGroups != null || query.candidateGroupLike != null || query.involvedUser != null || query.withCandidateGroups || query.withCandidateUsers)">
<bind name="I_JOIN" value="true" />
</if>
<!-- the process definition table is joined if
Expand Down Expand Up @@ -577,14 +577,14 @@
<if test="query.isWithoutTenantId">
${queryType} RES.TENANT_ID_ is null
</if>
<if test="query.candidateUser != null || query.candidateGroups != null || query.withCandidateGroups || query.withCandidateUsers">
<if test="query.candidateUser != null || query.candidateGroups != null || query.candidateGroupLike != null || query.withCandidateGroups || query.withCandidateUsers">
${queryType}
<trim prefixOverrides="and" prefix="(" suffix=")">
<if test="!query.includeAssignedTasks">
and RES.ASSIGNEE_ is null
</if>
and I.TYPE_ = 'candidate'
<if test="query.candidateUser != null || query.candidateGroups != null">
<if test="query.candidateUser != null || query.candidateGroups != null || query.candidateGroupLike != null">
and
(
<if test="query.candidateUser != null">
Expand All @@ -600,6 +600,14 @@
#{group}
</foreach>
</if>
<!-- If we need to add the candidateGroupsLike statement and there have been previous statements
in the candidates block, then we need to add an "or" first -->
<if test="query.candidateGroupLike != null &amp;&amp; (query.candidateUser != null || (query.candidateGroups != null &amp;&amp; query.candidateGroups.size &gt; 0))">
or
</if>
<if test="query.candidateGroupLike != null">
I.GROUP_ID_ LIKE #{candidateGroupLike}
</if>
)
</if>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ public void testTaskQuery() {
query.taskUnassigned();
query.taskAssigned();
query.taskDelegationState(testDelegationState);
query.taskCandidateGroupLike(testString);
query.taskCandidateGroupIn(testCandidateGroups);
query.taskCandidateGroupInExpression(testString);
query.withCandidateGroups();
Expand Down Expand Up @@ -290,6 +291,7 @@ public void testTaskQuery() {
assertTrue(query.isUnassigned());
assertTrue(query.isAssigned());
assertEquals(testDelegationState, query.getDelegationState());
assertEquals(testString, query.getCandidateGroupLike());
assertEquals(testCandidateGroups, query.getCandidateGroups());
assertTrue(query.isWithCandidateGroups());
assertTrue(query.isWithoutCandidateGroups());
Expand Down Expand Up @@ -657,6 +659,21 @@ public void testTaskQueryCandidateGroup() {
assertEquals(testGroup.getId(), query.getExpressions().get("taskCandidateGroup"));
}

@Test
public void testTaskQueryCandidateGroupLike() {
// given
TaskQueryImpl query = new TaskQueryImpl();
query.taskCandidateGroupLike(testGroup.getId());

saveQuery(query);

// when
query = filter.getQuery();

// then
assertEquals(testGroup.getId(), query.getCandidateGroupLike());
}

@Test
public void testTaskQueryCandidateUserIncludeAssignedTasks() {
TaskQueryImpl query = new TaskQueryImpl();
Expand Down Expand Up @@ -709,6 +726,23 @@ public void testTaskQueryCandidateGroupExpressionIncludeAssignedTasks() {
assertTrue(query.isIncludeAssignedTasks());
}

@Test
public void testTaskQueryCandidateGroupLikeIncludeAssignedTasks() {
// given
TaskQueryImpl query = new TaskQueryImpl();
query.taskCandidateGroupLike(testGroup.getId());
query.includeAssignedTasks();

saveQuery(query);

// when
query = filter.getQuery();

// then
assertEquals(testGroup.getId(), query.getCandidateGroupLike());
assertTrue(query.isIncludeAssignedTasks());
}

@Test
public void testTaskQueryCandidateGroupsIncludeAssignedTasks() {
TaskQueryImpl query = new TaskQueryImpl();
Expand Down Expand Up @@ -823,6 +857,56 @@ public void testExtendingTaskQueryWithAssigneeNotIn() {
assertEquals(0, extendingQueryTasks.size());
}

@Test
public void testExtendingEmptyTaskQueryWithCandidateGroupLike() {
// given 3 test tasks created during setup
TaskQuery query = taskService.createTaskQuery();
saveQuery(query);
List<Task> tasks = filterService.list(filter.getId());
assertEquals(3, tasks.size());

// when extending the query with a "candidate group like"
TaskQuery extendingQuery = taskService.createTaskQuery();
extendingQuery.taskCandidateGroupLike("%count%");

// then there is 1 unassigned task with the candidate group "accounting"
tasks = filterService.list(filter.getId(), extendingQuery);
assertEquals(1, tasks.size());
}

@Test
public void testExtendingCandidateGroupLikeTaskQueryWithEmpty() {
// given 3 existing tasks but only 1 unassigned task that matches the initial filter
TaskQuery query = taskService.createTaskQuery().taskCandidateGroupLike("%count%");
saveQuery(query);
List<Task> tasks = filterService.list(filter.getId());
assertEquals(1, tasks.size());

// when extending the query with an empty query
TaskQuery extendingQuery = taskService.createTaskQuery();

// then the empty query should be ignored in favor of the existing value for "candidate group like"
tasks = filterService.list(filter.getId(), extendingQuery);
assertEquals(1, tasks.size());
}

@Test
public void testExtendingCandidateGroupLikeTaskQueryWithCandidateGroupLike() {
// given 3 existing tasks but zero match the initial filter
TaskQuery query = taskService.createTaskQuery().taskCandidateGroupLike("HR");
saveQuery(query);
List<Task> tasks = filterService.list(filter.getId());
assertTrue(tasks.isEmpty());

// when extending the query with a "candidate groups like" query
TaskQuery extendingQuery = taskService.createTaskQuery();
extendingQuery.taskCandidateGroupLike("acc%");

// then the query should be return result of task matching the new filter
tasks = filterService.list(filter.getId(), extendingQuery);
assertEquals(1, tasks.size());
}

@Test
public void testExtendingTaskQueryListWithCandidateGroups() {
TaskQuery query = taskService.createTaskQuery();
Expand Down
Loading
Loading