TSK-1706: Implementation of the new UserService (#1692)

This commit is contained in:
tge20 2021-09-07 07:50:04 +02:00 committed by GitHub
parent 1d23aeecc0
commit 1e3f90509d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1840 additions and 26 deletions

View File

@ -13,6 +13,7 @@ public final class SampleDataProvider {
static final String TEST_CLASSIFICATION = "/sql/test-data/classification.sql";
static final String TEST_OBJECT_REFERENCE = "/sql/test-data/object-reference.sql";
static final String TEST_ATTACHMENT = "/sql/test-data/attachment.sql";
static final String TEST_USER = "/sql/test-data/user.sql";
static final String TEST_TASK_HISTORY_EVENT = "/sql/test-data/task-history-event.sql";
static final String TEST_WORKBASKET_HISTORY_EVENT = "/sql/test-data/workbasket-history-event.sql";
static final String TEST_CLASSIFICATION_HISTORY_EVENT =
@ -31,6 +32,7 @@ public final class SampleDataProvider {
private static final String SAMPLE_CLASSIFICATION = "/sql/sample-data/classification.sql";
private static final String SAMPLE_OBJECT_REFERENCE = "/sql/sample-data/object-reference.sql";
private static final String SAMPLE_ATTACHMENT = "/sql/sample-data/attachment.sql";
private static final String SAMPLE_USER = "/sql/sample-data/user.sql";
private SampleDataProvider() {}
@ -44,7 +46,8 @@ public final class SampleDataProvider {
SAMPLE_ATTACHMENT,
SAMPLE_WORKBASKET_ACCESS_LIST,
SAMPLE_OBJECT_REFERENCE,
SAMPLE_TASK_HISTORY_EVENT);
SAMPLE_TASK_HISTORY_EVENT,
SAMPLE_USER);
}
static Stream<String> getScriptsToClearDatabase() {
@ -67,7 +70,8 @@ public final class SampleDataProvider {
TEST_WORKBASKET_ACCESS_LIST,
TEST_DISTRIBUTION_TARGETS,
TEST_OBJECT_REFERENCE,
TEST_ATTACHMENT);
TEST_ATTACHMENT,
TEST_USER);
}
static Stream<String> getMonitorDataScripts() {

View File

@ -0,0 +1,15 @@
-- USER_INFO TABLE (USER_ID , FIRST_NAME , LASTNAME , FULL_NAME , LONG_NAME , E_MAIL , PHONE , MOBILE_PHONE , ORG_LEVEL_4 , ORG_LEVEL_3 , ORG_LEVEL_2 , ORG_LEVEL_1 , DATA
INSERT INTO USER_INFO VALUES('teamlead-1' , 'Titus' , 'Toll' , 'Toll, Titus' , 'Toll, Titus - (teamlead-1)' , 'titus.toll@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , 'xy' );
INSERT INTO USER_INFO VALUES('user-1-1' , 'Max' , 'Mustermann' , 'Mustermann, Max' , 'Mustermann, Max - (user-1-1)' , 'max.mustermann@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-1-2' , 'Elena' , 'Eifrig' , 'Eifrig, Elena' , 'Eifrig, Elena - (user-1-2)' , 'elena.eifrig@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-1-3' , 'Elena' , 'Faul' , 'Faul, Elena' , 'Faul, Elena - (user-1-3)' , 'elena.faul@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-1' , 'Simone' , 'Müller' , 'Müller, Simone' , 'Müller, Simone - (user-2-1)' , 'simone.müller@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-2' , 'Tim' , 'Schläfrig' , 'Schläfrig, Tim' , 'Schläfrig, Tim - (user-2-2)' , 'tim.schläfrig@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-3' , 'Thomas' , 'Bach' , 'Bach, Thomas' , 'Bach, Thomas - (user-2-3)' , 'thomas.bach@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-4' , 'Rolf' , 'Wieland' , 'Wieland, Rolf' , 'Wieland, Rolf - (user-2-4)' , 'rolf.wieland@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-5' , 'Heike' , 'Schmidt' , 'Schmidt, Heike' , 'Schmidt, Heike - (user-2-5)' , 'heike.schmidt@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-6' , 'Kurt' , 'Maier' , 'Maier, Kurt' , 'Maier, Kurt - (user-2-6)' , 'kurt.maier@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-7' , 'Wiebke' , 'Meyer' , 'Meyer, Wiebke' , 'Meyer, Wiebke - (user-2-7)' , 'wiebke.meyer@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-9' , 'Nathalie' , 'Fuchs' , 'Fuchs, Nathalie' , 'Fuchs, Nathalie - (user-2-9)' , 'nathalie.fuchs@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-10' , 'Johannes' , 'Renz' , 'Renz, Johannes' , 'Renz, Johannes - (user-2-10)' , 'johannes.renz@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-11' , 'Max' , 'Renz' , 'Renz, Max' , 'Renz, Max - (user-2-11)' , 'max.renz@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );

View File

@ -0,0 +1,15 @@
-- USER_INFO TABLE (USER_ID , FIRST_NAME , LASTNAME , FULL_NAME , LONG_NAME , E_MAIL , PHONE , MOBILE_PHONE , ORG_LEVEL_4 , ORG_LEVEL_3 , ORG_LEVEL_2 , ORG_LEVEL_1 , DATA
INSERT INTO USER_INFO VALUES('teamlead-1' , 'Titus' , 'Toll' , 'Toll, Titus' , 'Toll, Titus - (teamlead-1)' , 'titus.toll@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , 'xy' );
INSERT INTO USER_INFO VALUES('user-1-1' , 'Max' , 'Mustermann' , 'Mustermann, Max' , 'Mustermann, Max - (user-1-1)' , 'max.mustermann@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-1-2' , 'Elena' , 'Eifrig' , 'Eifrig, Elena' , 'Eifrig, Elena - (user-1-2)' , 'elena.eifrig@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-1-3' , 'Elena' , 'Faul' , 'Faul, Elena' , 'Faul, Elena - (user-1-3)' , 'elena.faul@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-1' , 'Simone' , 'Müller' , 'Müller, Simone' , 'Müller, Simone - (user-2-1)' , 'simone.müller@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-2' , 'Tim' , 'Schläfrig' , 'Schläfrig, Tim' , 'Schläfrig, Tim - (user-2-2)' , 'tim.schläfrig@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-3' , 'Thomas' , 'Bach' , 'Bach, Thomas' , 'Bach, Thomas - (user-2-3)' , 'thomas.bach@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-4' , 'Rolf' , 'Wieland' , 'Wieland, Rolf' , 'Wieland, Rolf - (user-2-4)' , 'rolf.wieland@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-5' , 'Heike' , 'Schmidt' , 'Schmidt, Heike' , 'Schmidt, Heike - (user-2-5)' , 'heike.schmidt@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-6' , 'Kurt' , 'Maier' , 'Maier, Kurt' , 'Maier, Kurt - (user-2-6)' , 'kurt.maier@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-7' , 'Wiebke' , 'Meyer' , 'Meyer, Wiebke' , 'Meyer, Wiebke - (user-2-7)' , 'wiebke.meyer@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-9' , 'Nathalie' , 'Fuchs' , 'Fuchs, Nathalie' , 'Fuchs, Nathalie - (user-2-9)' , 'nathalie.fuchs@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-10' , 'Johannes' , 'Renz' , 'Renz, Johannes' , 'Renz, Johannes - (user-2-10)' , 'johannes.renz@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );
INSERT INTO USER_INFO VALUES('user-2-11' , 'Max' , 'Renz' , 'Renz, Max' , 'Renz, Max - (user-2-11)' , 'max.renz@web.de' , '040-2951854' , '015637683197' , 'Novatec' , 'BPM' , 'Human Workflow' , 'TASKANA' , '' );

View File

@ -308,6 +308,20 @@ ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2
cn: Johannes Renz
userPassword: user-2-10
dn: uid=das_ist_eine_sehr_sehr_sehr_sehr_sehr_lange_user_id,cn=users,OU=Test,O=TASKANA
objectclass: inetorgperson
objectclass: organizationalperson
objectclass: person
objectclass: top
givenName: Max
description: desc
memberOf: cn=Organisationseinheit KSC 2,cn=Organisationseinheit KSC,cn=organisation,OU=Test,O=TASKANA
uid: das_ist_eine_sehr_sehr_sehr_sehr_sehr_lange_user_id
sn: Renz
ou: Organisationseinheit/Organisationseinheit KSC/Organisationseinheit KSC 2
cn: Max Renz
userPassword: user-2-11
########################
# Users Domäne B
########################

View File

@ -304,7 +304,7 @@ CREATE TABLE CLASSIFICATION_HISTORY_EVENT
PRIMARY KEY (ID)
);
-- USER can not be taken as table name because it is a reserved keyword.
CREATE TABLE USER_INFO (
USER_ID VARCHAR(32) NOT NULL,
FIRST_NAME VARCHAR(32) NULL,

View File

@ -9,6 +9,7 @@ import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.monitor.api.MonitorService;
import pro.taskana.task.api.TaskService;
import pro.taskana.user.api.UserService;
import pro.taskana.workbasket.api.WorkbasketService;
/** The TaskanaEngine represents an overall set of all needed services. */
@ -49,6 +50,8 @@ public interface TaskanaEngine {
*/
JobService getJobService();
UserService getUserService();
/**
* The Taskana configuration.
*

View File

@ -61,6 +61,9 @@ import pro.taskana.task.internal.TaskCommentMapper;
import pro.taskana.task.internal.TaskMapper;
import pro.taskana.task.internal.TaskQueryMapper;
import pro.taskana.task.internal.TaskServiceImpl;
import pro.taskana.user.api.UserService;
import pro.taskana.user.internal.UserMapper;
import pro.taskana.user.internal.UserServiceImpl;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.internal.DistributionTargetMapper;
import pro.taskana.workbasket.internal.WorkbasketAccessMapper;
@ -153,6 +156,12 @@ public class TaskanaEngineImpl implements TaskanaEngine {
return new JobServiceImpl(internalTaskanaEngineImpl, sessionManager.getMapper(JobMapper.class));
}
@Override
public UserService getUserService() {
return new UserServiceImpl(
internalTaskanaEngineImpl, sessionManager.getMapper(UserMapper.class));
}
@Override
public TaskanaEngineConfiguration getConfiguration() {
return this.taskanaEngineConfiguration;
@ -311,6 +320,7 @@ public class TaskanaEngineImpl implements TaskanaEngine {
configuration.addMapper(ClassificationQueryMapper.class);
configuration.addMapper(AttachmentMapper.class);
configuration.addMapper(JobMapper.class);
configuration.addMapper(UserMapper.class);
SqlSessionFactory localSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
return SqlSessionManager.newInstance(localSessionFactory);
}

View File

@ -0,0 +1,89 @@
package pro.taskana.user.api;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.user.api.exceptions.UserAlreadyExistException;
import pro.taskana.user.api.exceptions.UserNotFoundException;
import pro.taskana.user.api.models.User;
/** The UserService manages all operations concerning {@linkplain User Users}. */
public interface UserService {
/**
* Returns a new {@linkplain User} with all fields being empty.
*
* <p>It will be only generated but not yet inserted until method {@linkplain
* UserService#createUser(User)} is called.
*
* @return the new {@linkplain User}
*/
User newUser();
/**
* Gets a {@linkplain User}.
*
* <p>If a {@linkplain User} with the specified {@linkplain User#getId() id} is existing in the
* database, it is returned.
*
* @param id the {@linkplain User#getId() id} of the {@linkplain User} to be retrieved
* @return the retrieved {@linkplain User}
* @throws UserNotFoundException if there does not exist a {@linkplain User} with the specified
* {@linkplain User#getId() id} inside the database
*/
User getUser(String id) throws UserNotFoundException;
/**
* Creates a new {@linkplain User}.
*
* <p>If all checks have passed the specified {@linkplain User} will be inserted into the
* database. The {@linkplain User#getId() id} of the {@linkplain User} must be set and should be
* unique, such that no other {@linkplain User} with same {@linkplain User#getId() id} is already
* existing in the database. The {@linkplain User#getFirstName() first name} and the {@linkplain
* User#getLastName() last name} must not be null. The fields for the {@linkplain
* User#getFullName() full name} and the {@linkplain User#getLongName() long name} are set
* according to following rules:
*
* <ul>
* <li><b>fullName</b> = lastName, firstName
* <li><b>longName</b> = lastName, firstName - (id)
* </ul>
*
* @param userToCreate the {@linkplain User} which should be inserted
* @return the inserted {@linkplain User}
* @throws InvalidArgumentException if some fields are not set properly
* @throws NotAuthorizedException if the current user is not admin or business-admin
* @throws UserAlreadyExistException if there already exists a {@linkplain User} with the
* specified {@linkplain User#getId() id} inside the database
*/
User createUser(User userToCreate)
throws InvalidArgumentException, NotAuthorizedException, UserAlreadyExistException;
/**
* Updates an existing {@linkplain User}.
*
* <p>If a {@linkplain User} with the specified {@linkplain User#getId() id} exists in the
* database and if the current user is allowed to perform the operation, the {@linkplain User}
* gets updated according to the set fields of the passed object.
*
* @param userToUpdate the {@linkplain User} which should be updated
* @return the updated {@linkplain User}
* @throws NotAuthorizedException if the current user is not admin or business-admin
* @throws UserNotFoundException if there does not exist a {@linkplain User} with the specified
* {@linkplain User#getId() id} inside the database
*/
User updateUser(User userToUpdate) throws UserNotFoundException, NotAuthorizedException;
/**
* Deletes a {@linkplain User}.
*
* <p>If a {@linkplain User} with the specified {@linkplain User#getId() id} exists in the
* database and if the current user is allowed to perform the operation, the {@linkplain User}
* gets deleted.
*
* @param id the {@linkplain User#getId() id} of the {@linkplain User} which should be deleted
* @throws NotAuthorizedException if the current user is not admin or business-admin
* @throws UserNotFoundException if there does not exist a {@linkplain User} with the specified
* {@linkplain User#getId() id} inside the database
*/
void deleteUser(String id) throws UserNotFoundException, NotAuthorizedException;
}

View File

@ -0,0 +1,26 @@
package pro.taskana.user.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.user.api.models.User;
/**
* This exception is thrown when a {@linkplain User} was tried to be created with an {@linkplain
* User#getId() id} already existing.
*/
public class UserAlreadyExistException extends TaskanaException {
public static final String ERROR_KEY = "USER_ALREADY_EXISTS";
private final String userId;
public UserAlreadyExistException(String userId) {
super(
String.format("User with id '%s' already exists.", userId),
ErrorCode.of(ERROR_KEY, MapCreator.of("userId", userId)));
this.userId = userId;
}
public String getUserId() {
return userId;
}
}

View File

@ -0,0 +1,26 @@
package pro.taskana.user.api.exceptions;
import pro.taskana.common.api.exceptions.ErrorCode;
import pro.taskana.common.api.exceptions.NotFoundException;
import pro.taskana.common.internal.util.MapCreator;
import pro.taskana.user.api.models.User;
/**
* This exception is thrown when a specific {@linkplain User} referenced by its {@linkplain
* User#getId() id} is not in the database.
*/
public class UserNotFoundException extends NotFoundException {
public static final String ERROR_KEY = "USER_NOT_FOUND";
private final String userId;
public UserNotFoundException(String userId) {
super(
String.format("User with id '%s' was not found.", userId),
ErrorCode.of(ERROR_KEY, MapCreator.of("userId", userId)));
this.userId = userId;
}
public String getUserId() {
return userId;
}
}

View File

@ -0,0 +1,190 @@
package pro.taskana.user.api.models;
/** The User holds some relevant information about the TASKANA users. */
public interface User {
/**
* Gets the id of the User.
*
* @return userId
*/
String getId();
/**
* Sets the id of the User.
*
* @param id the id of the User
*/
void setId(String id);
/**
* Gets the first name of the User.
*
* @return firstName
*/
String getFirstName();
/**
* Sets the first name of the User.
*
* @param firstName the first name of the User
*/
void setFirstName(String firstName);
/**
* Gets the last name of the User.
*
* @return lastName
*/
String getLastName();
/**
* Sets the last name of the User.
*
* @param lastName the last name of the User
*/
void setLastName(String lastName);
/**
* Gets the full name of the User.
*
* @return fullName
*/
String getFullName();
/**
* Sets the full name of the User.
*
* @param fullName the full name of the User
*/
void setFullName(String fullName);
/**
* Gets the long name of the User.
*
* @return longName
*/
String getLongName();
/**
* Sets the long name of the User.
*
* @param longName the long name of the User
*/
void setLongName(String longName);
/**
* Gets the email address of the User.
*
* @return email
*/
String getEmail();
/**
* Sets the email address of the User.
*
* @param email the email address of the User
*/
void setEmail(String email);
/**
* Gets the phone number of the User.
*
* @return phone
*/
String getPhone();
/**
* Sets the phone number of the User.
*
* @param phone the phone number of the User
*/
void setPhone(String phone);
/**
* Gets the mobile phone number of the User.
*
* @return mobilePhone
*/
String getMobilePhone();
/**
* Sets the mobile phone number of the User.
*
* @param mobilePhone the mobile phone number of the User
*/
void setMobilePhone(String mobilePhone);
/**
* Gets the orgLevel4 of the User.
*
* @return orgLevel4
*/
String getOrgLevel4();
/**
* Sets the fourth organization level of the User.
*
* @param orgLevel4 the fourth organization level of the User
*/
void setOrgLevel4(String orgLevel4);
/**
* Gets the orgLevel3 of the User.
*
* @return orgLevel3
*/
String getOrgLevel3();
/**
* Sets the third organization level of the User.
*
* @param orgLevel3 the third organization level of the User
*/
void setOrgLevel3(String orgLevel3);
/**
* Gets the orgLevel2 of the User.
*
* @return orgLevel2
*/
String getOrgLevel2();
/**
* Sets the second organization level of the User.
*
* @param orgLevel2 the second organization level of the User
*/
void setOrgLevel2(String orgLevel2);
/**
* Gets the orgLevel1 of the User.
*
* @return orgLevel1
*/
String getOrgLevel1();
/**
* Sets the first organization level of the User.
*
* @param orgLevel1 the first organization level of the User
*/
void setOrgLevel1(String orgLevel1);
/**
* Gets the data of the User.
*
* @return data
*/
String getData();
/**
* Sets the data of the User.
*
* @param data the data of the User
*/
void setData(String data);
User copy();
}

View File

@ -0,0 +1,37 @@
package pro.taskana.user.internal;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;
import pro.taskana.user.internal.models.UserImpl;
public interface UserMapper {
@SelectProvider(type = UserMapperSqlProvider.class, method = "findById")
@Result(property = "id", column = "USER_ID")
@Result(property = "firstName", column = "FIRST_NAME")
@Result(property = "lastName", column = "LASTNAME")
@Result(property = "fullName", column = "FULL_NAME")
@Result(property = "longName", column = "LONG_NAME")
@Result(property = "email", column = "E_MAIL")
@Result(property = "phone", column = "PHONE")
@Result(property = "mobilePhone", column = "MOBILE_PHONE")
@Result(property = "orgLevel4", column = "ORG_LEVEL_4")
@Result(property = "orgLevel3", column = "ORG_LEVEL_3")
@Result(property = "orgLevel2", column = "ORG_LEVEL_2")
@Result(property = "orgLevel1", column = "ORG_LEVEL_1")
@Result(property = "data", column = "DATA")
UserImpl findById(@Param("id") String id);
@InsertProvider(type = UserMapperSqlProvider.class, method = "insert")
void insert(UserImpl user);
@UpdateProvider(type = UserMapperSqlProvider.class, method = "update")
void update(UserImpl user);
@DeleteProvider(type = UserMapperSqlProvider.class, method = "delete")
void delete(String id);
}

View File

@ -0,0 +1,54 @@
package pro.taskana.user.internal;
import static pro.taskana.common.internal.util.SqlProviderUtil.CLOSING_SCRIPT_TAG;
import static pro.taskana.common.internal.util.SqlProviderUtil.DB2_WITH_UR;
import static pro.taskana.common.internal.util.SqlProviderUtil.OPENING_SCRIPT_TAG;
public class UserMapperSqlProvider {
private static final String USER_INFO = "USER_INFO ";
private static final String USER_INFO_COLUMNS =
"USER_ID, FIRST_NAME, LASTNAME, FULL_NAME, LONG_NAME, E_MAIL, PHONE, MOBILE_PHONE, "
+ "ORG_LEVEL_4, ORG_LEVEL_3, ORG_LEVEL_2, ORG_LEVEL_1, DATA ";
private static final String USER_INFO_VALUES =
"#{id}, #{firstName}, #{lastName}, #{fullName}, #{longName}, #{email}, #{phone}, "
+ "#{mobilePhone}, #{orgLevel4}, #{orgLevel3}, #{orgLevel2}, #{orgLevel1}, #{data} ";
private static final String WHERE_USER_ID = "WHERE USER_ID = #{id} ";
private UserMapperSqlProvider() {}
public static String findById() {
return OPENING_SCRIPT_TAG
+ "SELECT "
+ USER_INFO_COLUMNS
+ "FROM "
+ USER_INFO
+ WHERE_USER_ID
+ DB2_WITH_UR
+ CLOSING_SCRIPT_TAG;
}
public static String insert() {
return "INSERT INTO "
+ USER_INFO
+ "( "
+ USER_INFO_COLUMNS
+ ") VALUES("
+ USER_INFO_VALUES
+ ")";
}
public static String update() {
return "UPDATE "
+ USER_INFO
+ "SET FIRST_NAME = #{firstName}, "
+ "LASTNAME = #{lastName}, FULL_NAME = #{fullName}, LONG_NAME = #{longName}, "
+ "E_MAIL = #{email}, PHONE = #{phone}, MOBILE_PHONE = #{mobilePhone}, "
+ "ORG_LEVEL_4 = #{orgLevel4}, ORG_LEVEL_3 = #{orgLevel3}, "
+ "ORG_LEVEL_2 = #{orgLevel2}, ORG_LEVEL_1 = #{orgLevel1}, DATA = #{data} "
+ WHERE_USER_ID;
}
public static String delete() {
return "DELETE FROM " + USER_INFO + WHERE_USER_ID;
}
}

View File

@ -0,0 +1,107 @@
package pro.taskana.user.internal;
import org.apache.ibatis.exceptions.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.common.api.TaskanaRole;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.internal.InternalTaskanaEngine;
import pro.taskana.user.api.UserService;
import pro.taskana.user.api.exceptions.UserAlreadyExistException;
import pro.taskana.user.api.exceptions.UserNotFoundException;
import pro.taskana.user.api.models.User;
import pro.taskana.user.internal.models.UserImpl;
public class UserServiceImpl implements UserService {
private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
private final InternalTaskanaEngine taskanaEngine;
private final UserMapper userMapper;
public UserServiceImpl(InternalTaskanaEngine taskanaEngine, UserMapper userMapper) {
this.taskanaEngine = taskanaEngine;
this.userMapper = userMapper;
}
@Override
public User newUser() {
return new UserImpl();
}
@Override
public User getUser(String id) throws UserNotFoundException {
User user = taskanaEngine.executeInDatabaseConnection(() -> userMapper.findById(id));
if (user == null) {
throw new UserNotFoundException(id);
}
return user;
}
@Override
public User createUser(User userToCreate)
throws InvalidArgumentException, NotAuthorizedException, UserAlreadyExistException {
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
validateAndPopulateFields(userToCreate);
insertIntoDatabase(userToCreate);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Method createUser() created User '{}'.", userToCreate);
}
return userToCreate;
}
@Override
public User updateUser(User userToUpdate) throws UserNotFoundException, NotAuthorizedException {
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
getUser(userToUpdate.getId());
taskanaEngine.executeInDatabaseConnection(() -> userMapper.update((UserImpl) userToUpdate));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Method updateUser() updated User '{}'.", userToUpdate);
}
return userToUpdate;
}
@Override
public void deleteUser(String id) throws UserNotFoundException, NotAuthorizedException {
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
getUser(id);
taskanaEngine.executeInDatabaseConnection(() -> userMapper.delete(id));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Method deleteUser() deleted User with id '{}'.", id);
}
}
private void insertIntoDatabase(User userToCreate) throws UserAlreadyExistException {
try {
taskanaEngine.openConnection();
userMapper.insert((UserImpl) userToCreate);
} catch (PersistenceException e) {
throw new UserAlreadyExistException(userToCreate.getId());
} finally {
taskanaEngine.returnConnection();
}
}
private void validateAndPopulateFields(User userToCreate) throws InvalidArgumentException {
if (userToCreate.getId() == null || userToCreate.getId().isEmpty()) {
throw new InvalidArgumentException("UserId must not be empty when creating User.");
}
if (userToCreate.getFirstName() == null || userToCreate.getLastName() == null) {
throw new InvalidArgumentException("First and last name of User must be set or empty.");
}
if (userToCreate.getFullName() == null || userToCreate.getFullName().isEmpty()) {
userToCreate.setFullName(userToCreate.getLastName() + ", " + userToCreate.getFirstName());
}
if (userToCreate.getLongName() == null || userToCreate.getLongName().isEmpty()) {
userToCreate.setLongName(userToCreate.getFullName() + " - (" + userToCreate.getId() + ")");
}
}
}

View File

@ -0,0 +1,250 @@
package pro.taskana.user.internal.models;
import java.util.Objects;
import pro.taskana.user.api.models.User;
public class UserImpl implements User {
private String id;
private String firstName;
private String lastName;
private String fullName;
private String longName;
private String email;
private String phone;
private String mobilePhone;
private String orgLevel4;
private String orgLevel3;
private String orgLevel2;
private String orgLevel1;
private String data;
public UserImpl() {}
protected UserImpl(UserImpl copyFrom) {
this.id = copyFrom.id;
this.firstName = copyFrom.firstName;
this.lastName = copyFrom.lastName;
this.fullName = copyFrom.fullName;
this.longName = copyFrom.longName;
this.email = copyFrom.email;
this.phone = copyFrom.phone;
this.mobilePhone = copyFrom.mobilePhone;
this.orgLevel4 = copyFrom.orgLevel4;
this.orgLevel3 = copyFrom.orgLevel3;
this.orgLevel2 = copyFrom.orgLevel2;
this.orgLevel1 = copyFrom.orgLevel1;
this.data = copyFrom.data;
}
@Override
public String getId() {
return id;
}
@Override
public void setId(String id) {
this.id = id;
}
@Override
public String getFirstName() {
return firstName;
}
@Override
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Override
public String getLastName() {
return lastName;
}
@Override
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String getFullName() {
return fullName;
}
@Override
public void setFullName(String fullName) {
this.fullName = fullName;
}
@Override
public String getLongName() {
return longName;
}
@Override
public void setLongName(String longName) {
this.longName = longName;
}
@Override
public String getEmail() {
return email;
}
@Override
public void setEmail(String email) {
this.email = email;
}
@Override
public String getPhone() {
return phone;
}
@Override
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String getMobilePhone() {
return mobilePhone;
}
@Override
public void setMobilePhone(String mobilePhone) {
this.mobilePhone = mobilePhone;
}
@Override
public String getOrgLevel4() {
return orgLevel4;
}
@Override
public void setOrgLevel4(String orgLevel4) {
this.orgLevel4 = orgLevel4;
}
@Override
public String getOrgLevel3() {
return orgLevel3;
}
@Override
public void setOrgLevel3(String orgLevel3) {
this.orgLevel3 = orgLevel3;
}
@Override
public String getOrgLevel2() {
return orgLevel2;
}
@Override
public void setOrgLevel2(String orgLevel2) {
this.orgLevel2 = orgLevel2;
}
@Override
public String getOrgLevel1() {
return orgLevel1;
}
@Override
public void setOrgLevel1(String orgLevel1) {
this.orgLevel1 = orgLevel1;
}
@Override
public String getData() {
return data;
}
@Override
public void setData(String data) {
this.data = data;
}
@Override
public UserImpl copy() {
return new UserImpl(this);
}
@Override
public int hashCode() {
return Objects.hash(
id,
firstName,
lastName,
fullName,
longName,
email,
phone,
mobilePhone,
orgLevel4,
orgLevel3,
orgLevel2,
orgLevel1,
data);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
UserImpl other = (UserImpl) obj;
return Objects.equals(id, other.id)
&& Objects.equals(firstName, other.firstName)
&& Objects.equals(lastName, other.lastName)
&& Objects.equals(fullName, other.fullName)
&& Objects.equals(longName, other.longName)
&& Objects.equals(email, other.email)
&& Objects.equals(phone, other.phone)
&& Objects.equals(mobilePhone, other.mobilePhone)
&& Objects.equals(orgLevel4, other.orgLevel4)
&& Objects.equals(orgLevel3, other.orgLevel3)
&& Objects.equals(orgLevel2, other.orgLevel2)
&& Objects.equals(orgLevel1, other.orgLevel1)
&& Objects.equals(data, other.data);
}
@Override
public String toString() {
return "UserImpl [id="
+ id
+ ", firstName="
+ firstName
+ ", lastname="
+ lastName
+ ", fullName="
+ fullName
+ ", longName="
+ longName
+ ", email="
+ email
+ ", phone="
+ phone
+ ", mobilePhone="
+ mobilePhone
+ ", orgLevel4="
+ orgLevel4
+ ", orgLevel3="
+ orgLevel3
+ ", orgLevel2="
+ orgLevel2
+ ", orgLevel1="
+ orgLevel1
+ ", data="
+ data
+ "]";
}
}

View File

@ -25,6 +25,7 @@ import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.sampledata.SampleDataGenerator;
import pro.taskana.task.api.models.Attachment;
import pro.taskana.task.api.models.ObjectReference;
import pro.taskana.user.api.models.User;
/** Base class for all acceptance tests. */
public abstract class AbstractAccTest {
@ -88,7 +89,7 @@ public abstract class AbstractAccTest {
.collect(Collectors.toMap("Property_"::concat, "Property Value of Property_"::concat));
}
protected Attachment createAttachment(
protected Attachment createExampleAttachment(
String classificationKey,
ObjectReference objRef,
String channel,
@ -112,6 +113,25 @@ public abstract class AbstractAccTest {
return attachment;
}
protected User createExampleUser(String id) {
User user = taskanaEngine.getUserService().newUser();
user.setId(id);
user.setFirstName("Hans");
user.setLastName("Georg");
user.setFullName("Georg, Hans");
user.setLongName("Georg, Hans - (user-10-20)");
user.setEmail("hans.georg@web.com");
user.setPhone("1234");
user.setMobilePhone("01574275632");
user.setOrgLevel4("level4");
user.setOrgLevel3("level3");
user.setOrgLevel2("level2");
user.setOrgLevel1("level1");
user.setData("ab");
return user;
}
protected TimeInterval toDaysInterval() {
Instant begin =
ZonedDateTime.of(LocalDate.now(ZoneId.of("UTC")), LocalTime.MIN, ZoneId.of("UTC"))

View File

@ -66,6 +66,10 @@ class ArchitectureTest {
"pro.taskana.monitor.internal",
"pro.taskana.task.api",
"pro.taskana.task.internal",
"pro.taskana.user.api",
"pro.taskana.user.api.exceptions",
"pro.taskana.user.api.models",
"pro.taskana.user.internal",
"pro.taskana.workbasket.api",
"pro.taskana.workbasket.internal",
"pro.taskana.spi.routing.api",

View File

@ -72,7 +72,7 @@ class UpdateObjectsUseUtcTimeStampsAccTest extends AbstractAccTest {
newTask.setDescription("Description of test task");
newTask.setNote("My note");
newTask.addAttachment(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_A",

View File

@ -308,7 +308,7 @@ class CreateTaskAccTest extends AbstractAccTest {
newTask.setClassificationKey("L12010");
Map<String, String> customAttributesForCreate = createSimpleCustomPropertyMap(27);
newTask.addAttachment(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_A",
@ -387,7 +387,7 @@ class CreateTaskAccTest extends AbstractAccTest {
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.addAttachment(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_A",
@ -399,7 +399,7 @@ class CreateTaskAccTest extends AbstractAccTest {
laterInstant,
createSimpleCustomPropertyMap(3)));
newTask.addAttachment(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_A",
@ -441,7 +441,7 @@ class CreateTaskAccTest extends AbstractAccTest {
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.addAttachment(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT", // prio 99, SL P2000D
createObjectReference(
"COMPANY_A",
@ -453,7 +453,7 @@ class CreateTaskAccTest extends AbstractAccTest {
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(3)));
newTask.addAttachment(
createAttachment(
createExampleAttachment(
"L1060", // prio 1, SL P1D
createObjectReference(
"COMPANY_A",
@ -507,7 +507,7 @@ class CreateTaskAccTest extends AbstractAccTest {
};
testCreateTask.accept(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
null,
"E-MAIL",
@ -515,7 +515,7 @@ class CreateTaskAccTest extends AbstractAccTest {
createSimpleCustomPropertyMap(3)));
testCreateTask.accept(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference("COMPANY_A", "SYSTEM_B", "INSTANCE_B", "ArchiveId", null),
"E-MAIL",
@ -523,7 +523,7 @@ class CreateTaskAccTest extends AbstractAccTest {
createSimpleCustomPropertyMap(3)));
testCreateTask.accept(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_A",
@ -536,7 +536,7 @@ class CreateTaskAccTest extends AbstractAccTest {
createSimpleCustomPropertyMap(3)));
testCreateTask.accept(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
null,
@ -632,7 +632,8 @@ class CreateTaskAccTest extends AbstractAccTest {
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", null));
testCreateTask.accept(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", null, "1234567"));
testCreateTask.accept(createObjectReference(null, "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
testCreateTask.accept(
createObjectReference(null, "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
}
@WithAccessId(user = "user-1-1")
@ -671,7 +672,7 @@ class CreateTaskAccTest extends AbstractAccTest {
newTask.setDescription("Description of test task");
newTask.setNote("My note");
newTask.addAttachment(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_A",

View File

@ -169,7 +169,7 @@ class QueryTasksAccTest extends AbstractAccTest {
void testQueryForAttachmentInSummary() throws Exception {
Attachment attachment =
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT", // priority 99, SL P2000D
createObjectReference(
"COMPANY_A",

View File

@ -53,7 +53,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
"TKI:000000000000000000000000000000000000"); // class T2000, prio 1, SL P1D
task.setClassificationKey("T2000");
attachment =
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT", // prio 99, SL P2000D
createObjectReference(
"COMPANY_A",
@ -113,7 +113,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
void should_UpdateTaskReceived_When_AddingTwoAttachments() throws Exception {
task.addAttachment(attachment);
Attachment attachment2 =
createAttachment(
createExampleAttachment(
"L10303",
createObjectReference(
"COMPANY_B",
@ -336,7 +336,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
task.addAttachment(attachment);
Attachment attachment2 =
createAttachment(
createExampleAttachment(
"L10303", // prio 101, SL PT7H
createObjectReference(
"COMPANY_B",
@ -408,7 +408,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
assertThat(task.getAttachments()).isEmpty();
task.addAttachment(attachment);
Attachment attachment2 =
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_B",
@ -427,7 +427,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
.isEqualTo("DOCTYPE_DEFAULT");
Attachment attachment3 =
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_C",
@ -470,7 +470,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.addAttachment(
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT", // prio 99, SL P2000D
createObjectReference(
"COMPANY_A",
@ -482,7 +482,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(3)));
newTask.addAttachment(
createAttachment(
createExampleAttachment(
"L1060", // prio 1, SL P1D
createObjectReference(
"COMPANY_A",
@ -529,7 +529,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
taskService.getTask(
"TKI:000000000000000000000000000000000000"); // class T2000, prio 1, SL P1D
attachment =
createAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT", // prio 99, SL P2000D
createObjectReference(
"COMPANY_A",

View File

@ -0,0 +1,224 @@
package acceptance.user;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import acceptance.AbstractAccTest;
import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Stream;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.function.ThrowingConsumer;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.MismatchedRoleException;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.user.api.UserService;
import pro.taskana.user.api.exceptions.UserAlreadyExistException;
import pro.taskana.user.api.exceptions.UserNotFoundException;
import pro.taskana.user.api.models.User;
/** Acceptance test which tests the functionality of the UserService. */
@ExtendWith(JaasExtension.class)
class UserServiceAccTest extends AbstractAccTest {
private static final UserService USER_SERVICE = taskanaEngine.getUserService();
@WithAccessId(user = "user-1-2")
@Test
void should_ReturnUserWithAllFields_When_IdExisting() throws Exception {
User user = USER_SERVICE.getUser("teamlead-1");
assertThat(user.getFirstName()).isEqualTo("Titus");
assertThat(user.getLastName()).isEqualTo("Toll");
assertThat(user.getFullName()).isEqualTo("Toll, Titus");
assertThat(user.getLongName()).isEqualTo("Toll, Titus - (teamlead-1)");
assertThat(user.getEmail()).isEqualTo("titus.toll@web.de");
assertThat(user.getPhone()).isEqualTo("040-2951854");
assertThat(user.getMobilePhone()).isEqualTo("015637683197");
assertThat(user.getOrgLevel4()).isEqualTo("Novatec");
assertThat(user.getOrgLevel3()).isEqualTo("BPM");
assertThat(user.getOrgLevel2()).isEqualTo("Human Workflow");
assertThat(user.getOrgLevel1()).isEqualTo("TASKANA");
assertThat(user.getData()).isEqualTo("xy");
}
@WithAccessId(user = "user-1-2")
@Test
void should_ThrowUserNotFoundException_When_TryingToGetUserWithNonExistingId() {
ThrowingCallable callable = () -> USER_SERVICE.getUser("NOT_EXISTING");
assertThatThrownBy(callable)
.isInstanceOf(UserNotFoundException.class)
.hasMessage("User with id 'NOT_EXISTING' was not found.");
}
@WithAccessId(user = "admin")
@Test
void should_InsertUserInDatabase_When_CreatingUser() throws Exception {
User userToCreate = createExampleUser("user-10-20");
USER_SERVICE.createUser(userToCreate);
User userInDatabse = USER_SERVICE.getUser(userToCreate.getId());
assertThat(userToCreate)
.hasNoNullFieldsOrProperties()
.isNotSameAs(userInDatabse)
.isEqualTo(userInDatabse);
}
@WithAccessId(user = "admin")
@Test
void should_SetTheLongAndFullNameAccordingToRules_When_CreatingUserWithThoseFieldsEmpty()
throws Exception {
User userToCreate = createExampleUser("user-10-21");
userToCreate.setLongName(null);
userToCreate.setFullName(null);
String fullName = userToCreate.getLastName() + ", " + userToCreate.getFirstName();
String longName =
userToCreate.getLastName()
+ ", "
+ userToCreate.getFirstName()
+ " - ("
+ userToCreate.getId()
+ ")";
User createdUser = USER_SERVICE.createUser(userToCreate);
assertThat(createdUser.getLongName()).isEqualTo(longName);
assertThat(createdUser.getFullName()).isEqualTo(fullName);
}
@WithAccessId(user = "admin")
@Test
void should_ThrowInvalidArgumentException_When_TryingToCreateUserWithFirstOrLastNameNull()
throws Exception {
User userToCreate = createExampleUser("user-10-20");
userToCreate.setFirstName(null);
ThrowingCallable callable = () -> USER_SERVICE.createUser(userToCreate);
assertThatThrownBy(callable)
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("First and last name of User must be set or empty.");
userToCreate.setFirstName("xy");
userToCreate.setLastName(null);
callable = () -> USER_SERVICE.createUser(userToCreate);
assertThatThrownBy(callable).isInstanceOf(InvalidArgumentException.class);
}
@WithAccessId(user = "admin")
@TestFactory
Stream<DynamicTest> should_ThrowInvalidArgumentException_When_TryingToCreateUserWithNotSetId()
throws Exception {
Iterator<String> iterator = Arrays.asList("", null).iterator();
ThrowingConsumer<String> test =
userId -> {
User userToCreate = createExampleUser("user-10-20");
userToCreate.setId(userId);
ThrowingCallable callable = () -> USER_SERVICE.createUser(userToCreate);
assertThatThrownBy(callable)
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("UserId must not be empty when creating User.");
};
return DynamicTest.stream(iterator, c -> "for " + c, test);
}
@WithAccessId(user = "admin")
@Test
void should_ThrowUserAlreadyExistException_When_TryingToCreateUserWithExistingId() {
User userToCreate = createExampleUser("teamlead-1"); // existing userId
ThrowingCallable callable = () -> USER_SERVICE.createUser(userToCreate);
assertThatThrownBy(callable)
.isInstanceOf(UserAlreadyExistException.class)
.hasMessage("User with id 'teamlead-1' already exists.");
}
@WithAccessId(user = "user-1-2")
@Test
void should_ThrowNotAuthorizedException_When_TryingToCreateUserWithoutAdminRole() {
User userToCreate = createExampleUser("user-10-22");
ThrowingCallable callable = () -> USER_SERVICE.createUser(userToCreate);
assertThatThrownBy(callable)
.isInstanceOf(MismatchedRoleException.class)
.hasMessage(
"Not authorized. The current user 'user-1-2' is not member of role(s) "
+ "'[BUSINESS_ADMIN, ADMIN]'.");
}
@WithAccessId(user = "admin")
@Test
void should_UpdateUserInDatabase_When_IdExisting() throws Exception {
User userToUpdate = createExampleUser("teamlead-1"); // existing userId
USER_SERVICE.updateUser(userToUpdate);
User userInDatabase = USER_SERVICE.getUser("teamlead-1");
assertThat(userToUpdate)
.hasNoNullFieldsOrProperties()
.isNotSameAs(userInDatabase)
.isEqualTo(userInDatabase);
}
@WithAccessId(user = "admin")
@Test
void should_ThrowUserNotFoundException_When_TryingToUpdateUserWithNonExistingId() {
User userToUpdate = createExampleUser("NOT_EXISTING");
ThrowingCallable callable = () -> USER_SERVICE.updateUser(userToUpdate);
assertThatThrownBy(callable)
.isInstanceOf(UserNotFoundException.class)
.hasMessage("User with id 'NOT_EXISTING' was not found.");
}
@WithAccessId(user = "user-1-2")
@Test
void should_ThrowNotAuthorizedException_When_TryingToUpdateUserWithNoAdminRole() {
User userToUpdate = createExampleUser("teamlead-1"); // existing userId
ThrowingCallable callable = () -> USER_SERVICE.updateUser(userToUpdate);
assertThatThrownBy(callable)
.isInstanceOf(MismatchedRoleException.class)
.hasMessage(
"Not authorized. The current user 'user-1-2' is not member of role(s) "
+ "'[BUSINESS_ADMIN, ADMIN]'.");
}
@WithAccessId(user = "admin")
@Test
void should_DeleteUserFromDatabase_When_IdExisting() throws Exception {
String id = "teamlead-1";
USER_SERVICE.getUser(id); // User existing
USER_SERVICE.deleteUser(id);
ThrowingCallable callable = () -> USER_SERVICE.getUser(id); // User deleted
assertThatThrownBy(callable).isInstanceOf(UserNotFoundException.class);
}
@WithAccessId(user = "admin")
@Test
void should_ThrowUserNotFoundException_When_TryingToDeleteUserWithNonExistingId() {
ThrowingCallable callable = () -> USER_SERVICE.deleteUser("NOT_EXISTING");
assertThatThrownBy(callable)
.isInstanceOf(UserNotFoundException.class)
.hasMessage("User with id 'NOT_EXISTING' was not found.");
}
@WithAccessId(user = "user-1-2")
@Test
void should_ThrowNotAuthorizedException_When_TryingToDeleteUserWithNoAdminRole() {
ThrowingCallable callable = () -> USER_SERVICE.deleteUser("teamlead-1");
assertThatThrownBy(callable)
.isInstanceOf(MismatchedRoleException.class)
.hasMessage(
"Not authorized. The current user 'user-1-2' is not member of role(s) "
+ "'[BUSINESS_ADMIN, ADMIN]'.");
}
}

View File

@ -17,6 +17,7 @@ import pro.taskana.SpringTaskanaEngineConfiguration;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.task.api.TaskService;
import pro.taskana.user.api.UserService;
import pro.taskana.workbasket.api.WorkbasketService;
/** Class to set /load configuration for Taskana Library. */
@ -73,6 +74,11 @@ public class TaskanaConfig {
return taskanaEngine.getClassificationService();
}
@Bean
public UserService userService(TaskanaEngine taskanaEngine) {
return taskanaEngine.getUserService();
}
@Bean
public ExampleBootstrap exampleBootstrap() {
return new ExampleBootstrap();

View File

@ -42,12 +42,14 @@ Additionally, an optional set of message variables, containing some technical in
| *404 NOT_FOUND* | TASK_NOT_FOUND | taskId
| *404 NOT_FOUND* | WORKBASKET_WITH_ID_NOT_FOUND | workbasketId
| *404 NOT_FOUND* | WORKBASKET_WITH_KEY_NOT_FOUND | workbasketKey, domain
| *404 NOT_FOUND* | USER_NOT_FOUND | userId
| *409 CONFLICT* | ATTACHMENT_ALREADY_EXISTS | attachmentId, taskId
| *409 CONFLICT* | CLASSIFICATION_ALREADY_EXISTS | classificationKey, domain
| *409 CONFLICT* | ENTITY_NOT_UP_TO_DATE | entityId
| *409 CONFLICT* | TASK_ALREADY_EXISTS | externalTaskId
| *409 CONFLICT* | WORKBASKET_ACCESS_ITEM_ALREADY_EXISTS | accessId, workbasketId
| *409 CONFLICT* | WORKBASKET_ALREADY_EXISTS | workbasketKey, domain
| *409 CONFLICT* | USER_ALREADY_EXISTS | userID
| *413 PAYLOAD_TOO_LARGE* | PAYLOAD_TOO_LARGE |
| *423 LOCKED* | CLASSIFICATION_IN_USE | classificationKey, domain
| *423 LOCKED* | WORKBASKET_IN_USE | workbasketId
@ -152,6 +154,13 @@ include::{snippets}/AccessIdControllerRestDocTest/searchForAccessIdDocTest/auto-
include::{snippets}/AccessIdControllerRestDocTest/getGroupsForAccessIdDocTest/auto-section.adoc[]
include::{snippets}/AccessIdControllerRestDocTest/searchUsersByNameOrAccessIdForRoleTest/auto-section.adoc[]
== User Resource
include::{snippets}/UserControllerRestDocTest/getUserDocTest/auto-section.adoc[]
include::{snippets}/UserControllerRestDocTest/createUserDocTest/auto-section.adoc[]
include::{snippets}/UserControllerRestDocTest/updateUserDocTest/auto-section.adoc[]
include::{snippets}/UserControllerRestDocTest/deleteUserDocTest/auto-section.adoc[]
== Configuration Resources
include::{snippets}/TaskanaEngineControllerRestDocTest/getAllDomainsDocTest/auto-section.adoc[]

View File

@ -18,6 +18,7 @@ import pro.taskana.classification.api.ClassificationService;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.monitor.api.MonitorService;
import pro.taskana.task.api.TaskService;
import pro.taskana.user.api.UserService;
import pro.taskana.workbasket.api.WorkbasketService;
/** Configuration for REST service. */
@ -52,6 +53,11 @@ public class RestConfiguration {
return taskanaEngine.getWorkbasketService();
}
@Bean
public UserService getUserService(TaskanaEngine taskanaEngine) {
return taskanaEngine.getUserService();
}
@Bean
@ConditionalOnMissingBean(TaskanaEngine.class)
public TaskanaEngine getTaskanaEngine(TaskanaEngineConfiguration taskanaEngineConfiguration)

View File

@ -69,5 +69,9 @@ public final class RestEndpoints {
public static final String URL_MONITOR_TASK_STATUS_REPORT = API_V1 + "monitor/task-status-report";
public static final String URL_MONITOR_TIMESTAMP_REPORT = API_V1 + "monitor/timestamp-report";
// user endpoints
public static final String URL_USERS = API_V1 + "users";
public static final String URL_USERS_ID = API_V1 + "users/{userId}";
private RestEndpoints() {}
}

View File

@ -50,6 +50,8 @@ import pro.taskana.task.api.exceptions.MismatchedTaskCommentCreatorException;
import pro.taskana.task.api.exceptions.TaskAlreadyExistException;
import pro.taskana.task.api.exceptions.TaskCommentNotFoundException;
import pro.taskana.task.api.exceptions.TaskNotFoundException;
import pro.taskana.user.api.exceptions.UserAlreadyExistException;
import pro.taskana.user.api.exceptions.UserNotFoundException;
import pro.taskana.workbasket.api.exceptions.MismatchedWorkbasketPermissionException;
import pro.taskana.workbasket.api.exceptions.NotAuthorizedToQueryWorkbasketException;
import pro.taskana.workbasket.api.exceptions.WorkbasketAccessItemAlreadyExistException;
@ -81,7 +83,8 @@ public class TaskanaRestExceptionHandler extends ResponseEntityExceptionHandler
TaskCommentNotFoundException.class,
TaskNotFoundException.class,
TaskanaHistoryEventNotFoundException.class,
WorkbasketNotFoundException.class
WorkbasketNotFoundException.class,
UserNotFoundException.class
})
protected ResponseEntity<Object> handleNotFound(TaskanaException ex, WebRequest req) {
return buildResponse(ex.getErrorCode(), ex, req, HttpStatus.NOT_FOUND);
@ -93,6 +96,7 @@ public class TaskanaRestExceptionHandler extends ResponseEntityExceptionHandler
ConcurrencyException.class,
WorkbasketAlreadyExistException.class,
WorkbasketAccessItemAlreadyExistException.class,
UserAlreadyExistException.class,
AttachmentPersistenceException.class,
WorkbasketMarkedForDeletionException.class
})

View File

@ -0,0 +1,126 @@
package pro.taskana.user.rest;
import static pro.taskana.common.rest.RestEndpoints.URL_USERS_ID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.rest.RestEndpoints;
import pro.taskana.user.api.UserService;
import pro.taskana.user.api.exceptions.UserAlreadyExistException;
import pro.taskana.user.api.exceptions.UserNotFoundException;
import pro.taskana.user.api.models.User;
import pro.taskana.user.rest.assembler.UserRepresentationModelAssembler;
import pro.taskana.user.rest.models.UserRepresentationModel;
/** Controller for all {@linkplain User} related endpoints. */
@RestController
@EnableHypermediaSupport(type = HypermediaType.HAL)
public class UserController {
private final UserService userService;
private final UserRepresentationModelAssembler assembler;
@Autowired
UserController(UserService userService, UserRepresentationModelAssembler assembler) {
this.userService = userService;
this.assembler = assembler;
}
/**
* This endpoint retrieves a User.
*
* @title Get a User
* @param userId the id of the requested User
* @return the requested User
* @throws UserNotFoundException if the id has not been found
*/
@GetMapping(URL_USERS_ID)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<UserRepresentationModel> getUser(@PathVariable String userId)
throws UserNotFoundException {
User user = userService.getUser(userId);
return ResponseEntity.ok(assembler.toModel(user));
}
/**
* This endpoint creates a User.
*
* @title Create a User
* @param repModel the User which should be created
* @return the inserted User
* @throws InvalidArgumentException if the id has not been set
* @throws UserAlreadyExistException if a User with id } is already existing
* @throws NotAuthorizedException if the current user is no admin or business-admin
*/
@PostMapping(RestEndpoints.URL_USERS)
@Transactional(rollbackFor = Exception.class)
public ResponseEntity<UserRepresentationModel> createUser(
@RequestBody UserRepresentationModel repModel)
throws InvalidArgumentException, UserAlreadyExistException, NotAuthorizedException {
User user = assembler.toEntityModel(repModel);
user = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(assembler.toModel(user));
}
/**
* This endpoint updates a User.
*
* @title Update a User
* @param userId the id of the User to update
* @param repModel the User with the updated fields
* @return the updated User
* @throws InvalidArgumentException if the id has not been set
* @throws UserNotFoundException if a User with id is not existing in the database
* @throws NotAuthorizedException if the current user is no admin or business-admin
*/
@PutMapping(URL_USERS_ID)
@Transactional(rollbackFor = Exception.class)
public ResponseEntity<UserRepresentationModel> updateUser(
@PathVariable(value = "userId") String userId, @RequestBody UserRepresentationModel repModel)
throws InvalidArgumentException, UserNotFoundException, NotAuthorizedException {
if (!userId.equals(repModel.getUserId())) {
throw new InvalidArgumentException(
String.format(
"UserId '%s' of the URI is not identical"
+ " with the userId '%s' of the object in the payload.",
userId, repModel.getUserId()));
}
User user = assembler.toEntityModel(repModel);
user = userService.updateUser(user);
return ResponseEntity.ok(assembler.toModel(user));
}
/**
* This endpoint deletes a User.
*
* @title Delete a User
* @param userId the id of the User to delete
* @return no content
* @throws UserNotFoundException if the id has not been found
* @throws NotAuthorizedException if the current user is no admin or business-admin
*/
@DeleteMapping(URL_USERS_ID)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<UserRepresentationModel> deleteUser(@PathVariable String userId)
throws UserNotFoundException, NotAuthorizedException {
userService.deleteUser(userId);
return ResponseEntity.noContent().build();
}
}

View File

@ -0,0 +1,56 @@
package pro.taskana.user.rest.assembler;
import org.springframework.hateoas.server.RepresentationModelAssembler;
import org.springframework.stereotype.Component;
import pro.taskana.user.api.models.User;
import pro.taskana.user.internal.models.UserImpl;
import pro.taskana.user.rest.models.UserRepresentationModel;
/**
* The assembler transforms a {@link User} to its resource counterpart {@linkplain
* UserRepresentationModel} and vice versa.
*/
@Component
public class UserRepresentationModelAssembler
implements RepresentationModelAssembler<User, UserRepresentationModel> {
@Override
public UserRepresentationModel toModel(User entity) {
UserRepresentationModel repModel = new UserRepresentationModel();
repModel.setUserId(entity.getId());
repModel.setFirstName(entity.getFirstName());
repModel.setLastName(entity.getLastName());
repModel.setFullName(entity.getFullName());
repModel.setLongName(entity.getLongName());
repModel.setEmail(entity.getEmail());
repModel.setPhone(entity.getPhone());
repModel.setMobilePhone(entity.getMobilePhone());
repModel.setOrgLevel4(entity.getOrgLevel4());
repModel.setOrgLevel3(entity.getOrgLevel3());
repModel.setOrgLevel2(entity.getOrgLevel2());
repModel.setOrgLevel1(entity.getOrgLevel1());
repModel.setData(entity.getData());
return repModel;
}
public User toEntityModel(UserRepresentationModel repModel) {
UserImpl user = new UserImpl();
user.setId(repModel.getUserId());
user.setFirstName(repModel.getFirstName());
user.setLastName(repModel.getLastName());
user.setFullName(repModel.getFullName());
user.setLongName(repModel.getLongName());
user.setEmail(repModel.getEmail());
user.setPhone(repModel.getPhone());
user.setMobilePhone(repModel.getMobilePhone());
user.setOrgLevel4(repModel.getOrgLevel4());
user.setOrgLevel3(repModel.getOrgLevel3());
user.setOrgLevel2(repModel.getOrgLevel2());
user.setOrgLevel1(repModel.getOrgLevel1());
user.setData(repModel.getData());
return user;
}
}

View File

@ -0,0 +1,191 @@
package pro.taskana.user.rest.models;
import java.util.Objects;
import javax.validation.constraints.NotNull;
import org.springframework.hateoas.RepresentationModel;
import pro.taskana.user.api.models.User;
/** The entityModel class for {@linkplain User}. */
public class UserRepresentationModel extends RepresentationModel<UserRepresentationModel> {
/** Unique Id. */
@NotNull protected String userId;
/** The first name of the User. */
protected String firstName;
/** The last name of the User. */
protected String lastName;
/** The full name of the User. */
protected String fullName;
/** The long name of the User. */
protected String longName;
/** The email of the User. */
protected String email;
/** The phone number of the User. */
protected String phone;
/** The mobile phone number of the User. */
protected String mobilePhone;
/** The fourth organisation level of the User. */
protected String orgLevel4;
/** The third organisation level of the User. */
protected String orgLevel3;
/** The second organisation level of the User. */
protected String orgLevel2;
/** The first organisation level of the User. */
protected String orgLevel1;
/** The data of the User. This field is used for additional information about the User. */
protected String data;
public String getUserId() {
return userId;
}
public void setUserId(String id) {
this.userId = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getLongName() {
return longName;
}
public void setLongName(String longName) {
this.longName = longName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getMobilePhone() {
return mobilePhone;
}
public void setMobilePhone(String mobilePhone) {
this.mobilePhone = mobilePhone;
}
public String getOrgLevel4() {
return orgLevel4;
}
public void setOrgLevel4(String orgLevel4) {
this.orgLevel4 = orgLevel4;
}
public String getOrgLevel3() {
return orgLevel3;
}
public void setOrgLevel3(String orgLevel3) {
this.orgLevel3 = orgLevel3;
}
public String getOrgLevel2() {
return orgLevel2;
}
public void setOrgLevel2(String orgLevel2) {
this.orgLevel2 = orgLevel2;
}
public String getOrgLevel1() {
return orgLevel1;
}
public void setOrgLevel1(String orgLevel1) {
this.orgLevel1 = orgLevel1;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
@Override
public int hashCode() {
return Objects.hash(
super.hashCode(),
userId,
firstName,
lastName,
fullName,
longName,
email,
phone,
mobilePhone,
orgLevel4,
orgLevel3,
orgLevel2,
orgLevel1,
data);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
if (!super.equals(obj)) {
return false;
}
UserRepresentationModel other = (UserRepresentationModel) obj;
return userId.equals(other.userId)
&& Objects.equals(firstName, other.firstName)
&& Objects.equals(lastName, other.lastName)
&& Objects.equals(fullName, other.fullName)
&& Objects.equals(longName, other.longName)
&& Objects.equals(email, other.email)
&& Objects.equals(phone, other.phone)
&& Objects.equals(mobilePhone, other.mobilePhone)
&& Objects.equals(orgLevel4, other.orgLevel4)
&& Objects.equals(orgLevel3, other.orgLevel3)
&& Objects.equals(orgLevel2, other.orgLevel2)
&& Objects.equals(orgLevel1, other.orgLevel1)
&& Objects.equals(data, other.data);
}
}

View File

@ -0,0 +1,145 @@
package pro.taskana.user.rest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static pro.taskana.common.test.rest.RestHelper.TEMPLATE;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpStatusCodeException;
import pro.taskana.common.rest.RestEndpoints;
import pro.taskana.common.test.rest.RestHelper;
import pro.taskana.common.test.rest.TaskanaSpringBootTest;
import pro.taskana.user.rest.models.UserRepresentationModel;
/** Tests the endpoints of the UserController. */
@TaskanaSpringBootTest
class UserControllerIntTest {
private final RestHelper restHelper;
@Autowired
UserControllerIntTest(RestHelper restHelper) {
this.restHelper = restHelper;
}
@Test
void should_ReturnExistingUser_When_CallingGetEndpoint() throws Exception {
String url = restHelper.toUrl(RestEndpoints.URL_USERS_ID, "teamlead-1");
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<UserRepresentationModel> responseEntity =
TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(UserRepresentationModel.class));
assertThat(responseEntity.getBody()).isNotNull();
}
@Test
void should_CreateValidUser_When_CallingCreateEndpoint() throws Exception {
UserRepresentationModel newUser = new UserRepresentationModel();
newUser.setUserId("12345");
newUser.setFirstName("Hans");
newUser.setLastName("Georg");
newUser.setFullName("Georg, Hans");
newUser.setLongName("Georg, Hans - (12345)");
newUser.setEmail("hans.georg@web.com");
newUser.setMobilePhone("017325862");
String url = restHelper.toUrl(RestEndpoints.URL_USERS);
HttpEntity<?> auth = new HttpEntity<>(newUser, RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<UserRepresentationModel> responseEntity =
TEMPLATE.exchange(
url,
HttpMethod.POST,
auth,
ParameterizedTypeReference.forType(UserRepresentationModel.class));
assertThat(responseEntity).isNotNull();
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.CREATED);
url = restHelper.toUrl(RestEndpoints.URL_USERS_ID, "12345");
auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
responseEntity =
TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(UserRepresentationModel.class));
assertThat(responseEntity.getBody()).isNotNull();
assertThat(responseEntity.getBody()).isEqualTo(newUser);
}
@Test
void should_UpdateExistingUser_When_CallingUpdateEndpoint() throws Exception {
String url = restHelper.toUrl(RestEndpoints.URL_USERS_ID, "teamlead-1");
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<UserRepresentationModel> responseEntity =
TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(UserRepresentationModel.class));
assertThat(responseEntity.getBody()).isNotNull();
UserRepresentationModel model = responseEntity.getBody();
model.setLastName("Mueller");
auth = new HttpEntity<>(model, RestHelper.generateHeadersForUser("teamlead-1"));
responseEntity =
TEMPLATE.exchange(
url,
HttpMethod.PUT,
auth,
ParameterizedTypeReference.forType(UserRepresentationModel.class));
assertThat(responseEntity.getBody()).isNotNull();
assertThat(responseEntity.getBody().getLastName()).isEqualTo("Mueller");
}
@Test
void should_DeleteExistingUser_When_CallingDeleteEndpoint() {
String url = restHelper.toUrl(RestEndpoints.URL_USERS_ID, "user-1-1");
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<UserRepresentationModel> responseEntity =
TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(UserRepresentationModel.class));
assertThat(responseEntity.getBody()).isNotNull();
assertThat(responseEntity.getBody().getUserId()).isEqualTo("user-1-1");
responseEntity =
TEMPLATE.exchange(
url,
HttpMethod.DELETE,
auth,
ParameterizedTypeReference.forType(UserRepresentationModel.class));
assertThat(responseEntity.getBody()).isNull();
ThrowingCallable httpCall =
() ->
TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(UserRepresentationModel.class));
assertThatThrownBy(httpCall)
.isInstanceOf(HttpStatusCodeException.class)
.extracting(HttpStatusCodeException.class::cast)
.extracting(HttpStatusCodeException::getStatusCode)
.isEqualTo(HttpStatus.NOT_FOUND);
}
}

View File

@ -0,0 +1,62 @@
package pro.taskana.user.rest;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import pro.taskana.common.rest.RestEndpoints;
import pro.taskana.common.test.BaseRestDocTest;
import pro.taskana.user.api.UserService;
import pro.taskana.user.api.models.User;
import pro.taskana.user.rest.assembler.UserRepresentationModelAssembler;
import pro.taskana.user.rest.models.UserRepresentationModel;
class UserControllerRestDocTest extends BaseRestDocTest {
@Autowired UserRepresentationModelAssembler assembler;
@Autowired UserService userService;
@Test
void getUserDocTest() throws Exception {
mockMvc
.perform(get(RestEndpoints.URL_USERS_ID, "teamlead-1"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
@Test
void createUserDocTest() throws Exception {
User user = userService.newUser();
user.setId("user-10-2");
user.setFirstName("Hans");
user.setLastName("Georg");
UserRepresentationModel repModel = assembler.toModel(user);
mockMvc
.perform(post(RestEndpoints.URL_USERS).content(objectMapper.writeValueAsString(repModel)))
.andExpect(MockMvcResultMatchers.status().isCreated());
}
@Test
void updateUserDocTest() throws Exception {
User user = userService.getUser("teamlead-1");
user.setFirstName("new name");
UserRepresentationModel repModel = assembler.toModel(user);
mockMvc
.perform(
put(RestEndpoints.URL_USERS_ID, "teamlead-1")
.content(objectMapper.writeValueAsString(repModel)))
.andExpect(MockMvcResultMatchers.status().isOk());
}
@Test
void deleteUserDocTest() throws Exception {
mockMvc
.perform(delete(RestEndpoints.URL_USERS_ID, "user-1-1"))
.andExpect(MockMvcResultMatchers.status().isNoContent());
}
}

View File

@ -0,0 +1,113 @@
package pro.taskana.user.rest.assembler;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import pro.taskana.common.test.rest.TaskanaSpringBootTest;
import pro.taskana.user.api.UserService;
import pro.taskana.user.api.models.User;
import pro.taskana.user.rest.models.UserRepresentationModel;
/** Test for {@linkplain UserRepresentationModelAssembler}. */
@TaskanaSpringBootTest
class UserRepresentationModelAssemblerTest {
private final UserRepresentationModelAssembler assembler;
private final UserService userService;
@Autowired
UserRepresentationModelAssemblerTest(
UserRepresentationModelAssembler assembler, UserService userService) {
this.assembler = assembler;
this.userService = userService;
}
@Test
void should_ReturnRepresentationModel_When_ConvertingUserEntityToRepresentationModel() {
User user = userService.newUser();
user.setId("user-1-2");
user.setFirstName("Hans");
user.setLastName("Georg");
user.setFullName("Hans Georg");
user.setLongName("Georg, Hans - user-1-2");
user.setEmail("hans.georg@web.com");
user.setPhone("1234");
user.setMobilePhone("01574275632");
user.setOrgLevel4("Novatec");
user.setOrgLevel3("BPM");
user.setOrgLevel2("Human Workflow");
user.setOrgLevel1("TASKANA");
user.setData("xy");
UserRepresentationModel repModel = assembler.toModel(user);
testEquality(user, repModel);
}
@Test
void should_ReturnEntity_When_ConvertingUserRepresentationModelToEntity() {
UserRepresentationModel repModel = new UserRepresentationModel();
repModel.setUserId("user-1-2");
repModel.setFirstName("Hans");
repModel.setLastName("Georg");
repModel.setFullName("Hans Georg");
repModel.setLongName("Georg, Hans - user-1-2");
repModel.setEmail("hans.georg@web.com");
repModel.setPhone("1234");
repModel.setMobilePhone("01574275632");
repModel.setOrgLevel4("Novatec");
repModel.setOrgLevel3("BPM");
repModel.setOrgLevel2("Human Workflow");
repModel.setOrgLevel1("TASKANA");
repModel.setData("xy");
User user = assembler.toEntityModel(repModel);
testEquality(user, repModel);
}
@Test
void should_BeEqual_When_ConvertingEntityToRepModelAndBackToEntity() {
User user = userService.newUser();
user.setId("user-1-2");
user.setFirstName("Hans");
user.setLastName("Georg");
user.setFullName("Hans Georg");
user.setLongName("Georg, Hans - user-1-2");
user.setEmail("hans.georg@web.com");
user.setPhone("1234");
user.setMobilePhone("01574275632");
user.setOrgLevel4("Novatec");
user.setOrgLevel3("BPM");
user.setOrgLevel2("Human Workflow");
user.setOrgLevel1("TASKANA");
user.setData("xy");
UserRepresentationModel repModel = assembler.toModel(user);
User userAfterConversion = assembler.toEntityModel(repModel);
assertThat(user)
.hasNoNullFieldsOrProperties()
.isNotSameAs(userAfterConversion)
.isEqualTo(userAfterConversion);
}
private static void testEquality(User entity, UserRepresentationModel repModel) {
assertThat(entity).hasNoNullFieldsOrProperties();
assertThat(entity).hasNoNullFieldsOrProperties();
assertThat(repModel).hasNoNullFieldsOrProperties();
assertThat(entity.getId()).isEqualTo(repModel.getUserId());
assertThat(entity.getFirstName()).isEqualTo(repModel.getFirstName());
assertThat(entity.getLastName()).isEqualTo(repModel.getLastName());
assertThat(entity.getFullName()).isEqualTo(repModel.getFullName());
assertThat(entity.getLongName()).isEqualTo(repModel.getLongName());
assertThat(entity.getEmail()).isEqualTo(repModel.getEmail());
assertThat(entity.getPhone()).isEqualTo(repModel.getPhone());
assertThat(entity.getMobilePhone()).isEqualTo(repModel.getMobilePhone());
assertThat(entity.getOrgLevel4()).isEqualTo(repModel.getOrgLevel4());
assertThat(entity.getOrgLevel3()).isEqualTo(repModel.getOrgLevel3());
assertThat(entity.getOrgLevel2()).isEqualTo(repModel.getOrgLevel2());
assertThat(entity.getOrgLevel1()).isEqualTo(repModel.getOrgLevel1());
assertThat(entity.getData()).isEqualTo(repModel.getData());
}
}

View File

@ -50,6 +50,9 @@ export const messageByErrorCode = {
TASK_INVALID_OWNER: 'Current user {currentUserId} is not the owner of the Task with id {taskId}',
TASK_INVALID_STATE: 'Task with id {taskId} is in state {taskState}. Required state(s): {requiredTaskStates}.',
USER_ALREADY_EXISTS: 'User with id {userId} cannot be created, because a User with that id does already exist',
USER_NOT_FOUND: 'User with id {userId} cannot be found',
IMPORT_EXPORT_UPLOAD_FAILED: 'Upload failed. The uploaded file probably exceeded the maximum file size of 10 MB.',
IMPORT_EXPORT_UPLOAD_FAILED_AUTH: 'Upload failed because you have no access to apply this operation.',
IMPORT_EXPORT_UPLOAD_FAILED_NOT_FOUND: 'Upload failed because operation was not found',