TSK-1647: Implemented an error key for every exception
Co-authored-by: Tristan Eisermann<19949441+Tristan2357@users.noreply.github.com> Co-authored-by: Tim Gerversmann<72377965+tge20@users.noreply.github.com> Co-authored-by: Sofie Hofmann<29145005+sofie29@users.noreply.github.com> javaDoc Exceptions blub TSK-1647: now validating existing & not authorized tasks :)
This commit is contained in:
parent
207d291787
commit
34d2bbfa92
|
@ -135,11 +135,6 @@ class LoggingAspectTest {
|
||||||
static class LoggingTestClass {
|
static class LoggingTestClass {
|
||||||
public void logInternalMethod() {}
|
public void logInternalMethod() {}
|
||||||
|
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
|
||||||
String logInternalMethodWithReturnValue() {
|
|
||||||
return "test string";
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
@SuppressWarnings("UnusedReturnValue")
|
||||||
public String logInternalMethodWithReturnValueNull() {
|
public String logInternalMethodWithReturnValueNull() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -165,6 +160,11 @@ class LoggingAspectTest {
|
||||||
@NoLogging
|
@NoLogging
|
||||||
public void doNotLogInternalMethod() {}
|
public void doNotLogInternalMethod() {}
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedReturnValue")
|
||||||
|
String logInternalMethodWithReturnValue() {
|
||||||
|
return "test string";
|
||||||
|
}
|
||||||
|
|
||||||
private void logInternalMethodPrivate() {}
|
private void logInternalMethodPrivate() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ public class BulkOperationResults<K, V extends Exception> {
|
||||||
/**
|
/**
|
||||||
* Returning a list of current errors as map. If there are no errors the result will be empty.
|
* Returning a list of current errors as map. If there are no errors the result will be empty.
|
||||||
*
|
*
|
||||||
* @return map of errors which can´t be null.
|
* @return map of errors which can't be null.
|
||||||
*/
|
*/
|
||||||
public Map<K, V> getErrorMap() {
|
public Map<K, V> getErrorMap() {
|
||||||
return this.errorMap;
|
return this.errorMap;
|
||||||
|
@ -82,7 +82,7 @@ public class BulkOperationResults<K, V extends Exception> {
|
||||||
/**
|
/**
|
||||||
* Map from any exception type to Exception.
|
* Map from any exception type to Exception.
|
||||||
*
|
*
|
||||||
* @return map of errors which can´t be null.
|
* @return map of errors which can't be null.
|
||||||
*/
|
*/
|
||||||
public BulkOperationResults<K, Exception> mapBulkOperationResults() {
|
public BulkOperationResults<K, Exception> mapBulkOperationResults() {
|
||||||
BulkOperationResults<K, Exception> bulkLogMapped = new BulkOperationResults<>();
|
BulkOperationResults<K, Exception> bulkLogMapped = new BulkOperationResults<>();
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package pro.taskana.common.api.exceptions;
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
/** Thrown in ConnectionManagementMode AUTOCOMMIT when an attempt to commit fails. */
|
/**
|
||||||
|
* This exception is thrown in ConnectionManagementMode AUTOCOMMIT when an attempt to commit fails.
|
||||||
|
*/
|
||||||
public class AutocommitFailedException extends TaskanaRuntimeException {
|
public class AutocommitFailedException extends TaskanaRuntimeException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "CONNECTION_AUTOCOMMIT_FAILED";
|
||||||
|
|
||||||
public AutocommitFailedException(Throwable cause) {
|
public AutocommitFailedException(Throwable cause) {
|
||||||
super("Autocommit failed", cause);
|
super("Autocommit failed", ErrorCode.of(ERROR_KEY), cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,11 @@ package pro.taskana.common.api.exceptions;
|
||||||
*/
|
*/
|
||||||
public class ConcurrencyException extends TaskanaException {
|
public class ConcurrencyException extends TaskanaException {
|
||||||
|
|
||||||
public ConcurrencyException(String msg) {
|
public static final String ERROR_KEY = "ENTITY_NOT_UP_TO_DATE";
|
||||||
super(msg);
|
|
||||||
|
public ConcurrencyException() {
|
||||||
|
super(
|
||||||
|
"The current entity cannot be updated since it has been modified while editing.",
|
||||||
|
ErrorCode.of(ERROR_KEY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package pro.taskana.common.api.exceptions;
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown if ConnectionManagementMode is CONNECTION_MANAGED_EXTERNALLY and an attempt is made to
|
* This exception is thrown when ConnectionManagementMode is CONNECTION_MANAGED_EXTERNALLY and an
|
||||||
* call an API method before the setConnection() method has been called.
|
* attempt is made to call an API method before the setConnection() method has been called.
|
||||||
*/
|
*/
|
||||||
public class ConnectionNotSetException extends TaskanaRuntimeException {
|
public class ConnectionNotSetException extends TaskanaRuntimeException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "CONNECTION_NOT_SET";
|
||||||
|
|
||||||
public ConnectionNotSetException() {
|
public ConnectionNotSetException() {
|
||||||
super("Connection not set");
|
super("Connection not set", ErrorCode.of(ERROR_KEY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
package pro.taskana.common.api.exceptions;
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
/**
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
* This exception is thrown if a domain name is specified which is not found in the configuration.
|
|
||||||
*/
|
/** This exception is thrown when the specified domain is not found in the configuration. */
|
||||||
public class DomainNotFoundException extends NotFoundException {
|
public class DomainNotFoundException extends NotFoundException {
|
||||||
|
|
||||||
public DomainNotFoundException(String domain, String msg) {
|
public static final String ERROR_KEY = "DOMAIN_NOT_FOUND";
|
||||||
super(domain, msg);
|
private final String domain;
|
||||||
|
|
||||||
|
public DomainNotFoundException(String domain) {
|
||||||
|
super(
|
||||||
|
String.format("Domain '%s' does not exist in the configuration", domain),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("domain", domain)));
|
||||||
|
this.domain = domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomain() {
|
||||||
|
return domain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class ErrorCode implements Serializable {
|
||||||
|
private final String key;
|
||||||
|
// Unfortunately this is necessary. The Map interface does not implement Serializable..
|
||||||
|
private final HashMap<String, Serializable> messageVariables;
|
||||||
|
|
||||||
|
private ErrorCode(String key, Map<String, Serializable> messageVariables) {
|
||||||
|
this.key = key;
|
||||||
|
this.messageVariables = new HashMap<>(messageVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ErrorCode of(String key, Map<String, Serializable> messageVariables) {
|
||||||
|
return new ErrorCode(key, messageVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ErrorCode of(String key) {
|
||||||
|
return new ErrorCode(key, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Serializable> getMessageVariables() {
|
||||||
|
return messageVariables;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(key, messageVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof ErrorCode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ErrorCode other = (ErrorCode) obj;
|
||||||
|
return Objects.equals(key, other.key)
|
||||||
|
&& Objects.equals(messageVariables, other.messageVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ErrorCode [key=" + key + ", messageVariables=" + messageVariables + "]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
package pro.taskana.common.api.exceptions;
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
/** This exception is thrown when a method is called with invalid argument. */
|
/** This exception is thrown when a method is called with an invalid argument. */
|
||||||
public class InvalidArgumentException extends TaskanaException {
|
public class InvalidArgumentException extends TaskanaException {
|
||||||
|
|
||||||
public InvalidArgumentException(String msg) {
|
public InvalidArgumentException(String msg) {
|
||||||
super(msg);
|
this(msg, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InvalidArgumentException(String msg, Throwable cause) {
|
public InvalidArgumentException(String msg, Throwable cause) {
|
||||||
super(msg, cause);
|
super(msg, ErrorCode.of("INVALID_ARGUMENT"), cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.TaskanaRole;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when the current user is not in a certain {@linkplain TaskanaRole role}
|
||||||
|
* it is supposed to be.
|
||||||
|
*/
|
||||||
|
public class MismatchedRoleException extends NotAuthorizedException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "ROLE_MISMATCHED";
|
||||||
|
private final String currentUserId;
|
||||||
|
private final TaskanaRole[] roles;
|
||||||
|
|
||||||
|
public MismatchedRoleException(String currentUserId, TaskanaRole... roles) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"Not authorized. The current user '%s' is not member of role(s) '%s'.",
|
||||||
|
currentUserId, Arrays.toString(roles)),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("roles", roles, "currentUserId", currentUserId)));
|
||||||
|
|
||||||
|
this.currentUserId = currentUserId;
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskanaRole[] getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentUserId() {
|
||||||
|
return currentUserId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,8 @@
|
||||||
package pro.taskana.common.api.exceptions;
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
/** This exception is used to communicate a not authorized user. */
|
/** This exception is thrown when a user is not authorized. */
|
||||||
public class NotAuthorizedException extends TaskanaException {
|
public class NotAuthorizedException extends TaskanaException {
|
||||||
|
protected NotAuthorizedException(String msg, ErrorCode errorCode) {
|
||||||
private final String currentUserId;
|
super(msg, errorCode);
|
||||||
|
|
||||||
public NotAuthorizedException(String msg, String currentUserId) {
|
|
||||||
super(msg + " - [CURRENT USER: {'" + currentUserId + "'}]");
|
|
||||||
this.currentUserId = currentUserId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCurrentUserId() {
|
|
||||||
return currentUserId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,9 @@
|
||||||
package pro.taskana.common.api.exceptions;
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
/** This exception will be thrown if a specific object is not in the database. */
|
/** This exception is thrown when a specific object is not in the database. */
|
||||||
public class NotFoundException extends TaskanaException {
|
public class NotFoundException extends TaskanaException {
|
||||||
|
|
||||||
private final String id;
|
protected NotFoundException(String message, ErrorCode errorCode) {
|
||||||
|
super(message, errorCode);
|
||||||
public NotFoundException(String id, String message) {
|
|
||||||
super(message);
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
package pro.taskana.common.api.exceptions;
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
/** This exception is thrown when a generic taskana problem is encountered. */
|
/** This exception is thrown when a generic TASKANA problem is encountered. */
|
||||||
public class SystemException extends TaskanaRuntimeException {
|
public class SystemException extends TaskanaRuntimeException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "CRITICAL_SYSTEM_ERROR";
|
||||||
|
|
||||||
public SystemException(String msg) {
|
public SystemException(String msg) {
|
||||||
super(msg);
|
this(msg, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SystemException(String msg, Throwable cause) {
|
public SystemException(String msg, Throwable cause) {
|
||||||
super(msg, cause);
|
super(msg, ErrorCode.of(ERROR_KEY), cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,18 @@ package pro.taskana.common.api.exceptions;
|
||||||
/** common base class for TASKANA's checked exceptions. */
|
/** common base class for TASKANA's checked exceptions. */
|
||||||
public class TaskanaException extends Exception {
|
public class TaskanaException extends Exception {
|
||||||
|
|
||||||
public TaskanaException() {
|
private final ErrorCode errorCode;
|
||||||
super();
|
|
||||||
|
protected TaskanaException(String message, ErrorCode errorCode) {
|
||||||
|
this(message, errorCode, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskanaException(String message) {
|
protected TaskanaException(String message, ErrorCode errorCode, Throwable cause) {
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskanaException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskanaException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
|
this.errorCode = errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskanaException(
|
public ErrorCode getErrorCode() {
|
||||||
String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
return errorCode;
|
||||||
super(message, cause, enableSuppression, writableStackTrace);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
package pro.taskana.common.api.exceptions;
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
/** Common base class for Taskana's runtime exceptions. */
|
/** The common base class for TASKANA's runtime exceptions. */
|
||||||
public class TaskanaRuntimeException extends RuntimeException {
|
public class TaskanaRuntimeException extends RuntimeException {
|
||||||
|
|
||||||
public TaskanaRuntimeException() {
|
private final ErrorCode errorCode;
|
||||||
super();
|
|
||||||
|
protected TaskanaRuntimeException(String message, ErrorCode errorCode) {
|
||||||
|
this(message, errorCode, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskanaRuntimeException(String message) {
|
protected TaskanaRuntimeException(String message, ErrorCode errorCode, Throwable cause) {
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskanaRuntimeException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskanaRuntimeException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
|
this.errorCode = errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskanaRuntimeException(
|
public ErrorCode getErrorCode() {
|
||||||
String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
return errorCode;
|
||||||
super(message, cause, enableSuppression, writableStackTrace);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TaskanaRuntimeException [errorCode=" + errorCode + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
package pro.taskana.common.api.exceptions;
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
/**
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
* This exception will be thrown if the database name doesn't match to one of the desired databases.
|
|
||||||
*/
|
|
||||||
public class UnsupportedDatabaseException extends RuntimeException {
|
|
||||||
|
|
||||||
public UnsupportedDatabaseException(String name) {
|
/**
|
||||||
super("Database with '" + name + "' not found");
|
* This exception is thrown when the database name doesn't match to one of the desired databases.
|
||||||
|
*/
|
||||||
|
public class UnsupportedDatabaseException extends TaskanaRuntimeException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "DATABASE_UNSUPPORTED";
|
||||||
|
private final String databaseProductName;
|
||||||
|
|
||||||
|
public UnsupportedDatabaseException(String databaseProductName) {
|
||||||
|
super(
|
||||||
|
String.format("Database '%s' is not supported", databaseProductName),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("databaseProductName", databaseProductName)));
|
||||||
|
this.databaseProductName = databaseProductName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDatabaseProductName() {
|
||||||
|
return databaseProductName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,25 @@
|
||||||
package pro.taskana.common.api.exceptions;
|
package pro.taskana.common.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.CustomHoliday;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
|
||||||
|
/** This exception is thrown when an entry for the {@linkplain CustomHoliday} has a wrong format. */
|
||||||
public class WrongCustomHolidayFormatException extends TaskanaException {
|
public class WrongCustomHolidayFormatException extends TaskanaException {
|
||||||
|
|
||||||
public WrongCustomHolidayFormatException(String message) {
|
public static final String ERROR_KEY = "CUSTOM_HOLIDAY_WRONG_FORMAT";
|
||||||
super(message);
|
private final String customHoliday;
|
||||||
|
|
||||||
|
public WrongCustomHolidayFormatException(String customHoliday) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"Wrong format for custom holiday entry '%s'! The format should be 'dd.MM' "
|
||||||
|
+ "i.e. 01.05 for the first of May.",
|
||||||
|
customHoliday),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("customHoliday", customHoliday)));
|
||||||
|
this.customHoliday = customHoliday;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCustomHoliday() {
|
||||||
|
return customHoliday;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,8 +96,8 @@ public class DbSchemaCreator {
|
||||||
|
|
||||||
Map<String, Object> queryResult = runner.selectOne(query);
|
Map<String, Object> queryResult = runner.selectOne(query);
|
||||||
|
|
||||||
ComparableVersion actualVersion = new ComparableVersion((String) queryResult.get("VERSION"));
|
ComparableVersion actualVersion = ComparableVersion.of((String) queryResult.get("VERSION"));
|
||||||
ComparableVersion minVersion = new ComparableVersion(expectedMinVersion);
|
ComparableVersion minVersion = ComparableVersion.of(expectedMinVersion);
|
||||||
|
|
||||||
if (actualVersion.compareTo(minVersion) < 0) {
|
if (actualVersion.compareTo(minVersion) < 0) {
|
||||||
LOGGER.error(
|
LOGGER.error(
|
||||||
|
|
|
@ -75,10 +75,14 @@ public class ComparableVersion implements Comparable<ComparableVersion> {
|
||||||
|
|
||||||
private ListItem items;
|
private ListItem items;
|
||||||
|
|
||||||
public ComparableVersion(String version) {
|
private ComparableVersion(String version) {
|
||||||
parseVersion(version);
|
parseVersion(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ComparableVersion of(String version) {
|
||||||
|
return new ComparableVersion(version);
|
||||||
|
}
|
||||||
|
|
||||||
public final void parseVersion(String version) {
|
public final void parseVersion(String version) {
|
||||||
this.value = version;
|
this.value = version;
|
||||||
|
|
||||||
|
@ -226,7 +230,7 @@ public class ComparableVersion implements Comparable<ComparableVersion> {
|
||||||
this.value = 0;
|
this.value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
IntItem(String str) {
|
private IntItem(String str) {
|
||||||
this.value = Integer.parseInt(str);
|
this.value = Integer.parseInt(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +298,7 @@ public class ComparableVersion implements Comparable<ComparableVersion> {
|
||||||
private static class LongItem implements Item {
|
private static class LongItem implements Item {
|
||||||
private final long value;
|
private final long value;
|
||||||
|
|
||||||
LongItem(String str) {
|
private LongItem(String str) {
|
||||||
this.value = Long.parseLong(str);
|
this.value = Long.parseLong(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +367,7 @@ public class ComparableVersion implements Comparable<ComparableVersion> {
|
||||||
private static class BigIntegerItem implements Item {
|
private static class BigIntegerItem implements Item {
|
||||||
private final BigInteger value;
|
private final BigInteger value;
|
||||||
|
|
||||||
BigIntegerItem(String str) {
|
private BigIntegerItem(String str) {
|
||||||
this.value = new BigInteger(str);
|
this.value = new BigInteger(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,7 +451,7 @@ public class ComparableVersion implements Comparable<ComparableVersion> {
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
||||||
StringItem(String value, boolean followedByDigit) {
|
private StringItem(String value, boolean followedByDigit) {
|
||||||
if (followedByDigit && value.length() == 1) {
|
if (followedByDigit && value.length() == 1) {
|
||||||
// a1 = alpha-1, b1 = beta-1, m1 = milestone-1
|
// a1 = alpha-1, b1 = beta-1, m1 = milestone-1
|
||||||
switch (value.charAt(0)) {
|
switch (value.charAt(0)) {
|
||||||
|
@ -549,6 +553,9 @@ public class ComparableVersion implements Comparable<ComparableVersion> {
|
||||||
* sub-lists (which start with '-(number)' in the version specification).
|
* sub-lists (which start with '-(number)' in the version specification).
|
||||||
*/
|
*/
|
||||||
private static class ListItem extends ArrayList<Item> implements Item {
|
private static class ListItem extends ArrayList<Item> implements Item {
|
||||||
|
|
||||||
|
private ListItem() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getType() {
|
public int getType() {
|
||||||
return LIST_ITEM;
|
return LIST_ITEM;
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package pro.taskana.common.internal.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
public class EnumUtil {
|
||||||
|
|
||||||
|
private EnumUtil() {
|
||||||
|
throw new IllegalStateException("Utility class");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <E extends Enum<E>> E[] allValuesExceptFor(E... values) {
|
||||||
|
if (values == null || values.length == 0) {
|
||||||
|
throw new IllegalArgumentException("values must be present");
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
E[] array = (E[]) Array.newInstance(values[0].getClass(), 0);
|
||||||
|
return EnumSet.complementOf(EnumSet.copyOf(Arrays.asList(values))).toArray(array);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package pro.taskana.common.internal.util;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/* This class will be removed once we fully migrate from JDK8*/
|
||||||
|
public class MapCreator {
|
||||||
|
|
||||||
|
private MapCreator() {
|
||||||
|
throw new IllegalStateException("Utility class");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> of() {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> of(K k1, V v1) {
|
||||||
|
Map<K, V> map = new HashMap<>();
|
||||||
|
map.put(k1, v1);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2) {
|
||||||
|
Map<K, V> map = new HashMap<>();
|
||||||
|
map.put(k1, v1);
|
||||||
|
map.put(k2, v2);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
|
||||||
|
Map<K, V> map = new HashMap<>();
|
||||||
|
map.put(k1, v1);
|
||||||
|
map.put(k2, v2);
|
||||||
|
map.put(k3, v3);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
|
||||||
|
Map<K, V> map = new HashMap<>();
|
||||||
|
map.put(k1, v1);
|
||||||
|
map.put(k2, v2);
|
||||||
|
map.put(k3, v3);
|
||||||
|
map.put(k4, v4);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
|
||||||
|
Map<K, V> map = new HashMap<>();
|
||||||
|
map.put(k1, v1);
|
||||||
|
map.put(k2, v2);
|
||||||
|
map.put(k3, v3);
|
||||||
|
map.put(k4, v4);
|
||||||
|
map.put(k5, v5);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,9 @@ import pro.taskana.common.api.exceptions.SystemException;
|
||||||
|
|
||||||
public class ObjectAttributeChangeDetector {
|
public class ObjectAttributeChangeDetector {
|
||||||
|
|
||||||
private ObjectAttributeChangeDetector() {}
|
private ObjectAttributeChangeDetector() {
|
||||||
|
throw new IllegalStateException("Utility class");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines changes in fields between two objects.
|
* Determines changes in fields between two objects.
|
||||||
|
|
|
@ -90,7 +90,6 @@ public class LogfileHistoryServiceImpl implements TaskanaHistory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteHistoryEventsByTaskIds(List<String> taskIds) {
|
public void deleteHistoryEventsByTaskIds(List<String> taskIds) {
|
||||||
|
|
||||||
throw new UnsupportedOperationException("HistoryLogger is not supposed to delete events");
|
throw new UnsupportedOperationException("HistoryLogger is not supposed to delete events");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,9 +108,7 @@ public class SimpleHistoryServiceImpl implements TaskanaHistory {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
taskanaHistoryEngine.openConnection();
|
taskanaHistoryEngine.openConnection();
|
||||||
|
|
||||||
taskHistoryEventMapper.deleteMultipleByTaskIds(taskIds);
|
taskHistoryEventMapper.deleteMultipleByTaskIds(taskIds);
|
||||||
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
LOGGER.error("Caught exception while trying to delete history events", e);
|
LOGGER.error("Caught exception while trying to delete history events", e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -126,9 +124,7 @@ public class SimpleHistoryServiceImpl implements TaskanaHistory {
|
||||||
resultEvent = taskHistoryEventMapper.findById(historyEventId);
|
resultEvent = taskHistoryEventMapper.findById(historyEventId);
|
||||||
|
|
||||||
if (resultEvent == null) {
|
if (resultEvent == null) {
|
||||||
throw new TaskanaHistoryEventNotFoundException(
|
throw new TaskanaHistoryEventNotFoundException(historyEventId);
|
||||||
historyEventId,
|
|
||||||
String.format("TaskHistoryEvent for id %s was not found", historyEventId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultEvent;
|
return resultEvent;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import pro.taskana.TaskanaEngineConfiguration;
|
import pro.taskana.TaskanaEngineConfiguration;
|
||||||
import pro.taskana.common.api.TaskanaEngine;
|
import pro.taskana.common.api.TaskanaEngine;
|
||||||
import pro.taskana.common.api.TaskanaRole;
|
import pro.taskana.common.api.TaskanaRole;
|
||||||
|
import pro.taskana.common.api.exceptions.MismatchedRoleException;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.internal.persistence.InstantTypeHandler;
|
import pro.taskana.common.internal.persistence.InstantTypeHandler;
|
||||||
import pro.taskana.common.internal.persistence.MapTypeHandler;
|
import pro.taskana.common.internal.persistence.MapTypeHandler;
|
||||||
|
@ -91,9 +92,7 @@ public class TaskanaHistoryEngineImpl implements TaskanaHistoryEngine {
|
||||||
taskanaEngine.getCurrentUserContext().getAccessIds(),
|
taskanaEngine.getCurrentUserContext().getAccessIds(),
|
||||||
Arrays.toString(roles));
|
Arrays.toString(roles));
|
||||||
}
|
}
|
||||||
throw new NotAuthorizedException(
|
throw new MismatchedRoleException(taskanaEngine.getCurrentUserContext().getUserid(), roles);
|
||||||
"current user is not member of role(s) " + Arrays.toString(roles),
|
|
||||||
taskanaEngine.getCurrentUserContext().getUserid());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ public class HistoryCleanupJob extends AbstractTaskanaJob {
|
||||||
LOGGER.info(
|
LOGGER.info(
|
||||||
"Job ended successfully. {} history events deleted.", totalNumberOfHistoryEventsDeleted);
|
"Job ended successfully. {} history events deleted.", totalNumberOfHistoryEventsDeleted);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TaskanaException("Error while processing HistoryCleanupJob.", e);
|
throw new SystemException("Error while processing HistoryCleanupJob.", e);
|
||||||
} finally {
|
} finally {
|
||||||
scheduleNextCleanupJob();
|
scheduleNextCleanupJob();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.client.HttpClientErrorException;
|
import org.springframework.web.client.HttpClientErrorException;
|
||||||
|
import org.springframework.web.client.HttpServerErrorException;
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import pro.taskana.common.rest.models.PageMetadata;
|
import pro.taskana.common.rest.models.PageMetadata;
|
||||||
|
@ -270,11 +271,11 @@ class TaskHistoryEventControllerIntTest {
|
||||||
TASK_HISTORY_EVENT_PAGED_REPRESENTATION_MODEL_TYPE);
|
TASK_HISTORY_EVENT_PAGED_REPRESENTATION_MODEL_TYPE);
|
||||||
|
|
||||||
assertThatThrownBy(httpCall)
|
assertThatThrownBy(httpCall)
|
||||||
.isInstanceOf(HttpClientErrorException.class)
|
.isInstanceOf(HttpServerErrorException.class)
|
||||||
.hasMessageContaining(
|
.hasMessageContaining(
|
||||||
"Unkown request parameters found: [anotherIllegalParam, illegalParam]")
|
"Unkown request parameters found: [anotherIllegalParam, illegalParam]")
|
||||||
.extracting(ex -> ((HttpClientErrorException) ex).getStatusCode())
|
.extracting(ex -> ((HttpServerErrorException) ex).getStatusCode())
|
||||||
.isEqualTo(HttpStatus.BAD_REQUEST);
|
.isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
|
@ -551,11 +551,7 @@ public class TaskanaEngineConfiguration {
|
||||||
if (parts.size() == 2) {
|
if (parts.size() == 2) {
|
||||||
return CustomHoliday.of(Integer.valueOf(parts.get(0)), Integer.valueOf(parts.get(1)));
|
return CustomHoliday.of(Integer.valueOf(parts.get(0)), Integer.valueOf(parts.get(1)));
|
||||||
}
|
}
|
||||||
throw new WrongCustomHolidayFormatException(
|
throw new WrongCustomHolidayFormatException(customHolidayEntry);
|
||||||
String.format(
|
|
||||||
"Wrong format for custom holiday entry %s! The format should be 'dd.MM' "
|
|
||||||
+ "i.e. 01.05 for the first of may. The value will be ignored!",
|
|
||||||
customHolidayEntry));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> splitStringAndTrimElements(String str, String separator) {
|
private List<String> splitStringAndTrimElements(String str, String separator) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package pro.taskana.classification.api;
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationAlreadyExistException;
|
import pro.taskana.classification.api.exceptions.ClassificationAlreadyExistException;
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationInUseException;
|
import pro.taskana.classification.api.exceptions.ClassificationInUseException;
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
||||||
|
import pro.taskana.classification.api.exceptions.MalformedServiceLevelException;
|
||||||
import pro.taskana.classification.api.models.Classification;
|
import pro.taskana.classification.api.models.Classification;
|
||||||
import pro.taskana.common.api.exceptions.ConcurrencyException;
|
import pro.taskana.common.api.exceptions.ConcurrencyException;
|
||||||
import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
||||||
|
@ -87,28 +88,30 @@ public interface ClassificationService {
|
||||||
* @throws NotAuthorizedException if the current user is not member of role BUSINESS_ADMIN or
|
* @throws NotAuthorizedException if the current user is not member of role BUSINESS_ADMIN or
|
||||||
* ADMIN
|
* ADMIN
|
||||||
* @throws DomainNotFoundException if the {@code domain} does not exist in the configuration
|
* @throws DomainNotFoundException if the {@code domain} does not exist in the configuration
|
||||||
* @throws InvalidArgumentException if the {@code serviceLevel} property does not comply with the
|
* @throws MalformedServiceLevelException if the {@code serviceLevel} property does not comply
|
||||||
* ISO 8601 specification
|
* with the ISO 8601 specification
|
||||||
|
* @throws InvalidArgumentException if the {@linkplain Classification} contains invalid properties
|
||||||
*/
|
*/
|
||||||
Classification createClassification(Classification classification)
|
Classification createClassification(Classification classification)
|
||||||
throws ClassificationAlreadyExistException, NotAuthorizedException, DomainNotFoundException,
|
throws ClassificationAlreadyExistException, NotAuthorizedException, DomainNotFoundException,
|
||||||
InvalidArgumentException;
|
InvalidArgumentException, MalformedServiceLevelException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a Classification.
|
* Updates a Classification.
|
||||||
*
|
*
|
||||||
* @param classification the Classification to update
|
* @param classification the Classification to update
|
||||||
* @return the updated Classification.
|
* @return the updated Classification.
|
||||||
* @throws ClassificationNotFoundException if the classification OR it´s parent does not exist.
|
* @throws ClassificationNotFoundException if the classification OR it's parent does not exist.
|
||||||
* @throws NotAuthorizedException if the caller got no ADMIN or BUSINESS_ADMIN permissions.
|
* @throws NotAuthorizedException if the caller got no ADMIN or BUSINESS_ADMIN permissions.
|
||||||
* @throws ConcurrencyException If the classification was modified in the meantime and is not the
|
* @throws ConcurrencyException If the classification was modified in the meantime and is not the
|
||||||
* most up to date anymore.
|
* most up to date anymore.
|
||||||
* @throws InvalidArgumentException if the ServiceLevel property does not comply with the ISO 8601
|
* @throws MalformedServiceLevelException if the {@code serviceLevel} property does not comply
|
||||||
* specification
|
* with the ISO 8601 specification
|
||||||
|
* @throws InvalidArgumentException if the {@linkplain Classification} contains invalid properties
|
||||||
*/
|
*/
|
||||||
Classification updateClassification(Classification classification)
|
Classification updateClassification(Classification classification)
|
||||||
throws ClassificationNotFoundException, NotAuthorizedException, ConcurrencyException,
|
throws ClassificationNotFoundException, NotAuthorizedException, ConcurrencyException,
|
||||||
InvalidArgumentException;
|
InvalidArgumentException, MalformedServiceLevelException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method provides a query builder for querying the database.
|
* This method provides a query builder for querying the database.
|
||||||
|
|
|
@ -1,17 +1,38 @@
|
||||||
package pro.taskana.classification.api.exceptions;
|
package pro.taskana.classification.api.exceptions;
|
||||||
|
|
||||||
import pro.taskana.classification.api.models.Classification;
|
import pro.taskana.classification.api.models.Classification;
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
|
||||||
/** Thrown, when a classification does already exits, but wanted to create with same ID+domain. */
|
/**
|
||||||
|
* This exception is thrown when a {@linkplain Classification} does already exits, but was tried to
|
||||||
|
* be created with the same {@linkplain Classification#getId() id} and {@linkplain
|
||||||
|
* Classification#getDomain() domain}.
|
||||||
|
*/
|
||||||
public class ClassificationAlreadyExistException extends TaskanaException {
|
public class ClassificationAlreadyExistException extends TaskanaException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "CLASSIFICATION_ALREADY_EXISTS";
|
||||||
|
private final String domain;
|
||||||
|
private final String classificationKey;
|
||||||
|
|
||||||
public ClassificationAlreadyExistException(Classification classification) {
|
public ClassificationAlreadyExistException(Classification classification) {
|
||||||
|
this(classification.getKey(), classification.getDomain());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassificationAlreadyExistException(String key, String domain) {
|
||||||
super(
|
super(
|
||||||
"A classification with key '"
|
String.format("A Classification with key '%s' already exists in domain '%s'.", key, domain),
|
||||||
+ classification.getKey()
|
ErrorCode.of(ERROR_KEY, MapCreator.of("classificationKey", key, "domain", domain)));
|
||||||
+ "' already exists in domain '"
|
classificationKey = key;
|
||||||
+ classification.getDomain()
|
this.domain = domain;
|
||||||
+ "'.");
|
}
|
||||||
|
|
||||||
|
public String getDomain() {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassificationKey() {
|
||||||
|
return classificationKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,48 @@
|
||||||
package pro.taskana.classification.api.exceptions;
|
package pro.taskana.classification.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.classification.api.models.Classification;
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.task.api.models.Attachment;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
|
||||||
/** Thrown if a specific task is not in the database. */
|
/**
|
||||||
|
* This exception is thrown when a specific {@linkplain Classification} was tried to be deleted
|
||||||
|
* while still being in use. <br>
|
||||||
|
* This could mean that there are either {@linkplain Task Tasks} or {@linkplain Attachment
|
||||||
|
* Attachments} associated with it.
|
||||||
|
*/
|
||||||
public class ClassificationInUseException extends TaskanaException {
|
public class ClassificationInUseException extends TaskanaException {
|
||||||
|
|
||||||
public ClassificationInUseException(String msg) {
|
public static final String ERROR_KEY = "CLASSIFICATION_IN_USE";
|
||||||
super(msg);
|
private final String classificationKey;
|
||||||
|
private final String domain;
|
||||||
|
|
||||||
|
public ClassificationInUseException(Classification classification, Throwable cause) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"The Classification with id = '%s' and key = '%s' in domain = '%s' "
|
||||||
|
+ "is in use and cannot be deleted. There are either Tasks or "
|
||||||
|
+ "Attachments associated with the Classification.",
|
||||||
|
classification.getId(), classification.getKey(), classification.getDomain()),
|
||||||
|
ErrorCode.of(
|
||||||
|
ERROR_KEY,
|
||||||
|
MapCreator.of(
|
||||||
|
"classificationKey",
|
||||||
|
classification.getKey(),
|
||||||
|
"domain",
|
||||||
|
classification.getDomain())),
|
||||||
|
cause);
|
||||||
|
classificationKey = classification.getKey();
|
||||||
|
domain = classification.getDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassificationInUseException(String msg, Throwable cause) {
|
public String getClassificationKey() {
|
||||||
super(msg, cause);
|
return classificationKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomain() {
|
||||||
|
return domain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,48 @@
|
||||||
package pro.taskana.classification.api.exceptions;
|
package pro.taskana.classification.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.classification.api.models.Classification;
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.NotFoundException;
|
import pro.taskana.common.api.exceptions.NotFoundException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
|
||||||
/** Thrown if a specific task is not in the database. */
|
/** Thrown if a specific {@linkplain Classification} is not in the database. */
|
||||||
public class ClassificationNotFoundException extends NotFoundException {
|
public class ClassificationNotFoundException extends NotFoundException {
|
||||||
|
|
||||||
private String key;
|
public static final String ERROR_KEY_ID = "CLASSIFICATION_WITH_ID_NOT_FOUND";
|
||||||
private String domain;
|
public static final String ERROR_KEY_KEY_DOMAIN = "CLASSIFICATION_WITH_KEY_NOT_FOUND";
|
||||||
|
private final String classificationId;
|
||||||
|
private final String classificationKey;
|
||||||
|
private final String domain;
|
||||||
|
|
||||||
public ClassificationNotFoundException(String id, String msg) {
|
public ClassificationNotFoundException(String classificationId) {
|
||||||
super(id, msg);
|
super(
|
||||||
|
String.format("Classification with id '%s' wasn't found", classificationId),
|
||||||
|
ErrorCode.of(ERROR_KEY_ID, MapCreator.of("classificationId", classificationId)));
|
||||||
|
this.classificationId = classificationId;
|
||||||
|
classificationKey = null;
|
||||||
|
domain = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassificationNotFoundException(String key, String domain, String msg) {
|
public ClassificationNotFoundException(String key, String domain) {
|
||||||
super(null, msg);
|
super(
|
||||||
this.key = key;
|
String.format(
|
||||||
|
"Classification with key '%s' and domain '%s' could not be found", key, domain),
|
||||||
|
ErrorCode.of(
|
||||||
|
ERROR_KEY_KEY_DOMAIN, MapCreator.of("classificationKey", key, "domain", domain)));
|
||||||
|
this.classificationKey = key;
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
|
classificationId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getClassificationKey() {
|
||||||
return key;
|
return classificationKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDomain() {
|
public String getDomain() {
|
||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getClassificationId() {
|
||||||
|
return classificationId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package pro.taskana.classification.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.classification.api.models.Classification;
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when the {@linkplain Classification#getServiceLevel() service level} of
|
||||||
|
* the {@linkplain Classification} has not the required format. The {@linkplain
|
||||||
|
* Classification#getServiceLevel() service level} has to be a positive ISO-8601 duration format and
|
||||||
|
* TASKANA only supports whole days. The format must be 'PnD'.
|
||||||
|
*/
|
||||||
|
public class MalformedServiceLevelException extends TaskanaException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "CLASSIFICATION_SERVICE_LEVEL_MALFORMED";
|
||||||
|
private final String serviceLevel;
|
||||||
|
private final String classificationKey;
|
||||||
|
private final String domain;
|
||||||
|
|
||||||
|
public MalformedServiceLevelException(
|
||||||
|
String serviceLevel, String classificationKey, String domain) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"The provided service level '%s' of the "
|
||||||
|
+ "Classification with key '%s' and domain '%s' is invalid."
|
||||||
|
+ "The service level has to be a positive ISO-8601 duration format. "
|
||||||
|
+ "Furthermore, TASKANA only supports whole days; "
|
||||||
|
+ "the service level must be in the format 'PnD'",
|
||||||
|
serviceLevel, classificationKey, domain),
|
||||||
|
ErrorCode.of(
|
||||||
|
ERROR_KEY,
|
||||||
|
MapCreator.of(
|
||||||
|
"classificationKey",
|
||||||
|
classificationKey,
|
||||||
|
"domain",
|
||||||
|
domain,
|
||||||
|
"serviceLevel",
|
||||||
|
serviceLevel)));
|
||||||
|
this.serviceLevel = serviceLevel;
|
||||||
|
this.classificationKey = classificationKey;
|
||||||
|
this.domain = domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServiceLevel() {
|
||||||
|
return serviceLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassificationKey() {
|
||||||
|
return classificationKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomain() {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
}
|
|
@ -338,7 +338,7 @@ public class ClassificationQueryImpl implements ClassificationQuery {
|
||||||
} catch (PersistenceException e) {
|
} catch (PersistenceException e) {
|
||||||
if (e.getMessage().contains("ERRORCODE=-4470")) {
|
if (e.getMessage().contains("ERRORCODE=-4470")) {
|
||||||
TaskanaRuntimeException ex =
|
TaskanaRuntimeException ex =
|
||||||
new TaskanaRuntimeException(
|
new SystemException(
|
||||||
"The offset beginning was set over the amount of result-rows.", e.getCause());
|
"The offset beginning was set over the amount of result-rows.", e.getCause());
|
||||||
ex.setStackTrace(e.getStackTrace());
|
ex.setStackTrace(e.getStackTrace());
|
||||||
throw ex;
|
throw ex;
|
||||||
|
|
|
@ -18,6 +18,7 @@ import pro.taskana.classification.api.ClassificationService;
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationAlreadyExistException;
|
import pro.taskana.classification.api.exceptions.ClassificationAlreadyExistException;
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationInUseException;
|
import pro.taskana.classification.api.exceptions.ClassificationInUseException;
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
||||||
|
import pro.taskana.classification.api.exceptions.MalformedServiceLevelException;
|
||||||
import pro.taskana.classification.api.models.Classification;
|
import pro.taskana.classification.api.models.Classification;
|
||||||
import pro.taskana.classification.api.models.ClassificationSummary;
|
import pro.taskana.classification.api.models.ClassificationSummary;
|
||||||
import pro.taskana.classification.internal.jobs.ClassificationChangedJob;
|
import pro.taskana.classification.internal.jobs.ClassificationChangedJob;
|
||||||
|
@ -62,19 +63,17 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
public Classification getClassification(String key, String domain)
|
public Classification getClassification(String key, String domain)
|
||||||
throws ClassificationNotFoundException {
|
throws ClassificationNotFoundException {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new ClassificationNotFoundException(
|
throw new ClassificationNotFoundException(null, domain);
|
||||||
null, domain, "Classification for null key and domain " + domain + " was not found.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Classification result = null;
|
Classification result;
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
result = classificationMapper.findByKeyAndDomain(key, domain);
|
result = classificationMapper.findByKeyAndDomain(key, domain);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = classificationMapper.findByKeyAndDomain(key, "");
|
result = classificationMapper.findByKeyAndDomain(key, "");
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new ClassificationNotFoundException(
|
throw new ClassificationNotFoundException(key, domain);
|
||||||
key, domain, "Classification for key = " + key + " and master domain was not found");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -86,15 +85,14 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
@Override
|
@Override
|
||||||
public Classification getClassification(String id) throws ClassificationNotFoundException {
|
public Classification getClassification(String id) throws ClassificationNotFoundException {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
throw new ClassificationNotFoundException(null, "Classification for null id is invalid.");
|
throw new ClassificationNotFoundException(null);
|
||||||
}
|
}
|
||||||
Classification result = null;
|
Classification result;
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
result = classificationMapper.findById(id);
|
result = classificationMapper.findById(id);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new ClassificationNotFoundException(
|
throw new ClassificationNotFoundException(id);
|
||||||
id, "Classification for id " + id + " was not found");
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -110,8 +108,7 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
Classification classification = this.classificationMapper.findById(classificationId);
|
Classification classification = this.classificationMapper.findById(classificationId);
|
||||||
if (classification == null) {
|
if (classification == null) {
|
||||||
throw new ClassificationNotFoundException(
|
throw new ClassificationNotFoundException(classificationId);
|
||||||
classificationId, "The classification \"" + classificationId + "\" wasn't found");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classification.getDomain().equals("")) {
|
if (classification.getDomain().equals("")) {
|
||||||
|
@ -150,13 +147,7 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
|
|
||||||
} catch (PersistenceException e) {
|
} catch (PersistenceException e) {
|
||||||
if (isReferentialIntegrityConstraintViolation(e)) {
|
if (isReferentialIntegrityConstraintViolation(e)) {
|
||||||
throw new ClassificationInUseException(
|
throw new ClassificationInUseException(classification, e);
|
||||||
String.format(
|
|
||||||
"The classification id = \"%s\" and key = \"%s\" in domain = \"%s\" "
|
|
||||||
+ "is in use and cannot be deleted. There are either tasks or "
|
|
||||||
+ "attachments associated with the classification.",
|
|
||||||
classificationId, classification.getKey(), classification.getDomain()),
|
|
||||||
e.getCause());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -173,13 +164,7 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
Classification classification =
|
Classification classification =
|
||||||
this.classificationMapper.findByKeyAndDomain(classificationKey, domain);
|
this.classificationMapper.findByKeyAndDomain(classificationKey, domain);
|
||||||
if (classification == null) {
|
if (classification == null) {
|
||||||
throw new ClassificationNotFoundException(
|
throw new ClassificationNotFoundException(classificationKey, domain);
|
||||||
classificationKey,
|
|
||||||
domain,
|
|
||||||
"The classification \""
|
|
||||||
+ classificationKey
|
|
||||||
+ "\" wasn't found in the domain "
|
|
||||||
+ domain);
|
|
||||||
}
|
}
|
||||||
deleteClassification(classification.getId());
|
deleteClassification(classification.getId());
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -190,13 +175,11 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
@Override
|
@Override
|
||||||
public Classification createClassification(Classification classification)
|
public Classification createClassification(Classification classification)
|
||||||
throws ClassificationAlreadyExistException, NotAuthorizedException, DomainNotFoundException,
|
throws ClassificationAlreadyExistException, NotAuthorizedException, DomainNotFoundException,
|
||||||
InvalidArgumentException {
|
InvalidArgumentException, MalformedServiceLevelException {
|
||||||
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
||||||
if (!taskanaEngine.domainExists(classification.getDomain())
|
if (!taskanaEngine.domainExists(classification.getDomain())
|
||||||
&& !"".equals(classification.getDomain())) {
|
&& !"".equals(classification.getDomain())) {
|
||||||
throw new DomainNotFoundException(
|
throw new DomainNotFoundException(classification.getDomain());
|
||||||
classification.getDomain(),
|
|
||||||
"Domain " + classification.getDomain() + " does not exist in the configuration.");
|
|
||||||
}
|
}
|
||||||
ClassificationImpl classificationImpl;
|
ClassificationImpl classificationImpl;
|
||||||
final boolean isClassificationExisting;
|
final boolean isClassificationExisting;
|
||||||
|
@ -246,14 +229,16 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
@Override
|
@Override
|
||||||
public Classification updateClassification(Classification classification)
|
public Classification updateClassification(Classification classification)
|
||||||
throws NotAuthorizedException, ConcurrencyException, ClassificationNotFoundException,
|
throws NotAuthorizedException, ConcurrencyException, ClassificationNotFoundException,
|
||||||
InvalidArgumentException {
|
InvalidArgumentException, MalformedServiceLevelException {
|
||||||
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
||||||
ClassificationImpl classificationImpl;
|
ClassificationImpl classificationImpl;
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
if (classification.getKey().equals(classification.getParentKey())) {
|
if (classification.getKey().equals(classification.getParentKey())) {
|
||||||
throw new InvalidArgumentException(
|
throw new InvalidArgumentException(
|
||||||
"The classification " + classification.getName() + " has the same key and parentKey");
|
String.format(
|
||||||
|
"The Classification '%s' has the same key and parent key",
|
||||||
|
classification.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
classificationImpl = (ClassificationImpl) classification;
|
classificationImpl = (ClassificationImpl) classification;
|
||||||
|
@ -306,36 +291,26 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
return classification;
|
return classification;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateServiceLevel(String serviceLevel) throws InvalidArgumentException {
|
private static void validateServiceLevel(Classification classification)
|
||||||
|
throws MalformedServiceLevelException {
|
||||||
|
String serviceLevel = classification.getServiceLevel();
|
||||||
Duration duration;
|
Duration duration;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
duration = Duration.parse(serviceLevel);
|
duration = Duration.parse(serviceLevel);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new InvalidArgumentException(
|
throw new MalformedServiceLevelException(
|
||||||
String.format(
|
serviceLevel, classification.getKey(), classification.getDomain());
|
||||||
"Invalid service level %s. "
|
|
||||||
+ "The formats accepted are based on the ISO-8601 duration format "
|
|
||||||
+ "PnDTnHnMn.nS with days considered to be exactly 24 hours. "
|
|
||||||
+ "For example: \"P2D\" represents a period of \"two days.\" ",
|
|
||||||
serviceLevel),
|
|
||||||
e.getCause());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (duration.isNegative()) {
|
if (duration.isNegative()) {
|
||||||
throw new InvalidArgumentException(
|
throw new MalformedServiceLevelException(
|
||||||
String.format(
|
serviceLevel, classification.getKey(), classification.getDomain());
|
||||||
"Invalid service level %s. Taskana does not support a negative service level.",
|
|
||||||
serviceLevel));
|
|
||||||
}
|
}
|
||||||
// check that the duration is based on format PnD, i.e. it must start with a P, end with a D
|
// check that the duration is based on format PnD, i.e. it must start with a P, end with a D
|
||||||
if (!serviceLevel.toLowerCase().startsWith("p") || !serviceLevel.toLowerCase().endsWith("d")) {
|
if (!serviceLevel.toLowerCase().startsWith("p") || !serviceLevel.toLowerCase().endsWith("d")) {
|
||||||
throw new InvalidArgumentException(
|
throw new MalformedServiceLevelException(
|
||||||
String.format(
|
serviceLevel, classification.getKey(), classification.getDomain());
|
||||||
"Invalid service level %s. Taskana only supports service "
|
|
||||||
+ "levels that contain a number of whole days "
|
|
||||||
+ "specified according to the format ''PnD'' where n is the number of days",
|
|
||||||
serviceLevel));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,11 +398,13 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
* Fill missing values and validate classification before saving the classification.
|
* Fill missing values and validate classification before saving the classification.
|
||||||
*
|
*
|
||||||
* @param classification the classification which will be verified.
|
* @param classification the classification which will be verified.
|
||||||
* @throws InvalidArgumentException if the given classification has no key, the service level is
|
* @throws InvalidArgumentException if the given classification has no key, the type is not valid
|
||||||
* null, the type is not valid or the category for the provided type is invalid.
|
* or the category for the provided type is invalid.
|
||||||
|
* @throws MalformedServiceLevelException if the given service level of the {@linkplain
|
||||||
|
* Classification} is invalid
|
||||||
*/
|
*/
|
||||||
private void initDefaultClassificationValues(ClassificationImpl classification)
|
private void initDefaultClassificationValues(ClassificationImpl classification)
|
||||||
throws InvalidArgumentException {
|
throws InvalidArgumentException, MalformedServiceLevelException {
|
||||||
Instant now = Instant.now();
|
Instant now = Instant.now();
|
||||||
if (classification.getId() == null || "".equals(classification.getId())) {
|
if (classification.getId() == null || "".equals(classification.getId())) {
|
||||||
classification.setId(IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_CLASSIFICATION));
|
classification.setId(IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_CLASSIFICATION));
|
||||||
|
@ -448,7 +425,7 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
if (classification.getServiceLevel() == null || classification.getServiceLevel().isEmpty()) {
|
if (classification.getServiceLevel() == null || classification.getServiceLevel().isEmpty()) {
|
||||||
classification.setServiceLevel("P0D");
|
classification.setServiceLevel("P0D");
|
||||||
} else {
|
} else {
|
||||||
validateServiceLevel(classification.getServiceLevel());
|
validateServiceLevel(classification);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (classification.getKey() == null) {
|
if (classification.getKey() == null) {
|
||||||
|
@ -538,10 +515,7 @@ public class ClassificationServiceImpl implements ClassificationService {
|
||||||
Classification oldClassification =
|
Classification oldClassification =
|
||||||
this.getClassification(classificationImpl.getKey(), classificationImpl.getDomain());
|
this.getClassification(classificationImpl.getKey(), classificationImpl.getDomain());
|
||||||
if (!oldClassification.getModified().equals(classificationImpl.getModified())) {
|
if (!oldClassification.getModified().equals(classificationImpl.getModified())) {
|
||||||
throw new ConcurrencyException(
|
throw new ConcurrencyException();
|
||||||
"The current Classification has been modified while editing. "
|
|
||||||
+ "The values can not be updated. Classification "
|
|
||||||
+ classificationImpl.toString());
|
|
||||||
}
|
}
|
||||||
return oldClassification;
|
return oldClassification;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import pro.taskana.common.api.ScheduledJob;
|
import pro.taskana.common.api.ScheduledJob;
|
||||||
import pro.taskana.common.api.TaskanaEngine;
|
import pro.taskana.common.api.TaskanaEngine;
|
||||||
|
import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
import pro.taskana.common.internal.jobs.AbstractTaskanaJob;
|
import pro.taskana.common.internal.jobs.AbstractTaskanaJob;
|
||||||
import pro.taskana.common.internal.transaction.TaskanaTransactionProvider;
|
import pro.taskana.common.internal.transaction.TaskanaTransactionProvider;
|
||||||
|
@ -21,9 +22,9 @@ public class ClassificationChangedJob extends AbstractTaskanaJob {
|
||||||
public static final String PRIORITY_CHANGED = "priorityChanged";
|
public static final String PRIORITY_CHANGED = "priorityChanged";
|
||||||
public static final String SERVICE_LEVEL_CHANGED = "serviceLevelChanged";
|
public static final String SERVICE_LEVEL_CHANGED = "serviceLevelChanged";
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(ClassificationChangedJob.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(ClassificationChangedJob.class);
|
||||||
private String classificationId;
|
private final String classificationId;
|
||||||
private boolean priorityChanged;
|
private final boolean priorityChanged;
|
||||||
private boolean serviceLevelChanged;
|
private final boolean serviceLevelChanged;
|
||||||
|
|
||||||
public ClassificationChangedJob(
|
public ClassificationChangedJob(
|
||||||
TaskanaEngine engine, TaskanaTransactionProvider<Object> txProvider, ScheduledJob job) {
|
TaskanaEngine engine, TaskanaTransactionProvider<Object> txProvider, ScheduledJob job) {
|
||||||
|
@ -46,7 +47,7 @@ public class ClassificationChangedJob extends AbstractTaskanaJob {
|
||||||
}
|
}
|
||||||
LOGGER.info("ClassificationChangedJob ended successfully.");
|
LOGGER.info("ClassificationChangedJob ended successfully.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TaskanaException("Error while processing ClassificationChangedJob.", e);
|
throw new SystemException("Error while processing ClassificationChangedJob.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,11 +83,11 @@ public interface TaskanaEngine {
|
||||||
void setConnectionManagementMode(ConnectionManagementMode mode);
|
void setConnectionManagementMode(ConnectionManagementMode mode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the connection to be used by taskana in mode CONNECTION_MANAGED_EXTERNALLY. If this Api is
|
* Set the connection to be used by TASKANA in mode {@linkplain
|
||||||
* called, taskana uses the connection passed by the client for all subsequent API calls until the
|
* ConnectionManagementMode#EXPLICIT}. If this Api is called, taskana uses the connection passed
|
||||||
* client resets this connection. Control over commit and rollback of the connection is the
|
* by the client for all subsequent API calls until the client resets this connection. Control
|
||||||
* responsibility of the client. In order to close the connection, closeConnection() or
|
* over commit and rollback of the connection is the responsibility of the client. In order to
|
||||||
* setConnection(null) has to be called.
|
* close the connection, closeConnection() or setConnection(null) has to be called.
|
||||||
*
|
*
|
||||||
* @param connection - The java.sql.Connection that is controlled by the client
|
* @param connection - The java.sql.Connection that is controlled by the client
|
||||||
* @throws SQLException if a database access error occurs
|
* @throws SQLException if a database access error occurs
|
||||||
|
@ -126,7 +126,6 @@ public interface TaskanaEngine {
|
||||||
*/
|
*/
|
||||||
<T> T runAsAdmin(Supplier<T> supplier);
|
<T> T runAsAdmin(Supplier<T> supplier);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the CurrentUserContext class.
|
* Returns the CurrentUserContext class.
|
||||||
*
|
*
|
||||||
|
|
|
@ -83,5 +83,4 @@ public interface InternalTaskanaEngine {
|
||||||
* @return the CreateTaskPreprocessorManager instance.
|
* @return the CreateTaskPreprocessorManager instance.
|
||||||
*/
|
*/
|
||||||
CreateTaskPreprocessorManager getCreateTaskPreprocessorManager();
|
CreateTaskPreprocessorManager getCreateTaskPreprocessorManager();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,9 @@ import pro.taskana.common.api.TaskanaRole;
|
||||||
import pro.taskana.common.api.WorkingDaysToDaysConverter;
|
import pro.taskana.common.api.WorkingDaysToDaysConverter;
|
||||||
import pro.taskana.common.api.exceptions.AutocommitFailedException;
|
import pro.taskana.common.api.exceptions.AutocommitFailedException;
|
||||||
import pro.taskana.common.api.exceptions.ConnectionNotSetException;
|
import pro.taskana.common.api.exceptions.ConnectionNotSetException;
|
||||||
|
import pro.taskana.common.api.exceptions.MismatchedRoleException;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.api.exceptions.SystemException;
|
import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
|
||||||
import pro.taskana.common.api.security.CurrentUserContext;
|
import pro.taskana.common.api.security.CurrentUserContext;
|
||||||
import pro.taskana.common.api.security.UserPrincipal;
|
import pro.taskana.common.api.security.UserPrincipal;
|
||||||
import pro.taskana.common.internal.configuration.DB;
|
import pro.taskana.common.internal.configuration.DB;
|
||||||
|
@ -239,23 +239,16 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
||||||
currentUserContext.getAccessIds(),
|
currentUserContext.getAccessIds(),
|
||||||
rolesAsString);
|
rolesAsString);
|
||||||
}
|
}
|
||||||
throw new NotAuthorizedException(
|
throw new MismatchedRoleException(currentUserContext.getUserid(), roles);
|
||||||
"current user is not member of role(s) " + Arrays.toString(roles),
|
|
||||||
currentUserContext.getUserid());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public CurrentUserContext getCurrentUserContext() {
|
|
||||||
return currentUserContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T runAsAdmin(Supplier<T> supplier) {
|
public <T> T runAsAdmin(Supplier<T> supplier) {
|
||||||
|
|
||||||
String adminName =
|
String adminName =
|
||||||
this.getConfiguration().getRoleMap().get(TaskanaRole.ADMIN).stream()
|
this.getConfiguration().getRoleMap().get(TaskanaRole.ADMIN).stream()
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new TaskanaRuntimeException("There is no admin configured"));
|
.orElseThrow(() -> new SystemException("There is no admin configured"));
|
||||||
|
|
||||||
Subject subject = new Subject();
|
Subject subject = new Subject();
|
||||||
subject.getPrincipals().add(new UserPrincipal(adminName));
|
subject.getPrincipals().add(new UserPrincipal(adminName));
|
||||||
|
@ -263,6 +256,11 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
||||||
return Subject.doAs(subject, (PrivilegedAction<T>) supplier::get);
|
return Subject.doAs(subject, (PrivilegedAction<T>) supplier::get);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CurrentUserContext getCurrentUserContext() {
|
||||||
|
return currentUserContext;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method creates the sqlSessionManager of myBatis. It integrates all the SQL mappers and
|
* This method creates the sqlSessionManager of myBatis. It integrates all the SQL mappers and
|
||||||
* sets the databaseId attribute.
|
* sets the databaseId attribute.
|
||||||
|
|
|
@ -1,10 +1,27 @@
|
||||||
package pro.taskana.spi.history.api.exceptions;
|
package pro.taskana.spi.history.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.NotFoundException;
|
import pro.taskana.common.api.exceptions.NotFoundException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.spi.history.api.events.task.TaskHistoryEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when the {@linkplain TaskHistoryEvent} with the specified {@linkplain
|
||||||
|
* TaskHistoryEvent#getId() id} was not found.
|
||||||
|
*/
|
||||||
public class TaskanaHistoryEventNotFoundException extends NotFoundException {
|
public class TaskanaHistoryEventNotFoundException extends NotFoundException {
|
||||||
|
|
||||||
public TaskanaHistoryEventNotFoundException(String id, String msg) {
|
public static final String ERROR_KEY = "HISTORY_EVENT_NOT_FOUND";
|
||||||
super(id, msg);
|
private final String historyEventId;
|
||||||
|
|
||||||
|
public TaskanaHistoryEventNotFoundException(String historyEventId) {
|
||||||
|
super(
|
||||||
|
String.format("TaskHistoryEvent with id '%s' was not found", historyEventId),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("historyEventId", historyEventId)));
|
||||||
|
this.historyEventId = historyEventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHistoryEventId() {
|
||||||
|
return historyEventId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ public interface TaskService {
|
||||||
*
|
*
|
||||||
* @param taskId id of the task which should be unclaimed.
|
* @param taskId id of the task which should be unclaimed.
|
||||||
* @return updated unclaimed task
|
* @return updated unclaimed task
|
||||||
* @throws TaskNotFoundException if the task can´t be found or does not exist
|
* @throws TaskNotFoundException if the task can't be found or does not exist
|
||||||
* @throws InvalidStateException if the task is already in an end state.
|
* @throws InvalidStateException if the task is already in an end state.
|
||||||
* @throws InvalidOwnerException if the task is claimed by another user.
|
* @throws InvalidOwnerException if the task is claimed by another user.
|
||||||
* @throws NotAuthorizedException if the current user has no read permission for the workbasket
|
* @throws NotAuthorizedException if the current user has no read permission for the workbasket
|
||||||
|
@ -75,7 +75,7 @@ public interface TaskService {
|
||||||
*
|
*
|
||||||
* @param taskId id of the task which should be unclaimed.
|
* @param taskId id of the task which should be unclaimed.
|
||||||
* @return updated unclaimed task
|
* @return updated unclaimed task
|
||||||
* @throws TaskNotFoundException if the task can´t be found or does not exist
|
* @throws TaskNotFoundException if the task can't be found or does not exist
|
||||||
* @throws InvalidStateException if the task is already in an end state.
|
* @throws InvalidStateException if the task is already in an end state.
|
||||||
* @throws InvalidOwnerException if forceCancel is false and the task is claimed by another user.
|
* @throws InvalidOwnerException if forceCancel is false and the task is claimed by another user.
|
||||||
* @throws NotAuthorizedException if the current user has no read permission for the workbasket
|
* @throws NotAuthorizedException if the current user has no read permission for the workbasket
|
||||||
|
@ -91,8 +91,8 @@ public interface TaskService {
|
||||||
*
|
*
|
||||||
* @param taskId - Id of the Task which should be completed.
|
* @param taskId - Id of the Task which should be completed.
|
||||||
* @return Task - updated task after completion.
|
* @return Task - updated task after completion.
|
||||||
* @throws InvalidStateException if Task wasn´t claimed before.
|
* @throws InvalidStateException if Task wasn't claimed before.
|
||||||
* @throws TaskNotFoundException if the given Task can´t be found in DB.
|
* @throws TaskNotFoundException if the given Task can't be found in DB.
|
||||||
* @throws InvalidOwnerException if current user is not the task-owner or administrator.
|
* @throws InvalidOwnerException if current user is not the task-owner or administrator.
|
||||||
* @throws NotAuthorizedException if the current user has no read permission for the workbasket
|
* @throws NotAuthorizedException if the current user has no read permission for the workbasket
|
||||||
* the task is in
|
* the task is in
|
||||||
|
@ -107,8 +107,8 @@ public interface TaskService {
|
||||||
*
|
*
|
||||||
* @param taskId - Id of the Task which should be completed.
|
* @param taskId - Id of the Task which should be completed.
|
||||||
* @return Task - updated task after completion.
|
* @return Task - updated task after completion.
|
||||||
* @throws InvalidStateException if Task wasn´t claimed before.
|
* @throws InvalidStateException if Task wasn't claimed before.
|
||||||
* @throws TaskNotFoundException if the given Task can´t be found in DB.
|
* @throws TaskNotFoundException if the given Task can't be found in DB.
|
||||||
* @throws InvalidOwnerException if current user is not the task-owner or administrator.
|
* @throws InvalidOwnerException if current user is not the task-owner or administrator.
|
||||||
* @throws NotAuthorizedException if the current user has no read permission for the workbasket
|
* @throws NotAuthorizedException if the current user has no read permission for the workbasket
|
||||||
* the task is in
|
* the task is in
|
||||||
|
|
|
@ -13,7 +13,7 @@ public enum TaskState {
|
||||||
public static final TaskState[] END_STATES = {COMPLETED, CANCELLED, TERMINATED};
|
public static final TaskState[] END_STATES = {COMPLETED, CANCELLED, TERMINATED};
|
||||||
|
|
||||||
public boolean in(TaskState... states) {
|
public boolean in(TaskState... states) {
|
||||||
return Arrays.stream(states).anyMatch(state -> state == this);
|
return Arrays.asList(states).contains(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEndState() {
|
public boolean isEndState() {
|
||||||
|
|
|
@ -1,15 +1,40 @@
|
||||||
package pro.taskana.task.api.exceptions;
|
package pro.taskana.task.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.task.api.models.Attachment;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown, when an attachment should be inserted to the DB, but it does already exist.<br>
|
* This exception is thrown when an {@linkplain Attachment} should be inserted to the DB, but it
|
||||||
* This may happen when a not inserted attachment with ID will be added twice on a task. This can´t
|
* does already exist. <br>
|
||||||
* be happen it the correct Task-Methods will be used instead the List ones.
|
* This may happen when a not inserted {@linkplain Attachment} with the same {@linkplain
|
||||||
|
* Attachment#getId() id} will be added twice on a {@linkplain Task}. This can't happen if the
|
||||||
|
* correct {@linkplain Task}-Methods will be used instead of the List ones.
|
||||||
*/
|
*/
|
||||||
public class AttachmentPersistenceException extends TaskanaException {
|
public class AttachmentPersistenceException extends TaskanaException {
|
||||||
|
public static final String ERROR_KEY = "ATTACHMENT_ALREADY_EXISTS";
|
||||||
|
private final String attachmentId;
|
||||||
|
private final String taskId;
|
||||||
|
|
||||||
public AttachmentPersistenceException(String msg, Throwable cause) {
|
public AttachmentPersistenceException(String attachmentId, String taskId, Throwable cause) {
|
||||||
super(msg, cause);
|
super(
|
||||||
|
String.format(
|
||||||
|
"Cannot insert Attachment with id '%s' for Task with id '%s' "
|
||||||
|
+ "because it already exists.",
|
||||||
|
attachmentId, taskId),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("attachmentId", attachmentId, "taskId", taskId)),
|
||||||
|
cause);
|
||||||
|
this.attachmentId = attachmentId;
|
||||||
|
this.taskId = taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAttachmentId() {
|
||||||
|
return attachmentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTaskId() {
|
||||||
|
return taskId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package pro.taskana.task.api.exceptions;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.task.api.CallbackState;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
import pro.taskana.task.internal.models.MinimalTaskSummary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when the {@linkplain MinimalTaskSummary#getCallbackState() callback
|
||||||
|
* state} of the {@linkplain Task} doesn't allow the requested operation.
|
||||||
|
*/
|
||||||
|
public class InvalidCallbackStateException extends InvalidStateException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "TASK_INVALID_CALLBACK_STATE";
|
||||||
|
private final String taskId;
|
||||||
|
private final CallbackState taskCallbackState;
|
||||||
|
private final CallbackState[] requiredCallbackStates;
|
||||||
|
|
||||||
|
public InvalidCallbackStateException(
|
||||||
|
String taskId, CallbackState taskCallbackState, CallbackState... requiredCallbackStates) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"Expected callback state of Task with id '%s' to be: '%s', but found '%s'",
|
||||||
|
taskId, Arrays.toString(requiredCallbackStates), taskCallbackState),
|
||||||
|
ErrorCode.of(
|
||||||
|
ERROR_KEY,
|
||||||
|
MapCreator.of(
|
||||||
|
"taskId",
|
||||||
|
taskId,
|
||||||
|
"taskCallbackState",
|
||||||
|
taskCallbackState,
|
||||||
|
"requiredCallbackStates",
|
||||||
|
requiredCallbackStates)));
|
||||||
|
this.taskId = taskId;
|
||||||
|
this.taskCallbackState = taskCallbackState;
|
||||||
|
this.requiredCallbackStates = requiredCallbackStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallbackState getTaskCallbackState() {
|
||||||
|
return taskCallbackState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallbackState[] getRequiredCallbackStates() {
|
||||||
|
return requiredCallbackStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTaskId() {
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,29 @@
|
||||||
package pro.taskana.task.api.exceptions;
|
package pro.taskana.task.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
|
||||||
/** This exception is thrown when the task state doesn't allow the requested operation. */
|
/** This exception is thrown when the current user is not the owner of the {@linkplain Task}. */
|
||||||
public class InvalidOwnerException extends TaskanaException {
|
public class InvalidOwnerException extends TaskanaException {
|
||||||
|
public static final String ERROR_KEY = "TASK_INVALID_OWNER";
|
||||||
|
private final String taskId;
|
||||||
|
private final String currentUserId;
|
||||||
|
|
||||||
public InvalidOwnerException(String msg) {
|
public InvalidOwnerException(String currentUserId, String taskId) {
|
||||||
super(msg);
|
super(
|
||||||
|
String.format("User '%s' is not owner of Task '%s'.", currentUserId, taskId),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("taskId", taskId, "currentUserId", currentUserId)));
|
||||||
|
this.taskId = taskId;
|
||||||
|
this.currentUserId = currentUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTaskId() {
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentUserId() {
|
||||||
|
return currentUserId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package pro.taskana.task.api.exceptions;
|
package pro.taskana.task.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
|
||||||
/** This exception is thrown when the task state doesn't allow the requested operation. */
|
/** This exception is thrown when the current state doesn't allow the requested operation. */
|
||||||
public class InvalidStateException extends TaskanaException {
|
public class InvalidStateException extends TaskanaException {
|
||||||
|
|
||||||
public InvalidStateException(String msg) {
|
protected InvalidStateException(String msg, ErrorCode errorCode) {
|
||||||
super(msg);
|
super(msg, errorCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package pro.taskana.task.api.exceptions;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.task.api.TaskState;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when the {@linkplain Task#getState() state} of the {@linkplain Task}
|
||||||
|
* doesn't allow the requested operation.
|
||||||
|
*/
|
||||||
|
public class InvalidTaskStateException extends InvalidStateException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "TASK_INVALID_STATE";
|
||||||
|
private final String taskId;
|
||||||
|
private final TaskState taskState;
|
||||||
|
private final TaskState[] requiredTaskStates;
|
||||||
|
|
||||||
|
public InvalidTaskStateException(
|
||||||
|
String taskId, TaskState taskState, TaskState... requiredTaskStates) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"Task with id '%s' is in state: '%s', but must be in one of these states: '%s'",
|
||||||
|
taskId, taskState, Arrays.toString(requiredTaskStates)),
|
||||||
|
ErrorCode.of(
|
||||||
|
ERROR_KEY,
|
||||||
|
MapCreator.of(
|
||||||
|
"taskId",
|
||||||
|
taskId,
|
||||||
|
"taskState",
|
||||||
|
taskState,
|
||||||
|
"requiredTaskStates",
|
||||||
|
requiredTaskStates)));
|
||||||
|
|
||||||
|
this.taskId = taskId;
|
||||||
|
this.taskState = taskState;
|
||||||
|
this.requiredTaskStates = requiredTaskStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTaskId() {
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskState getTaskState() {
|
||||||
|
return taskState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskState[] getRequiredTaskStates() {
|
||||||
|
return requiredTaskStates;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package pro.taskana.task.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.task.api.models.TaskComment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when the current user is not the creator of the {@linkplain TaskComment}
|
||||||
|
* it tries to modify.
|
||||||
|
*/
|
||||||
|
public class MismatchedTaskCommentCreatorException extends NotAuthorizedException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "TASK_COMMENT_CREATOR_MISMATCHED";
|
||||||
|
private final String currentUserId;
|
||||||
|
private final String taskCommentId;
|
||||||
|
|
||||||
|
public MismatchedTaskCommentCreatorException(String currentUserId, String taskCommentId) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"Not authorized. Current user '%s' is not the creator of TaskComment with id '%s'.",
|
||||||
|
currentUserId, taskCommentId),
|
||||||
|
ErrorCode.of(
|
||||||
|
ERROR_KEY,
|
||||||
|
MapCreator.of("currentUserId", currentUserId, "taskCommentId", taskCommentId)));
|
||||||
|
|
||||||
|
this.currentUserId = currentUserId;
|
||||||
|
this.taskCommentId = taskCommentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentUserId() {
|
||||||
|
return currentUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTaskCommentId() {
|
||||||
|
return taskCommentId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,27 @@
|
||||||
package pro.taskana.task.api.exceptions;
|
package pro.taskana.task.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when a Task is going to be created, but a Task with the same ID does already exist. The
|
* This exception is thrown when a {@linkplain Task} is going to be created, but a {@linkplain Task}
|
||||||
* Task ID should be unique.
|
* with the same {@linkplain Task#getExternalId() external id} does already exist.
|
||||||
*/
|
*/
|
||||||
public class TaskAlreadyExistException extends TaskanaException {
|
public class TaskAlreadyExistException extends TaskanaException {
|
||||||
|
|
||||||
public TaskAlreadyExistException(String id) {
|
public static final String ERROR_KEY = "TASK_ALREADY_EXISTS";
|
||||||
super(id);
|
private final String externalId;
|
||||||
|
|
||||||
|
public TaskAlreadyExistException(String externalId) {
|
||||||
|
super(
|
||||||
|
String.format("Task with external id '%s' already exists", externalId),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("externalTaskId", externalId)));
|
||||||
|
this.externalId = externalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExternalId() {
|
||||||
|
return externalId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
package pro.taskana.task.api.exceptions;
|
package pro.taskana.task.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.NotFoundException;
|
import pro.taskana.common.api.exceptions.NotFoundException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.task.api.models.TaskComment;
|
||||||
|
|
||||||
/** This exception will be thrown if a specific task comment is not in the database. */
|
/** This exception is thrown when a specific {@linkplain TaskComment} is not in the database. */
|
||||||
public class TaskCommentNotFoundException extends NotFoundException {
|
public class TaskCommentNotFoundException extends NotFoundException {
|
||||||
|
|
||||||
public TaskCommentNotFoundException(String id, String msg) {
|
public static final String ERROR_KEY = "TASK_COMMENT_NOT_FOUND";
|
||||||
super(id, msg);
|
private final String taskCommentId;
|
||||||
|
|
||||||
|
public TaskCommentNotFoundException(String taskCommentId) {
|
||||||
|
super(
|
||||||
|
String.format("TaskComment with id '%s' was not found", taskCommentId),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("taskCommentId", taskCommentId)));
|
||||||
|
this.taskCommentId = taskCommentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTaskCommentId() {
|
||||||
|
return taskCommentId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
package pro.taskana.task.api.exceptions;
|
package pro.taskana.task.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.NotFoundException;
|
import pro.taskana.common.api.exceptions.NotFoundException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
|
||||||
/** This exception will be thrown if a specific task is not in the database. */
|
/** This exception is thrown when a specific {@linkplain Task} is not in the database. */
|
||||||
public class TaskNotFoundException extends NotFoundException {
|
public class TaskNotFoundException extends NotFoundException {
|
||||||
|
|
||||||
public TaskNotFoundException(String id, String msg) {
|
public static final String ERROR_KEY = "TASK_NOT_FOUND";
|
||||||
super(id, msg);
|
private final String taskId;
|
||||||
|
|
||||||
|
public TaskNotFoundException(String taskId) {
|
||||||
|
super(
|
||||||
|
String.format("Task with id '%s' was not found.", taskId),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("taskId", taskId)));
|
||||||
|
this.taskId = taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTaskId() {
|
||||||
|
return taskId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
package pro.taskana.task.api.exceptions;
|
|
||||||
|
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
|
||||||
|
|
||||||
/** This exception will be thrown if a specific task is not in the database. */
|
|
||||||
public class UpdateFailedException extends TaskanaException {
|
|
||||||
|
|
||||||
public UpdateFailedException(String msg) {
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -119,7 +119,7 @@ public interface Task extends TaskSummary {
|
||||||
/**
|
/**
|
||||||
* Return the attachments for this task. <br>
|
* Return the attachments for this task. <br>
|
||||||
* Do not use List.add()/addAll() for adding Elements, because it can cause redundant data. Use
|
* Do not use List.add()/addAll() for adding Elements, because it can cause redundant data. Use
|
||||||
* addAttachment(). Clear() and remove() can be used, because it´s a controllable change.
|
* addAttachment(). Clear() and remove() can be used, because it's a controllable change.
|
||||||
*
|
*
|
||||||
* @return the {@link List list} of {@link Attachment attachments} for this task
|
* @return the {@link List list} of {@link Attachment attachments} for this task
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -14,7 +14,6 @@ import org.slf4j.LoggerFactory;
|
||||||
import pro.taskana.classification.api.ClassificationService;
|
import pro.taskana.classification.api.ClassificationService;
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
||||||
import pro.taskana.classification.api.models.ClassificationSummary;
|
import pro.taskana.classification.api.models.ClassificationSummary;
|
||||||
import pro.taskana.common.api.BulkOperationResults;
|
|
||||||
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.internal.util.IdGenerator;
|
import pro.taskana.common.internal.util.IdGenerator;
|
||||||
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
|
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
|
||||||
|
@ -36,46 +35,6 @@ public class AttachmentHandler {
|
||||||
this.classificationService = classificationService;
|
this.classificationService = classificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Attachment> augmentAttachmentsByClassification(
|
|
||||||
List<AttachmentImpl> attachmentImpls, BulkOperationResults<String, Exception> bulkLog) {
|
|
||||||
List<Attachment> result = new ArrayList<>();
|
|
||||||
if (attachmentImpls == null || attachmentImpls.isEmpty()) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
List<ClassificationSummary> classifications =
|
|
||||||
classificationService
|
|
||||||
.createClassificationQuery()
|
|
||||||
.idIn(
|
|
||||||
attachmentImpls.stream()
|
|
||||||
.map(t -> t.getClassificationSummary().getId())
|
|
||||||
.distinct()
|
|
||||||
.toArray(String[]::new))
|
|
||||||
.list();
|
|
||||||
for (AttachmentImpl att : attachmentImpls) {
|
|
||||||
ClassificationSummary classificationSummary =
|
|
||||||
classifications.stream()
|
|
||||||
.filter(cl -> cl.getId().equals(att.getClassificationSummary().getId()))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
if (classificationSummary == null) {
|
|
||||||
String id = att.getClassificationSummary().getId();
|
|
||||||
bulkLog.addError(
|
|
||||||
att.getClassificationSummary().getId(),
|
|
||||||
new ClassificationNotFoundException(
|
|
||||||
id,
|
|
||||||
String.format(
|
|
||||||
"When processing task updates due to change "
|
|
||||||
+ "of classification, the classification with id %s was not found",
|
|
||||||
id)));
|
|
||||||
} else {
|
|
||||||
att.setClassificationSummary(classificationSummary);
|
|
||||||
result.add(att);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void insertAndDeleteAttachmentsOnTaskUpdate(TaskImpl newTaskImpl, TaskImpl oldTaskImpl)
|
void insertAndDeleteAttachmentsOnTaskUpdate(TaskImpl newTaskImpl, TaskImpl oldTaskImpl)
|
||||||
throws AttachmentPersistenceException, InvalidArgumentException,
|
throws AttachmentPersistenceException, InvalidArgumentException,
|
||||||
ClassificationNotFoundException {
|
ClassificationNotFoundException {
|
||||||
|
@ -113,11 +72,7 @@ public class AttachmentHandler {
|
||||||
attachmentImpl);
|
attachmentImpl);
|
||||||
}
|
}
|
||||||
} catch (PersistenceException e) {
|
} catch (PersistenceException e) {
|
||||||
throw new AttachmentPersistenceException(
|
throw new AttachmentPersistenceException(attachmentImpl.getId(), task.getId(), e);
|
||||||
String.format(
|
|
||||||
"Cannot insert the Attachement %s for Task %s because it already exists.",
|
|
||||||
attachmentImpl.getId(), task.getId()),
|
|
||||||
e.getCause());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,11 +154,7 @@ public class AttachmentHandler {
|
||||||
attachmentImpl);
|
attachmentImpl);
|
||||||
}
|
}
|
||||||
} catch (PersistenceException e) {
|
} catch (PersistenceException e) {
|
||||||
throw new AttachmentPersistenceException(
|
throw new AttachmentPersistenceException(attachmentImpl.getId(), newTaskImpl.getId(), e);
|
||||||
String.format(
|
|
||||||
"Cannot insert the Attachement %s for Task %s because it already exists.",
|
|
||||||
attachmentImpl.getId(), newTaskImpl.getId()),
|
|
||||||
e.getCause());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ import pro.taskana.common.api.WorkingDaysToDaysConverter;
|
||||||
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
import pro.taskana.common.internal.InternalTaskanaEngine;
|
import pro.taskana.common.internal.InternalTaskanaEngine;
|
||||||
import pro.taskana.task.api.exceptions.UpdateFailedException;
|
import pro.taskana.common.internal.util.Pair;
|
||||||
import pro.taskana.task.api.models.Attachment;
|
import pro.taskana.task.api.models.Attachment;
|
||||||
import pro.taskana.task.api.models.AttachmentSummary;
|
import pro.taskana.task.api.models.AttachmentSummary;
|
||||||
import pro.taskana.task.api.models.Task;
|
import pro.taskana.task.api.models.Task;
|
||||||
|
@ -38,6 +38,7 @@ class ServiceLevelHandler {
|
||||||
private final TaskMapper taskMapper;
|
private final TaskMapper taskMapper;
|
||||||
private final AttachmentMapper attachmentMapper;
|
private final AttachmentMapper attachmentMapper;
|
||||||
private final WorkingDaysToDaysConverter converter;
|
private final WorkingDaysToDaysConverter converter;
|
||||||
|
private final TaskServiceImpl taskServiceImpl;
|
||||||
|
|
||||||
ServiceLevelHandler(
|
ServiceLevelHandler(
|
||||||
InternalTaskanaEngine taskanaEngine,
|
InternalTaskanaEngine taskanaEngine,
|
||||||
|
@ -47,6 +48,7 @@ class ServiceLevelHandler {
|
||||||
this.taskMapper = taskMapper;
|
this.taskMapper = taskMapper;
|
||||||
this.attachmentMapper = attachmentMapper;
|
this.attachmentMapper = attachmentMapper;
|
||||||
converter = taskanaEngine.getEngine().getWorkingDaysToDaysConverter();
|
converter = taskanaEngine.getEngine().getWorkingDaysToDaysConverter();
|
||||||
|
taskServiceImpl = (TaskServiceImpl) taskanaEngine.getEngine().getTaskService();
|
||||||
}
|
}
|
||||||
|
|
||||||
// use the same algorithm as setPlannedPropertyOfTasksImpl to refresh
|
// use the same algorithm as setPlannedPropertyOfTasksImpl to refresh
|
||||||
|
@ -351,7 +353,7 @@ class ServiceLevelHandler {
|
||||||
private BulkOperationResults<String, TaskanaException>
|
private BulkOperationResults<String, TaskanaException>
|
||||||
updateDuePropertyOfTasksWithIdenticalDueDate(
|
updateDuePropertyOfTasksWithIdenticalDueDate(
|
||||||
InstantDurationHolder durationHolder, List<TaskDuration> taskDurationList) {
|
InstantDurationHolder durationHolder, List<TaskDuration> taskDurationList) {
|
||||||
BulkLog bulkLog = new BulkLog();
|
final BulkLog bulkLog = new BulkLog();
|
||||||
TaskImpl referenceTask = new TaskImpl();
|
TaskImpl referenceTask = new TaskImpl();
|
||||||
referenceTask.setPlanned(durationHolder.getPlanned());
|
referenceTask.setPlanned(durationHolder.getPlanned());
|
||||||
referenceTask.setModified(Instant.now());
|
referenceTask.setModified(Instant.now());
|
||||||
|
@ -359,61 +361,30 @@ class ServiceLevelHandler {
|
||||||
getFollowingWorkingDays(referenceTask.getPlanned(), durationHolder.getDuration()));
|
getFollowingWorkingDays(referenceTask.getPlanned(), durationHolder.getDuration()));
|
||||||
List<String> taskIdsToUpdate =
|
List<String> taskIdsToUpdate =
|
||||||
taskDurationList.stream().map(TaskDuration::getTaskId).collect(Collectors.toList());
|
taskDurationList.stream().map(TaskDuration::getTaskId).collect(Collectors.toList());
|
||||||
long numTasksUpdated = taskMapper.updateTaskDueDates(taskIdsToUpdate, referenceTask);
|
Pair<List<MinimalTaskSummary>, BulkLog> existingAndAuthorizedTasks =
|
||||||
if (numTasksUpdated != taskIdsToUpdate.size()) {
|
taskServiceImpl.getMinimalTaskSummaries(taskIdsToUpdate);
|
||||||
BulkLog checkResult =
|
bulkLog.addAllErrors(existingAndAuthorizedTasks.getRight());
|
||||||
checkResultsOfTasksUpdateAndAddErrorsToBulkLog(
|
|
||||||
taskIdsToUpdate, referenceTask, numTasksUpdated);
|
taskMapper.updateTaskDueDates(existingAndAuthorizedTasks.getLeft(), referenceTask);
|
||||||
bulkLog.addAllErrors(checkResult);
|
|
||||||
}
|
|
||||||
return bulkLog;
|
return bulkLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BulkLog updatePlannedPropertyOfAffectedTasks(
|
private BulkLog updatePlannedPropertyOfAffectedTasks(
|
||||||
Instant planned, Map<Duration, List<String>> durationToTaskIdsMap) {
|
Instant planned, Map<Duration, List<String>> taskIdsByDueDuration) {
|
||||||
BulkLog bulkLog = new BulkLog();
|
final BulkLog bulkLog = new BulkLog();
|
||||||
TaskImpl referenceTask = new TaskImpl();
|
TaskImpl referenceTask = new TaskImpl();
|
||||||
referenceTask.setPlanned(planned);
|
referenceTask.setPlanned(planned);
|
||||||
referenceTask.setModified(Instant.now());
|
referenceTask.setModified(Instant.now());
|
||||||
for (Map.Entry<Duration, List<String>> entry : durationToTaskIdsMap.entrySet()) {
|
|
||||||
List<String> taskIdsToUpdate = entry.getValue();
|
|
||||||
referenceTask.setDue(getFollowingWorkingDays(referenceTask.getPlanned(), entry.getKey()));
|
|
||||||
long numTasksUpdated = taskMapper.updateTaskDueDates(taskIdsToUpdate, referenceTask);
|
|
||||||
if (numTasksUpdated != taskIdsToUpdate.size()) {
|
|
||||||
BulkLog checkResult =
|
|
||||||
checkResultsOfTasksUpdateAndAddErrorsToBulkLog(
|
|
||||||
taskIdsToUpdate, referenceTask, numTasksUpdated);
|
|
||||||
bulkLog.addAllErrors(checkResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bulkLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BulkLog checkResultsOfTasksUpdateAndAddErrorsToBulkLog(
|
taskIdsByDueDuration.forEach(
|
||||||
List<String> taskIdsToUpdate, TaskImpl referenceTask, long numTasksUpdated) {
|
(duration, taskIds) -> {
|
||||||
BulkLog bulkLog = new BulkLog();
|
referenceTask.setDue(getFollowingWorkingDays(planned, duration));
|
||||||
long numErrors = taskIdsToUpdate.size() - numTasksUpdated;
|
Pair<List<MinimalTaskSummary>, BulkLog> existingAndAuthorizedTasks =
|
||||||
long numErrorsLogged = 0;
|
taskServiceImpl.getMinimalTaskSummaries(taskIds);
|
||||||
if (numErrors > 0) {
|
bulkLog.addAllErrors(existingAndAuthorizedTasks.getRight());
|
||||||
List<MinimalTaskSummary> taskSummaries = taskMapper.findExistingTasks(taskIdsToUpdate, null);
|
taskMapper.updateTaskDueDates(existingAndAuthorizedTasks.getLeft(), referenceTask);
|
||||||
for (MinimalTaskSummary task : taskSummaries) {
|
});
|
||||||
if (referenceTask.getDue() != task.getDue()) {
|
|
||||||
bulkLog.addError(
|
|
||||||
task.getTaskId(),
|
|
||||||
new UpdateFailedException(
|
|
||||||
String.format("Could not set Due Date of Task with Id %s. ", task.getTaskId())));
|
|
||||||
numErrorsLogged++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
long numErrorsNotLogged = numErrors - numErrorsLogged;
|
|
||||||
if (numErrorsNotLogged != 0) {
|
|
||||||
for (int i = 1; i <= numErrorsNotLogged; i++) {
|
|
||||||
bulkLog.addError(
|
|
||||||
String.format("UnknownTaskId: %d", i),
|
|
||||||
new UpdateFailedException("Update of unknown task failed"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bulkLog;
|
return bulkLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,17 +612,12 @@ class ServiceLevelHandler {
|
||||||
&& newClassificationIds.containsAll(oldClassificationIds);
|
&& newClassificationIds.containsAll(oldClassificationIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class BulkLog extends BulkOperationResults<String, TaskanaException> {
|
static class BulkLog extends BulkOperationResults<String, TaskanaException> {}
|
||||||
|
|
||||||
BulkLog() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static final class DurationPrioHolder {
|
static final class DurationPrioHolder {
|
||||||
|
|
||||||
private Duration duration;
|
private final Duration duration;
|
||||||
private int priority;
|
private final int priority;
|
||||||
|
|
||||||
DurationPrioHolder(Duration duration, int priority) {
|
DurationPrioHolder(Duration duration, int priority) {
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
|
|
|
@ -13,6 +13,7 @@ import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.api.exceptions.SystemException;
|
import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.common.internal.InternalTaskanaEngine;
|
import pro.taskana.common.internal.InternalTaskanaEngine;
|
||||||
import pro.taskana.common.internal.util.IdGenerator;
|
import pro.taskana.common.internal.util.IdGenerator;
|
||||||
|
import pro.taskana.task.api.exceptions.MismatchedTaskCommentCreatorException;
|
||||||
import pro.taskana.task.api.exceptions.TaskCommentNotFoundException;
|
import pro.taskana.task.api.exceptions.TaskCommentNotFoundException;
|
||||||
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||||
import pro.taskana.task.api.models.TaskComment;
|
import pro.taskana.task.api.models.TaskComment;
|
||||||
|
@ -75,12 +76,7 @@ class TaskCommentServiceImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new NotAuthorizedException(
|
throw new MismatchedTaskCommentCreatorException(userId, taskCommentImplToUpdate.getId());
|
||||||
String.format(
|
|
||||||
"Not authorized, TaskComment creator and current user must match. "
|
|
||||||
+ "TaskComment creator is %s but current user is %s",
|
|
||||||
taskCommentImplToUpdate.getCreator(), userId),
|
|
||||||
userId);
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
taskanaEngine.returnConnection();
|
taskanaEngine.returnConnection();
|
||||||
|
@ -136,12 +132,7 @@ class TaskCommentServiceImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new NotAuthorizedException(
|
throw new MismatchedTaskCommentCreatorException(userId, taskCommentToDelete.getId());
|
||||||
String.format(
|
|
||||||
"Not authorized, TaskComment creator and current user must match. "
|
|
||||||
+ "TaskComment creator is %s but current user is %s",
|
|
||||||
taskCommentToDelete.getCreator(), userId),
|
|
||||||
userId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -186,9 +177,7 @@ class TaskCommentServiceImpl {
|
||||||
result = taskCommentMapper.findById(taskCommentId);
|
result = taskCommentMapper.findById(taskCommentId);
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new TaskCommentNotFoundException(
|
throw new TaskCommentNotFoundException(taskCommentId);
|
||||||
taskCommentId,
|
|
||||||
String.format("TaskComment for taskCommentId '%s' was not found", taskCommentId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
taskService.getTask(result.getTaskId());
|
taskService.getTask(result.getTaskId());
|
||||||
|
@ -204,11 +193,7 @@ class TaskCommentServiceImpl {
|
||||||
TaskComment oldTaskComment, TaskComment taskCommentImplToUpdate) throws ConcurrencyException {
|
TaskComment oldTaskComment, TaskComment taskCommentImplToUpdate) throws ConcurrencyException {
|
||||||
|
|
||||||
if (!oldTaskComment.getModified().equals(taskCommentImplToUpdate.getModified())) {
|
if (!oldTaskComment.getModified().equals(taskCommentImplToUpdate.getModified())) {
|
||||||
|
throw new ConcurrencyException();
|
||||||
throw new ConcurrencyException(
|
|
||||||
"The current TaskComment has been modified while editing. "
|
|
||||||
+ "The values can not be updated. TaskComment "
|
|
||||||
+ taskCommentImplToUpdate.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package pro.taskana.task.internal;
|
package pro.taskana.task.internal;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -131,10 +132,9 @@ public interface TaskMapper {
|
||||||
|
|
||||||
@Update(
|
@Update(
|
||||||
"<script>UPDATE TASK SET OWNER = #{owner}, MODIFIED = #{modified} "
|
"<script>UPDATE TASK SET OWNER = #{owner}, MODIFIED = #{modified} "
|
||||||
+ "WHERE STATE = 'READY' "
|
+ "WHERE ID IN <foreach item='taskId' index='index' separator=',' open='(' close=')' collection='taskIds'>#{taskId}</foreach> "
|
||||||
+ "AND ID IN <foreach item='taskId' index='index' separator=',' open='(' close=')' collection='taskIds'>#{taskId}</foreach> "
|
|
||||||
+ "</script>")
|
+ "</script>")
|
||||||
int setOwnerOfTasks(
|
void setOwnerOfTasks(
|
||||||
@Param("owner") String owner,
|
@Param("owner") String owner,
|
||||||
@Param("taskIds") List<String> taskIds,
|
@Param("taskIds") List<String> taskIds,
|
||||||
@Param("modified") Instant modified);
|
@Param("modified") Instant modified);
|
||||||
|
@ -184,7 +184,7 @@ public interface TaskMapper {
|
||||||
@Result(property = "planned", column = "PLANNED")
|
@Result(property = "planned", column = "PLANNED")
|
||||||
@Result(property = "callbackState", column = "CALLBACK_STATE")
|
@Result(property = "callbackState", column = "CALLBACK_STATE")
|
||||||
List<MinimalTaskSummary> findExistingTasks(
|
List<MinimalTaskSummary> findExistingTasks(
|
||||||
@Param("taskIds") List<String> taskIds, @Param("externalIds") List<String> externalIds);
|
@Param("taskIds") Collection<String> taskIds, @Param("externalIds") List<String> externalIds);
|
||||||
|
|
||||||
@Update(
|
@Update(
|
||||||
"<script>"
|
"<script>"
|
||||||
|
@ -222,14 +222,15 @@ public interface TaskMapper {
|
||||||
|
|
||||||
@Update(
|
@Update(
|
||||||
"<script>"
|
"<script>"
|
||||||
+ "<if test='taskIds != null'> "
|
+ "<if test='taskSummaries != null'> "
|
||||||
+ "UPDATE TASK SET MODIFIED = #{referenceTask.modified}, "
|
+ "UPDATE TASK SET MODIFIED = #{referenceTask.modified}, "
|
||||||
+ "PLANNED = #{referenceTask.planned}, DUE = #{referenceTask.due} "
|
+ "PLANNED = #{referenceTask.planned}, DUE = #{referenceTask.due} "
|
||||||
+ "WHERE ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>) "
|
+ "WHERE ID IN(<foreach item='taskSummary' collection='taskSummaries' separator=',' >#{taskSummary.taskId}</foreach>) "
|
||||||
+ "</if> "
|
+ "</if> "
|
||||||
+ "</script>")
|
+ "</script>")
|
||||||
long updateTaskDueDates(
|
void updateTaskDueDates(
|
||||||
@Param("taskIds") List<String> taskIds, @Param("referenceTask") TaskImpl referenceTask);
|
@Param("taskSummaries") List<MinimalTaskSummary> taskSummaries,
|
||||||
|
@Param("referenceTask") TaskImpl referenceTask);
|
||||||
|
|
||||||
@Update(
|
@Update(
|
||||||
"<script>"
|
"<script>"
|
||||||
|
@ -239,7 +240,7 @@ public interface TaskMapper {
|
||||||
+ "WHERE ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>) "
|
+ "WHERE ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>) "
|
||||||
+ "</if> "
|
+ "</if> "
|
||||||
+ "</script>")
|
+ "</script>")
|
||||||
long updatePriorityOfTasks(
|
void updatePriorityOfTasks(
|
||||||
@Param("taskIds") List<String> taskIds, @Param("referenceTask") TaskImpl referenceTask);
|
@Param("taskIds") List<String> taskIds, @Param("referenceTask") TaskImpl referenceTask);
|
||||||
|
|
||||||
@Select(
|
@Select(
|
||||||
|
@ -261,10 +262,10 @@ public interface TaskMapper {
|
||||||
"<script> "
|
"<script> "
|
||||||
+ "<choose>"
|
+ "<choose>"
|
||||||
+ "<when test='accessIds == null'>"
|
+ "<when test='accessIds == null'>"
|
||||||
+ "SELECT t.ID FROM TASK t WHERE 1 = 2 "
|
+ "SELECT NULL LIMIT 0 "
|
||||||
+ "</when>"
|
+ "</when>"
|
||||||
+ "<otherwise>"
|
+ "<otherwise>"
|
||||||
+ "SELECT t.ID FROM TASK t WHERE t.ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>)"
|
+ "SELECT t.ID, t.WORKBASKET_ID FROM TASK t WHERE t.ID IN(<foreach item='taskSummary' collection='taskSummaries' separator=',' >#{taskSummary.taskId}</foreach>)"
|
||||||
+ "AND NOT (t.WORKBASKET_ID IN ( "
|
+ "AND NOT (t.WORKBASKET_ID IN ( "
|
||||||
+ "<choose>"
|
+ "<choose>"
|
||||||
+ "<when test=\"_databaseId == 'db2'\">"
|
+ "<when test=\"_databaseId == 'db2'\">"
|
||||||
|
@ -279,7 +280,9 @@ public interface TaskMapper {
|
||||||
+ "</otherwise>"
|
+ "</otherwise>"
|
||||||
+ "</choose>"
|
+ "</choose>"
|
||||||
+ "</script>")
|
+ "</script>")
|
||||||
@Result(property = "id", column = "ID")
|
@Result(property = "left", column = "ID")
|
||||||
List<String> filterTaskIdsNotAuthorizedFor(
|
@Result(property = "right", column = "WORKBASKET_ID")
|
||||||
@Param("taskIds") List<String> taskIds, @Param("accessIds") List<String> accessIds);
|
List<Pair<String, String>> getTaskAndWorkbasketIdsNotAuthorizedFor(
|
||||||
|
@Param("taskSummaries") List<MinimalTaskSummary> taskSummaries,
|
||||||
|
@Param("accessIds") List<String> accessIds);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package pro.taskana.task.internal;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.ibatis.exceptions.PersistenceException;
|
import org.apache.ibatis.exceptions.PersistenceException;
|
||||||
import org.apache.ibatis.session.RowBounds;
|
import org.apache.ibatis.session.RowBounds;
|
||||||
|
@ -18,6 +17,7 @@ import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
||||||
import pro.taskana.common.internal.InternalTaskanaEngine;
|
import pro.taskana.common.internal.InternalTaskanaEngine;
|
||||||
import pro.taskana.common.internal.configuration.DB;
|
import pro.taskana.common.internal.configuration.DB;
|
||||||
|
import pro.taskana.common.internal.util.EnumUtil;
|
||||||
import pro.taskana.task.api.CallbackState;
|
import pro.taskana.task.api.CallbackState;
|
||||||
import pro.taskana.task.api.ObjectReferenceQuery;
|
import pro.taskana.task.api.ObjectReferenceQuery;
|
||||||
import pro.taskana.task.api.TaskCustomField;
|
import pro.taskana.task.api.TaskCustomField;
|
||||||
|
@ -231,8 +231,7 @@ public class TaskQueryImpl implements TaskQuery {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TaskQuery stateNotIn(TaskState... states) {
|
public TaskQuery stateNotIn(TaskState... states) {
|
||||||
this.stateIn =
|
this.stateIn = EnumUtil.allValuesExceptFor(states);
|
||||||
EnumSet.complementOf(EnumSet.copyOf(Arrays.asList(states))).toArray(new TaskState[0]);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -947,7 +946,7 @@ public class TaskQueryImpl implements TaskQuery {
|
||||||
} catch (PersistenceException e) {
|
} catch (PersistenceException e) {
|
||||||
if (e.getMessage().contains("ERRORCODE=-4470")) {
|
if (e.getMessage().contains("ERRORCODE=-4470")) {
|
||||||
TaskanaRuntimeException ex =
|
TaskanaRuntimeException ex =
|
||||||
new TaskanaRuntimeException(
|
new SystemException(
|
||||||
"The offset beginning was set over the amount of result-rows.", e.getCause());
|
"The offset beginning was set over the amount of result-rows.", e.getCause());
|
||||||
ex.setStackTrace(e.getStackTrace());
|
ex.setStackTrace(e.getStackTrace());
|
||||||
throw ex;
|
throw ex;
|
||||||
|
@ -1619,7 +1618,7 @@ public class TaskQueryImpl implements TaskQuery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (NotAuthorizedException e) {
|
} catch (NotAuthorizedException e) {
|
||||||
throw new NotAuthorizedToQueryWorkbasketException(e.getMessage(), e.getCause());
|
throw new NotAuthorizedToQueryWorkbasketException(e.getMessage(), e.getErrorCode(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package pro.taskana.task.internal;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -34,6 +35,7 @@ import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
import pro.taskana.common.internal.InternalTaskanaEngine;
|
import pro.taskana.common.internal.InternalTaskanaEngine;
|
||||||
import pro.taskana.common.internal.util.CheckedConsumer;
|
import pro.taskana.common.internal.util.CheckedConsumer;
|
||||||
import pro.taskana.common.internal.util.CollectionUtil;
|
import pro.taskana.common.internal.util.CollectionUtil;
|
||||||
|
import pro.taskana.common.internal.util.EnumUtil;
|
||||||
import pro.taskana.common.internal.util.IdGenerator;
|
import pro.taskana.common.internal.util.IdGenerator;
|
||||||
import pro.taskana.common.internal.util.ObjectAttributeChangeDetector;
|
import pro.taskana.common.internal.util.ObjectAttributeChangeDetector;
|
||||||
import pro.taskana.common.internal.util.Pair;
|
import pro.taskana.common.internal.util.Pair;
|
||||||
|
@ -52,12 +54,13 @@ import pro.taskana.task.api.TaskQuery;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
|
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
|
||||||
|
import pro.taskana.task.api.exceptions.InvalidCallbackStateException;
|
||||||
import pro.taskana.task.api.exceptions.InvalidOwnerException;
|
import pro.taskana.task.api.exceptions.InvalidOwnerException;
|
||||||
import pro.taskana.task.api.exceptions.InvalidStateException;
|
import pro.taskana.task.api.exceptions.InvalidStateException;
|
||||||
|
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
|
||||||
import pro.taskana.task.api.exceptions.TaskAlreadyExistException;
|
import pro.taskana.task.api.exceptions.TaskAlreadyExistException;
|
||||||
import pro.taskana.task.api.exceptions.TaskCommentNotFoundException;
|
import pro.taskana.task.api.exceptions.TaskCommentNotFoundException;
|
||||||
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||||
import pro.taskana.task.api.exceptions.UpdateFailedException;
|
|
||||||
import pro.taskana.task.api.models.Attachment;
|
import pro.taskana.task.api.models.Attachment;
|
||||||
import pro.taskana.task.api.models.ObjectReference;
|
import pro.taskana.task.api.models.ObjectReference;
|
||||||
import pro.taskana.task.api.models.Task;
|
import pro.taskana.task.api.models.Task;
|
||||||
|
@ -71,6 +74,7 @@ import pro.taskana.task.internal.models.TaskImpl;
|
||||||
import pro.taskana.task.internal.models.TaskSummaryImpl;
|
import pro.taskana.task.internal.models.TaskSummaryImpl;
|
||||||
import pro.taskana.workbasket.api.WorkbasketPermission;
|
import pro.taskana.workbasket.api.WorkbasketPermission;
|
||||||
import pro.taskana.workbasket.api.WorkbasketService;
|
import pro.taskana.workbasket.api.WorkbasketService;
|
||||||
|
import pro.taskana.workbasket.api.exceptions.MismatchedWorkbasketPermissionException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
||||||
import pro.taskana.workbasket.api.models.Workbasket;
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
import pro.taskana.workbasket.api.models.WorkbasketSummary;
|
import pro.taskana.workbasket.api.models.WorkbasketSummary;
|
||||||
|
@ -193,9 +197,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workbasket.isMarkedForDeletion()) {
|
if (workbasket.isMarkedForDeletion()) {
|
||||||
throw new WorkbasketNotFoundException(
|
throw new WorkbasketNotFoundException(workbasket.getId());
|
||||||
workbasket.getId(),
|
|
||||||
"The workbasket " + workbasket.getId() + " was marked for deletion");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task.setWorkbasketSummary(workbasket.asSummary());
|
task.setWorkbasketSummary(workbasket.asSummary());
|
||||||
|
@ -260,8 +262,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
if (msg != null
|
if (msg != null
|
||||||
&& (msg.contains("violation") || msg.contains("violates") || msg.contains("verletzt"))
|
&& (msg.contains("violation") || msg.contains("violates") || msg.contains("verletzt"))
|
||||||
&& msg.contains("external_id")) {
|
&& msg.contains("external_id")) {
|
||||||
throw new TaskAlreadyExistException(
|
throw new TaskAlreadyExistException(task.getExternalId());
|
||||||
"Task with external id " + task.getExternalId() + " already exists");
|
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -274,7 +275,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Task getTask(String id) throws NotAuthorizedException, TaskNotFoundException {
|
public Task getTask(String id) throws NotAuthorizedException, TaskNotFoundException {
|
||||||
TaskImpl resultTask = null;
|
TaskImpl resultTask;
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
|
|
||||||
|
@ -285,13 +286,10 @@ public class TaskServiceImpl implements TaskService {
|
||||||
String workbasketId = resultTask.getWorkbasketSummary().getId();
|
String workbasketId = resultTask.getWorkbasketSummary().getId();
|
||||||
List<WorkbasketSummary> workbaskets = query.idIn(workbasketId).list();
|
List<WorkbasketSummary> workbaskets = query.idIn(workbasketId).list();
|
||||||
if (workbaskets.isEmpty()) {
|
if (workbaskets.isEmpty()) {
|
||||||
String currentUser = taskanaEngine.getEngine().getCurrentUserContext().getUserid();
|
throw new MismatchedWorkbasketPermissionException(
|
||||||
throw new NotAuthorizedException(
|
taskanaEngine.getEngine().getCurrentUserContext().getUserid(),
|
||||||
"The current user "
|
workbasketId,
|
||||||
+ currentUser
|
WorkbasketPermission.READ);
|
||||||
+ " has no read permission for workbasket "
|
|
||||||
+ workbasketId,
|
|
||||||
taskanaEngine.getEngine().getCurrentUserContext().getUserid());
|
|
||||||
} else {
|
} else {
|
||||||
resultTask.setWorkbasketSummary(workbaskets.get(0));
|
resultTask.setWorkbasketSummary(workbaskets.get(0));
|
||||||
}
|
}
|
||||||
|
@ -322,7 +320,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
resultTask.setClassificationSummary(classification);
|
resultTask.setClassificationSummary(classification);
|
||||||
return resultTask;
|
return resultTask;
|
||||||
} else {
|
} else {
|
||||||
throw new TaskNotFoundException(id, String.format("Task with id %s was not found.", id));
|
throw new TaskNotFoundException(id);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
taskanaEngine.returnConnection();
|
taskanaEngine.returnConnection();
|
||||||
|
@ -346,7 +344,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
@Override
|
@Override
|
||||||
public Task setTaskRead(String taskId, boolean isRead)
|
public Task setTaskRead(String taskId, boolean isRead)
|
||||||
throws TaskNotFoundException, NotAuthorizedException {
|
throws TaskNotFoundException, NotAuthorizedException {
|
||||||
TaskImpl task = null;
|
TaskImpl task;
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
task = (TaskImpl) getTask(taskId);
|
task = (TaskImpl) getTask(taskId);
|
||||||
|
@ -692,43 +690,35 @@ public class TaskServiceImpl implements TaskService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BulkOperationResults<String, TaskanaException> setOwnerOfTasks(
|
public BulkOperationResults<String, TaskanaException> setOwnerOfTasks(
|
||||||
String owner, List<String> argTaskIds) {
|
String owner, List<String> taskIds) {
|
||||||
|
|
||||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||||
if (argTaskIds == null || argTaskIds.isEmpty()) {
|
if (taskIds == null || taskIds.isEmpty()) {
|
||||||
return bulkLog;
|
return bulkLog;
|
||||||
}
|
}
|
||||||
// remove duplicates
|
|
||||||
List<String> taskIds = argTaskIds.stream().distinct().collect(Collectors.toList());
|
|
||||||
final int requestSize = taskIds.size();
|
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
// use only elements we are authorized for
|
Pair<List<MinimalTaskSummary>, BulkLog> existingAndAuthorizedTasks =
|
||||||
Pair<List<MinimalTaskSummary>, BulkLog> resultsPair = getMinimalTaskSummaries(taskIds);
|
getMinimalTaskSummaries(taskIds);
|
||||||
// set the Owner of these tasks we are authorized for
|
bulkLog.addAllErrors(existingAndAuthorizedTasks.getRight());
|
||||||
List<MinimalTaskSummary> existingMinimalTaskSummaries = resultsPair.getLeft();
|
Pair<List<String>, BulkLog> taskIdsToUpdate =
|
||||||
taskIds =
|
filterOutTasksWhichAreNotReady(existingAndAuthorizedTasks.getLeft());
|
||||||
existingMinimalTaskSummaries.stream()
|
bulkLog.addAllErrors(taskIdsToUpdate.getRight());
|
||||||
.map(MinimalTaskSummary::getTaskId)
|
|
||||||
.collect(Collectors.toList());
|
if (!taskIdsToUpdate.getLeft().isEmpty()) {
|
||||||
bulkLog.addAllErrors(resultsPair.getRight());
|
taskMapper.setOwnerOfTasks(owner, taskIdsToUpdate.getLeft(), Instant.now());
|
||||||
if (!taskIds.isEmpty()) {
|
|
||||||
final int numberOfAffectedTasks = taskMapper.setOwnerOfTasks(owner, taskIds, Instant.now());
|
|
||||||
if (numberOfAffectedTasks != taskIds.size()) { // all tasks were updated
|
|
||||||
// check the outcome
|
|
||||||
existingMinimalTaskSummaries = taskMapper.findExistingTasks(taskIds, null);
|
|
||||||
bulkLog.addAllErrors(
|
|
||||||
addExceptionsForTasksWhoseOwnerWasNotSet(owner, existingMinimalTaskSummaries));
|
|
||||||
if (LOGGER.isDebugEnabled()) {
|
|
||||||
LOGGER.debug(
|
|
||||||
"Received the Request to set owner on {} tasks, actually modified tasks = {}"
|
|
||||||
+ ", could not set owner on {} tasks.",
|
|
||||||
requestSize,
|
|
||||||
numberOfAffectedTasks,
|
|
||||||
bulkLog.getFailedIds().size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug(
|
||||||
|
"Received the Request to set owner on {} tasks, actually modified tasks = {}"
|
||||||
|
+ ", could not set owner on {} tasks.",
|
||||||
|
taskIds.size(),
|
||||||
|
taskIdsToUpdate.getLeft().size(),
|
||||||
|
bulkLog.getFailedIds().size());
|
||||||
|
}
|
||||||
|
|
||||||
return bulkLog;
|
return bulkLog;
|
||||||
} finally {
|
} finally {
|
||||||
taskanaEngine.returnConnection();
|
taskanaEngine.returnConnection();
|
||||||
|
@ -874,10 +864,10 @@ public class TaskServiceImpl implements TaskService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<List<MinimalTaskSummary>, BulkLog> getMinimalTaskSummaries(List<String> argTaskIds) {
|
Pair<List<MinimalTaskSummary>, BulkLog> getMinimalTaskSummaries(Collection<String> argTaskIds) {
|
||||||
BulkLog bulkLog = new BulkLog();
|
BulkLog bulkLog = new BulkLog();
|
||||||
// remove duplicates
|
// remove duplicates
|
||||||
List<String> taskIds = argTaskIds.stream().distinct().collect(Collectors.toList());
|
Set<String> taskIds = new HashSet<>(argTaskIds);
|
||||||
// get existing tasks
|
// get existing tasks
|
||||||
List<MinimalTaskSummary> minimalTaskSummaries = taskMapper.findExistingTasks(taskIds, null);
|
List<MinimalTaskSummary> minimalTaskSummaries = taskMapper.findExistingTasks(taskIds, null);
|
||||||
bulkLog.addAllErrors(addExceptionsForNonExistingTasksToBulkLog(taskIds, minimalTaskSummaries));
|
bulkLog.addAllErrors(addExceptionsForNonExistingTasksToBulkLog(taskIds, minimalTaskSummaries));
|
||||||
|
@ -894,39 +884,40 @@ public class TaskServiceImpl implements TaskService {
|
||||||
if (taskanaEngine.getEngine().isUserInRole(TaskanaRole.ADMIN, TaskanaRole.TASK_ADMIN)) {
|
if (taskanaEngine.getEngine().isUserInRole(TaskanaRole.ADMIN, TaskanaRole.TASK_ADMIN)) {
|
||||||
return Pair.of(existingTasks, bulkLog);
|
return Pair.of(existingTasks, bulkLog);
|
||||||
} else {
|
} else {
|
||||||
List<String> taskIds =
|
|
||||||
existingTasks.stream().map(MinimalTaskSummary::getTaskId).collect(Collectors.toList());
|
|
||||||
List<String> accessIds = taskanaEngine.getEngine().getCurrentUserContext().getAccessIds();
|
List<String> accessIds = taskanaEngine.getEngine().getCurrentUserContext().getAccessIds();
|
||||||
List<String> taskIdsNotAuthorizedFor =
|
List<Pair<String, String>> taskAndWorkbasketIdsNotAuthorizedFor =
|
||||||
taskMapper.filterTaskIdsNotAuthorizedFor(taskIds, accessIds);
|
taskMapper.getTaskAndWorkbasketIdsNotAuthorizedFor(existingTasks, accessIds);
|
||||||
String userId = taskanaEngine.getEngine().getCurrentUserContext().getUserid();
|
String userId = taskanaEngine.getEngine().getCurrentUserContext().getUserid();
|
||||||
for (String taskId : taskIdsNotAuthorizedFor) {
|
|
||||||
|
for (Pair<String, String> taskAndWorkbasketIds : taskAndWorkbasketIdsNotAuthorizedFor) {
|
||||||
bulkLog.addError(
|
bulkLog.addError(
|
||||||
taskId,
|
taskAndWorkbasketIds.getLeft(),
|
||||||
new NotAuthorizedException(
|
new MismatchedWorkbasketPermissionException(
|
||||||
String.format("User %s is not authorized for task %s ", userId, taskId), userId));
|
userId, taskAndWorkbasketIds.getRight(), WorkbasketPermission.READ));
|
||||||
}
|
}
|
||||||
taskIds.removeAll(taskIdsNotAuthorizedFor);
|
|
||||||
|
Set<String> taskIdsToRemove =
|
||||||
|
taskAndWorkbasketIdsNotAuthorizedFor.stream()
|
||||||
|
.map(Pair::getLeft)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
List<MinimalTaskSummary> tasksAuthorizedFor =
|
List<MinimalTaskSummary> tasksAuthorizedFor =
|
||||||
existingTasks.stream()
|
existingTasks.stream()
|
||||||
.filter(t -> taskIds.contains(t.getTaskId()))
|
.filter(t -> !taskIdsToRemove.contains(t.getTaskId()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
return Pair.of(tasksAuthorizedFor, bulkLog);
|
return Pair.of(tasksAuthorizedFor, bulkLog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BulkLog addExceptionsForNonExistingTasksToBulkLog(
|
BulkLog addExceptionsForNonExistingTasksToBulkLog(
|
||||||
List<String> requestTaskIds, List<MinimalTaskSummary> existingMinimalTaskSummaries) {
|
Collection<String> requestTaskIds, List<MinimalTaskSummary> existingMinimalTaskSummaries) {
|
||||||
BulkLog bulkLog = new BulkLog();
|
BulkLog bulkLog = new BulkLog();
|
||||||
List<String> nonExistingTaskIds = new ArrayList<>(requestTaskIds);
|
Set<String> existingTaskIds =
|
||||||
List<String> existingTaskIds =
|
|
||||||
existingMinimalTaskSummaries.stream()
|
existingMinimalTaskSummaries.stream()
|
||||||
.map(MinimalTaskSummary::getTaskId)
|
.map(MinimalTaskSummary::getTaskId)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toSet());
|
||||||
nonExistingTaskIds.removeAll(existingTaskIds);
|
requestTaskIds.stream()
|
||||||
nonExistingTaskIds.forEach(
|
.filter(taskId -> !existingTaskIds.contains(taskId))
|
||||||
taskId ->
|
.forEach(taskId -> bulkLog.addError(taskId, new TaskNotFoundException(taskId)));
|
||||||
bulkLog.addError(taskId, new TaskNotFoundException(taskId, "Task was not found")));
|
|
||||||
return bulkLog;
|
return bulkLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -940,6 +931,24 @@ public class TaskServiceImpl implements TaskService {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Pair<List<String>, BulkLog> filterOutTasksWhichAreNotReady(
|
||||||
|
Collection<MinimalTaskSummary> minimalTaskSummaries) {
|
||||||
|
List<String> filteredTasks = new ArrayList<>(minimalTaskSummaries.size());
|
||||||
|
BulkLog bulkLog = new BulkLog();
|
||||||
|
|
||||||
|
for (MinimalTaskSummary taskSummary : minimalTaskSummaries) {
|
||||||
|
if (taskSummary.getTaskState() != TaskState.READY) {
|
||||||
|
bulkLog.addError(
|
||||||
|
taskSummary.getTaskId(),
|
||||||
|
new InvalidTaskStateException(
|
||||||
|
taskSummary.getTaskId(), taskSummary.getTaskState(), TaskState.READY));
|
||||||
|
} else {
|
||||||
|
filteredTasks.add(taskSummary.getTaskId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Pair.of(filteredTasks, bulkLog);
|
||||||
|
}
|
||||||
|
|
||||||
private List<TaskSummaryImpl> augmentTaskSummariesByContainedSummariesWithoutPartitioning(
|
private List<TaskSummaryImpl> augmentTaskSummariesByContainedSummariesWithoutPartitioning(
|
||||||
List<TaskSummaryImpl> taskSummaries) {
|
List<TaskSummaryImpl> taskSummaries) {
|
||||||
List<String> taskIds =
|
List<String> taskIds =
|
||||||
|
@ -1021,10 +1030,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
pair -> {
|
pair -> {
|
||||||
if (pair.getRight() == null) {
|
if (pair.getRight() == null) {
|
||||||
String taskId = pair.getLeft();
|
String taskId = pair.getLeft();
|
||||||
bulkLog.addError(
|
bulkLog.addError(taskId, new TaskNotFoundException(taskId));
|
||||||
taskId,
|
|
||||||
new TaskNotFoundException(
|
|
||||||
taskId, String.format("Task with id %s was not found.", taskId)));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -1056,7 +1062,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
&& !oldTaskImpl.getClaimed().equals(newTaskImpl.getClaimed())
|
&& !oldTaskImpl.getClaimed().equals(newTaskImpl.getClaimed())
|
||||||
|| oldTaskImpl.getState() != null
|
|| oldTaskImpl.getState() != null
|
||||||
&& !oldTaskImpl.getState().equals(newTaskImpl.getState())) {
|
&& !oldTaskImpl.getState().equals(newTaskImpl.getState())) {
|
||||||
throw new ConcurrencyException("The task has already been updated by another user");
|
throw new ConcurrencyException();
|
||||||
}
|
}
|
||||||
newTaskImpl.setModified(Instant.now());
|
newTaskImpl.setModified(Instant.now());
|
||||||
}
|
}
|
||||||
|
@ -1064,14 +1070,12 @@ public class TaskServiceImpl implements TaskService {
|
||||||
private TaskImpl terminateCancelCommonActions(String taskId, TaskState targetState)
|
private TaskImpl terminateCancelCommonActions(String taskId, TaskState targetState)
|
||||||
throws NotAuthorizedException, TaskNotFoundException, InvalidStateException {
|
throws NotAuthorizedException, TaskNotFoundException, InvalidStateException {
|
||||||
if (taskId == null || taskId.isEmpty()) {
|
if (taskId == null || taskId.isEmpty()) {
|
||||||
throw new TaskNotFoundException(
|
throw new TaskNotFoundException(taskId);
|
||||||
taskId, String.format("Task with id %s was not found.", taskId));
|
|
||||||
}
|
}
|
||||||
TaskImpl task = (TaskImpl) getTask(taskId);
|
TaskImpl task = (TaskImpl) getTask(taskId);
|
||||||
TaskState state = task.getState();
|
TaskState state = task.getState();
|
||||||
if (state.isEndState()) {
|
if (state.isEndState()) {
|
||||||
throw new InvalidStateException(
|
throw new InvalidTaskStateException(taskId, state, TaskState.READY);
|
||||||
String.format("Task with Id %s is already in an end state.", taskId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Instant now = Instant.now();
|
Instant now = Instant.now();
|
||||||
|
@ -1088,30 +1092,6 @@ public class TaskServiceImpl implements TaskService {
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BulkOperationResults<String, TaskanaException> addExceptionsForTasksWhoseOwnerWasNotSet(
|
|
||||||
String owner, List<MinimalTaskSummary> existingMinimalTaskSummaries) {
|
|
||||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
|
||||||
|
|
||||||
for (MinimalTaskSummary taskSummary : existingMinimalTaskSummaries) {
|
|
||||||
if (!owner.equals(taskSummary.getOwner())) { // owner was not set
|
|
||||||
if (!TaskState.READY.equals(taskSummary.getTaskState())) { // due to invalid state
|
|
||||||
bulkLog.addError(
|
|
||||||
taskSummary.getTaskId(),
|
|
||||||
new InvalidStateException(
|
|
||||||
String.format(
|
|
||||||
"Task with id %s is in state %s and not in state ready.",
|
|
||||||
taskSummary.getTaskId(), taskSummary.getTaskState())));
|
|
||||||
} else { // due to unknown reason
|
|
||||||
bulkLog.addError(
|
|
||||||
taskSummary.getTaskId(),
|
|
||||||
new UpdateFailedException(
|
|
||||||
String.format("Could not set owner of Task %s .", taskSummary.getTaskId())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bulkLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task claim(String taskId, boolean forceClaim)
|
private Task claim(String taskId, boolean forceClaim)
|
||||||
throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
|
throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
|
||||||
NotAuthorizedException {
|
NotAuthorizedException {
|
||||||
|
@ -1159,16 +1139,14 @@ public class TaskServiceImpl implements TaskService {
|
||||||
private void checkPreconditionsForClaimTask(TaskSummary task, boolean forced)
|
private void checkPreconditionsForClaimTask(TaskSummary task, boolean forced)
|
||||||
throws InvalidStateException, InvalidOwnerException {
|
throws InvalidStateException, InvalidOwnerException {
|
||||||
TaskState state = task.getState();
|
TaskState state = task.getState();
|
||||||
if (!state.in(TaskState.READY, TaskState.CLAIMED)) {
|
if (state.isEndState()) {
|
||||||
throw new InvalidStateException(
|
throw new InvalidTaskStateException(
|
||||||
String.format("Task with Id %s is already in an end state.", task.getId()));
|
task.getId(), task.getState(), EnumUtil.allValuesExceptFor(TaskState.END_STATES));
|
||||||
}
|
}
|
||||||
if (!forced
|
|
||||||
&& state == TaskState.CLAIMED
|
String userId = taskanaEngine.getEngine().getCurrentUserContext().getUserid();
|
||||||
&& !task.getOwner().equals(taskanaEngine.getEngine().getCurrentUserContext().getUserid())) {
|
if (!forced && state == TaskState.CLAIMED && !task.getOwner().equals(userId)) {
|
||||||
throw new InvalidOwnerException(
|
throw new InvalidOwnerException(userId, task.getId());
|
||||||
String.format(
|
|
||||||
"Task with id %s is already claimed by %s.", task.getId(), task.getOwner()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1179,17 +1157,17 @@ public class TaskServiceImpl implements TaskService {
|
||||||
private static void checkIfTaskIsTerminatedOrCancelled(TaskSummary task)
|
private static void checkIfTaskIsTerminatedOrCancelled(TaskSummary task)
|
||||||
throws InvalidStateException {
|
throws InvalidStateException {
|
||||||
if (task.getState().in(TaskState.CANCELLED, TaskState.TERMINATED)) {
|
if (task.getState().in(TaskState.CANCELLED, TaskState.TERMINATED)) {
|
||||||
throw new InvalidStateException(
|
throw new InvalidTaskStateException(
|
||||||
String.format(
|
task.getId(),
|
||||||
"Cannot complete task %s because it is in state %s.", task.getId(), task.getState()));
|
task.getState(),
|
||||||
|
EnumUtil.allValuesExceptFor(TaskState.CANCELLED, TaskState.TERMINATED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkPreconditionsForCompleteTask(TaskSummary task)
|
private void checkPreconditionsForCompleteTask(TaskSummary task)
|
||||||
throws InvalidStateException, InvalidOwnerException {
|
throws InvalidStateException, InvalidOwnerException {
|
||||||
if (taskIsNotClaimed(task)) {
|
if (taskIsNotClaimed(task)) {
|
||||||
throw new InvalidStateException(
|
throw new InvalidTaskStateException(task.getId(), task.getState(), TaskState.CLAIMED);
|
||||||
String.format("Task with Id %s has to be claimed before.", task.getId()));
|
|
||||||
} else if (!taskanaEngine
|
} else if (!taskanaEngine
|
||||||
.getEngine()
|
.getEngine()
|
||||||
.getCurrentUserContext()
|
.getCurrentUserContext()
|
||||||
|
@ -1197,11 +1175,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
.contains(task.getOwner())
|
.contains(task.getOwner())
|
||||||
&& !taskanaEngine.getEngine().isUserInRole(TaskanaRole.ADMIN)) {
|
&& !taskanaEngine.getEngine().isUserInRole(TaskanaRole.ADMIN)) {
|
||||||
throw new InvalidOwnerException(
|
throw new InvalidOwnerException(
|
||||||
String.format(
|
taskanaEngine.getEngine().getCurrentUserContext().getUserid(), task.getId());
|
||||||
"Owner of task %s is %s, but current user is %s ",
|
|
||||||
task.getId(),
|
|
||||||
task.getOwner(),
|
|
||||||
taskanaEngine.getEngine().getCurrentUserContext().getUserid()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1215,12 +1189,11 @@ public class TaskServiceImpl implements TaskService {
|
||||||
task = (TaskImpl) getTask(taskId);
|
task = (TaskImpl) getTask(taskId);
|
||||||
TaskState state = task.getState();
|
TaskState state = task.getState();
|
||||||
if (state.isEndState()) {
|
if (state.isEndState()) {
|
||||||
throw new InvalidStateException(
|
throw new InvalidTaskStateException(
|
||||||
String.format("Task with Id %s is already in an end state.", taskId));
|
taskId, state, EnumUtil.allValuesExceptFor(TaskState.END_STATES));
|
||||||
}
|
}
|
||||||
if (state == TaskState.CLAIMED && !forceUnclaim && !userId.equals(task.getOwner())) {
|
if (state == TaskState.CLAIMED && !forceUnclaim && !userId.equals(task.getOwner())) {
|
||||||
throw new InvalidOwnerException(
|
throw new InvalidOwnerException(userId, taskId);
|
||||||
String.format("Task with id %s is already claimed by %s.", taskId, task.getOwner()));
|
|
||||||
}
|
}
|
||||||
Instant now = Instant.now();
|
Instant now = Instant.now();
|
||||||
task.setOwner(null);
|
task.setOwner(null);
|
||||||
|
@ -1294,15 +1267,14 @@ public class TaskServiceImpl implements TaskService {
|
||||||
task = (TaskImpl) getTask(taskId);
|
task = (TaskImpl) getTask(taskId);
|
||||||
|
|
||||||
if (!(task.getState().isEndState()) && !forceDelete) {
|
if (!(task.getState().isEndState()) && !forceDelete) {
|
||||||
throw new InvalidStateException(
|
throw new InvalidTaskStateException(taskId, task.getState(), TaskState.END_STATES);
|
||||||
"Cannot delete Task " + taskId + " because it is not in an end state.");
|
|
||||||
}
|
}
|
||||||
if ((!task.getState().in(TaskState.TERMINATED, TaskState.CANCELLED))
|
if ((!task.getState().in(TaskState.TERMINATED, TaskState.CANCELLED))
|
||||||
&& CallbackState.CALLBACK_PROCESSING_REQUIRED.equals(task.getCallbackState())) {
|
&& CallbackState.CALLBACK_PROCESSING_REQUIRED.equals(task.getCallbackState())) {
|
||||||
throw new InvalidStateException(
|
throw new InvalidCallbackStateException(
|
||||||
String.format(
|
taskId,
|
||||||
"Task wit Id %s cannot be deleted because its callback is not yet processed",
|
task.getCallbackState(),
|
||||||
taskId));
|
EnumUtil.allValuesExceptFor(CallbackState.CALLBACK_PROCESSING_REQUIRED));
|
||||||
}
|
}
|
||||||
|
|
||||||
attachmentMapper.deleteMultipleByTaskIds(Collections.singletonList(taskId));
|
attachmentMapper.deleteMultipleByTaskIds(Collections.singletonList(taskId));
|
||||||
|
@ -1337,23 +1309,23 @@ public class TaskServiceImpl implements TaskService {
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (foundSummary == null) {
|
if (foundSummary == null) {
|
||||||
bulkLog.addError(
|
bulkLog.addError(currentTaskId, new TaskNotFoundException(currentTaskId));
|
||||||
currentTaskId,
|
|
||||||
new TaskNotFoundException(
|
|
||||||
currentTaskId, String.format("Task with id %s was not found.", currentTaskId)));
|
|
||||||
taskIdIterator.remove();
|
taskIdIterator.remove();
|
||||||
} else if (!(foundSummary.getTaskState().isEndState())) {
|
} else if (!(foundSummary.getTaskState().isEndState())) {
|
||||||
bulkLog.addError(currentTaskId, new InvalidStateException(currentTaskId));
|
bulkLog.addError(
|
||||||
|
currentTaskId,
|
||||||
|
new InvalidTaskStateException(
|
||||||
|
currentTaskId, foundSummary.getTaskState(), TaskState.END_STATES));
|
||||||
taskIdIterator.remove();
|
taskIdIterator.remove();
|
||||||
} else {
|
} else {
|
||||||
if ((!foundSummary.getTaskState().in(TaskState.CANCELLED, TaskState.TERMINATED))
|
if ((!foundSummary.getTaskState().in(TaskState.CANCELLED, TaskState.TERMINATED))
|
||||||
&& CallbackState.CALLBACK_PROCESSING_REQUIRED.equals(foundSummary.getCallbackState())) {
|
&& CallbackState.CALLBACK_PROCESSING_REQUIRED.equals(foundSummary.getCallbackState())) {
|
||||||
bulkLog.addError(
|
bulkLog.addError(
|
||||||
currentTaskId,
|
currentTaskId,
|
||||||
new InvalidStateException(
|
new InvalidCallbackStateException(
|
||||||
String.format(
|
currentTaskId,
|
||||||
"Task wit Id %s cannot be deleted because its callback is not yet processed",
|
foundSummary.getCallbackState(),
|
||||||
currentTaskId)));
|
EnumUtil.allValuesExceptFor(CallbackState.CALLBACK_PROCESSING_REQUIRED)));
|
||||||
taskIdIterator.remove();
|
taskIdIterator.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1376,23 +1348,20 @@ public class TaskServiceImpl implements TaskService {
|
||||||
.filter(taskSummary -> currentExternalId.equals(taskSummary.getExternalId()))
|
.filter(taskSummary -> currentExternalId.equals(taskSummary.getExternalId()))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
if (foundSummary.isPresent()) {
|
if (foundSummary.isPresent()) {
|
||||||
if (!desiredCallbackStateCanBeSetForFoundSummary(
|
Optional<TaskanaException> invalidStateException =
|
||||||
foundSummary.get(), desiredCallbackState)) {
|
desiredCallbackStateCanBeSetForFoundSummary(foundSummary.get(), desiredCallbackState);
|
||||||
bulkLog.addError(currentExternalId, new InvalidStateException(currentExternalId));
|
if (invalidStateException.isPresent()) {
|
||||||
|
bulkLog.addError(currentExternalId, invalidStateException.get());
|
||||||
externalIdIterator.remove();
|
externalIdIterator.remove();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bulkLog.addError(
|
bulkLog.addError(currentExternalId, new TaskNotFoundException(currentExternalId));
|
||||||
currentExternalId,
|
|
||||||
new TaskNotFoundException(
|
|
||||||
currentExternalId,
|
|
||||||
String.format("Task with id %s was not found.", currentExternalId)));
|
|
||||||
externalIdIterator.remove();
|
externalIdIterator.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean desiredCallbackStateCanBeSetForFoundSummary(
|
private Optional<TaskanaException> desiredCallbackStateCanBeSetForFoundSummary(
|
||||||
MinimalTaskSummary foundSummary, CallbackState desiredCallbackState) {
|
MinimalTaskSummary foundSummary, CallbackState desiredCallbackState) {
|
||||||
|
|
||||||
CallbackState currentTaskCallbackState = foundSummary.getCallbackState();
|
CallbackState currentTaskCallbackState = foundSummary.getCallbackState();
|
||||||
|
@ -1400,21 +1369,48 @@ public class TaskServiceImpl implements TaskService {
|
||||||
|
|
||||||
switch (desiredCallbackState) {
|
switch (desiredCallbackState) {
|
||||||
case CALLBACK_PROCESSING_COMPLETED:
|
case CALLBACK_PROCESSING_COMPLETED:
|
||||||
return currentTaskState.isEndState();
|
if (!currentTaskState.isEndState()) {
|
||||||
|
return Optional.of(
|
||||||
|
new InvalidTaskStateException(
|
||||||
|
foundSummary.getTaskId(), foundSummary.getTaskState(), TaskState.END_STATES));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case CLAIMED:
|
case CLAIMED:
|
||||||
if (!currentTaskState.equals(TaskState.CLAIMED)) {
|
if (!currentTaskState.equals(TaskState.CLAIMED)) {
|
||||||
return false;
|
return Optional.of(
|
||||||
} else {
|
new InvalidTaskStateException(
|
||||||
return currentTaskCallbackState.equals(CallbackState.CALLBACK_PROCESSING_REQUIRED);
|
foundSummary.getTaskId(), foundSummary.getTaskState(), TaskState.CLAIMED));
|
||||||
}
|
}
|
||||||
|
if (!currentTaskCallbackState.equals(CallbackState.CALLBACK_PROCESSING_REQUIRED)) {
|
||||||
|
return Optional.of(
|
||||||
|
new InvalidCallbackStateException(
|
||||||
|
foundSummary.getTaskId(),
|
||||||
|
currentTaskCallbackState,
|
||||||
|
CallbackState.CALLBACK_PROCESSING_REQUIRED));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case CALLBACK_PROCESSING_REQUIRED:
|
case CALLBACK_PROCESSING_REQUIRED:
|
||||||
return !currentTaskCallbackState.equals(CallbackState.CALLBACK_PROCESSING_COMPLETED);
|
if (currentTaskCallbackState.equals(CallbackState.CALLBACK_PROCESSING_COMPLETED)) {
|
||||||
|
return Optional.of(
|
||||||
|
new InvalidCallbackStateException(
|
||||||
|
foundSummary.getTaskId(),
|
||||||
|
currentTaskCallbackState,
|
||||||
|
EnumUtil.allValuesExceptFor(CallbackState.CALLBACK_PROCESSING_COMPLETED)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return Optional.of(
|
||||||
|
new InvalidArgumentException(
|
||||||
|
String.format(
|
||||||
|
"desired callbackState has to be in '%s'",
|
||||||
|
Arrays.toString(
|
||||||
|
new CallbackState[] {
|
||||||
|
CallbackState.CALLBACK_PROCESSING_COMPLETED,
|
||||||
|
CallbackState.CLAIMED,
|
||||||
|
CallbackState.CALLBACK_PROCESSING_REQUIRED
|
||||||
|
}))));
|
||||||
}
|
}
|
||||||
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void standardSettingsOnTaskCreation(TaskImpl task, Classification classification)
|
private void standardSettingsOnTaskCreation(TaskImpl task, Classification classification)
|
||||||
|
@ -1773,10 +1769,8 @@ public class TaskServiceImpl implements TaskService {
|
||||||
// owner can only be changed if task is in state ready
|
// owner can only be changed if task is in state ready
|
||||||
boolean isOwnerChanged = !Objects.equals(newTaskImpl1.getOwner(), oldTaskImpl.getOwner());
|
boolean isOwnerChanged = !Objects.equals(newTaskImpl1.getOwner(), oldTaskImpl.getOwner());
|
||||||
if (isOwnerChanged && oldTaskImpl.getState() != TaskState.READY) {
|
if (isOwnerChanged && oldTaskImpl.getState() != TaskState.READY) {
|
||||||
throw new InvalidStateException(
|
throw new InvalidTaskStateException(
|
||||||
String.format(
|
oldTaskImpl.getId(), oldTaskImpl.getState(), TaskState.READY);
|
||||||
"Task with id %s is in state %s and not in state ready.",
|
|
||||||
oldTaskImpl.getId(), oldTaskImpl.getState()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,14 @@ import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
import pro.taskana.common.internal.InternalTaskanaEngine;
|
import pro.taskana.common.internal.InternalTaskanaEngine;
|
||||||
|
import pro.taskana.common.internal.util.EnumUtil;
|
||||||
import pro.taskana.common.internal.util.IdGenerator;
|
import pro.taskana.common.internal.util.IdGenerator;
|
||||||
import pro.taskana.common.internal.util.ObjectAttributeChangeDetector;
|
import pro.taskana.common.internal.util.ObjectAttributeChangeDetector;
|
||||||
import pro.taskana.spi.history.api.events.task.TaskTransferredEvent;
|
import pro.taskana.spi.history.api.events.task.TaskTransferredEvent;
|
||||||
import pro.taskana.spi.history.internal.HistoryEventManager;
|
import pro.taskana.spi.history.internal.HistoryEventManager;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
import pro.taskana.task.api.exceptions.InvalidStateException;
|
import pro.taskana.task.api.exceptions.InvalidStateException;
|
||||||
|
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
|
||||||
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||||
import pro.taskana.task.api.models.Task;
|
import pro.taskana.task.api.models.Task;
|
||||||
import pro.taskana.task.api.models.TaskSummary;
|
import pro.taskana.task.api.models.TaskSummary;
|
||||||
|
@ -29,6 +31,7 @@ import pro.taskana.task.internal.models.TaskImpl;
|
||||||
import pro.taskana.task.internal.models.TaskSummaryImpl;
|
import pro.taskana.task.internal.models.TaskSummaryImpl;
|
||||||
import pro.taskana.workbasket.api.WorkbasketPermission;
|
import pro.taskana.workbasket.api.WorkbasketPermission;
|
||||||
import pro.taskana.workbasket.api.WorkbasketService;
|
import pro.taskana.workbasket.api.WorkbasketService;
|
||||||
|
import pro.taskana.workbasket.api.exceptions.MismatchedWorkbasketPermissionException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
||||||
import pro.taskana.workbasket.api.models.WorkbasketSummary;
|
import pro.taskana.workbasket.api.models.WorkbasketSummary;
|
||||||
import pro.taskana.workbasket.internal.WorkbasketQueryImpl;
|
import pro.taskana.workbasket.internal.WorkbasketQueryImpl;
|
||||||
|
@ -154,8 +157,8 @@ final class TaskTransferrer {
|
||||||
Task task, WorkbasketSummary destinationWorkbasket, WorkbasketSummary originWorkbasket)
|
Task task, WorkbasketSummary destinationWorkbasket, WorkbasketSummary originWorkbasket)
|
||||||
throws NotAuthorizedException, WorkbasketNotFoundException, InvalidStateException {
|
throws NotAuthorizedException, WorkbasketNotFoundException, InvalidStateException {
|
||||||
if (task.getState().isEndState()) {
|
if (task.getState().isEndState()) {
|
||||||
throw new InvalidStateException(
|
throw new InvalidTaskStateException(
|
||||||
String.format("Task '%s' is in end state and cannot be transferred.", task.getId()));
|
task.getId(), task.getState(), EnumUtil.allValuesExceptFor(TaskState.END_STATES));
|
||||||
}
|
}
|
||||||
workbasketService.checkAuthorization(originWorkbasket.getId(), WorkbasketPermission.TRANSFER);
|
workbasketService.checkAuthorization(originWorkbasket.getId(), WorkbasketPermission.TRANSFER);
|
||||||
checkDestinationWorkbasket(destinationWorkbasket);
|
checkDestinationWorkbasket(destinationWorkbasket);
|
||||||
|
@ -167,9 +170,7 @@ final class TaskTransferrer {
|
||||||
destinationWorkbasket.getId(), WorkbasketPermission.APPEND);
|
destinationWorkbasket.getId(), WorkbasketPermission.APPEND);
|
||||||
|
|
||||||
if (destinationWorkbasket.isMarkedForDeletion()) {
|
if (destinationWorkbasket.isMarkedForDeletion()) {
|
||||||
throw new WorkbasketNotFoundException(
|
throw new WorkbasketNotFoundException(destinationWorkbasket.getId());
|
||||||
destinationWorkbasket.getId(),
|
|
||||||
String.format("Workbasket '%s' was marked for deletion.", destinationWorkbasket.getId()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,18 +205,17 @@ final class TaskTransferrer {
|
||||||
if (taskId == null || taskId.isEmpty()) {
|
if (taskId == null || taskId.isEmpty()) {
|
||||||
error = new InvalidArgumentException("TaskId should not be null or empty");
|
error = new InvalidArgumentException("TaskId should not be null or empty");
|
||||||
} else if (taskSummary == null) {
|
} else if (taskSummary == null) {
|
||||||
error =
|
error = new TaskNotFoundException(taskId);
|
||||||
new TaskNotFoundException(
|
|
||||||
taskId, String.format("Task with id '%s' was not found.", taskId));
|
|
||||||
} else if (taskSummary.getState().isEndState()) {
|
} else if (taskSummary.getState().isEndState()) {
|
||||||
error =
|
error =
|
||||||
new InvalidStateException(
|
new InvalidTaskStateException(
|
||||||
String.format("Task in end state with id '%s' cannot be transferred.", taskId));
|
taskId, taskSummary.getState(), EnumUtil.allValuesExceptFor(TaskState.END_STATES));
|
||||||
} else if (!sourceWorkbasketIds.contains(taskSummary.getWorkbasketSummary().getId())) {
|
} else if (!sourceWorkbasketIds.contains(taskSummary.getWorkbasketSummary().getId())) {
|
||||||
error =
|
error =
|
||||||
new NotAuthorizedException(
|
new MismatchedWorkbasketPermissionException(
|
||||||
"The workbasket of this task got not TRANSFER permissions. TaskId=" + taskId,
|
taskanaEngine.getEngine().getCurrentUserContext().getUserid(),
|
||||||
taskanaEngine.getEngine().getCurrentUserContext().getUserid());
|
taskSummary.getWorkbasketSummary().getId(),
|
||||||
|
WorkbasketPermission.TRANSFER);
|
||||||
}
|
}
|
||||||
return Optional.ofNullable(error);
|
return Optional.ofNullable(error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import pro.taskana.common.api.TaskanaEngine;
|
||||||
import pro.taskana.common.api.TimeInterval;
|
import pro.taskana.common.api.TimeInterval;
|
||||||
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
|
import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
import pro.taskana.common.internal.JobServiceImpl;
|
import pro.taskana.common.internal.JobServiceImpl;
|
||||||
import pro.taskana.common.internal.jobs.AbstractTaskanaJob;
|
import pro.taskana.common.internal.jobs.AbstractTaskanaJob;
|
||||||
|
@ -62,7 +63,7 @@ public class TaskCleanupJob extends AbstractTaskanaJob {
|
||||||
|
|
||||||
LOGGER.info("Job ended successfully. {} tasks deleted.", totalNumberOfTasksDeleted);
|
LOGGER.info("Job ended successfully. {} tasks deleted.", totalNumberOfTasksDeleted);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TaskanaException("Error while processing TaskCleanupJob.", e);
|
throw new SystemException("Error while processing TaskCleanupJob.", e);
|
||||||
} finally {
|
} finally {
|
||||||
scheduleNextCleanupJob();
|
scheduleNextCleanupJob();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import pro.taskana.common.api.ScheduledJob;
|
import pro.taskana.common.api.ScheduledJob;
|
||||||
import pro.taskana.common.api.TaskanaEngine;
|
import pro.taskana.common.api.TaskanaEngine;
|
||||||
|
import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
import pro.taskana.common.internal.jobs.AbstractTaskanaJob;
|
import pro.taskana.common.internal.jobs.AbstractTaskanaJob;
|
||||||
import pro.taskana.common.internal.transaction.TaskanaTransactionProvider;
|
import pro.taskana.common.internal.transaction.TaskanaTransactionProvider;
|
||||||
|
@ -43,7 +44,7 @@ public class TaskRefreshJob extends AbstractTaskanaJob {
|
||||||
affectedTaskIds, serviceLevelChanged, priorityChanged);
|
affectedTaskIds, serviceLevelChanged, priorityChanged);
|
||||||
LOGGER.info("TaskRefreshJob ended successfully.");
|
LOGGER.info("TaskRefreshJob ended successfully.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TaskanaException("Error while processing TaskRefreshJob.", e);
|
throw new SystemException("Error while processing TaskRefreshJob.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
||||||
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
import pro.taskana.workbasket.api.exceptions.InvalidWorkbasketException;
|
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketAccessItemAlreadyExistException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketAccessItemAlreadyExistException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketInUseException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketInUseException;
|
||||||
|
@ -56,14 +55,14 @@ public interface WorkbasketService {
|
||||||
*
|
*
|
||||||
* @param workbasket The Workbasket to create
|
* @param workbasket The Workbasket to create
|
||||||
* @return the created and inserted Workbasket
|
* @return the created and inserted Workbasket
|
||||||
* @throws InvalidWorkbasketException If a required property of the Workbasket is not set.
|
* @throws InvalidArgumentException If a required property of the Workbasket is not set.
|
||||||
* @throws NotAuthorizedException if the current user is not member of role BUSINESS_ADMIN or
|
* @throws NotAuthorizedException if the current user is not member of role BUSINESS_ADMIN or
|
||||||
* ADMIN
|
* ADMIN
|
||||||
* @throws WorkbasketAlreadyExistException if the Workbasket exists already
|
* @throws WorkbasketAlreadyExistException if the Workbasket exists already
|
||||||
* @throws DomainNotFoundException if the domain does not exist in the configuration.
|
* @throws DomainNotFoundException if the domain does not exist in the configuration.
|
||||||
*/
|
*/
|
||||||
Workbasket createWorkbasket(Workbasket workbasket)
|
Workbasket createWorkbasket(Workbasket workbasket)
|
||||||
throws InvalidWorkbasketException, NotAuthorizedException, WorkbasketAlreadyExistException,
|
throws InvalidArgumentException, NotAuthorizedException, WorkbasketAlreadyExistException,
|
||||||
DomainNotFoundException;
|
DomainNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,20 +70,21 @@ public interface WorkbasketService {
|
||||||
*
|
*
|
||||||
* @param workbasket The Workbasket to update
|
* @param workbasket The Workbasket to update
|
||||||
* @return the updated Workbasket
|
* @return the updated Workbasket
|
||||||
* @throws InvalidWorkbasketException if workbasket name or type is invalid
|
* @throws InvalidArgumentException if workbasket name or type is invalid
|
||||||
* @throws NotAuthorizedException if the current user is not authorized to update the work basket
|
* @throws NotAuthorizedException if the current user is not authorized to update the {@linkplain
|
||||||
* @throws WorkbasketNotFoundException if the workbasket cannot be found.
|
* Workbasket}
|
||||||
* @throws ConcurrencyException if an attempt is made to update the workbasket and another user
|
* @throws WorkbasketNotFoundException if the {@linkplain Workbasket} cannot be found.
|
||||||
* updated it already
|
* @throws ConcurrencyException if an attempt is made to update the {@linkplain Workbasket} and
|
||||||
|
* another user updated it already
|
||||||
*/
|
*/
|
||||||
Workbasket updateWorkbasket(Workbasket workbasket)
|
Workbasket updateWorkbasket(Workbasket workbasket)
|
||||||
throws InvalidWorkbasketException, NotAuthorizedException, WorkbasketNotFoundException,
|
throws InvalidArgumentException, NotAuthorizedException, WorkbasketNotFoundException,
|
||||||
ConcurrencyException;
|
ConcurrencyException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new WorkbasketAccessItem which is not inserted.
|
* Returns a new WorkbasketAccessItem which is not inserted.
|
||||||
*
|
*
|
||||||
* @param workbasketId the workbasket id used to identify the referenced workbasket
|
* @param workbasketId the workbasket id used to identify the referenced {@linkplain Workbasket}
|
||||||
* @param accessId the group id or user id for which access is controlled
|
* @param accessId the group id or user id for which access is controlled
|
||||||
* @return new WorkbasketAccessItem
|
* @return new WorkbasketAccessItem
|
||||||
*/
|
*/
|
||||||
|
@ -138,7 +138,8 @@ public interface WorkbasketService {
|
||||||
* specified, the current user needs all of them.
|
* specified, the current user needs all of them.
|
||||||
* @throws NotAuthorizedException if the current user has not the requested authorization for the
|
* @throws NotAuthorizedException if the current user has not the requested authorization for the
|
||||||
* specified workbasket
|
* specified workbasket
|
||||||
* @throws WorkbasketNotFoundException if the workbasket cannot be found for the given ID.
|
* @throws WorkbasketNotFoundException if the {@linkplain Workbasket} cannot be found for the
|
||||||
|
* given {@linkplain Workbasket#getId() id}.
|
||||||
*/
|
*/
|
||||||
void checkAuthorization(String workbasketId, WorkbasketPermission... permission)
|
void checkAuthorization(String workbasketId, WorkbasketPermission... permission)
|
||||||
throws NotAuthorizedException, WorkbasketNotFoundException;
|
throws NotAuthorizedException, WorkbasketNotFoundException;
|
||||||
|
@ -159,21 +160,24 @@ public interface WorkbasketService {
|
||||||
throws NotAuthorizedException, WorkbasketNotFoundException;
|
throws NotAuthorizedException, WorkbasketNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all {@link WorkbasketAccessItem s} for a Workbasket.
|
* Get all {@link WorkbasketAccessItem s} for a {@linkplain Workbasket}.
|
||||||
*
|
*
|
||||||
* @param workbasketId the id of the Workbasket
|
* @param workbasketId the {@linkplain Workbasket#getId() id} of the {@linkplain Workbasket}
|
||||||
* @return List of WorkbasketAccessItems for the Workbasket with workbasketKey
|
* @return List of {@linkplain WorkbasketAccessItem}s for the {@linkplain Workbasket}
|
||||||
* @throws NotAuthorizedException if the current user is not member of role BUSINESS_ADMIN or
|
* @throws NotAuthorizedException if the current user is not member of role {@linkplain
|
||||||
* ADMIN
|
* pro.taskana.common.api.TaskanaRole#BUSINESS_ADMIN} or {@linkplain
|
||||||
|
* pro.taskana.common.api.TaskanaRole#ADMIN}
|
||||||
|
* @throws WorkbasketNotFoundException if the {@linkplain Workbasket} cannot be found for the
|
||||||
|
* given {@linkplain Workbasket#getId() id}.
|
||||||
*/
|
*/
|
||||||
List<WorkbasketAccessItem> getWorkbasketAccessItems(String workbasketId)
|
List<WorkbasketAccessItem> getWorkbasketAccessItems(String workbasketId)
|
||||||
throws NotAuthorizedException;
|
throws NotAuthorizedException, WorkbasketNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setting up the new WorkbasketAccessItems for a Workbasket. Already stored values will be
|
* Setting up the new WorkbasketAccessItems for a Workbasket. Already stored values will be
|
||||||
* completely replaced by the current ones.
|
* completely replaced by the current ones.
|
||||||
*
|
*
|
||||||
* <p>Preconditions for each {@link WorkbasketAccessItem} in {@code wbAccessItems}:
|
* <p>Preconditions for each {@link WorkbasketAccessItem} then {@code wbAccessItems}:
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link WorkbasketAccessItem#getWorkbasketId()} is not null
|
* <li>{@link WorkbasketAccessItem#getWorkbasketId()} is not null
|
||||||
|
@ -189,10 +193,12 @@ public interface WorkbasketService {
|
||||||
* ADMIN
|
* ADMIN
|
||||||
* @throws WorkbasketAccessItemAlreadyExistException if {@code wbAccessItems} contains multiple
|
* @throws WorkbasketAccessItemAlreadyExistException if {@code wbAccessItems} contains multiple
|
||||||
* accessItems with the same accessId.
|
* accessItems with the same accessId.
|
||||||
|
* @throws WorkbasketNotFoundException if the {@linkplain Workbasket} cannot be found for the
|
||||||
|
* given {@linkplain Workbasket#getId() id}.
|
||||||
*/
|
*/
|
||||||
void setWorkbasketAccessItems(String workbasketId, List<WorkbasketAccessItem> wbAccessItems)
|
void setWorkbasketAccessItems(String workbasketId, List<WorkbasketAccessItem> wbAccessItems)
|
||||||
throws InvalidArgumentException, NotAuthorizedException,
|
throws InvalidArgumentException, NotAuthorizedException,
|
||||||
WorkbasketAccessItemAlreadyExistException;
|
WorkbasketAccessItemAlreadyExistException, WorkbasketNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method provides a query builder for querying the database.
|
* This method provides a query builder for querying the database.
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
package pro.taskana.workbasket.api.exceptions;
|
|
||||||
|
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This exception is thrown when a request is made to insert or update a workbasket that is missing
|
|
||||||
* a required property.
|
|
||||||
*/
|
|
||||||
public class InvalidWorkbasketException extends TaskanaException {
|
|
||||||
|
|
||||||
public InvalidWorkbasketException(String msg) {
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package pro.taskana.workbasket.api.exceptions;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.workbasket.api.WorkbasketPermission;
|
||||||
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when the current user does not have a certain {@linkplain
|
||||||
|
* WorkbasketPermission permission} on a {@linkplain Workbasket}.
|
||||||
|
*/
|
||||||
|
public class MismatchedWorkbasketPermissionException extends NotAuthorizedException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY_KEY_DOMAIN = "WORKBASKET_WITH_KEY_MISMATCHED_PERMISSION";
|
||||||
|
public static final String ERROR_KEY_ID = "WORKBASKET_WITH_ID_MISMATCHED_PERMISSION";
|
||||||
|
private final String currentUserId;
|
||||||
|
private final WorkbasketPermission[] requiredPermissions;
|
||||||
|
private final String workbasketId;
|
||||||
|
private final String workbasketKey;
|
||||||
|
private final String domain;
|
||||||
|
|
||||||
|
public MismatchedWorkbasketPermissionException(
|
||||||
|
String currentUserId, String workbasketId, WorkbasketPermission... requiredPermissions) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"Not authorized. The current user '%s' has no '%s' permission(s) for Workbasket '%s'.",
|
||||||
|
currentUserId, Arrays.toString(requiredPermissions), workbasketId),
|
||||||
|
ErrorCode.of(
|
||||||
|
ERROR_KEY_ID,
|
||||||
|
MapCreator.of(
|
||||||
|
"currentUserId",
|
||||||
|
currentUserId,
|
||||||
|
"workbasketId",
|
||||||
|
workbasketId,
|
||||||
|
"requiredPermissions",
|
||||||
|
requiredPermissions)));
|
||||||
|
|
||||||
|
this.currentUserId = currentUserId;
|
||||||
|
this.requiredPermissions = requiredPermissions;
|
||||||
|
this.workbasketId = workbasketId;
|
||||||
|
workbasketKey = null;
|
||||||
|
domain = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MismatchedWorkbasketPermissionException(
|
||||||
|
String currentUserId,
|
||||||
|
String workbasketKey,
|
||||||
|
String domain,
|
||||||
|
WorkbasketPermission... requiredPermissions) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"Not authorized. The current user '%s' has no '%s' permission for "
|
||||||
|
+ "Workbasket with key '%s' in domain '%s'.",
|
||||||
|
currentUserId, Arrays.toString(requiredPermissions), workbasketKey, domain),
|
||||||
|
ErrorCode.of(
|
||||||
|
ERROR_KEY_KEY_DOMAIN,
|
||||||
|
MapCreator.of(
|
||||||
|
"currentUserId",
|
||||||
|
currentUserId,
|
||||||
|
"workbasketKey",
|
||||||
|
workbasketKey,
|
||||||
|
"domain",
|
||||||
|
domain,
|
||||||
|
"requiredPermissions",
|
||||||
|
requiredPermissions)));
|
||||||
|
|
||||||
|
this.currentUserId = currentUserId;
|
||||||
|
this.requiredPermissions = requiredPermissions;
|
||||||
|
this.workbasketKey = workbasketKey;
|
||||||
|
this.domain = domain;
|
||||||
|
workbasketId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWorkbasketKey() {
|
||||||
|
return workbasketKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomain() {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWorkbasketId() {
|
||||||
|
return workbasketId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorkbasketPermission[] getRequiredPermissions() {
|
||||||
|
return requiredPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentUserId() {
|
||||||
|
return currentUserId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,14 @@
|
||||||
package pro.taskana.workbasket.api.exceptions;
|
package pro.taskana.workbasket.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
||||||
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
|
|
||||||
/** This exception is used to communicate that a user is not authorized to query a Workbasket. */
|
/** This exception is thrown when a user is not authorized to query a {@linkplain Workbasket}. */
|
||||||
public class NotAuthorizedToQueryWorkbasketException extends TaskanaRuntimeException {
|
public class NotAuthorizedToQueryWorkbasketException extends TaskanaRuntimeException {
|
||||||
|
|
||||||
public NotAuthorizedToQueryWorkbasketException(String msg) {
|
public NotAuthorizedToQueryWorkbasketException(
|
||||||
super(msg);
|
String message, ErrorCode errorCode, Throwable cause) {
|
||||||
}
|
super(message, errorCode, cause);
|
||||||
|
|
||||||
public NotAuthorizedToQueryWorkbasketException(String msg, Throwable cause) {
|
|
||||||
super(msg, cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,35 @@
|
||||||
package pro.taskana.workbasket.api.exceptions;
|
package pro.taskana.workbasket.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
import pro.taskana.workbasket.api.models.WorkbasketAccessItem;
|
import pro.taskana.workbasket.api.models.WorkbasketAccessItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when an already existing {@linkplain WorkbasketAccessItem} was tried to
|
||||||
|
* be created.
|
||||||
|
*/
|
||||||
public class WorkbasketAccessItemAlreadyExistException extends TaskanaException {
|
public class WorkbasketAccessItemAlreadyExistException extends TaskanaException {
|
||||||
|
|
||||||
public WorkbasketAccessItemAlreadyExistException(WorkbasketAccessItem accessItem) {
|
public static final String ERROR_KEY = "WORKBASKET_ACCESS_ITEM_ALREADY_EXISTS";
|
||||||
|
private final String accessId;
|
||||||
|
private final String workbasketId;
|
||||||
|
|
||||||
|
public WorkbasketAccessItemAlreadyExistException(String accessId, String workbasketId) {
|
||||||
super(
|
super(
|
||||||
String.format(
|
String.format(
|
||||||
"WorkbasketAccessItem for accessId '%s' "
|
"WorkbasketAccessItem with access id '%s' and workbasket id '%s' already exists.",
|
||||||
+ "and WorkbasketId '%s', WorkbasketKey '%s' exists already.",
|
accessId, workbasketId),
|
||||||
accessItem.getAccessId(), accessItem.getWorkbasketId(), accessItem.getWorkbasketKey()));
|
ErrorCode.of(ERROR_KEY, MapCreator.of("accessId", accessId, "workbasketId", workbasketId)));
|
||||||
|
this.accessId = accessId;
|
||||||
|
this.workbasketId = workbasketId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessId() {
|
||||||
|
return accessId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWorkbasketId() {
|
||||||
|
return workbasketId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,33 @@
|
||||||
package pro.taskana.workbasket.api.exceptions;
|
package pro.taskana.workbasket.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
import pro.taskana.workbasket.api.models.Workbasket;
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
|
|
||||||
/** Thrown, when a workbasket does already exits, but wanted to create with same ID. */
|
/**
|
||||||
|
* This exception is thrown when an already existing {@linkplain Workbasket} was tried to be
|
||||||
|
* created.
|
||||||
|
*/
|
||||||
public class WorkbasketAlreadyExistException extends TaskanaException {
|
public class WorkbasketAlreadyExistException extends TaskanaException {
|
||||||
|
|
||||||
public WorkbasketAlreadyExistException(Workbasket workbasket) {
|
public static final String ERROR_KEY = "WORKBASKET_ALREADY_EXISTS";
|
||||||
|
private final String key;
|
||||||
|
private final String domain;
|
||||||
|
|
||||||
|
public WorkbasketAlreadyExistException(String key, String domain) {
|
||||||
super(
|
super(
|
||||||
"A workbasket with key '"
|
String.format("A Workbasket with key '%s' already exists in domain '%s'.", key, domain),
|
||||||
+ workbasket.getKey()
|
ErrorCode.of(ERROR_KEY, MapCreator.of("workbasketKey", key, "domain", domain)));
|
||||||
+ "' already exists in domain '"
|
this.key = key;
|
||||||
+ workbasket.getDomain()
|
this.domain = domain;
|
||||||
+ "'.");
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomain() {
|
||||||
|
return domain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,29 @@
|
||||||
package pro.taskana.workbasket.api.exceptions;
|
package pro.taskana.workbasket.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
|
|
||||||
/** Thrown if a specific Workbasket does have content and should be deleted. */
|
/**
|
||||||
|
* This exception is thrown when a specific {@linkplain Workbasket} does have content and is tried
|
||||||
|
* to be deleted.
|
||||||
|
*/
|
||||||
public class WorkbasketInUseException extends TaskanaException {
|
public class WorkbasketInUseException extends TaskanaException {
|
||||||
|
|
||||||
public WorkbasketInUseException(String msg) {
|
public static final String ERROR_KEY = "WORKBASKET_IN_USE";
|
||||||
super(msg);
|
private final String workbasketId;
|
||||||
|
|
||||||
|
public WorkbasketInUseException(String workbasketId) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"Workbasket '%s' contains non-completed Tasks and can't be marked for deletion.",
|
||||||
|
workbasketId),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("workbasketId", workbasketId)));
|
||||||
|
this.workbasketId = workbasketId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWorkbasketId() {
|
||||||
|
return workbasketId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package pro.taskana.workbasket.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when a {@linkplain Workbasket}, which was {@linkplain
|
||||||
|
* Workbasket#isMarkedForDeletion() marked for deletion}, could not be deleted.
|
||||||
|
*/
|
||||||
|
public class WorkbasketMarkedForDeletionException extends TaskanaException {
|
||||||
|
|
||||||
|
public static final String ERROR_KEY = "WORKBASKET_MARKED_FOR_DELETION";
|
||||||
|
private final String workbasketId;
|
||||||
|
|
||||||
|
public WorkbasketMarkedForDeletionException(String workbasketId) {
|
||||||
|
super(
|
||||||
|
String.format(
|
||||||
|
"Workbasket with id '%s' could not be deleted, but was marked for deletion.",
|
||||||
|
workbasketId),
|
||||||
|
ErrorCode.of(ERROR_KEY, MapCreator.of("workbasketId", workbasketId)));
|
||||||
|
this.workbasketId = workbasketId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWorkbasketId() {
|
||||||
|
return workbasketId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,23 +1,41 @@
|
||||||
package pro.taskana.workbasket.api.exceptions;
|
package pro.taskana.workbasket.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
import pro.taskana.common.api.exceptions.NotFoundException;
|
import pro.taskana.common.api.exceptions.NotFoundException;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
|
|
||||||
/** This exception will be thrown if a specific workbasket is not in the database. */
|
/** This exception is thrown when a specific {@linkplain Workbasket} is not in the database. */
|
||||||
public class WorkbasketNotFoundException extends NotFoundException {
|
public class WorkbasketNotFoundException extends NotFoundException {
|
||||||
|
|
||||||
private String key;
|
public static final String ERROR_KEY_ID = "WORKBASKET_WITH_ID_NOT_FOUND";
|
||||||
private String domain;
|
public static final String ERROR_KEY_KEY_DOMAIN = "WORKBASKET_WITH_KEY_NOT_FOUND";
|
||||||
|
private final String id;
|
||||||
|
private final String key;
|
||||||
|
private final String domain;
|
||||||
|
|
||||||
public WorkbasketNotFoundException(String id, String msg) {
|
public WorkbasketNotFoundException(String id) {
|
||||||
super(id, msg);
|
super(
|
||||||
|
String.format("Workbasket with id '%s' was not found.", id),
|
||||||
|
ErrorCode.of(ERROR_KEY_ID, MapCreator.of("workbasketId", id)));
|
||||||
|
this.id = id;
|
||||||
|
key = null;
|
||||||
|
domain = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorkbasketNotFoundException(String key, String domain, String msg) {
|
public WorkbasketNotFoundException(String key, String domain) {
|
||||||
super(null, msg);
|
super(
|
||||||
|
String.format("Workbasket with key '%s' and domain '%s' was not found.", key, domain),
|
||||||
|
ErrorCode.of(ERROR_KEY_KEY_DOMAIN, MapCreator.of("workbasketKey", key, "domain", domain)));
|
||||||
|
id = null;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.domain = domain;
|
this.domain = domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import java.util.List;
|
||||||
import org.apache.ibatis.exceptions.PersistenceException;
|
import org.apache.ibatis.exceptions.PersistenceException;
|
||||||
import org.apache.ibatis.session.RowBounds;
|
import org.apache.ibatis.session.RowBounds;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
||||||
import pro.taskana.common.internal.InternalTaskanaEngine;
|
import pro.taskana.common.internal.InternalTaskanaEngine;
|
||||||
import pro.taskana.workbasket.api.AbstractWorkbasketAccessItemQuery;
|
import pro.taskana.workbasket.api.AbstractWorkbasketAccessItemQuery;
|
||||||
|
@ -93,7 +94,7 @@ abstract class AbstractWorkbasketAccessItemQueryImpl<
|
||||||
} catch (PersistenceException e) {
|
} catch (PersistenceException e) {
|
||||||
if (e.getMessage().contains("ERRORCODE=-4470")) {
|
if (e.getMessage().contains("ERRORCODE=-4470")) {
|
||||||
TaskanaRuntimeException ex =
|
TaskanaRuntimeException ex =
|
||||||
new TaskanaRuntimeException(
|
new SystemException(
|
||||||
"The offset beginning was set over the amount of result-rows.", e.getCause());
|
"The offset beginning was set over the amount of result-rows.", e.getCause());
|
||||||
ex.setStackTrace(e.getStackTrace());
|
ex.setStackTrace(e.getStackTrace());
|
||||||
throw ex;
|
throw ex;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import java.util.List;
|
||||||
import org.apache.ibatis.exceptions.PersistenceException;
|
import org.apache.ibatis.exceptions.PersistenceException;
|
||||||
import org.apache.ibatis.session.RowBounds;
|
import org.apache.ibatis.session.RowBounds;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
||||||
import pro.taskana.common.internal.InternalTaskanaEngine;
|
import pro.taskana.common.internal.InternalTaskanaEngine;
|
||||||
import pro.taskana.workbasket.api.AccessItemQueryColumnName;
|
import pro.taskana.workbasket.api.AccessItemQueryColumnName;
|
||||||
|
@ -116,7 +117,7 @@ public class WorkbasketAccessItemQueryImpl implements WorkbasketAccessItemQuery
|
||||||
} catch (PersistenceException e) {
|
} catch (PersistenceException e) {
|
||||||
if (e.getMessage().contains("ERRORCODE=-4470")) {
|
if (e.getMessage().contains("ERRORCODE=-4470")) {
|
||||||
TaskanaRuntimeException ex =
|
TaskanaRuntimeException ex =
|
||||||
new TaskanaRuntimeException(
|
new SystemException(
|
||||||
"The offset beginning was set over the amount of result-rows.", e.getCause());
|
"The offset beginning was set over the amount of result-rows.", e.getCause());
|
||||||
ex.setStackTrace(e.getStackTrace());
|
ex.setStackTrace(e.getStackTrace());
|
||||||
throw ex;
|
throw ex;
|
||||||
|
|
|
@ -177,10 +177,10 @@ public class WorkbasketQueryImpl implements WorkbasketQuery {
|
||||||
.checkRoleMembership(TaskanaRole.ADMIN, TaskanaRole.BUSINESS_ADMIN, TaskanaRole.TASK_ADMIN);
|
.checkRoleMembership(TaskanaRole.ADMIN, TaskanaRole.BUSINESS_ADMIN, TaskanaRole.TASK_ADMIN);
|
||||||
// Checking pre-conditions
|
// Checking pre-conditions
|
||||||
if (permission == null) {
|
if (permission == null) {
|
||||||
throw new InvalidArgumentException("Permission can´t be null.");
|
throw new InvalidArgumentException("Permission can't be null.");
|
||||||
}
|
}
|
||||||
if (accessIds == null || accessIds.length == 0) {
|
if (accessIds == null || accessIds.length == 0) {
|
||||||
throw new InvalidArgumentException("accessIds can´t be NULL or empty.");
|
throw new InvalidArgumentException("accessIds can't be NULL or empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up permissions and ids
|
// set up permissions and ids
|
||||||
|
@ -376,7 +376,7 @@ public class WorkbasketQueryImpl implements WorkbasketQuery {
|
||||||
} catch (PersistenceException e) {
|
} catch (PersistenceException e) {
|
||||||
if (e.getMessage().contains("ERRORCODE=-4470")) {
|
if (e.getMessage().contains("ERRORCODE=-4470")) {
|
||||||
TaskanaRuntimeException ex =
|
TaskanaRuntimeException ex =
|
||||||
new TaskanaRuntimeException(
|
new SystemException(
|
||||||
"The offset beginning was set over the amount of result-rows.", e.getCause());
|
"The offset beginning was set over the amount of result-rows.", e.getCause());
|
||||||
ex.setStackTrace(e.getStackTrace());
|
ex.setStackTrace(e.getStackTrace());
|
||||||
throw ex;
|
throw ex;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.apache.ibatis.exceptions.PersistenceException;
|
import org.apache.ibatis.exceptions.PersistenceException;
|
||||||
|
@ -40,10 +41,11 @@ import pro.taskana.workbasket.api.WorkbasketAccessItemQuery;
|
||||||
import pro.taskana.workbasket.api.WorkbasketPermission;
|
import pro.taskana.workbasket.api.WorkbasketPermission;
|
||||||
import pro.taskana.workbasket.api.WorkbasketQuery;
|
import pro.taskana.workbasket.api.WorkbasketQuery;
|
||||||
import pro.taskana.workbasket.api.WorkbasketService;
|
import pro.taskana.workbasket.api.WorkbasketService;
|
||||||
import pro.taskana.workbasket.api.exceptions.InvalidWorkbasketException;
|
import pro.taskana.workbasket.api.exceptions.MismatchedWorkbasketPermissionException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketAccessItemAlreadyExistException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketAccessItemAlreadyExistException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketInUseException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketInUseException;
|
||||||
|
import pro.taskana.workbasket.api.exceptions.WorkbasketMarkedForDeletionException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
||||||
import pro.taskana.workbasket.api.models.Workbasket;
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
import pro.taskana.workbasket.api.models.WorkbasketAccessItem;
|
import pro.taskana.workbasket.api.models.WorkbasketAccessItem;
|
||||||
|
@ -82,10 +84,11 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
result = workbasketMapper.findById(workbasketId);
|
result = workbasketMapper.findById(workbasketId);
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new WorkbasketNotFoundException(
|
throw new WorkbasketNotFoundException(workbasketId);
|
||||||
workbasketId, "Workbasket with id " + workbasketId + " was not found.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!taskanaEngine
|
if (!taskanaEngine
|
||||||
.getEngine()
|
.getEngine()
|
||||||
.isUserInRole(TaskanaRole.ADMIN, TaskanaRole.BUSINESS_ADMIN, TaskanaRole.TASK_ADMIN)) {
|
.isUserInRole(TaskanaRole.ADMIN, TaskanaRole.BUSINESS_ADMIN, TaskanaRole.TASK_ADMIN)) {
|
||||||
|
@ -100,30 +103,25 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
@Override
|
@Override
|
||||||
public Workbasket getWorkbasket(String workbasketKey, String domain)
|
public Workbasket getWorkbasket(String workbasketKey, String domain)
|
||||||
throws WorkbasketNotFoundException, NotAuthorizedException {
|
throws WorkbasketNotFoundException, NotAuthorizedException {
|
||||||
Workbasket result;
|
if (!taskanaEngine
|
||||||
try {
|
.getEngine()
|
||||||
taskanaEngine.openConnection();
|
.isUserInRole(TaskanaRole.ADMIN, TaskanaRole.BUSINESS_ADMIN, TaskanaRole.TASK_ADMIN)) {
|
||||||
result = workbasketMapper.findByKeyAndDomain(workbasketKey, domain);
|
this.checkAuthorization(workbasketKey, domain, WorkbasketPermission.READ);
|
||||||
if (result == null) {
|
|
||||||
throw new WorkbasketNotFoundException(
|
|
||||||
workbasketKey,
|
|
||||||
domain,
|
|
||||||
"Workbasket with key " + workbasketKey + " and domain " + domain + " was not found.");
|
|
||||||
}
|
|
||||||
if (!taskanaEngine
|
|
||||||
.getEngine()
|
|
||||||
.isUserInRole(TaskanaRole.ADMIN, TaskanaRole.BUSINESS_ADMIN, TaskanaRole.TASK_ADMIN)) {
|
|
||||||
this.checkAuthorization(workbasketKey, domain, WorkbasketPermission.READ);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} finally {
|
|
||||||
taskanaEngine.returnConnection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Workbasket workbasket =
|
||||||
|
taskanaEngine.openAndReturnConnection(
|
||||||
|
() -> workbasketMapper.findByKeyAndDomain(workbasketKey, domain));
|
||||||
|
if (workbasket == null) {
|
||||||
|
throw new WorkbasketNotFoundException(workbasketKey, domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
return workbasket;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Workbasket createWorkbasket(Workbasket newWorkbasket)
|
public Workbasket createWorkbasket(Workbasket newWorkbasket)
|
||||||
throws InvalidWorkbasketException, NotAuthorizedException, WorkbasketAlreadyExistException,
|
throws InvalidArgumentException, NotAuthorizedException, WorkbasketAlreadyExistException,
|
||||||
DomainNotFoundException {
|
DomainNotFoundException {
|
||||||
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
||||||
|
|
||||||
|
@ -136,7 +134,8 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
Workbasket existingWorkbasket =
|
Workbasket existingWorkbasket =
|
||||||
workbasketMapper.findByKeyAndDomain(newWorkbasket.getKey(), newWorkbasket.getDomain());
|
workbasketMapper.findByKeyAndDomain(newWorkbasket.getKey(), newWorkbasket.getDomain());
|
||||||
if (existingWorkbasket != null) {
|
if (existingWorkbasket != null) {
|
||||||
throw new WorkbasketAlreadyExistException(existingWorkbasket);
|
throw new WorkbasketAlreadyExistException(
|
||||||
|
existingWorkbasket.getKey(), existingWorkbasket.getDomain());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (workbasket.getId() == null || workbasket.getId().isEmpty()) {
|
if (workbasket.getId() == null || workbasket.getId().isEmpty()) {
|
||||||
|
@ -169,8 +168,8 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Workbasket updateWorkbasket(Workbasket workbasketToUpdate)
|
public Workbasket updateWorkbasket(Workbasket workbasketToUpdate)
|
||||||
throws NotAuthorizedException, WorkbasketNotFoundException, ConcurrencyException,
|
throws InvalidArgumentException, NotAuthorizedException, WorkbasketNotFoundException,
|
||||||
InvalidWorkbasketException {
|
ConcurrencyException {
|
||||||
|
|
||||||
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
||||||
WorkbasketImpl workbasketImplToUpdate = (WorkbasketImpl) workbasketToUpdate;
|
WorkbasketImpl workbasketImplToUpdate = (WorkbasketImpl) workbasketToUpdate;
|
||||||
|
@ -179,8 +178,21 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
|
|
||||||
Workbasket oldWorkbasket =
|
Workbasket oldWorkbasket;
|
||||||
this.getWorkbasket(workbasketImplToUpdate.getKey(), workbasketImplToUpdate.getDomain());
|
|
||||||
|
if (workbasketImplToUpdate.getId() == null || workbasketImplToUpdate.getId().isEmpty()) {
|
||||||
|
oldWorkbasket =
|
||||||
|
getWorkbasket(workbasketImplToUpdate.getKey(), workbasketImplToUpdate.getDomain());
|
||||||
|
} else {
|
||||||
|
oldWorkbasket = getWorkbasket(workbasketImplToUpdate.getId());
|
||||||
|
// changing key or domain is not allowed
|
||||||
|
if (!oldWorkbasket.getKey().equals(workbasketToUpdate.getKey())
|
||||||
|
|| !oldWorkbasket.getDomain().equals(workbasketToUpdate.getDomain())) {
|
||||||
|
throw new WorkbasketNotFoundException(
|
||||||
|
workbasketToUpdate.getKey(), workbasketToUpdate.getDomain());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
checkModifiedHasNotChanged(oldWorkbasket, workbasketImplToUpdate);
|
checkModifiedHasNotChanged(oldWorkbasket, workbasketImplToUpdate);
|
||||||
workbasketImplToUpdate.setModified(Instant.now());
|
workbasketImplToUpdate.setModified(Instant.now());
|
||||||
|
|
||||||
|
@ -248,11 +260,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
}
|
}
|
||||||
WorkbasketImpl wb = workbasketMapper.findById(workbasketAccessItem.getWorkbasketId());
|
WorkbasketImpl wb = workbasketMapper.findById(workbasketAccessItem.getWorkbasketId());
|
||||||
if (wb == null) {
|
if (wb == null) {
|
||||||
throw new WorkbasketNotFoundException(
|
throw new WorkbasketNotFoundException(workbasketAccessItem.getWorkbasketId());
|
||||||
workbasketAccessItem.getWorkbasketId(),
|
|
||||||
String.format(
|
|
||||||
"WorkbasketAccessItem %s refers to a not existing workbasket",
|
|
||||||
workbasketAccessItem));
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
workbasketAccessMapper.insert(accessItem);
|
workbasketAccessMapper.insert(accessItem);
|
||||||
|
@ -286,7 +294,8 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
"UC_ACCESSID_WBID_INDEX_E" // H2
|
"UC_ACCESSID_WBID_INDEX_E" // H2
|
||||||
);
|
);
|
||||||
if (accessItemExistsIdentifier.anyMatch(e.getMessage()::contains)) {
|
if (accessItemExistsIdentifier.anyMatch(e.getMessage()::contains)) {
|
||||||
throw new WorkbasketAccessItemAlreadyExistException(accessItem);
|
throw new WorkbasketAccessItemAlreadyExistException(
|
||||||
|
accessItem.getAccessId(), accessItem.getWorkbasketId());
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -386,40 +395,26 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
|
|
||||||
if (workbasketMapper.findById(workbasketId) == null) {
|
if (workbasketMapper.findById(workbasketId) == null) {
|
||||||
throw new WorkbasketNotFoundException(
|
throw new WorkbasketNotFoundException(workbasketId);
|
||||||
workbasketId, "Workbasket with id " + workbasketId + " was not found.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skipAuthorizationCheck(requestedPermissions)) {
|
if (skipAuthorizationCheck(requestedPermissions)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> accessIds = taskanaEngine.getEngine().getCurrentUserContext().getAccessIds();
|
Optional<List<WorkbasketPermission>> grantedPermissions =
|
||||||
WorkbasketAccessItem wbAcc =
|
Optional.ofNullable(
|
||||||
workbasketAccessMapper.findByWorkbasketAndAccessId(workbasketId, accessIds);
|
workbasketAccessMapper.findByWorkbasketAndAccessId(
|
||||||
if (wbAcc == null) {
|
workbasketId,
|
||||||
throw new NotAuthorizedException(
|
taskanaEngine.getEngine().getCurrentUserContext().getAccessIds()))
|
||||||
"Not authorized. Permission '"
|
.map(this::getPermissionsFromWorkbasketAccessItem);
|
||||||
+ Arrays.toString(requestedPermissions)
|
|
||||||
+ "' on workbasket '"
|
|
||||||
+ workbasketId
|
|
||||||
+ "' is needed.",
|
|
||||||
taskanaEngine.getEngine().getCurrentUserContext().getUserid());
|
|
||||||
}
|
|
||||||
|
|
||||||
List<WorkbasketPermission> grantedPermissions =
|
if (!grantedPermissions.isPresent()
|
||||||
this.getPermissionsFromWorkbasketAccessItem(wbAcc);
|
|| !grantedPermissions.get().containsAll(Arrays.asList(requestedPermissions))) {
|
||||||
|
throw new MismatchedWorkbasketPermissionException(
|
||||||
for (WorkbasketPermission perm : requestedPermissions) {
|
taskanaEngine.getEngine().getCurrentUserContext().getUserid(),
|
||||||
if (!grantedPermissions.contains(perm)) {
|
workbasketId,
|
||||||
throw new NotAuthorizedException(
|
requestedPermissions);
|
||||||
"Not authorized. Permission '"
|
|
||||||
+ perm.name()
|
|
||||||
+ "' on workbasket '"
|
|
||||||
+ workbasketId
|
|
||||||
+ "' is needed.",
|
|
||||||
taskanaEngine.getEngine().getCurrentUserContext().getUserid());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
taskanaEngine.returnConnection();
|
taskanaEngine.returnConnection();
|
||||||
|
@ -434,44 +429,27 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
|
|
||||||
if (workbasketMapper.findByKeyAndDomain(workbasketKey, domain) == null) {
|
if (workbasketMapper.findByKeyAndDomain(workbasketKey, domain) == null) {
|
||||||
throw new WorkbasketNotFoundException(
|
throw new WorkbasketNotFoundException(workbasketKey, domain);
|
||||||
workbasketKey,
|
|
||||||
domain,
|
|
||||||
"Workbasket with key " + workbasketKey + " and domain " + domain + " was not found");
|
|
||||||
}
|
}
|
||||||
if (skipAuthorizationCheck(requestedPermissions)) {
|
if (skipAuthorizationCheck(requestedPermissions)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<String> accessIds = taskanaEngine.getEngine().getCurrentUserContext().getAccessIds();
|
|
||||||
WorkbasketAccessItem wbAcc =
|
|
||||||
workbasketAccessMapper.findByWorkbasketKeyDomainAndAccessId(
|
|
||||||
workbasketKey, domain, accessIds);
|
|
||||||
if (wbAcc == null) {
|
|
||||||
throw new NotAuthorizedException(
|
|
||||||
"Not authorized. Permission '"
|
|
||||||
+ Arrays.toString(requestedPermissions)
|
|
||||||
+ "' on workbasket with key '"
|
|
||||||
+ workbasketKey
|
|
||||||
+ "' and domain '"
|
|
||||||
+ domain
|
|
||||||
+ "' is needed.",
|
|
||||||
taskanaEngine.getEngine().getCurrentUserContext().getUserid());
|
|
||||||
}
|
|
||||||
List<WorkbasketPermission> grantedPermissions =
|
|
||||||
this.getPermissionsFromWorkbasketAccessItem(wbAcc);
|
|
||||||
|
|
||||||
for (WorkbasketPermission perm : requestedPermissions) {
|
Optional<List<WorkbasketPermission>> grantedPermissions =
|
||||||
if (!grantedPermissions.contains(perm)) {
|
Optional.ofNullable(
|
||||||
throw new NotAuthorizedException(
|
workbasketAccessMapper.findByWorkbasketKeyDomainAndAccessId(
|
||||||
"Not authorized. Permission '"
|
workbasketKey,
|
||||||
+ perm.name()
|
domain,
|
||||||
+ "' on workbasket with key '"
|
taskanaEngine.getEngine().getCurrentUserContext().getAccessIds()))
|
||||||
+ workbasketKey
|
.map(this::getPermissionsFromWorkbasketAccessItem);
|
||||||
+ "' and domain '"
|
|
||||||
+ domain
|
if (!grantedPermissions.isPresent()
|
||||||
+ "' is needed.",
|
|| !grantedPermissions.get().containsAll(Arrays.asList(requestedPermissions))) {
|
||||||
taskanaEngine.getEngine().getCurrentUserContext().getUserid());
|
throw new MismatchedWorkbasketPermissionException(
|
||||||
}
|
taskanaEngine.getEngine().getCurrentUserContext().getUserid(),
|
||||||
|
workbasketKey,
|
||||||
|
domain,
|
||||||
|
requestedPermissions);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
taskanaEngine.returnConnection();
|
taskanaEngine.returnConnection();
|
||||||
|
@ -480,7 +458,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<WorkbasketAccessItem> getWorkbasketAccessItems(String workbasketId)
|
public List<WorkbasketAccessItem> getWorkbasketAccessItems(String workbasketId)
|
||||||
throws NotAuthorizedException {
|
throws NotAuthorizedException, WorkbasketNotFoundException {
|
||||||
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
||||||
List<WorkbasketAccessItem> result = new ArrayList<>();
|
List<WorkbasketAccessItem> result = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
|
@ -498,15 +476,16 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
public void setWorkbasketAccessItems(
|
public void setWorkbasketAccessItems(
|
||||||
String workbasketId, List<WorkbasketAccessItem> wbAccessItems)
|
String workbasketId, List<WorkbasketAccessItem> wbAccessItems)
|
||||||
throws NotAuthorizedException, WorkbasketAccessItemAlreadyExistException,
|
throws NotAuthorizedException, WorkbasketAccessItemAlreadyExistException,
|
||||||
InvalidArgumentException {
|
InvalidArgumentException, WorkbasketNotFoundException {
|
||||||
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
|
||||||
|
|
||||||
Set<String> ids = new HashSet<>();
|
|
||||||
Set<WorkbasketAccessItemImpl> accessItems =
|
Set<WorkbasketAccessItemImpl> accessItems =
|
||||||
checkAccessItemsPreconditionsAndSetId(workbasketId, ids, wbAccessItems);
|
checkAccessItemsPreconditionsAndSetId(workbasketId, wbAccessItems);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
|
// this is necessary to verify that the requested workbasket exists.
|
||||||
|
getWorkbasket(workbasketId);
|
||||||
|
|
||||||
List<WorkbasketAccessItemImpl> originalAccessItems = new ArrayList<>();
|
List<WorkbasketAccessItemImpl> originalAccessItems = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -813,11 +792,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
.runAsAdmin(() -> getCountTasksNotCompletedByWorkbasketId(workbasketId));
|
.runAsAdmin(() -> getCountTasksNotCompletedByWorkbasketId(workbasketId));
|
||||||
|
|
||||||
if (countTasksNotCompletedInWorkbasket > 0) {
|
if (countTasksNotCompletedInWorkbasket > 0) {
|
||||||
String errorMessage =
|
throw new WorkbasketInUseException(workbasketId);
|
||||||
String.format(
|
|
||||||
"Workbasket %s contains %s non-completed tasks and can´t be marked for deletion.",
|
|
||||||
workbasketId, countTasksNotCompletedInWorkbasket);
|
|
||||||
throw new WorkbasketInUseException(errorMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long countTasksInWorkbasket =
|
long countTasksInWorkbasket =
|
||||||
|
@ -872,25 +847,10 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
if (!deleteWorkbasket(workbasketIdForDeleting)) {
|
if (!deleteWorkbasket(workbasketIdForDeleting)) {
|
||||||
bulkLog.addError(
|
bulkLog.addError(
|
||||||
workbasketIdForDeleting,
|
workbasketIdForDeleting,
|
||||||
new WorkbasketInUseException(
|
new WorkbasketMarkedForDeletionException(workbasketIdForDeleting));
|
||||||
"Workbasket with id "
|
|
||||||
+ workbasketIdForDeleting
|
|
||||||
+ " contains completed tasks not deleted and will not be deleted."));
|
|
||||||
}
|
}
|
||||||
} catch (WorkbasketInUseException ex) {
|
|
||||||
bulkLog.addError(
|
|
||||||
workbasketIdForDeleting,
|
|
||||||
new WorkbasketInUseException(
|
|
||||||
"Workbasket with id "
|
|
||||||
+ workbasketIdForDeleting
|
|
||||||
+ " is in use and will not be deleted."));
|
|
||||||
} catch (TaskanaException ex) {
|
} catch (TaskanaException ex) {
|
||||||
bulkLog.addError(
|
bulkLog.addError(workbasketIdForDeleting, ex);
|
||||||
workbasketIdForDeleting,
|
|
||||||
new TaskanaException(
|
|
||||||
"Workbasket with id "
|
|
||||||
+ workbasketIdForDeleting
|
|
||||||
+ " Throw an exception and couldn't be deleted."));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bulkLog;
|
return bulkLog;
|
||||||
|
@ -990,22 +950,18 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
Workbasket oldWorkbasket, WorkbasketImpl workbasketImplToUpdate) throws ConcurrencyException {
|
Workbasket oldWorkbasket, WorkbasketImpl workbasketImplToUpdate) throws ConcurrencyException {
|
||||||
|
|
||||||
if (!oldWorkbasket.getModified().equals(workbasketImplToUpdate.getModified())) {
|
if (!oldWorkbasket.getModified().equals(workbasketImplToUpdate.getModified())) {
|
||||||
|
throw new ConcurrencyException();
|
||||||
throw new ConcurrencyException(
|
|
||||||
"The current Workbasket has been modified while editing. "
|
|
||||||
+ "The values can not be updated. Workbasket "
|
|
||||||
+ workbasketImplToUpdate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<WorkbasketAccessItemImpl> checkAccessItemsPreconditionsAndSetId(
|
private Set<WorkbasketAccessItemImpl> checkAccessItemsPreconditionsAndSetId(
|
||||||
String workbasketId, Set<String> ids, List<WorkbasketAccessItem> wbAccessItems)
|
String workbasketId, List<WorkbasketAccessItem> wbAccessItems)
|
||||||
throws InvalidArgumentException, WorkbasketAccessItemAlreadyExistException {
|
throws InvalidArgumentException, WorkbasketAccessItemAlreadyExistException {
|
||||||
|
|
||||||
|
Set<String> ids = new HashSet<>();
|
||||||
Set<WorkbasketAccessItemImpl> accessItems = new HashSet<>();
|
Set<WorkbasketAccessItemImpl> accessItems = new HashSet<>();
|
||||||
|
|
||||||
for (WorkbasketAccessItem workbasketAccessItem : wbAccessItems) {
|
for (WorkbasketAccessItem workbasketAccessItem : wbAccessItems) {
|
||||||
|
|
||||||
if (workbasketAccessItem != null) {
|
if (workbasketAccessItem != null) {
|
||||||
WorkbasketAccessItemImpl wbAccessItemImpl = (WorkbasketAccessItemImpl) workbasketAccessItem;
|
WorkbasketAccessItemImpl wbAccessItemImpl = (WorkbasketAccessItemImpl) workbasketAccessItem;
|
||||||
|
|
||||||
|
@ -1027,7 +983,8 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_WORKBASKET_AUTHORIZATION));
|
IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_WORKBASKET_AUTHORIZATION));
|
||||||
}
|
}
|
||||||
if (ids.contains(wbAccessItemImpl.getAccessId())) {
|
if (ids.contains(wbAccessItemImpl.getAccessId())) {
|
||||||
throw new WorkbasketAccessItemAlreadyExistException(wbAccessItemImpl);
|
throw new WorkbasketAccessItemAlreadyExistException(
|
||||||
|
wbAccessItemImpl.getAccessId(), wbAccessItemImpl.getWorkbasketId());
|
||||||
}
|
}
|
||||||
ids.add(wbAccessItemImpl.getAccessId());
|
ids.add(wbAccessItemImpl.getAccessId());
|
||||||
accessItems.add(wbAccessItemImpl);
|
accessItems.add(wbAccessItemImpl);
|
||||||
|
@ -1084,44 +1041,42 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateWorkbasket(Workbasket workbasket)
|
private void validateWorkbasket(Workbasket workbasket)
|
||||||
throws InvalidWorkbasketException, DomainNotFoundException {
|
throws DomainNotFoundException, InvalidArgumentException {
|
||||||
// check that required properties (database not null) are set
|
// check that required properties (database not null) are set
|
||||||
validateNameAndType(workbasket);
|
validateNameAndType(workbasket);
|
||||||
|
|
||||||
if (workbasket.getId() == null || workbasket.getId().length() == 0) {
|
if (workbasket.getId() == null || workbasket.getId().length() == 0) {
|
||||||
throw new InvalidWorkbasketException("Id must not be null for " + workbasket);
|
throw new InvalidArgumentException("Id must not be null for " + workbasket);
|
||||||
}
|
}
|
||||||
if (workbasket.getKey() == null || workbasket.getKey().length() == 0) {
|
if (workbasket.getKey() == null || workbasket.getKey().length() == 0) {
|
||||||
throw new InvalidWorkbasketException("Key must not be null for " + workbasket);
|
throw new InvalidArgumentException("Key must not be null for " + workbasket);
|
||||||
}
|
}
|
||||||
if (workbasket.getDomain() == null) {
|
if (workbasket.getDomain() == null) {
|
||||||
throw new InvalidWorkbasketException("Domain must not be null for " + workbasket);
|
throw new InvalidArgumentException("Domain must not be null for " + workbasket);
|
||||||
}
|
}
|
||||||
if (!taskanaEngine.domainExists(workbasket.getDomain())) {
|
if (!taskanaEngine.domainExists(workbasket.getDomain())) {
|
||||||
throw new DomainNotFoundException(
|
throw new DomainNotFoundException(workbasket.getDomain());
|
||||||
workbasket.getDomain(),
|
|
||||||
"Domain " + workbasket.getDomain() + " does not exist in the configuration.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateId(String workbasketId) throws InvalidArgumentException {
|
private void validateId(String workbasketId) throws InvalidArgumentException {
|
||||||
if (workbasketId == null) {
|
if (workbasketId == null) {
|
||||||
throw new InvalidArgumentException("The WorkbasketId can´t be NULL");
|
throw new InvalidArgumentException("The WorkbasketId can't be NULL");
|
||||||
}
|
}
|
||||||
if (workbasketId.isEmpty()) {
|
if (workbasketId.isEmpty()) {
|
||||||
throw new InvalidArgumentException("The WorkbasketId can´t be EMPTY for deleteWorkbasket()");
|
throw new InvalidArgumentException("The WorkbasketId can't be EMPTY for deleteWorkbasket()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateNameAndType(Workbasket workbasket) throws InvalidWorkbasketException {
|
private void validateNameAndType(Workbasket workbasket) throws InvalidArgumentException {
|
||||||
if (workbasket.getName() == null) {
|
if (workbasket.getName() == null) {
|
||||||
throw new InvalidWorkbasketException("Name must not be NULL for " + workbasket);
|
throw new InvalidArgumentException("Name must not be NULL for " + workbasket);
|
||||||
}
|
}
|
||||||
if (workbasket.getName().length() == 0) {
|
if (workbasket.getName().length() == 0) {
|
||||||
throw new InvalidWorkbasketException("Name must not be EMPTY for " + workbasket);
|
throw new InvalidArgumentException("Name must not be EMPTY for " + workbasket);
|
||||||
}
|
}
|
||||||
if (workbasket.getType() == null) {
|
if (workbasket.getType() == null) {
|
||||||
throw new InvalidWorkbasketException("Type must not be NULL for " + workbasket);
|
throw new InvalidArgumentException("Type must not be NULL for " + workbasket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import pro.taskana.common.api.ScheduledJob.Type;
|
||||||
import pro.taskana.common.api.TaskanaEngine;
|
import pro.taskana.common.api.TaskanaEngine;
|
||||||
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
|
import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
import pro.taskana.common.internal.JobServiceImpl;
|
import pro.taskana.common.internal.JobServiceImpl;
|
||||||
import pro.taskana.common.internal.jobs.AbstractTaskanaJob;
|
import pro.taskana.common.internal.jobs.AbstractTaskanaJob;
|
||||||
|
@ -49,7 +50,7 @@ public class WorkbasketCleanupJob extends AbstractTaskanaJob {
|
||||||
LOGGER.info(
|
LOGGER.info(
|
||||||
"Job ended successfully. {} workbaskets deleted.", totalNumberOfWorkbasketDeleted);
|
"Job ended successfully. {} workbaskets deleted.", totalNumberOfWorkbasketDeleted);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new TaskanaException("Error while processing WorkbasketCleanupJob.", e);
|
throw new SystemException("Error while processing WorkbasketCleanupJob.", e);
|
||||||
} finally {
|
} finally {
|
||||||
scheduleNextCleanupJob();
|
scheduleNextCleanupJob();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package acceptance;
|
package acceptance;
|
||||||
|
|
||||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
|
||||||
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods;
|
||||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
|
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
|
||||||
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS;
|
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS;
|
||||||
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS;
|
import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS;
|
||||||
|
@ -29,12 +30,17 @@ import org.apache.ibatis.annotations.Insert;
|
||||||
import org.apache.ibatis.annotations.Select;
|
import org.apache.ibatis.annotations.Select;
|
||||||
import org.apache.ibatis.annotations.Update;
|
import org.apache.ibatis.annotations.Update;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.DynamicTest;
|
import org.junit.jupiter.api.DynamicTest;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestFactory;
|
import org.junit.jupiter.api.TestFactory;
|
||||||
import org.junit.jupiter.api.function.ThrowingConsumer;
|
import org.junit.jupiter.api.function.ThrowingConsumer;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.ErrorCode;
|
||||||
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
||||||
import pro.taskana.common.internal.logging.LoggingAspect;
|
import pro.taskana.common.internal.logging.LoggingAspect;
|
||||||
|
import pro.taskana.common.internal.util.MapCreator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test architecture of classes in taskana. For more info and examples see
|
* Test architecture of classes in taskana. For more info and examples see
|
||||||
|
@ -80,7 +86,42 @@ class ArchitectureTest {
|
||||||
.should()
|
.should()
|
||||||
.onlyDependOnClassesThat(
|
.onlyDependOnClassesThat(
|
||||||
Predicates.resideOutsideOfPackage("..pro.taskana..internal..")
|
Predicates.resideOutsideOfPackage("..pro.taskana..internal..")
|
||||||
.or(Predicates.assignableTo(LoggingAspect.class)));
|
.or(
|
||||||
|
Predicates.assignableTo(LoggingAspect.class)
|
||||||
|
.or(Predicates.assignableTo(MapCreator.class))));
|
||||||
|
myRule.check(importedClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void utilityClassesShouldNotBeInitializable() {
|
||||||
|
ArchRule myRule =
|
||||||
|
classes()
|
||||||
|
.that()
|
||||||
|
.resideInAPackage("..util..")
|
||||||
|
.and()
|
||||||
|
.areNotNestedClasses()
|
||||||
|
.should()
|
||||||
|
.haveOnlyPrivateConstructors();
|
||||||
|
|
||||||
|
myRule.check(importedClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled("until we have renamed all tests")
|
||||||
|
void testMethodNamesShouldMatchAccordingToOurGuidelines() {
|
||||||
|
ArchRule myRule =
|
||||||
|
methods()
|
||||||
|
.that()
|
||||||
|
.areAnnotatedWith(Test.class)
|
||||||
|
.or()
|
||||||
|
.areAnnotatedWith(TestFactory.class)
|
||||||
|
.and()
|
||||||
|
.areNotDeclaredIn(ArchitectureTest.class)
|
||||||
|
.should()
|
||||||
|
.bePackagePrivate()
|
||||||
|
.andShould()
|
||||||
|
.haveNameMatching("^should_[A-Z][^_]+_(For|When)_[A-Z][^_]+$");
|
||||||
|
|
||||||
myRule.check(importedClasses);
|
myRule.check(importedClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +140,19 @@ class ArchitectureTest {
|
||||||
@Test
|
@Test
|
||||||
void onlyExceptionsShouldResideInExceptionPackage() {
|
void onlyExceptionsShouldResideInExceptionPackage() {
|
||||||
ArchRule myRule =
|
ArchRule myRule =
|
||||||
classes().that().resideInAPackage("..exceptions").should().beAssignableTo(Throwable.class);
|
classes()
|
||||||
|
.that()
|
||||||
|
.resideInAPackage("..exceptions")
|
||||||
|
.and()
|
||||||
|
.doNotBelongToAnyOf(ErrorCode.class)
|
||||||
|
.should()
|
||||||
|
.beAssignableTo(
|
||||||
|
Predicates.assignableTo(TaskanaException.class)
|
||||||
|
.or(Predicates.assignableTo(TaskanaRuntimeException.class)))
|
||||||
|
.andShould()
|
||||||
|
.bePublic()
|
||||||
|
.andShould()
|
||||||
|
.haveSimpleNameEndingWith("Exception");
|
||||||
myRule.check(importedClasses);
|
myRule.check(importedClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ import pro.taskana.common.internal.persistence.MapTypeHandler;
|
||||||
import pro.taskana.task.internal.models.TaskImpl;
|
import pro.taskana.task.internal.models.TaskImpl;
|
||||||
|
|
||||||
/** This class contains specific mybatis mappings for task tests. */
|
/** This class contains specific mybatis mappings for task tests. */
|
||||||
|
|
||||||
@SuppressWarnings({"checkstyle:LineLength"})
|
@SuppressWarnings({"checkstyle:LineLength"})
|
||||||
public interface TaskTestMapper {
|
public interface TaskTestMapper {
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
import pro.taskana.classification.api.ClassificationService;
|
import pro.taskana.classification.api.ClassificationService;
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationAlreadyExistException;
|
import pro.taskana.classification.api.exceptions.ClassificationAlreadyExistException;
|
||||||
|
import pro.taskana.classification.api.exceptions.MalformedServiceLevelException;
|
||||||
import pro.taskana.classification.api.models.Classification;
|
import pro.taskana.classification.api.models.Classification;
|
||||||
import pro.taskana.classification.internal.models.ClassificationImpl;
|
import pro.taskana.classification.internal.models.ClassificationImpl;
|
||||||
import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
||||||
|
@ -146,7 +147,7 @@ class CreateClassificationAccTest extends AbstractAccTest {
|
||||||
classification.setServiceLevel("P-1D");
|
classification.setServiceLevel("P-1D");
|
||||||
|
|
||||||
assertThatThrownBy(() -> CLASSIFICATION_SERVICE.createClassification(classification))
|
assertThatThrownBy(() -> CLASSIFICATION_SERVICE.createClassification(classification))
|
||||||
.isInstanceOf(InvalidArgumentException.class);
|
.isInstanceOf(MalformedServiceLevelException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
|
@ -166,7 +167,7 @@ class CreateClassificationAccTest extends AbstractAccTest {
|
||||||
CLASSIFICATION_SERVICE.newClassification("Key2", "DOMAIN_B", "TASK");
|
CLASSIFICATION_SERVICE.newClassification("Key2", "DOMAIN_B", "TASK");
|
||||||
classification2.setServiceLevel("abc");
|
classification2.setServiceLevel("abc");
|
||||||
assertThatThrownBy(() -> CLASSIFICATION_SERVICE.createClassification(classification2))
|
assertThatThrownBy(() -> CLASSIFICATION_SERVICE.createClassification(classification2))
|
||||||
.isInstanceOf(InvalidArgumentException.class);
|
.isInstanceOf(MalformedServiceLevelException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
|
|
|
@ -141,7 +141,7 @@ class UpdateClassificationAccTest extends AbstractAccTest {
|
||||||
Thread.sleep(20); // to avoid identity of modified timestamps between classification and base
|
Thread.sleep(20); // to avoid identity of modified timestamps between classification and base
|
||||||
classificationService.updateClassification(base);
|
classificationService.updateClassification(base);
|
||||||
|
|
||||||
classification.setName("NOW IT´S MY TURN");
|
classification.setName("NOW IT'S MY TURN");
|
||||||
classification.setDescription("IT SHOULD BE TO LATE...");
|
classification.setDescription("IT SHOULD BE TO LATE...");
|
||||||
ThrowingCallable call = () -> classificationService.updateClassification(classification);
|
ThrowingCallable call = () -> classificationService.updateClassification(classification);
|
||||||
assertThatThrownBy(call).isInstanceOf(ConcurrencyException.class);
|
assertThatThrownBy(call).isInstanceOf(ConcurrencyException.class);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
import acceptance.AbstractAccTest;
|
import acceptance.AbstractAccTest;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -20,6 +21,7 @@ import pro.taskana.common.test.security.WithAccessId;
|
||||||
import pro.taskana.task.api.CallbackState;
|
import pro.taskana.task.api.CallbackState;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
|
import pro.taskana.task.api.exceptions.InvalidCallbackStateException;
|
||||||
import pro.taskana.task.api.exceptions.InvalidStateException;
|
import pro.taskana.task.api.exceptions.InvalidStateException;
|
||||||
import pro.taskana.task.api.models.Task;
|
import pro.taskana.task.api.models.Task;
|
||||||
import pro.taskana.task.api.models.TaskSummary;
|
import pro.taskana.task.api.models.TaskSummary;
|
||||||
|
@ -64,37 +66,42 @@ class CallbackStateAccTest extends AbstractAccTest {
|
||||||
.isEqualTo(CallbackState.CALLBACK_PROCESSING_REQUIRED);
|
.isEqualTo(CallbackState.CALLBACK_PROCESSING_REQUIRED);
|
||||||
|
|
||||||
assertThat(createdTask.getState()).isEqualTo(TaskState.READY);
|
assertThat(createdTask.getState()).isEqualTo(TaskState.READY);
|
||||||
String endOfMessage = " cannot be deleted because its callback is not yet processed";
|
|
||||||
|
|
||||||
ThrowingCallable call =
|
ThrowingCallable call = () -> taskService.forceDeleteTask(createdTask.getId());
|
||||||
() -> {
|
CallbackState[] expectedCallbackStates = {
|
||||||
taskService.forceDeleteTask(createdTask.getId());
|
CallbackState.NONE, CallbackState.CLAIMED, CallbackState.CALLBACK_PROCESSING_COMPLETED
|
||||||
};
|
};
|
||||||
assertThatThrownBy(call)
|
assertThatThrownBy(call)
|
||||||
.isInstanceOf(InvalidStateException.class)
|
.isInstanceOf(InvalidStateException.class)
|
||||||
.hasMessageEndingWith(endOfMessage);
|
.hasMessage(
|
||||||
|
"Expected callback state of Task with id '%s' to be: '%s', but found '%s'",
|
||||||
|
createdTask.getId(),
|
||||||
|
Arrays.toString(expectedCallbackStates),
|
||||||
|
createdTask.getCallbackState());
|
||||||
|
|
||||||
final TaskImpl createdTask2 = (TaskImpl) taskService.claim(createdTask.getId());
|
final TaskImpl createdTask2 = (TaskImpl) taskService.claim(createdTask.getId());
|
||||||
|
|
||||||
assertThat(createdTask2.getState()).isEqualTo(TaskState.CLAIMED);
|
assertThat(createdTask2.getState()).isEqualTo(TaskState.CLAIMED);
|
||||||
|
|
||||||
call =
|
call = () -> taskService.forceDeleteTask(createdTask2.getId());
|
||||||
() -> {
|
|
||||||
taskService.forceDeleteTask(createdTask2.getId());
|
|
||||||
};
|
|
||||||
assertThatThrownBy(call)
|
assertThatThrownBy(call)
|
||||||
.isInstanceOf(InvalidStateException.class)
|
.isInstanceOf(InvalidStateException.class)
|
||||||
.hasMessageEndingWith(endOfMessage);
|
.hasMessage(
|
||||||
|
"Expected callback state of Task with id '%s' to be: '%s', but found '%s'",
|
||||||
|
createdTask2.getId(),
|
||||||
|
Arrays.toString(expectedCallbackStates),
|
||||||
|
createdTask2.getCallbackState());
|
||||||
|
|
||||||
final TaskImpl createdTask3 = (TaskImpl) taskService.completeTask(createdTask.getId());
|
final TaskImpl createdTask3 = (TaskImpl) taskService.completeTask(createdTask.getId());
|
||||||
|
|
||||||
call =
|
call = () -> taskService.forceDeleteTask(createdTask3.getId());
|
||||||
() -> {
|
|
||||||
taskService.forceDeleteTask(createdTask3.getId());
|
|
||||||
};
|
|
||||||
assertThatThrownBy(call)
|
assertThatThrownBy(call)
|
||||||
.isInstanceOf(InvalidStateException.class)
|
.isInstanceOf(InvalidStateException.class)
|
||||||
.hasMessageEndingWith(endOfMessage);
|
.hasMessage(
|
||||||
|
"Expected callback state of Task with id '%s' to be: '%s', but found '%s'",
|
||||||
|
createdTask3.getId(),
|
||||||
|
Arrays.toString(expectedCallbackStates),
|
||||||
|
createdTask3.getCallbackState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
|
@ -143,13 +150,9 @@ class CallbackStateAccTest extends AbstractAccTest {
|
||||||
BulkOperationResults<String, TaskanaException> bulkResult1 = taskService.deleteTasks(taskIds);
|
BulkOperationResults<String, TaskanaException> bulkResult1 = taskService.deleteTasks(taskIds);
|
||||||
|
|
||||||
assertThat(bulkResult1.containsErrors()).isTrue();
|
assertThat(bulkResult1.containsErrors()).isTrue();
|
||||||
List<String> failedTaskIds = bulkResult1.getFailedIds();
|
|
||||||
|
|
||||||
assertThat(failedTaskIds).hasSize(3);
|
assertThat(bulkResult1.getErrorMap().values())
|
||||||
for (String taskId : failedTaskIds) {
|
.hasOnlyElementsOfType(InvalidCallbackStateException.class);
|
||||||
TaskanaException excpt = bulkResult1.getErrorForId(taskId);
|
|
||||||
assertThat(excpt.getClass().getName()).isEqualTo(InvalidStateException.class.getName());
|
|
||||||
}
|
|
||||||
List<String> externalIds =
|
List<String> externalIds =
|
||||||
List.of(
|
List.of(
|
||||||
createdTask1.getExternalId(),
|
createdTask1.getExternalId(),
|
||||||
|
|
|
@ -17,6 +17,7 @@ import pro.taskana.common.api.BulkOperationResults;
|
||||||
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.util.EnumUtil;
|
||||||
import pro.taskana.common.test.security.JaasExtension;
|
import pro.taskana.common.test.security.JaasExtension;
|
||||||
import pro.taskana.common.test.security.WithAccessId;
|
import pro.taskana.common.test.security.WithAccessId;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
|
@ -362,7 +363,9 @@ class CompleteTaskAccTest extends AbstractAccTest {
|
||||||
assertThat(results.getFailedIds()).containsExactlyInAnyOrder(id);
|
assertThat(results.getFailedIds()).containsExactlyInAnyOrder(id);
|
||||||
assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class);
|
assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class);
|
||||||
assertThat(results.getErrorForId(id))
|
assertThat(results.getErrorForId(id))
|
||||||
.hasMessage("Task with Id %s has to be claimed before.", id);
|
.hasMessage(
|
||||||
|
"Task with id '%s' is in state: '%s', but must be in one of these states: '[%s]'",
|
||||||
|
id, TaskState.READY, TaskState.CLAIMED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "user-1-2")
|
@WithAccessId(user = "user-1-2")
|
||||||
|
@ -371,6 +374,8 @@ class CompleteTaskAccTest extends AbstractAccTest {
|
||||||
String id1 = "TKI:300000000000000000000000000000000000"; // task is canceled
|
String id1 = "TKI:300000000000000000000000000000000000"; // task is canceled
|
||||||
String id2 = "TKI:300000000000000000000000000000000010"; // task is terminated
|
String id2 = "TKI:300000000000000000000000000000000010"; // task is terminated
|
||||||
List<String> taskIdList = List.of(id1, id2);
|
List<String> taskIdList = List.of(id1, id2);
|
||||||
|
TaskState[] requiredStates =
|
||||||
|
EnumUtil.allValuesExceptFor(TaskState.TERMINATED, TaskState.CANCELLED);
|
||||||
|
|
||||||
BulkOperationResults<String, TaskanaException> results = TASK_SERVICE.completeTasks(taskIdList);
|
BulkOperationResults<String, TaskanaException> results = TASK_SERVICE.completeTasks(taskIdList);
|
||||||
|
|
||||||
|
@ -378,9 +383,13 @@ class CompleteTaskAccTest extends AbstractAccTest {
|
||||||
assertThat(results.getFailedIds()).containsExactlyInAnyOrder(id1, id2);
|
assertThat(results.getFailedIds()).containsExactlyInAnyOrder(id1, id2);
|
||||||
assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class);
|
assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class);
|
||||||
assertThat(results.getErrorForId(id1))
|
assertThat(results.getErrorForId(id1))
|
||||||
.hasMessage("Cannot complete task %s because it is in state CANCELLED.", id1);
|
.hasMessage(
|
||||||
|
"Task with id '%s' is in state: '%s', but must be in one of these states: '%s'",
|
||||||
|
id1, TaskState.CANCELLED, Arrays.toString(requiredStates));
|
||||||
assertThat(results.getErrorForId(id2))
|
assertThat(results.getErrorForId(id2))
|
||||||
.hasMessage("Cannot complete task %s because it is in state TERMINATED.", id2);
|
.hasMessage(
|
||||||
|
"Task with id '%s' is in state: '%s', but must be in one of these states: '%s'",
|
||||||
|
id2, TaskState.TERMINATED, Arrays.toString(requiredStates));
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "user-1-2")
|
@WithAccessId(user = "user-1-2")
|
||||||
|
@ -483,6 +492,8 @@ class CompleteTaskAccTest extends AbstractAccTest {
|
||||||
String id1 = "TKI:300000000000000000000000000000000000"; // task is canceled
|
String id1 = "TKI:300000000000000000000000000000000000"; // task is canceled
|
||||||
String id2 = "TKI:300000000000000000000000000000000010"; // task is terminated
|
String id2 = "TKI:300000000000000000000000000000000010"; // task is terminated
|
||||||
List<String> taskIdList = List.of(id1, id2);
|
List<String> taskIdList = List.of(id1, id2);
|
||||||
|
TaskState[] requiredStates =
|
||||||
|
EnumUtil.allValuesExceptFor(TaskState.TERMINATED, TaskState.CANCELLED);
|
||||||
|
|
||||||
BulkOperationResults<String, TaskanaException> results =
|
BulkOperationResults<String, TaskanaException> results =
|
||||||
TASK_SERVICE.forceCompleteTasks(taskIdList);
|
TASK_SERVICE.forceCompleteTasks(taskIdList);
|
||||||
|
@ -491,9 +502,13 @@ class CompleteTaskAccTest extends AbstractAccTest {
|
||||||
assertThat(results.getFailedIds()).containsExactlyInAnyOrder(id1, id2);
|
assertThat(results.getFailedIds()).containsExactlyInAnyOrder(id1, id2);
|
||||||
assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class);
|
assertThat(results.getErrorMap().values()).hasOnlyElementsOfType(InvalidStateException.class);
|
||||||
assertThat(results.getErrorForId(id1))
|
assertThat(results.getErrorForId(id1))
|
||||||
.hasMessage("Cannot complete task %s because it is in state CANCELLED.", id1);
|
.hasMessage(
|
||||||
|
"Task with id '%s' is in state: '%s', but must be in one of these states: '%s'",
|
||||||
|
id1, TaskState.CANCELLED, Arrays.toString(requiredStates));
|
||||||
assertThat(results.getErrorForId(id2))
|
assertThat(results.getErrorForId(id2))
|
||||||
.hasMessage("Cannot complete task %s because it is in state TERMINATED.", id2);
|
.hasMessage(
|
||||||
|
"Task with id '%s' is in state: '%s', but must be in one of these states: '%s'",
|
||||||
|
id2, TaskState.TERMINATED, Arrays.toString(requiredStates));
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "user-1-2")
|
@WithAccessId(user = "user-1-2")
|
||||||
|
|
|
@ -778,8 +778,7 @@ class CreateTaskAccTest extends AbstractAccTest {
|
||||||
|
|
||||||
@WithAccessId(user = "user-1-1")
|
@WithAccessId(user = "user-1-1")
|
||||||
@Test
|
@Test
|
||||||
void should_FetchAttachmentClassification_When_CreatingTaskWithAttachments()
|
void should_FetchAttachmentClassification_When_CreatingTaskWithAttachments() throws Exception {
|
||||||
throws Exception {
|
|
||||||
Attachment attachment = taskService.newAttachment();
|
Attachment attachment = taskService.newAttachment();
|
||||||
attachment.setObjectReference(
|
attachment.setObjectReference(
|
||||||
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
|
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
|
||||||
|
|
|
@ -5,7 +5,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
import acceptance.AbstractAccTest;
|
import acceptance.AbstractAccTest;
|
||||||
import acceptance.TaskanaEngineProxy;
|
import acceptance.TaskanaEngineProxy;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -19,6 +18,7 @@ import pro.taskana.common.test.security.JaasExtension;
|
||||||
import pro.taskana.common.test.security.WithAccessId;
|
import pro.taskana.common.test.security.WithAccessId;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
import pro.taskana.task.api.exceptions.InvalidStateException;
|
import pro.taskana.task.api.exceptions.InvalidStateException;
|
||||||
|
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
|
||||||
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||||
import pro.taskana.task.api.models.Task;
|
import pro.taskana.task.api.models.Task;
|
||||||
import pro.taskana.task.internal.AttachmentMapper;
|
import pro.taskana.task.internal.AttachmentMapper;
|
||||||
|
@ -27,36 +27,25 @@ import pro.taskana.task.internal.AttachmentMapper;
|
||||||
@ExtendWith(JaasExtension.class)
|
@ExtendWith(JaasExtension.class)
|
||||||
class DeleteTaskAccTest extends AbstractAccTest {
|
class DeleteTaskAccTest extends AbstractAccTest {
|
||||||
|
|
||||||
DeleteTaskAccTest() {
|
private final TaskService taskService = taskanaEngine.getTaskService();
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
@WithAccessId(user = "user-1-2")
|
@WithAccessId(user = "user-1-2")
|
||||||
@Test
|
@Test
|
||||||
void testDeleteSingleTaskNotAuthorized() {
|
void testDeleteSingleTaskNotAuthorized() {
|
||||||
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
ThrowingCallable call =
|
ThrowingCallable call =
|
||||||
() -> {
|
() -> taskService.deleteTask("TKI:000000000000000000000000000000000037");
|
||||||
taskService.deleteTask("TKI:000000000000000000000000000000000037");
|
|
||||||
};
|
|
||||||
assertThatThrownBy(call).isInstanceOf(NotAuthorizedException.class);
|
assertThatThrownBy(call).isInstanceOf(NotAuthorizedException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
@Test
|
@Test
|
||||||
void should_DeleteAttachments_When_MultipleTasksAreDeleted() throws Exception {
|
void should_DeleteAttachments_When_MultipleTasksAreDeleted() throws Exception {
|
||||||
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
|
|
||||||
TaskanaEngineProxy engineProxy = new TaskanaEngineProxy(taskanaEngine);
|
TaskanaEngineProxy engineProxy = new TaskanaEngineProxy(taskanaEngine);
|
||||||
AttachmentMapper attachmentMapper =
|
AttachmentMapper attachmentMapper =
|
||||||
engineProxy.getEngine().getSqlSession().getMapper(AttachmentMapper.class);
|
engineProxy.getEngine().getSqlSession().getMapper(AttachmentMapper.class);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
engineProxy.openConnection();
|
engineProxy.openConnection();
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
attachmentMapper.findAttachmentSummariesByTaskIds(
|
attachmentMapper.findAttachmentSummariesByTaskIds(
|
||||||
List.of(
|
List.of(
|
||||||
|
@ -72,14 +61,13 @@ class DeleteTaskAccTest extends AbstractAccTest {
|
||||||
"TKI:000000000000000000000000000000000067",
|
"TKI:000000000000000000000000000000000067",
|
||||||
"TKI:000000000000000000000000000000000068"));
|
"TKI:000000000000000000000000000000000068"));
|
||||||
try {
|
try {
|
||||||
|
engineProxy.openConnection();
|
||||||
assertThat(
|
assertThat(
|
||||||
attachmentMapper.findAttachmentSummariesByTaskIds(
|
attachmentMapper.findAttachmentSummariesByTaskIds(
|
||||||
List.of(
|
List.of(
|
||||||
"TKI:000000000000000000000000000000000067",
|
"TKI:000000000000000000000000000000000067",
|
||||||
"TKI:000000000000000000000000000000000068")))
|
"TKI:000000000000000000000000000000000068")))
|
||||||
.isEmpty();
|
.isEmpty();
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
engineProxy.returnConnection();
|
engineProxy.returnConnection();
|
||||||
}
|
}
|
||||||
|
@ -88,17 +76,12 @@ class DeleteTaskAccTest extends AbstractAccTest {
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
@Test
|
@Test
|
||||||
void should_DeleteAttachments_When_SingleTaskIsDeleted() throws Exception {
|
void should_DeleteAttachments_When_SingleTaskIsDeleted() throws Exception {
|
||||||
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
|
|
||||||
TaskanaEngineProxy engineProxy = new TaskanaEngineProxy(taskanaEngine);
|
TaskanaEngineProxy engineProxy = new TaskanaEngineProxy(taskanaEngine);
|
||||||
AttachmentMapper attachmentMapper =
|
AttachmentMapper attachmentMapper =
|
||||||
engineProxy.getSqlSession().getMapper(AttachmentMapper.class);
|
engineProxy.getSqlSession().getMapper(AttachmentMapper.class);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
engineProxy.openConnection();
|
engineProxy.openConnection();
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
attachmentMapper.findAttachmentsByTaskId("TKI:000000000000000000000000000000000069"))
|
attachmentMapper.findAttachmentsByTaskId("TKI:000000000000000000000000000000000069"))
|
||||||
.hasSize(1);
|
.hasSize(1);
|
||||||
|
@ -110,7 +93,7 @@ class DeleteTaskAccTest extends AbstractAccTest {
|
||||||
taskService.deleteTask("TKI:000000000000000000000000000000000069");
|
taskService.deleteTask("TKI:000000000000000000000000000000000069");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
engineProxy.openConnection();
|
||||||
assertThat(
|
assertThat(
|
||||||
attachmentMapper.findAttachmentsByTaskId("TKI:000000000000000000000000000000000069"))
|
attachmentMapper.findAttachmentsByTaskId("TKI:000000000000000000000000000000000069"))
|
||||||
.isEmpty();
|
.isEmpty();
|
||||||
|
@ -124,9 +107,6 @@ class DeleteTaskAccTest extends AbstractAccTest {
|
||||||
@WithAccessId(user = "user-1-1")
|
@WithAccessId(user = "user-1-1")
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
void should_ThrowException_When_UserIsNotInAdminRoleButTriesToBulkDeleteTasks() {
|
void should_ThrowException_When_UserIsNotInAdminRoleButTriesToBulkDeleteTasks() {
|
||||||
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
|
|
||||||
List<String> taskIds =
|
List<String> taskIds =
|
||||||
List.of(
|
List.of(
|
||||||
"TKI:000000000000000000000000000000000008",
|
"TKI:000000000000000000000000000000000008",
|
||||||
|
@ -134,26 +114,18 @@ class DeleteTaskAccTest extends AbstractAccTest {
|
||||||
"TKI:000000000000000000000000000000000008",
|
"TKI:000000000000000000000000000000000008",
|
||||||
"TKI:000000000000000000000000000000000010");
|
"TKI:000000000000000000000000000000000010");
|
||||||
|
|
||||||
ThrowingCallable call =
|
ThrowingCallable call = () -> taskService.deleteTasks(taskIds);
|
||||||
() -> {
|
|
||||||
taskService.deleteTasks(taskIds);
|
|
||||||
};
|
|
||||||
assertThatThrownBy(call).isInstanceOf(NotAuthorizedException.class);
|
assertThatThrownBy(call).isInstanceOf(NotAuthorizedException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
@Test
|
@Test
|
||||||
void testDeleteSingleTask() throws Exception {
|
void testDeleteSingleTask() throws Exception {
|
||||||
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
Task task = taskService.getTask("TKI:000000000000000000000000000000000036");
|
Task task = taskService.getTask("TKI:000000000000000000000000000000000036");
|
||||||
|
|
||||||
taskService.deleteTask(task.getId());
|
taskService.deleteTask(task.getId());
|
||||||
|
|
||||||
ThrowingCallable call =
|
ThrowingCallable call = () -> taskService.getTask("TKI:000000000000000000000000000000000036");
|
||||||
() -> {
|
|
||||||
taskService.getTask("TKI:000000000000000000000000000000000036");
|
|
||||||
};
|
|
||||||
assertThatThrownBy(call).isInstanceOf(TaskNotFoundException.class);
|
assertThatThrownBy(call).isInstanceOf(TaskNotFoundException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,97 +134,71 @@ class DeleteTaskAccTest extends AbstractAccTest {
|
||||||
@WithAccessId(user = "user-1-1")
|
@WithAccessId(user = "user-1-1")
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
void should_ThrowException_When_UserIsNotInAdminRole() {
|
void should_ThrowException_When_UserIsNotInAdminRole() {
|
||||||
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
|
|
||||||
ThrowingCallable deleteTaskCall =
|
ThrowingCallable deleteTaskCall =
|
||||||
() -> {
|
() -> taskService.deleteTask("TKI:000000000000000000000000000000000041");
|
||||||
taskService.deleteTask("TKI:000000000000000000000000000000000041");
|
|
||||||
};
|
|
||||||
assertThatThrownBy(deleteTaskCall).isInstanceOf(NotAuthorizedException.class);
|
assertThatThrownBy(deleteTaskCall).isInstanceOf(NotAuthorizedException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
@Test
|
@Test
|
||||||
void testThrowsExceptionIfTaskIsNotCompleted() throws Exception {
|
void testThrowsExceptionIfTaskIsNotCompleted() throws Exception {
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
Task task = taskService.getTask("TKI:000000000000000000000000000000000029");
|
Task task = taskService.getTask("TKI:000000000000000000000000000000000029");
|
||||||
|
|
||||||
ThrowingCallable call =
|
ThrowingCallable call = () -> taskService.deleteTask(task.getId());
|
||||||
() -> {
|
|
||||||
taskService.deleteTask(task.getId());
|
|
||||||
};
|
|
||||||
assertThatThrownBy(call).isInstanceOf(InvalidStateException.class);
|
assertThatThrownBy(call).isInstanceOf(InvalidStateException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
@Test
|
@Test
|
||||||
void testForceDeleteTaskIfNotCompleted() throws Exception {
|
void testForceDeleteTaskIfNotCompleted() throws Exception {
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
Task task = taskService.getTask("TKI:000000000000000000000000000000000027");
|
Task task = taskService.getTask("TKI:000000000000000000000000000000000027");
|
||||||
|
|
||||||
ThrowingCallable call =
|
ThrowingCallable call = () -> taskService.deleteTask(task.getId());
|
||||||
() -> {
|
|
||||||
taskService.deleteTask(task.getId());
|
|
||||||
};
|
|
||||||
assertThatThrownBy(call)
|
assertThatThrownBy(call)
|
||||||
.describedAs("Should not be possible to delete claimed task without force flag")
|
.describedAs("Should not be possible to delete claimed task without force flag")
|
||||||
.isInstanceOf(InvalidStateException.class);
|
.isInstanceOf(InvalidStateException.class);
|
||||||
|
|
||||||
taskService.forceDeleteTask(task.getId());
|
taskService.forceDeleteTask(task.getId());
|
||||||
|
|
||||||
call =
|
call = () -> taskService.getTask("TKI:000000000000000000000000000000000027");
|
||||||
() -> {
|
|
||||||
taskService.getTask("TKI:000000000000000000000000000000000027");
|
|
||||||
};
|
|
||||||
assertThatThrownBy(call).isInstanceOf(TaskNotFoundException.class);
|
assertThatThrownBy(call).isInstanceOf(TaskNotFoundException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
@Test
|
@Test
|
||||||
void testBulkDeleteTask() throws Exception {
|
void testBulkDeleteTask() throws Exception {
|
||||||
|
List<String> taskIdList =
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
List.of(
|
||||||
ArrayList<String> taskIdList = new ArrayList<>();
|
"TKI:000000000000000000000000000000000037", "TKI:000000000000000000000000000000000038");
|
||||||
taskIdList.add("TKI:000000000000000000000000000000000037");
|
|
||||||
taskIdList.add("TKI:000000000000000000000000000000000038");
|
|
||||||
|
|
||||||
BulkOperationResults<String, TaskanaException> results = taskService.deleteTasks(taskIdList);
|
BulkOperationResults<String, TaskanaException> results = taskService.deleteTasks(taskIdList);
|
||||||
|
|
||||||
assertThat(results.containsErrors()).isFalse();
|
assertThat(results.containsErrors()).isFalse();
|
||||||
ThrowingCallable call =
|
ThrowingCallable call = () -> taskService.getTask("TKI:000000000000000000000000000000000038");
|
||||||
() -> {
|
|
||||||
taskService.getTask("TKI:000000000000000000000000000000000038");
|
|
||||||
};
|
|
||||||
assertThatThrownBy(call).isInstanceOf(TaskNotFoundException.class);
|
assertThatThrownBy(call).isInstanceOf(TaskNotFoundException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
@Test
|
@Test
|
||||||
void testBulkDeleteTasksWithException() throws Exception {
|
void testBulkDeleteTasksWithException() throws Exception {
|
||||||
|
List<String> taskIdList =
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
List.of(
|
||||||
ArrayList<String> taskIdList = new ArrayList<>();
|
"TKI:000000000000000000000000000000000039",
|
||||||
taskIdList.add("TKI:000000000000000000000000000000000039");
|
"TKI:000000000000000000000000000000000040",
|
||||||
taskIdList.add("TKI:000000000000000000000000000000000040");
|
"TKI:000000000000000000000000000000000028");
|
||||||
taskIdList.add("TKI:000000000000000000000000000000000028");
|
|
||||||
|
|
||||||
BulkOperationResults<String, TaskanaException> results = taskService.deleteTasks(taskIdList);
|
BulkOperationResults<String, TaskanaException> results = taskService.deleteTasks(taskIdList);
|
||||||
|
|
||||||
String expectedFailedId = "TKI:000000000000000000000000000000000028";
|
|
||||||
assertThat(results.containsErrors()).isTrue();
|
assertThat(results.containsErrors()).isTrue();
|
||||||
List<String> failedTaskIds = results.getFailedIds();
|
|
||||||
assertThat(failedTaskIds).hasSize(1);
|
assertThat(results.getErrorMap().keySet())
|
||||||
assertThat(failedTaskIds.get(0)).isEqualTo(expectedFailedId);
|
.containsExactly("TKI:000000000000000000000000000000000028");
|
||||||
assertThat(InvalidStateException.class)
|
assertThat(results.getErrorMap().values())
|
||||||
.isSameAs(results.getErrorMap().get(expectedFailedId).getClass());
|
.hasOnlyElementsOfType(InvalidTaskStateException.class);
|
||||||
|
|
||||||
Task notDeletedTask = taskService.getTask("TKI:000000000000000000000000000000000028");
|
Task notDeletedTask = taskService.getTask("TKI:000000000000000000000000000000000028");
|
||||||
assertThat(notDeletedTask).isNotNull();
|
assertThat(notDeletedTask).isNotNull();
|
||||||
ThrowingCallable call =
|
ThrowingCallable call = () -> taskService.getTask("TKI:000000000000000000000000000000000040");
|
||||||
() -> {
|
|
||||||
taskService.getTask("TKI:000000000000000000000000000000000040");
|
|
||||||
};
|
|
||||||
assertThatThrownBy(call).isInstanceOf(TaskNotFoundException.class);
|
assertThatThrownBy(call).isInstanceOf(TaskNotFoundException.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,8 +115,7 @@ class QueryTasksByObjectReferenceAccTest extends AbstractAccTest {
|
||||||
objectReference.setSystemInstance("00");
|
objectReference.setSystemInstance("00");
|
||||||
objectReference.setType("VNR");
|
objectReference.setType("VNR");
|
||||||
objectReference.setValue("67890123");
|
objectReference.setValue("67890123");
|
||||||
long count =
|
long count = TASK_SERVICE.createTaskQuery().primaryObjectReferenceIn(objectReference).count();
|
||||||
TASK_SERVICE.createTaskQuery().primaryObjectReferenceIn(objectReference).count();
|
|
||||||
assertThat(count).isEqualTo(1);
|
assertThat(count).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,11 @@ import pro.taskana.common.test.security.WithAccessId;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
import pro.taskana.task.api.exceptions.InvalidStateException;
|
import pro.taskana.task.api.exceptions.InvalidStateException;
|
||||||
|
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
|
||||||
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||||
import pro.taskana.task.api.models.Task;
|
import pro.taskana.task.api.models.Task;
|
||||||
import pro.taskana.task.api.models.TaskSummary;
|
import pro.taskana.task.api.models.TaskSummary;
|
||||||
|
import pro.taskana.workbasket.api.exceptions.MismatchedWorkbasketPermissionException;
|
||||||
|
|
||||||
/** Acceptance test for all "set owner" scenarios. */
|
/** Acceptance test for all "set owner" scenarios. */
|
||||||
@ExtendWith(JaasExtension.class)
|
@ExtendWith(JaasExtension.class)
|
||||||
|
@ -85,11 +87,12 @@ class SetOwnerAccTest extends AbstractAccTest {
|
||||||
String anyUserName = "TestUser3";
|
String anyUserName = "TestUser3";
|
||||||
|
|
||||||
assertThatThrownBy(() -> taskService.getTask(taskReadyId))
|
assertThatThrownBy(() -> taskService.getTask(taskReadyId))
|
||||||
.isInstanceOf(NotAuthorizedException.class);
|
.isInstanceOf(MismatchedWorkbasketPermissionException.class);
|
||||||
BulkOperationResults<String, TaskanaException> results =
|
BulkOperationResults<String, TaskanaException> results =
|
||||||
taskService.setOwnerOfTasks(anyUserName, List.of(taskReadyId));
|
taskService.setOwnerOfTasks(anyUserName, List.of(taskReadyId));
|
||||||
assertThat(results.containsErrors()).isTrue();
|
assertThat(results.containsErrors()).isTrue();
|
||||||
assertThat(results.getErrorForId(taskReadyId)).isInstanceOf(NotAuthorizedException.class);
|
assertThat(results.getErrorForId(taskReadyId))
|
||||||
|
.isInstanceOf(MismatchedWorkbasketPermissionException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "user-b-2")
|
@WithAccessId(user = "user-b-2")
|
||||||
|
@ -156,7 +159,8 @@ class SetOwnerAccTest extends AbstractAccTest {
|
||||||
resetDb(false);
|
resetDb(false);
|
||||||
List<TaskSummary> allTaskSummaries =
|
List<TaskSummary> allTaskSummaries =
|
||||||
new TaskanaEngineProxy(taskanaEngine)
|
new TaskanaEngineProxy(taskanaEngine)
|
||||||
.getEngine().getEngine()
|
.getEngine()
|
||||||
|
.getEngine()
|
||||||
.runAsAdmin(() -> taskanaEngine.getTaskService().createTaskQuery().list());
|
.runAsAdmin(() -> taskanaEngine.getTaskService().createTaskQuery().list());
|
||||||
List<String> allTaskIds =
|
List<String> allTaskIds =
|
||||||
allTaskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
|
allTaskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
|
||||||
|
@ -165,17 +169,19 @@ class SetOwnerAccTest extends AbstractAccTest {
|
||||||
assertThat(allTaskSummaries).hasSize(88);
|
assertThat(allTaskSummaries).hasSize(88);
|
||||||
assertThat(results.containsErrors()).isTrue();
|
assertThat(results.containsErrors()).isTrue();
|
||||||
|
|
||||||
Condition<Object> invalidStateException =
|
Condition<Object> invalidTaskStateException =
|
||||||
new Condition<>(c -> c.getClass() == InvalidStateException.class, "InvalidStateException");
|
|
||||||
Condition<Object> notAuthorizedException =
|
|
||||||
new Condition<>(
|
new Condition<>(
|
||||||
c -> c.getClass() == NotAuthorizedException.class, "NotAuthorizedException");
|
c -> c.getClass() == InvalidTaskStateException.class, "InvalidStateException");
|
||||||
|
Condition<Object> mismatchedWorkbasketPermissionException =
|
||||||
|
new Condition<>(
|
||||||
|
c -> c.getClass() == MismatchedWorkbasketPermissionException.class,
|
||||||
|
"MismatchedWorkbasketPermissionException");
|
||||||
assertThat(results.getErrorMap())
|
assertThat(results.getErrorMap())
|
||||||
.hasSize(86)
|
.hasSize(86)
|
||||||
.extractingFromEntries(Entry::getValue)
|
.extractingFromEntries(Entry::getValue)
|
||||||
.hasOnlyElementsOfTypes(InvalidStateException.class, NotAuthorizedException.class)
|
.hasOnlyElementsOfTypes(InvalidTaskStateException.class, NotAuthorizedException.class)
|
||||||
.areExactly(28, invalidStateException)
|
.areExactly(28, invalidTaskStateException)
|
||||||
.areExactly(58, notAuthorizedException);
|
.areExactly(58, mismatchedWorkbasketPermissionException);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
|
|
|
@ -37,7 +37,8 @@ class TaskEngineAccTest extends AbstractAccTest {
|
||||||
assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isFalse();
|
assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isFalse();
|
||||||
|
|
||||||
new TaskanaEngineProxy(taskanaEngine)
|
new TaskanaEngineProxy(taskanaEngine)
|
||||||
.getEngine().getEngine()
|
.getEngine()
|
||||||
|
.getEngine()
|
||||||
.runAsAdmin(() -> assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isTrue());
|
.runAsAdmin(() -> assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isTrue());
|
||||||
|
|
||||||
assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isFalse();
|
assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isFalse();
|
||||||
|
|
|
@ -24,9 +24,11 @@ import pro.taskana.common.test.security.WithAccessId;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
import pro.taskana.task.api.exceptions.InvalidStateException;
|
import pro.taskana.task.api.exceptions.InvalidStateException;
|
||||||
|
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
|
||||||
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||||
import pro.taskana.task.api.models.Task;
|
import pro.taskana.task.api.models.Task;
|
||||||
import pro.taskana.task.api.models.TaskSummary;
|
import pro.taskana.task.api.models.TaskSummary;
|
||||||
|
import pro.taskana.workbasket.api.exceptions.MismatchedWorkbasketPermissionException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
||||||
import pro.taskana.workbasket.api.models.Workbasket;
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
|
|
||||||
|
@ -140,8 +142,8 @@ class TransferTaskAccTest extends AbstractAccTest {
|
||||||
.extracting(Throwable::getMessage)
|
.extracting(Throwable::getMessage)
|
||||||
.asString()
|
.asString()
|
||||||
.startsWith(
|
.startsWith(
|
||||||
"Not authorized. Permission 'TRANSFER' on workbasket "
|
"Not authorized. The current user 'teamlead-1' has no '[TRANSFER]' permission(s) "
|
||||||
+ "'WBI:100000000000000000000000000000000005' is needed.");
|
+ "for Workbasket 'WBI:100000000000000000000000000000000005'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "user-1-1", groups = GROUP_1_DN)
|
@WithAccessId(user = "user-1-1", groups = GROUP_1_DN)
|
||||||
|
@ -156,8 +158,8 @@ class TransferTaskAccTest extends AbstractAccTest {
|
||||||
.extracting(Throwable::getMessage)
|
.extracting(Throwable::getMessage)
|
||||||
.asString()
|
.asString()
|
||||||
.startsWith(
|
.startsWith(
|
||||||
"Not authorized. Permission 'APPEND' on workbasket "
|
"Not authorized. The current user 'user-1-1' has no '[APPEND]' permission(s) "
|
||||||
+ "'WBI:100000000000000000000000000000000008' is needed.");
|
+ "for Workbasket 'WBI:100000000000000000000000000000000008'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "teamlead-1")
|
@WithAccessId(user = "teamlead-1")
|
||||||
|
@ -229,13 +231,13 @@ class TransferTaskAccTest extends AbstractAccTest {
|
||||||
assertThat(results.containsErrors()).isTrue();
|
assertThat(results.containsErrors()).isTrue();
|
||||||
assertThat(results.getErrorMap().values()).hasSize(6);
|
assertThat(results.getErrorMap().values()).hasSize(6);
|
||||||
assertThat(results.getErrorForId("TKI:000000000000000000000000000000000041").getClass())
|
assertThat(results.getErrorForId("TKI:000000000000000000000000000000000041").getClass())
|
||||||
.isEqualTo(NotAuthorizedException.class);
|
.isEqualTo(MismatchedWorkbasketPermissionException.class);
|
||||||
assertThat(results.getErrorForId("TKI:200000000000000000000000000000000008").getClass())
|
assertThat(results.getErrorForId("TKI:200000000000000000000000000000000008").getClass())
|
||||||
.isEqualTo(NotAuthorizedException.class);
|
.isEqualTo(MismatchedWorkbasketPermissionException.class);
|
||||||
assertThat(results.getErrorForId("TKI:000000000000000000000000000000000099").getClass())
|
assertThat(results.getErrorForId("TKI:000000000000000000000000000000000099").getClass())
|
||||||
.isEqualTo(TaskNotFoundException.class);
|
.isEqualTo(TaskNotFoundException.class);
|
||||||
assertThat(results.getErrorForId("TKI:100000000000000000000000000000000006").getClass())
|
assertThat(results.getErrorForId("TKI:100000000000000000000000000000000006").getClass())
|
||||||
.isEqualTo(InvalidStateException.class);
|
.isEqualTo(InvalidTaskStateException.class);
|
||||||
assertThat(results.getErrorForId("").getClass()).isEqualTo(InvalidArgumentException.class);
|
assertThat(results.getErrorForId("").getClass()).isEqualTo(InvalidArgumentException.class);
|
||||||
assertThat(results.getErrorForId(null).getClass()).isEqualTo(InvalidArgumentException.class);
|
assertThat(results.getErrorForId(null).getClass()).isEqualTo(InvalidArgumentException.class);
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,8 @@ class UpdateTaskAccTest extends AbstractAccTest {
|
||||||
// TODO flaky test ... if speed is too high,
|
// TODO flaky test ... if speed is too high,
|
||||||
assertThatThrownBy(() -> taskService.updateTask(task2))
|
assertThatThrownBy(() -> taskService.updateTask(task2))
|
||||||
.isInstanceOf(ConcurrencyException.class)
|
.isInstanceOf(ConcurrencyException.class)
|
||||||
.hasMessage("The task has already been updated by another user");
|
.hasMessage(
|
||||||
|
"The current entity cannot be updated since it has been modified while editing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
|
|
|
@ -588,8 +588,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
|
||||||
|
|
||||||
@WithAccessId(user = "user-1-1")
|
@WithAccessId(user = "user-1-1")
|
||||||
@Test
|
@Test
|
||||||
void should_FetchAttachmentClassification_When_UpdatingTaskWithAttachments()
|
void should_FetchAttachmentClassification_When_UpdatingTaskWithAttachments() throws Exception {
|
||||||
throws Exception {
|
|
||||||
ClassificationSummary classification =
|
ClassificationSummary classification =
|
||||||
classificationService.newClassification("T2000", "DOMAIN_A", "").asSummary();
|
classificationService.newClassification("T2000", "DOMAIN_A", "").asSummary();
|
||||||
attachment.setClassificationSummary(classification);
|
attachment.setClassificationSummary(classification);
|
||||||
|
|
|
@ -11,13 +11,13 @@ import org.junit.jupiter.api.TestTemplate;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
||||||
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.test.security.JaasExtension;
|
import pro.taskana.common.test.security.JaasExtension;
|
||||||
import pro.taskana.common.test.security.WithAccessId;
|
import pro.taskana.common.test.security.WithAccessId;
|
||||||
import pro.taskana.workbasket.api.WorkbasketPermission;
|
import pro.taskana.workbasket.api.WorkbasketPermission;
|
||||||
import pro.taskana.workbasket.api.WorkbasketService;
|
import pro.taskana.workbasket.api.WorkbasketService;
|
||||||
import pro.taskana.workbasket.api.WorkbasketType;
|
import pro.taskana.workbasket.api.WorkbasketType;
|
||||||
import pro.taskana.workbasket.api.exceptions.InvalidWorkbasketException;
|
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketAccessItemAlreadyExistException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketAccessItemAlreadyExistException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
|
||||||
import pro.taskana.workbasket.api.models.Workbasket;
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
|
@ -111,21 +111,21 @@ class CreateWorkbasketAccTest extends AbstractAccTest {
|
||||||
workbasket.setOrgLevel1("company");
|
workbasket.setOrgLevel1("company");
|
||||||
// missing key
|
// missing key
|
||||||
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket))
|
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket))
|
||||||
.isInstanceOf(InvalidWorkbasketException.class);
|
.isInstanceOf(InvalidArgumentException.class);
|
||||||
|
|
||||||
Workbasket workbasket2 = workbasketService.newWorkbasket("key", "novatec");
|
Workbasket workbasket2 = workbasketService.newWorkbasket("key", "novatec");
|
||||||
workbasket2.setType(WorkbasketType.GROUP);
|
workbasket2.setType(WorkbasketType.GROUP);
|
||||||
workbasket2.setOrgLevel1("company");
|
workbasket2.setOrgLevel1("company");
|
||||||
// missing name
|
// missing name
|
||||||
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket2))
|
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket2))
|
||||||
.isInstanceOf(InvalidWorkbasketException.class);
|
.isInstanceOf(InvalidArgumentException.class);
|
||||||
|
|
||||||
Workbasket workbasket3 = workbasketService.newWorkbasket("key", "novatec");
|
Workbasket workbasket3 = workbasketService.newWorkbasket("key", "novatec");
|
||||||
workbasket3.setName("Megabasket");
|
workbasket3.setName("Megabasket");
|
||||||
workbasket3.setOrgLevel1("company");
|
workbasket3.setOrgLevel1("company");
|
||||||
// missing type
|
// missing type
|
||||||
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket3))
|
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket3))
|
||||||
.isInstanceOf(InvalidWorkbasketException.class);
|
.isInstanceOf(InvalidArgumentException.class);
|
||||||
|
|
||||||
Workbasket workbasket4 = workbasketService.newWorkbasket("key", null);
|
Workbasket workbasket4 = workbasketService.newWorkbasket("key", null);
|
||||||
workbasket4.setName("Megabasket");
|
workbasket4.setName("Megabasket");
|
||||||
|
@ -133,7 +133,7 @@ class CreateWorkbasketAccTest extends AbstractAccTest {
|
||||||
workbasket4.setOrgLevel1("company");
|
workbasket4.setOrgLevel1("company");
|
||||||
// missing domain
|
// missing domain
|
||||||
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket4))
|
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket4))
|
||||||
.isInstanceOf(InvalidWorkbasketException.class);
|
.isInstanceOf(InvalidArgumentException.class);
|
||||||
|
|
||||||
Workbasket workbasket5 = workbasketService.newWorkbasket("", "novatec");
|
Workbasket workbasket5 = workbasketService.newWorkbasket("", "novatec");
|
||||||
workbasket5.setName("Megabasket");
|
workbasket5.setName("Megabasket");
|
||||||
|
@ -141,7 +141,7 @@ class CreateWorkbasketAccTest extends AbstractAccTest {
|
||||||
workbasket5.setOrgLevel1("company");
|
workbasket5.setOrgLevel1("company");
|
||||||
// empty key
|
// empty key
|
||||||
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket5))
|
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket5))
|
||||||
.isInstanceOf(InvalidWorkbasketException.class);
|
.isInstanceOf(InvalidArgumentException.class);
|
||||||
|
|
||||||
Workbasket workbasket6 = workbasketService.newWorkbasket("key", "novatec");
|
Workbasket workbasket6 = workbasketService.newWorkbasket("key", "novatec");
|
||||||
workbasket6.setName("");
|
workbasket6.setName("");
|
||||||
|
@ -149,7 +149,7 @@ class CreateWorkbasketAccTest extends AbstractAccTest {
|
||||||
workbasket6.setOrgLevel1("company");
|
workbasket6.setOrgLevel1("company");
|
||||||
// empty name
|
// empty name
|
||||||
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket))
|
assertThatThrownBy(() -> workbasketService.createWorkbasket(workbasket))
|
||||||
.isInstanceOf(InvalidWorkbasketException.class);
|
.isInstanceOf(InvalidArgumentException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
|
|
|
@ -40,7 +40,8 @@ class DeleteWorkbasketAccTest extends AbstractAccTest {
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
@Test
|
@Test
|
||||||
void testDeleteWorkbasket() throws Exception {
|
void should_ThrowWorkbasketNotFoundException_When_TheWorkbasketHasAlreadyBeenDeleted()
|
||||||
|
throws Exception {
|
||||||
Workbasket wb = workbasketService.getWorkbasket("USER-2-2", "DOMAIN_A");
|
Workbasket wb = workbasketService.getWorkbasket("USER-2-2", "DOMAIN_A");
|
||||||
|
|
||||||
ThrowingCallable call =
|
ThrowingCallable call =
|
||||||
|
@ -75,17 +76,19 @@ class DeleteWorkbasketAccTest extends AbstractAccTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testGetWorkbasketNotAuthorized() {
|
void should_ThrowNotAuthorizedException_When_UnauthorizedTryingToGetWorkbaskets() {
|
||||||
assertThatThrownBy(() -> workbasketService.getWorkbasket("TEAMLEAD-2", "DOMAIN_A"))
|
assertThatThrownBy(() -> workbasketService.getWorkbasket("TEAMLEAD-2", "DOMAIN_A"))
|
||||||
.isInstanceOf(NotAuthorizedException.class);
|
.isInstanceOf(NotAuthorizedException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
@Test
|
@Test
|
||||||
void testDeleteWorkbasketAlsoAsDistributionTarget() throws Exception {
|
void should_RemoveWorkbasketsFromDistributionTargets_WhenWorkbasketIsDeleted() throws Exception {
|
||||||
Workbasket wb = workbasketService.getWorkbasket("GPK_KSC_1", "DOMAIN_A");
|
Workbasket wb = workbasketService.getWorkbasket("GPK_KSC_1", "DOMAIN_A");
|
||||||
int distTargets =
|
int distTargets =
|
||||||
workbasketService.getDistributionTargets("WBI:100000000000000000000000000000000001").size();
|
workbasketService
|
||||||
|
.getDistributionTargets("GPK_KSC", "DOMAIN_A")
|
||||||
|
.size(); // has GPK_KSC_1 as a distribution target (+ 3 other Workbaskets)
|
||||||
|
|
||||||
ThrowingCallable call =
|
ThrowingCallable call =
|
||||||
() -> {
|
() -> {
|
||||||
|
@ -97,14 +100,13 @@ class DeleteWorkbasketAccTest extends AbstractAccTest {
|
||||||
.describedAs("There should be no result for a deleted Workbasket.")
|
.describedAs("There should be no result for a deleted Workbasket.")
|
||||||
.isInstanceOf(WorkbasketNotFoundException.class);
|
.isInstanceOf(WorkbasketNotFoundException.class);
|
||||||
|
|
||||||
int newDistTargets =
|
int newDistTargets = workbasketService.getDistributionTargets("GPK_KSC", "DOMAIN_A").size();
|
||||||
workbasketService.getDistributionTargets("WBI:100000000000000000000000000000000001").size();
|
|
||||||
assertThat(newDistTargets).isEqualTo(3).isLessThan(distTargets);
|
assertThat(newDistTargets).isEqualTo(3).isLessThan(distTargets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
@Test
|
@Test
|
||||||
void testDeleteWorkbasketWithNullOrEmptyParam() {
|
void should_ThrowInvalidArgumentException_When_TryingToDeleteNullOrEmptyWorkbasket() {
|
||||||
// Test Null-Value
|
// Test Null-Value
|
||||||
assertThatThrownBy(() -> workbasketService.deleteWorkbasket(null))
|
assertThatThrownBy(() -> workbasketService.deleteWorkbasket(null))
|
||||||
.describedAs(
|
.describedAs(
|
||||||
|
@ -122,14 +124,15 @@ class DeleteWorkbasketAccTest extends AbstractAccTest {
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
@Test
|
@Test
|
||||||
void testDeleteWorkbasketButNotExisting() {
|
void should_ThrowWorkbasketNotFoundException_When_TryingToDeleteNonExistingWorkbasket() {
|
||||||
assertThatThrownBy(() -> workbasketService.deleteWorkbasket("SOME NOT EXISTING ID"))
|
assertThatThrownBy(() -> workbasketService.deleteWorkbasket("SOME NOT EXISTING ID"))
|
||||||
.isInstanceOf(WorkbasketNotFoundException.class);
|
.isInstanceOf(WorkbasketNotFoundException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "user-1-2", groups = "businessadmin")
|
@WithAccessId(user = "user-1-2", groups = "businessadmin")
|
||||||
@Test
|
@Test
|
||||||
void testDeleteWorkbasketWhichIsUsed() throws Exception {
|
void should_ThrowWorkbasketInUseException_When_TryingToDeleteWorkbasketWhichIsInUse()
|
||||||
|
throws Exception {
|
||||||
Workbasket wb =
|
Workbasket wb =
|
||||||
workbasketService.getWorkbasket("user-1-2", "DOMAIN_A"); // all rights, DOMAIN_A with Tasks
|
workbasketService.getWorkbasket("user-1-2", "DOMAIN_A"); // all rights, DOMAIN_A with Tasks
|
||||||
assertThatThrownBy(() -> workbasketService.deleteWorkbasket(wb.getId()))
|
assertThatThrownBy(() -> workbasketService.deleteWorkbasket(wb.getId()))
|
||||||
|
@ -138,7 +141,7 @@ class DeleteWorkbasketAccTest extends AbstractAccTest {
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
@Test
|
@Test
|
||||||
void testCascadingDeleteOfAccessItems() throws Exception {
|
void should_DeleteWorkbasketAccessItems_When_WorkbasketIsDeleted() throws Exception {
|
||||||
Workbasket wb = workbasketService.getWorkbasket("WBI:100000000000000000000000000000000008");
|
Workbasket wb = workbasketService.getWorkbasket("WBI:100000000000000000000000000000000008");
|
||||||
String wbId = wb.getId();
|
String wbId = wb.getId();
|
||||||
// create 2 access Items
|
// create 2 access Items
|
||||||
|
@ -170,7 +173,7 @@ class DeleteWorkbasketAccTest extends AbstractAccTest {
|
||||||
|
|
||||||
@WithAccessId(user = "admin")
|
@WithAccessId(user = "admin")
|
||||||
@Test
|
@Test
|
||||||
void testMarkWorkbasketForDeletion() throws Exception {
|
void should_MarkWorkbasketForDeletion_When_TryingToDeleteWorkbasketWithTasks() throws Exception {
|
||||||
final Workbasket wb =
|
final Workbasket wb =
|
||||||
workbasketService.getWorkbasket("WBI:100000000000000000000000000000000006");
|
workbasketService.getWorkbasket("WBI:100000000000000000000000000000000006");
|
||||||
|
|
||||||
|
@ -183,8 +186,8 @@ class DeleteWorkbasketAccTest extends AbstractAccTest {
|
||||||
task = (TaskImpl) taskService.getTask("TKI:000000000000000000000000000000000066");
|
task = (TaskImpl) taskService.getTask("TKI:000000000000000000000000000000000066");
|
||||||
taskService.forceCompleteTask(task.getId());
|
taskService.forceCompleteTask(task.getId());
|
||||||
|
|
||||||
boolean markedForDeletion = workbasketService.deleteWorkbasket(wb.getId());
|
boolean canBeDeletedNow = workbasketService.deleteWorkbasket(wb.getId());
|
||||||
assertThat(markedForDeletion).isFalse();
|
assertThat(canBeDeletedNow).isFalse();
|
||||||
|
|
||||||
Workbasket wb2 = workbasketService.getWorkbasket(wb.getId());
|
Workbasket wb2 = workbasketService.getWorkbasket(wb.getId());
|
||||||
assertThat(wb2.isMarkedForDeletion()).isTrue();
|
assertThat(wb2.isMarkedForDeletion()).isTrue();
|
||||||
|
|
|
@ -19,7 +19,7 @@ class GetWorkbasketAuthorizationsAccTest extends AbstractAccTest {
|
||||||
@WithAccessId(user = "user-1-1")
|
@WithAccessId(user = "user-1-1")
|
||||||
@WithAccessId(user = "taskadmin")
|
@WithAccessId(user = "taskadmin")
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
void should_ThrowException_When_UserRoleIsNotAdminOrBusinessAdmin() {
|
void should_ThrowNotAuthorizedException_When_UserRoleIsNotAdminOrBusinessAdmin() {
|
||||||
|
|
||||||
final WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
|
final WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,13 @@ import org.junit.jupiter.api.TestTemplate;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
import pro.taskana.common.api.exceptions.ConcurrencyException;
|
import pro.taskana.common.api.exceptions.ConcurrencyException;
|
||||||
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.test.security.JaasExtension;
|
import pro.taskana.common.test.security.JaasExtension;
|
||||||
import pro.taskana.common.test.security.WithAccessId;
|
import pro.taskana.common.test.security.WithAccessId;
|
||||||
import pro.taskana.workbasket.api.WorkbasketCustomField;
|
import pro.taskana.workbasket.api.WorkbasketCustomField;
|
||||||
import pro.taskana.workbasket.api.WorkbasketService;
|
import pro.taskana.workbasket.api.WorkbasketService;
|
||||||
import pro.taskana.workbasket.api.WorkbasketType;
|
import pro.taskana.workbasket.api.WorkbasketType;
|
||||||
import pro.taskana.workbasket.api.exceptions.InvalidWorkbasketException;
|
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
||||||
import pro.taskana.workbasket.api.models.Workbasket;
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
import pro.taskana.workbasket.internal.models.WorkbasketImpl;
|
import pro.taskana.workbasket.internal.models.WorkbasketImpl;
|
||||||
|
@ -67,11 +67,11 @@ class UpdateWorkbasketAccTest extends AbstractAccTest {
|
||||||
(WorkbasketImpl) workbasketService.getWorkbasket("GPK_KSC", "DOMAIN_A");
|
(WorkbasketImpl) workbasketService.getWorkbasket("GPK_KSC", "DOMAIN_A");
|
||||||
workbasket.setName(null);
|
workbasket.setName(null);
|
||||||
assertThatThrownBy(() -> workbasketService.updateWorkbasket(workbasket))
|
assertThatThrownBy(() -> workbasketService.updateWorkbasket(workbasket))
|
||||||
.isInstanceOf(InvalidWorkbasketException.class);
|
.isInstanceOf(InvalidArgumentException.class);
|
||||||
|
|
||||||
workbasket.setName("");
|
workbasket.setName("");
|
||||||
assertThatThrownBy(() -> workbasketService.updateWorkbasket(workbasket))
|
assertThatThrownBy(() -> workbasketService.updateWorkbasket(workbasket))
|
||||||
.isInstanceOf(InvalidWorkbasketException.class);
|
.isInstanceOf(InvalidArgumentException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
|
@ -84,7 +84,7 @@ class UpdateWorkbasketAccTest extends AbstractAccTest {
|
||||||
workbasket.setType(null);
|
workbasket.setType(null);
|
||||||
|
|
||||||
assertThatThrownBy(() -> workbasketService.updateWorkbasket(workbasket))
|
assertThatThrownBy(() -> workbasketService.updateWorkbasket(workbasket))
|
||||||
.isInstanceOf(InvalidWorkbasketException.class);
|
.isInstanceOf(InvalidArgumentException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
|
@ -118,6 +118,20 @@ class UpdateWorkbasketAccTest extends AbstractAccTest {
|
||||||
.isThrownBy(() -> workbasketService.updateWorkbasket(workbasket));
|
.isThrownBy(() -> workbasketService.updateWorkbasket(workbasket));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WithAccessId(user = "businessadmin")
|
||||||
|
@Test
|
||||||
|
void should_ThrowException_When_TryingToUpdateUnknownWorkbasket() {
|
||||||
|
|
||||||
|
WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
|
||||||
|
|
||||||
|
Workbasket workbasket = workbasketService.newWorkbasket("InvalidKey", "InvalidDomain");
|
||||||
|
workbasket.setName("bla bla");
|
||||||
|
workbasket.setType(WorkbasketType.PERSONAL);
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> workbasketService.updateWorkbasket(workbasket))
|
||||||
|
.isInstanceOf(WorkbasketNotFoundException.class);
|
||||||
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "user-1-1")
|
@WithAccessId(user = "user-1-1")
|
||||||
@WithAccessId(user = "taskadmin")
|
@WithAccessId(user = "taskadmin")
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
|
|
|
@ -25,6 +25,7 @@ import pro.taskana.task.api.models.TaskSummary;
|
||||||
import pro.taskana.workbasket.api.WorkbasketPermission;
|
import pro.taskana.workbasket.api.WorkbasketPermission;
|
||||||
import pro.taskana.workbasket.api.WorkbasketService;
|
import pro.taskana.workbasket.api.WorkbasketService;
|
||||||
import pro.taskana.workbasket.api.exceptions.NotAuthorizedToQueryWorkbasketException;
|
import pro.taskana.workbasket.api.exceptions.NotAuthorizedToQueryWorkbasketException;
|
||||||
|
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
||||||
import pro.taskana.workbasket.api.models.WorkbasketAccessItem;
|
import pro.taskana.workbasket.api.models.WorkbasketAccessItem;
|
||||||
import pro.taskana.workbasket.internal.models.WorkbasketAccessItemImpl;
|
import pro.taskana.workbasket.internal.models.WorkbasketAccessItemImpl;
|
||||||
|
|
||||||
|
@ -43,12 +44,27 @@ class UpdateWorkbasketAuthorizationsAccTest extends AbstractAccTest {
|
||||||
workbasketService.newWorkbasketAccessItem(
|
workbasketService.newWorkbasketAccessItem(
|
||||||
"WBI:100000000000000000000000000000000008", "newAccessIdForUpdate");
|
"WBI:100000000000000000000000000000000008", "newAccessIdForUpdate");
|
||||||
|
|
||||||
workbasketAccessItem.setPermission(WorkbasketPermission.CUSTOM_1, true);
|
|
||||||
|
|
||||||
assertThatThrownBy(() -> workbasketService.updateWorkbasketAccessItem(workbasketAccessItem))
|
assertThatThrownBy(() -> workbasketService.updateWorkbasketAccessItem(workbasketAccessItem))
|
||||||
.isInstanceOf(NotAuthorizedException.class);
|
.isInstanceOf(NotAuthorizedException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithAccessId(user = "admin")
|
||||||
|
void
|
||||||
|
should_ThrowWorkbasketNotFoundException_When_TryingToSetAccessItemsOfNonExistingWorkbasket() {
|
||||||
|
|
||||||
|
final WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
|
||||||
|
|
||||||
|
WorkbasketAccessItem workbasketAccessItem =
|
||||||
|
workbasketService.newWorkbasketAccessItem("WBI:1337gibtEsNicht007", "newAccessIdForUpdate");
|
||||||
|
|
||||||
|
assertThatThrownBy(
|
||||||
|
() ->
|
||||||
|
workbasketService.setWorkbasketAccessItems(
|
||||||
|
"WBI:1337gibtEsNicht007", List.of(workbasketAccessItem)))
|
||||||
|
.isInstanceOf(WorkbasketNotFoundException.class);
|
||||||
|
}
|
||||||
|
|
||||||
@WithAccessId(user = "businessadmin")
|
@WithAccessId(user = "businessadmin")
|
||||||
@Test
|
@Test
|
||||||
void testUpdateWorkbasketAccessItemSucceeds() throws Exception {
|
void testUpdateWorkbasketAccessItemSucceeds() throws Exception {
|
||||||
|
|
|
@ -137,8 +137,7 @@ class WorkbasketServiceImplTest {
|
||||||
verify(taskanaEngine, times(2)).checkRoleMembership(any());
|
verify(taskanaEngine, times(2)).checkRoleMembership(any());
|
||||||
verify(internalTaskanaEngineMock, times(2)).getEngine();
|
verify(internalTaskanaEngineMock, times(2)).getEngine();
|
||||||
verify(internalTaskanaEngineMock, times(1)).domainExists(any());
|
verify(internalTaskanaEngineMock, times(1)).domainExists(any());
|
||||||
verify(distributionTargetMapperMock)
|
verify(distributionTargetMapperMock).deleteAllDistributionTargetsBySourceId(expectedWb.getId());
|
||||||
.deleteAllDistributionTargetsBySourceId(expectedWb.getId());
|
|
||||||
verify(workbasketMapperMock).update(expectedWb);
|
verify(workbasketMapperMock).update(expectedWb);
|
||||||
verify(internalTaskanaEngineMock, times(1)).getHistoryEventManager();
|
verify(internalTaskanaEngineMock, times(1)).getHistoryEventManager();
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationAlreadyExistException;
|
import pro.taskana.classification.api.exceptions.ClassificationAlreadyExistException;
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
||||||
|
import pro.taskana.classification.api.exceptions.MalformedServiceLevelException;
|
||||||
import pro.taskana.classification.api.models.Classification;
|
import pro.taskana.classification.api.models.Classification;
|
||||||
import pro.taskana.common.api.TaskanaEngine;
|
import pro.taskana.common.api.TaskanaEngine;
|
||||||
import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
||||||
|
@ -21,7 +22,6 @@ import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||||
import pro.taskana.task.api.models.ObjectReference;
|
import pro.taskana.task.api.models.ObjectReference;
|
||||||
import pro.taskana.task.api.models.Task;
|
import pro.taskana.task.api.models.Task;
|
||||||
import pro.taskana.workbasket.api.WorkbasketType;
|
import pro.taskana.workbasket.api.WorkbasketType;
|
||||||
import pro.taskana.workbasket.api.exceptions.InvalidWorkbasketException;
|
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
||||||
import pro.taskana.workbasket.api.models.Workbasket;
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
|
@ -37,11 +37,11 @@ public class ExampleBootstrap {
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void test()
|
public void test()
|
||||||
throws TaskNotFoundException, NotAuthorizedException, WorkbasketNotFoundException,
|
throws InvalidArgumentException, WorkbasketAlreadyExistException, DomainNotFoundException,
|
||||||
ClassificationNotFoundException, InvalidStateException, InvalidOwnerException,
|
NotAuthorizedException, ClassificationAlreadyExistException,
|
||||||
TaskAlreadyExistException, InvalidArgumentException, DomainNotFoundException,
|
MalformedServiceLevelException, TaskAlreadyExistException, WorkbasketNotFoundException,
|
||||||
InvalidWorkbasketException, WorkbasketAlreadyExistException,
|
ClassificationNotFoundException, AttachmentPersistenceException, TaskNotFoundException,
|
||||||
ClassificationAlreadyExistException, AttachmentPersistenceException {
|
InvalidOwnerException, InvalidStateException {
|
||||||
System.out.println("---------------------------> Start App");
|
System.out.println("---------------------------> Start App");
|
||||||
|
|
||||||
Workbasket wb = taskanaEngine.getWorkbasketService().newWorkbasket("workbasket", "DOMAIN_A");
|
Workbasket wb = taskanaEngine.getWorkbasketService().newWorkbasket("workbasket", "DOMAIN_A");
|
||||||
|
|
|
@ -11,10 +11,10 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import pro.taskana.common.api.TaskanaEngine;
|
import pro.taskana.common.api.TaskanaEngine;
|
||||||
import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
import pro.taskana.common.api.exceptions.DomainNotFoundException;
|
||||||
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.internal.util.IdGenerator;
|
import pro.taskana.common.internal.util.IdGenerator;
|
||||||
import pro.taskana.workbasket.api.WorkbasketType;
|
import pro.taskana.workbasket.api.WorkbasketType;
|
||||||
import pro.taskana.workbasket.api.exceptions.InvalidWorkbasketException;
|
|
||||||
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
|
import pro.taskana.workbasket.api.exceptions.WorkbasketAlreadyExistException;
|
||||||
import pro.taskana.workbasket.api.models.Workbasket;
|
import pro.taskana.workbasket.api.models.Workbasket;
|
||||||
import pro.taskana.workbasket.internal.models.WorkbasketImpl;
|
import pro.taskana.workbasket.internal.models.WorkbasketImpl;
|
||||||
|
@ -51,7 +51,7 @@ public class TaskanaTestController {
|
||||||
@GetMapping(path = "/transaction")
|
@GetMapping(path = "/transaction")
|
||||||
public @ResponseBody String transaction(
|
public @ResponseBody String transaction(
|
||||||
@RequestParam(value = "rollback", defaultValue = "false") String rollback)
|
@RequestParam(value = "rollback", defaultValue = "false") String rollback)
|
||||||
throws InvalidWorkbasketException, NotAuthorizedException, WorkbasketAlreadyExistException,
|
throws InvalidArgumentException, NotAuthorizedException, WorkbasketAlreadyExistException,
|
||||||
DomainNotFoundException {
|
DomainNotFoundException {
|
||||||
taskanaEngine.getWorkbasketService().createWorkbasket(createWorkBasket("key", "workbasket"));
|
taskanaEngine.getWorkbasketService().createWorkbasket(createWorkBasket("key", "workbasket"));
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ public class TaskanaTestController {
|
||||||
@GetMapping(path = "/transaction-many")
|
@GetMapping(path = "/transaction-many")
|
||||||
public @ResponseBody String transactionMany(
|
public @ResponseBody String transactionMany(
|
||||||
@RequestParam(value = "rollback", defaultValue = "false") String rollback)
|
@RequestParam(value = "rollback", defaultValue = "false") String rollback)
|
||||||
throws InvalidWorkbasketException, NotAuthorizedException, WorkbasketAlreadyExistException,
|
throws InvalidArgumentException, NotAuthorizedException, WorkbasketAlreadyExistException,
|
||||||
DomainNotFoundException {
|
DomainNotFoundException {
|
||||||
taskanaEngine.getWorkbasketService().createWorkbasket(createWorkBasket("key1", "workbasket1"));
|
taskanaEngine.getWorkbasketService().createWorkbasket(createWorkBasket("key1", "workbasket1"));
|
||||||
taskanaEngine.getWorkbasketService().createWorkbasket(createWorkBasket("key2", "workbasket2"));
|
taskanaEngine.getWorkbasketService().createWorkbasket(createWorkBasket("key2", "workbasket2"));
|
||||||
|
@ -84,7 +84,7 @@ public class TaskanaTestController {
|
||||||
@GetMapping(path = "/customdb")
|
@GetMapping(path = "/customdb")
|
||||||
public @ResponseBody String transactionCustomdb(
|
public @ResponseBody String transactionCustomdb(
|
||||||
@RequestParam(value = "rollback", defaultValue = "false") String rollback)
|
@RequestParam(value = "rollback", defaultValue = "false") String rollback)
|
||||||
throws InvalidWorkbasketException, NotAuthorizedException, WorkbasketAlreadyExistException,
|
throws InvalidArgumentException, NotAuthorizedException, WorkbasketAlreadyExistException,
|
||||||
DomainNotFoundException {
|
DomainNotFoundException {
|
||||||
taskanaEngine.getWorkbasketService().createWorkbasket(createWorkBasket("key1", "workbasket1"));
|
taskanaEngine.getWorkbasketService().createWorkbasket(createWorkBasket("key1", "workbasket1"));
|
||||||
taskanaEngine.getWorkbasketService().createWorkbasket(createWorkBasket("key2", "workbasket2"));
|
taskanaEngine.getWorkbasketService().createWorkbasket(createWorkBasket("key2", "workbasket2"));
|
||||||
|
|
|
@ -209,13 +209,12 @@ class TaskanaTransactionIntTest {
|
||||||
taskCleanupJob.run();
|
taskCleanupJob.run();
|
||||||
|
|
||||||
ThrowingCallable httpCall =
|
ThrowingCallable httpCall =
|
||||||
() -> {
|
() ->
|
||||||
workbasketService.deleteWorkbasket(
|
workbasketService.deleteWorkbasket(
|
||||||
workbasketService.getWorkbasket("key3", "DOMAIN_A").getId());
|
workbasketService.getWorkbasket("key3", "DOMAIN_A").getId());
|
||||||
};
|
|
||||||
assertThatThrownBy(httpCall)
|
assertThatThrownBy(httpCall)
|
||||||
.isInstanceOf(WorkbasketInUseException.class)
|
.isInstanceOf(WorkbasketInUseException.class)
|
||||||
.hasMessageContaining("contains 1 non-completed tasks");
|
.hasMessageContaining("contains non-completed Tasks");
|
||||||
|
|
||||||
WorkbasketCleanupJob job =
|
WorkbasketCleanupJob job =
|
||||||
new WorkbasketCleanupJob(taskanaEngine, springTransactionProvider, null);
|
new WorkbasketCleanupJob(taskanaEngine, springTransactionProvider, null);
|
||||||
|
|
|
@ -1,13 +1,7 @@
|
||||||
package pro.taskana.example.jobs;
|
package pro.taskana.example.jobs;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.security.Principal;
|
|
||||||
import java.security.PrivilegedActionException;
|
|
||||||
import java.security.PrivilegedExceptionAction;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.security.auth.Subject;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -16,8 +10,6 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import pro.taskana.common.api.ScheduledJob.Type;
|
import pro.taskana.common.api.ScheduledJob.Type;
|
||||||
import pro.taskana.common.api.TaskanaEngine;
|
import pro.taskana.common.api.TaskanaEngine;
|
||||||
import pro.taskana.common.api.TaskanaRole;
|
|
||||||
import pro.taskana.common.api.security.UserPrincipal;
|
|
||||||
import pro.taskana.common.internal.jobs.JobRunner;
|
import pro.taskana.common.internal.jobs.JobRunner;
|
||||||
import pro.taskana.common.internal.transaction.TaskanaTransactionProvider;
|
import pro.taskana.common.internal.transaction.TaskanaTransactionProvider;
|
||||||
import pro.taskana.task.internal.jobs.TaskCleanupJob;
|
import pro.taskana.task.internal.jobs.TaskCleanupJob;
|
||||||
|
@ -57,49 +49,18 @@ public class JobScheduler {
|
||||||
@Scheduled(cron = "${taskana.jobscheduler.async.cron}")
|
@Scheduled(cron = "${taskana.jobscheduler.async.cron}")
|
||||||
public void triggerJobs() {
|
public void triggerJobs() {
|
||||||
LOGGER.info("AsyncJobs started.");
|
LOGGER.info("AsyncJobs started.");
|
||||||
try {
|
runAsyncJobsAsAdmin();
|
||||||
runAsyncJobsAsAdmin();
|
LOGGER.info("AsyncJobs completed.");
|
||||||
LOGGER.info("AsyncJobs completed.");
|
|
||||||
} catch (PrivilegedActionException e) {
|
|
||||||
LOGGER.info("AsyncJobs failed.", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
private void runAsyncJobsAsAdmin() {
|
||||||
* Creates an admin subject and runs the job using the subject.
|
taskanaEngine.runAsAdmin(
|
||||||
*/
|
|
||||||
private void runAsyncJobsAsAdmin() throws PrivilegedActionException {
|
|
||||||
PrivilegedExceptionAction<Object> jobs =
|
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
JobRunner runner = new JobRunner(taskanaEngine);
|
||||||
JobRunner runner = new JobRunner(taskanaEngine);
|
runner.registerTransactionProvider(springTransactionProvider);
|
||||||
runner.registerTransactionProvider(springTransactionProvider);
|
LOGGER.info("Running Jobs");
|
||||||
LOGGER.info("Running Jobs");
|
runner.runJobs();
|
||||||
runner.runJobs();
|
return "Successful";
|
||||||
return "Successful";
|
});
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new Exception(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Subject.doAs(getAdminSubject(), jobs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Subject getAdminSubject() {
|
|
||||||
Subject subject = new Subject();
|
|
||||||
List<Principal> principalList = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
principalList.add(
|
|
||||||
new UserPrincipal(
|
|
||||||
taskanaEngine
|
|
||||||
.getConfiguration()
|
|
||||||
.getRoleMap()
|
|
||||||
.get(TaskanaRole.ADMIN)
|
|
||||||
.iterator()
|
|
||||||
.next()));
|
|
||||||
} catch (Throwable t) {
|
|
||||||
LOGGER.warn("Could not determine a configured admin user.", t);
|
|
||||||
}
|
|
||||||
subject.getPrincipals().addAll(principalList);
|
|
||||||
return subject;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ class LdapEmptySearchRootsTest extends LdapTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_ReturnFullDnForUser_When_AccessIdOfUserIsGiven() {
|
void should_ReturnFullDnForUser_When_AccessIdOfUserIsGiven() throws Exception {
|
||||||
String dn = ldapClient.searchDnForAccessId("otheruser");
|
String dn = ldapClient.searchDnForAccessId("otheruser");
|
||||||
assertThat(dn).isEqualTo("uid=otheruser,cn=other-users,ou=test,o=taskana");
|
assertThat(dn).isEqualTo("uid=otheruser,cn=other-users,ou=test,o=taskana");
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue