Skip to content
Open
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
8 changes: 7 additions & 1 deletion extract/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@
<artifactId>javase</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
<type>jar</type>
</dependency>
</dependencies>

<profiles>
Expand Down Expand Up @@ -262,7 +268,7 @@
<skipTests>false</skipTests>
<summaryFile>target/failsafe-reports/failsafe-summary-integration.xml</summaryFile>
<includes>
<include>ch.asit_asso.extract.integration.**.*IntegrationTest</include>
<include>ch.asit_asso.extract.integration.**.*RequestsOwnershipIntegrationTest</include>
</includes>
<systemPropertyVariables>
<extract.log.path>${project.build.directory}/logs</extract.log.path>
Expand Down
111 changes: 110 additions & 1 deletion extract/src/main/java/ch/asit_asso/extract/domain/Request.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

import java.io.Serializable;
import java.util.Calendar;
import java.util.Collection;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
Expand All @@ -28,13 +31,16 @@
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlTransient;
import org.apache.commons.lang3.StringUtils;


Expand Down Expand Up @@ -252,7 +258,40 @@ public class Request implements Serializable {
private Connector connector;



/**
* The operators that supervise this process.
*/
@JoinTable(name = "requests_users",
joinColumns = {
@JoinColumn(name = "id_request", referencedColumnName = "id_request",
foreignKey = @ForeignKey(name = "FK_REQUESTS_USERS_REQUESTS")
)
},
inverseJoinColumns = {
@JoinColumn(name = "id_user", referencedColumnName = "id_user",
foreignKey = @ForeignKey(name = "FK_REQUESTS_USERS_USER")
)
}
)
@ManyToMany
private Collection<User> usersCollection;


@JoinTable(name = "requests_usergroups",
joinColumns = {
@JoinColumn(name = "id_request", referencedColumnName = "id_request",
foreignKey = @ForeignKey(name = "FK_REQUESTS_USERGROUPS_REQUESTS")
)
},
inverseJoinColumns = {
@JoinColumn(name = "id_usergroup", referencedColumnName = "id_usergroup",
foreignKey = @ForeignKey(name = "FK_REQUESTS_USERGROUPS_USERGROUP")
)
}
)
@ManyToMany
private Collection<UserGroup> userGroupsCollection;

/**
* The possible states of a data item order processing.
*/
Expand Down Expand Up @@ -1006,4 +1045,74 @@ public final void reject(final String rejectionRemark) {
this.setRejected(true);
}


/**
* Obtains the users that supervise this particular task. This only contains the users defined
* directly, not those defined through a user group. To get all the operators independently of
* how they've been defined, please use the method
* {@link #getDistinctOperators()}
*
* @return a collection that contains the operators
*/
@XmlTransient
public Collection<User> getUsersCollection() {
return usersCollection;
}



/**
* Defines the users that supervise this particular task.
*
* @param users a collection that contains the operators for this task
*/
public void setUsersCollection(final Collection<User> users) {
this.usersCollection = users;
}


/**
* Obtains the user groups that supervise this particular task.
*
* @return a collection that contains the groups of operators
*/
@XmlTransient
public Collection<UserGroup> getUserGroupsCollection() {
return userGroupsCollection;
}



/**
* Defines the user groups that supervise this particular task.
*
* @param userGroups a collection that contains the groups of operators for this task
*/
public void setUserGroupsCollection(Collection<UserGroup> userGroups) {
this.userGroupsCollection = userGroups;
}

/**
* Obtains a list of all the users allowed to manage this process, including those defined through a user group,
* without duplicates.
*
* @return a collection that contains all the operators for this process
*/
public final Collection<User> getDistinctOperators() {
List<User> operators = new ArrayList<>(this.getUsersCollection());

for (UserGroup operatorsGroup : this.getUserGroupsCollection()) {

for (User groupOperator : operatorsGroup.getUsersCollection()) {

if (operators.contains((groupOperator))) {
continue;
}

operators.add(groupOperator);
}
}

return operators;
}
}
18 changes: 12 additions & 6 deletions extract/src/main/java/ch/asit_asso/extract/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,19 @@
@NamedQuery(name = "User.findAllActiveApplicationUsers",
query = "SELECT u FROM User u WHERE u.login != " + "'" + User.SYSTEM_USER_LOGIN + "' and u.active = true"),
@NamedQuery(name = "User.getUserAssociatedRequestsByStatusOrderByEndDate",
query = "SELECT r FROM Request r WHERE (r.process IN (SELECT p FROM User u JOIN u.processesCollection p WHERE u.id = :userId)"
+ " OR r.process IN (SELECT p FROM User u JOIN u.userGroupsCollection g JOIN g.processesCollection p WHERE u.id = :userId))"
+ " AND r.status = :status ORDER BY r.endDate DESC"),
query = "SELECT r FROM Request r WHERE ("
+ " r.process IN (SELECT p FROM User u JOIN u.processesCollection p WHERE u.id = :userId)"
+ " OR r.process IN (SELECT p FROM User u JOIN u.userGroupsCollection g JOIN g.processesCollection p WHERE u.id = :userId)"
+ " OR :userId IN (SELECT uc.id FROM r.usersCollection uc)"
+ " OR :userId IN (SELECT uc.id FROM r.userGroupsCollection ug LEFT JOIN ug.usersCollection uc)"
+ ") AND r.status = :status ORDER BY r.endDate DESC"),
@NamedQuery(name = "User.getUserAssociatedRequestsByStatusNot",
query = "SELECT r FROM Request r WHERE (r.process IN (SELECT p FROM User u JOIN u.processesCollection p WHERE u.id = :userId)"
+ " OR r.process IN (SELECT p FROM User u JOIN u.userGroupsCollection g JOIN g.processesCollection p WHERE u.id = :userId))"
+ " AND r.status != :status")
query = "SELECT r FROM Request r WHERE ("
+ " r.process IN (SELECT p FROM User u JOIN u.processesCollection p WHERE u.id = :userId)"
+ " OR r.process IN (SELECT p FROM User u JOIN u.userGroupsCollection g JOIN g.processesCollection p WHERE u.id = :userId)"
+ " OR :userId IN (SELECT uc.id FROM r.usersCollection uc)"
+ " OR :userId IN (SELECT uc.id FROM r.userGroupsCollection ug LEFT JOIN ug.usersCollection uc)"
+ ") AND r.status != :status")


})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import ch.asit_asso.extract.domain.Process;
import ch.asit_asso.extract.domain.Request;
import ch.asit_asso.extract.domain.Request_;
import ch.asit_asso.extract.domain.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.domain.Specification;
Expand Down Expand Up @@ -237,7 +238,28 @@ public Predicate toPredicate(final Root<Request> root, final CriteriaQuery<?> qu
};
}

/**
* Obtains the criteria to filter the request based on the process it is associated with.
*
* @param processesList a list that contains the process that the request can be associated with
* @return the set of criteria to apply the process filter
*/
public static Specification<Request> isBoundToUser(final User user) {

return new Specification<Request>() {

@Override
public Predicate toPredicate(final Root<Request> root, final CriteriaQuery<?> query,
final CriteriaBuilder builder) {
var result = builder.isMember(user, root.get(Request_.usersCollection));
for (var ug: user.getUserGroupsCollection()) {
result = builder.or(result, builder.isMember(ug, root.get(Request_.userGroupsCollection)));
}
return result;
}

};
}

/**
* Obtains the criteria to return only the request whose process has completed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,10 @@ private Page<Request> getFinishedRequests(final int pageStart, final String sort
String.join(", ",
userProcesses.stream().map((process) ->
process.getId().toString()).toArray(String[]::new)));

final Specification<Request> userCriteria
= RequestSpecification.isProcessInList(userProcesses);
= RequestSpecification.isProcessInList(userProcesses).or(
RequestSpecification.isBoundToUser(currentUser));

return this.requestsRepository.findAll(Specification.where(userCriteria).and(searchCriteria), paging);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
Expand All @@ -32,20 +33,26 @@
import ch.asit_asso.extract.domain.RequestHistoryRecord;
import ch.asit_asso.extract.domain.Task;
import ch.asit_asso.extract.domain.User;
import ch.asit_asso.extract.domain.UserGroup;
import ch.asit_asso.extract.orchestrator.OrchestratorSettings;
import ch.asit_asso.extract.persistence.ProcessesRepository;
import ch.asit_asso.extract.persistence.RemarkRepository;
import ch.asit_asso.extract.persistence.RequestHistoryRepository;
import ch.asit_asso.extract.persistence.RequestsRepository;
import ch.asit_asso.extract.persistence.SystemParametersRepository;
import ch.asit_asso.extract.persistence.TasksRepository;
import ch.asit_asso.extract.persistence.UserGroupsRepository;
import ch.asit_asso.extract.persistence.UsersRepository;
import ch.asit_asso.extract.utils.FileSystemUtils;
import ch.asit_asso.extract.utils.FileSystemUtils.RequestDataFolder;
import ch.asit_asso.extract.utils.ZipUtils;
import ch.asit_asso.extract.web.Message;
import ch.asit_asso.extract.web.Message.MessageType;
import ch.asit_asso.extract.web.model.RequestModel;
import ch.asit_asso.extract.web.model.UserModel;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
Expand Down Expand Up @@ -169,7 +176,11 @@ public class RequestsController extends BaseController {
@Autowired
private UsersRepository usersRepository;


/**
* The Spring Data object that links the user data objects with the data source.
*/
@Autowired
private UserGroupsRepository userGroupsRepository;

/**
* Processes a request to display detailed information about an order.
Expand Down Expand Up @@ -207,6 +218,9 @@ public final String viewItem(@PathVariable final int itemId, final ModelMap mode
this.parametersRepository.getValidationFocusProperties().split(","));

model.addAttribute("request", requestModel);
model.addAttribute("allactiveusers", this.getAllActiveUsers());
model.addAttribute("allusergroups", this.getAllUserGroups());

Task currentTask = this.getCurrentTask(requestModel);

if (currentTask != null) {
Expand Down Expand Up @@ -977,7 +991,7 @@ public final synchronized String handleValidateStandbyRequest(@PathVariable fina
this.addStatusMessage(redirectAttributes, "requestDetails.error.request.notAllowed", MessageType.ERROR);

return REDIRECT_TO_ACCESS_DENIED;
}
}

if (!this.canRequestBeValidated(request, currentStep, redirectAttributes)) {
return RequestsController.REDIRECT_TO_LIST;
Expand All @@ -996,6 +1010,38 @@ public final synchronized String handleValidateStandbyRequest(@PathVariable fina
}


@PostMapping("{requestId}/assign")
public final synchronized String handleAssignRequest(
@PathVariable final int requestId,
@RequestParam List<Integer> usersIds,
@RequestParam List<Integer> userGroupsIds) {
var request = getDomainRequest(requestId);
assert request != null : "The request cannot be null.";
assert request.getProcess() != null : "The request must be associated with a process.";
if (!this.canCurrentUserViewRequestDetails(request)) {
this.logger.warn("The user {} tried to assign users to the request {} but is not allowed to.",
this.getCurrentUserLogin(), request.getId());
return REDIRECT_TO_ACCESS_DENIED;
}

var usersToAdd = usersIds.stream().map(this.usersRepository::findById)
.distinct()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());

var groupsToAdd = userGroupsIds.stream().map(this.userGroupsRepository::findById)
.distinct()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());

request.setUsersCollection(usersToAdd);
request.setUserGroupsCollection(groupsToAdd);
this.requestsRepository.save(request);

return String.format(RequestsController.REDIRECT_TO_DETAILS_FORMAT, requestId);
}

/**
* Adds record entries for the remaining tasks when a requests is set to skip to the end of its process.
Expand Down Expand Up @@ -1162,8 +1208,11 @@ private boolean canCurrentUserViewRequestDetails(final Request request) {
return false;
}

Integer[] operatorsIds = process.getDistinctOperators().stream().map(User::getId).toArray(Integer[]::new);
return ArrayUtils.contains(operatorsIds, this.getCurrentUserId());
var currentId = this.getCurrentUserId();
return Stream.concat(
process.getDistinctOperators().stream(),
request.getDistinctOperators().stream()
).map(User::getId).anyMatch((id) -> currentId == id);
}


Expand Down Expand Up @@ -1904,5 +1953,24 @@ void validateRequest(final Request request, final String remark) {
private Request getDomainRequest(int requestId) {
return this.requestsRepository.findById(requestId).orElse(null);
}

/**
* Fetches a list of users from the repository and returns a collection of active user objects.
*
* @return a list of existing active users
*/
private List<UserModel> getAllActiveUsers() {
final List<UserModel> usersList = new ArrayList<>();

for (User domainUser : this.usersRepository.findAllActiveApplicationUsers()) {

usersList.add(new UserModel(domainUser));
}

return usersList;
}

private Collection<UserGroup> getAllUserGroups() {
return this.userGroupsRepository.findAllByOrderByName();
}
}
Loading