Skip to content

Commit

Permalink
Merge pull request #4604 from inception-project/bugfix/4602-Project-s…
Browse files Browse the repository at this point in the history
…pecific-user-preferences-may-be-saved-without-a-user

#4602 - Project-specific user preferences may be saved without a user
  • Loading branch information
reckart authored Mar 7, 2024
2 parents bc836ae + 3af8052 commit 3759b2f
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package de.tudarmstadt.ukp.inception.preferences;

import static de.tudarmstadt.ukp.inception.support.json.JSONUtil.toJsonString;
import static java.util.Objects.requireNonNull;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
Expand Down Expand Up @@ -66,11 +67,14 @@ public PreferencesServiceImpl(EntityManager aEntityManager)
@Transactional
public <T> T loadTraitsForUser(Key<T> aKey, User aUser)
{
requireNonNull(aKey, "Parameter [key] must be specified");
requireNonNull(aUser, "Parameter [user] must be specified");

try {
Optional<UserPreference> preference = getRawUserPreference(aKey, aUser);
var preference = getRawUserPreference(aKey, aUser);
if (preference.isPresent()) {
String json = preference.get().getTraits();
T result = JSONUtil.fromJsonString(aKey.getTraitClass(), json);
var json = preference.get().getTraits();
var result = JSONUtil.fromJsonString(aKey.getTraitClass(), json);
LOG.debug("Loaded preferences for key {} and user {}: [{}]", aKey, aUser, result);
return result;
}
Expand All @@ -89,9 +93,12 @@ public <T> T loadTraitsForUser(Key<T> aKey, User aUser)
@Transactional
public <T> void saveTraitsForUser(Key<T> aKey, User aUser, T aTraits)
{
requireNonNull(aKey, "Parameter [key] must be specified");
requireNonNull(aUser, "Parameter [user] must be specified");
requireNonNull(aTraits, "Parameter [traits] must be specified");

try {
UserPreference preference = getRawUserPreference(aKey, aUser)
.orElseGet(UserPreference::new);
var preference = getRawUserPreference(aKey, aUser).orElseGet(UserPreference::new);
preference.setUser(aUser);
preference.setName(aKey.getName());
preference.setTraits(toJsonString(aTraits));
Expand All @@ -106,6 +113,9 @@ public <T> void saveTraitsForUser(Key<T> aKey, User aUser, T aTraits)

private <T> Optional<UserPreference> getRawUserPreference(Key<T> aKey, User aUser)
{
requireNonNull(aKey, "Parameter [key] must be specified");
requireNonNull(aUser, "Parameter [user] must be specified");

var query = String.join("\n", //
"FROM UserPreference ", //
"WHERE user = :user ", //
Expand Down Expand Up @@ -151,6 +161,10 @@ public <T> Optional<T> loadOptionalTraitsForUserAndProject(Key<T> aKey, User aUs
@Transactional
public <T> T loadTraitsForUserAndProject(Key<T> aKey, User aUser, Project aProject)
{
requireNonNull(aKey, "Parameter [key] must be specified");
requireNonNull(aUser, "Parameter [user] must be specified");
requireNonNull(aProject, "Parameter [project] must be specified");

try {
var pref = getUserProjectPreference(aKey, aUser, aProject);
if (pref.isPresent()) {
Expand All @@ -175,8 +189,13 @@ public <T> T loadTraitsForUserAndProject(Key<T> aKey, User aUser, Project aProje
public <T> void saveTraitsForUserAndProject(Key<T> aKey, User aUser, Project aProject,
T aTraits)
{
requireNonNull(aKey, "Parameter [key] must be specified");
requireNonNull(aUser, "Parameter [user] must be specified");
requireNonNull(aProject, "Parameter [project] must be specified");
requireNonNull(aTraits, "Parameter [traits] must be specified");

try {
UserProjectPreference preference = getUserProjectPreference(aKey, aUser, aProject)
var preference = getUserProjectPreference(aKey, aUser, aProject)
.orElseGet(UserProjectPreference::new);
preference.setUser(aUser);
preference.setProject(aProject);
Expand All @@ -195,14 +214,18 @@ public <T> void saveTraitsForUserAndProject(Key<T> aKey, User aUser, Project aPr
private <T> Optional<UserProjectPreference> getUserProjectPreference(Key<T> aKey, User aUser,
Project aProject)
{
String query = String.join("\n", //
requireNonNull(aKey, "Parameter [key] must be specified");
requireNonNull(aUser, "Parameter [user] must be specified");
requireNonNull(aProject, "Parameter [project] must be specified");

var query = String.join("\n", //
"FROM UserProjectPreference ", //
"WHERE user = :user", //
"AND project = :project", //
"AND name = :name");

try {
UserProjectPreference pref = entityManager //
var pref = entityManager //
.createQuery(query, UserProjectPreference.class) //
.setParameter("user", aUser) //
.setParameter("project", aProject) //
Expand All @@ -220,16 +243,14 @@ private <T> Optional<UserProjectPreference> getUserProjectPreference(Key<T> aKey
@Transactional
public List<UserProjectPreference> listUserPreferencesForProject(Project aProject)
{
requireNonNull(aProject, "Parameter [project] must be specified");

var builder = entityManager.getCriteriaBuilder();
var query = builder.createQuery(UserProjectPreference.class);
var root = query.from(UserProjectPreference.class);

query.select(root);
query.where( //
builder.equal(root.get(UserProjectPreference_.project), aProject), //
// TODO We have (had) a bug somewhere that wrote nulls to the USER column
builder.isNotNull(root.get(UserProjectPreference_.user)));
query.where(builder.equal(root.get(UserProjectPreference_.project), aProject));

return entityManager.createQuery(query).getResultList();
}
Expand All @@ -238,18 +259,23 @@ public List<UserProjectPreference> listUserPreferencesForProject(Project aProjec
@Transactional
public void saveUserProjectPreference(UserProjectPreference aPreference)
{
requireNonNull(aPreference, "Parameter [preference] must be specified");

entityManager.persist(aPreference);
}

@Override
@Transactional
public <T> T loadDefaultTraitsForProject(Key<T> aKey, Project aProject)
{
requireNonNull(aKey, "Parameter [key] must be specified");
requireNonNull(aProject, "Parameter [project] must be specified");

try {
Optional<DefaultProjectPreference> pref = getDefaultProjectPreference(aKey, aProject);
var pref = getDefaultProjectPreference(aKey, aProject);
if (pref.isPresent()) {
String json = pref.get().getTraits();
T result = JSONUtil.fromJsonString(aKey.getTraitClass(), json);
var json = pref.get().getTraits();
var result = JSONUtil.fromJsonString(aKey.getTraitClass(), json);
LOG.debug("Loaded default preferences for key {} and project {}: [{}]", aKey,
aProject, result);
return result;
Expand All @@ -269,8 +295,11 @@ public <T> T loadDefaultTraitsForProject(Key<T> aKey, Project aProject)
@Transactional
public <T> void saveDefaultTraitsForProject(Key<T> aKey, Project aProject, T aTraits)
{
requireNonNull(aKey, "Parameter [key] must be specified");
requireNonNull(aProject, "Parameter [project] must be specified");

try {
DefaultProjectPreference preference = getDefaultProjectPreference(aKey, aProject)
var preference = getDefaultProjectPreference(aKey, aProject)
.orElseGet(DefaultProjectPreference::new);
preference.setProject(aProject);
preference.setName(aKey.getName());
Expand All @@ -288,7 +317,10 @@ public <T> void saveDefaultTraitsForProject(Key<T> aKey, Project aProject, T aTr
@Override
public <T> void clearDefaultTraitsForProject(Key<T> aKey, Project aProject)
{
String query = String.join("\n", //
requireNonNull(aKey, "Parameter [key] must be specified");
requireNonNull(aProject, "Parameter [project] must be specified");

var query = String.join("\n", //
"DELETE DefaultProjectPreference ", //
"WHERE project = :project", //
"AND name = :name");
Expand All @@ -302,13 +334,16 @@ public <T> void clearDefaultTraitsForProject(Key<T> aKey, Project aProject)
private <T> Optional<DefaultProjectPreference> getDefaultProjectPreference(Key<T> aKey,
Project aProject)
{
String query = String.join("\n", //
requireNonNull(aKey, "Parameter [key] must be specified");
requireNonNull(aProject, "Parameter [project] must be specified");

var query = String.join("\n", //
"FROM DefaultProjectPreference ", //
"WHERE project = :project", //
"AND name = :name");

try {
DefaultProjectPreference pref = entityManager //
var pref = entityManager //
.createQuery(query, DefaultProjectPreference.class) //
.setParameter("project", aProject) //
.setParameter("name", aKey.getName()) //
Expand All @@ -325,7 +360,9 @@ private <T> Optional<DefaultProjectPreference> getDefaultProjectPreference(Key<T
@Transactional
public List<DefaultProjectPreference> listDefaultTraitsForProject(Project aProject)
{
String query = String.join("\n", //
requireNonNull(aProject, "Parameter [project] must be specified");

var query = String.join("\n", //
"FROM DefaultProjectPreference ", //
"WHERE project = :project");

Expand All @@ -339,26 +376,31 @@ public List<DefaultProjectPreference> listDefaultTraitsForProject(Project aProje
@Transactional
public void saveDefaultProjectPreference(DefaultProjectPreference aPreference)
{
requireNonNull(aPreference, "Parameter [preference] must be specified");

entityManager.persist(aPreference);
}

/*
* Use default constructor of aClass to create new instance of T
*/
@SuppressWarnings("unchecked")
private <T> T buildDefault(Class<T> aClass)
{
requireNonNull(aClass, "Parameter [class] must be specified");

try {
return aClass.getConstructor().newInstance();
}
catch (NoSuchMethodException e) {
if (Map.class.isAssignableFrom(aClass)) {
return (T) new LinkedHashMap();
return (T) new LinkedHashMap<>();
}

return ExceptionUtils.rethrow(e);
return ExceptionUtils.wrapAndThrow(e);
}
catch (Exception e) {
return ExceptionUtils.rethrow(e);
return ExceptionUtils.wrapAndThrow(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipFile;

import org.slf4j.Logger;
Expand Down Expand Up @@ -61,12 +60,11 @@ public DefaultProjectPreferencesExporter(PreferencesService aPreferencesService)
public void exportData(FullProjectExportRequest aRequest, ProjectExportTaskMonitor aMonitor,
ExportedProject aExProject, File aFile)
{
Project project = aRequest.getProject();
var project = aRequest.getProject();

List<ExportedDefaultProjectPreference> exportedDefaultPreferences = new ArrayList<>();
for (DefaultProjectPreference defaultPreference : preferencesService
.listDefaultTraitsForProject(project)) {
ExportedDefaultProjectPreference exportedDefaultPreference = new ExportedDefaultProjectPreference();
var exportedDefaultPreferences = new ArrayList<>();
for (var defaultPreference : preferencesService.listDefaultTraitsForProject(project)) {
var exportedDefaultPreference = new ExportedDefaultProjectPreference();
exportedDefaultPreference.setName(defaultPreference.getName());
exportedDefaultPreference.setTraits(defaultPreference.getTraits());
exportedDefaultPreferences.add(exportedDefaultPreference);
Expand All @@ -81,11 +79,11 @@ public void exportData(FullProjectExportRequest aRequest, ProjectExportTaskMonit
public void importData(ProjectImportRequest aRequest, Project aProject,
ExportedProject aExProject, ZipFile aZip)
{
ExportedDefaultProjectPreference[] exportedDefaultPreferences = aExProject
.getArrayProperty(KEY, ExportedDefaultProjectPreference.class);
var exportedDefaultPreferences = aExProject.getArrayProperty(KEY,
ExportedDefaultProjectPreference.class);

for (ExportedDefaultProjectPreference exportedDefaultPreference : exportedDefaultPreferences) {
DefaultProjectPreference defaultPreference = new DefaultProjectPreference();
for (var exportedDefaultPreference : exportedDefaultPreferences) {
var defaultPreference = new DefaultProjectPreference();
defaultPreference.setProject(aProject);
defaultPreference.setName(exportedDefaultPreference.getName());
defaultPreference.setTraits(exportedDefaultPreference.getTraits());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.clarin.webanno.project.exporters.ProjectPermissionsExporter;
import de.tudarmstadt.ukp.clarin.webanno.security.UserDao;
import de.tudarmstadt.ukp.clarin.webanno.security.model.User;
import de.tudarmstadt.ukp.inception.preferences.PreferencesService;
import de.tudarmstadt.ukp.inception.preferences.config.PreferencesServiceAutoConfig;
import de.tudarmstadt.ukp.inception.preferences.model.UserProjectPreference;
Expand Down Expand Up @@ -74,12 +73,11 @@ public List<Class<? extends ProjectExporter>> getImportDependencies()
public void exportData(FullProjectExportRequest aRequest, ProjectExportTaskMonitor aMonitor,
ExportedProject aExProject, File aFile)
{
Project project = aRequest.getProject();
var project = aRequest.getProject();

List<ExportedUserProjectPreference> exportedDefaultPreferences = new ArrayList<>();
for (UserProjectPreference userPreference : preferencesService
.listUserPreferencesForProject(project)) {
ExportedUserProjectPreference exportedDefaultPreference = new ExportedUserProjectPreference();
var exportedDefaultPreferences = new ArrayList<ExportedUserProjectPreference>();
for (var userPreference : preferencesService.listUserPreferencesForProject(project)) {
var exportedDefaultPreference = new ExportedUserProjectPreference();
exportedDefaultPreference.setUser(userPreference.getUser().getUsername());
exportedDefaultPreference.setName(userPreference.getName());
exportedDefaultPreference.setTraits(userPreference.getTraits());
Expand All @@ -95,19 +93,19 @@ public void exportData(FullProjectExportRequest aRequest, ProjectExportTaskMonit
public void importData(ProjectImportRequest aRequest, Project aProject,
ExportedProject aExProject, ZipFile aZip)
{
ExportedUserProjectPreference[] exportedDefaultPreferences = aExProject
.getArrayProperty(KEY, ExportedUserProjectPreference.class);
var exportedDefaultPreferences = aExProject.getArrayProperty(KEY,
ExportedUserProjectPreference.class);

int importedPreferences = 0;
int missingUsers = 0;
for (ExportedUserProjectPreference exportedDefaultPreference : exportedDefaultPreferences) {
User user = userRepository.get(exportedDefaultPreference.getUser());
var importedPreferences = 0;
var missingUsers = 0;
for (var exportedDefaultPreference : exportedDefaultPreferences) {
var user = userRepository.get(exportedDefaultPreference.getUser());
if (user == null) {
missingUsers++;
continue;
}

UserProjectPreference userPreference = new UserProjectPreference();
var userPreference = new UserProjectPreference();
userPreference.setProject(aProject);
userPreference.setUser(user);
userPreference.setName(exportedDefaultPreference.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,38 @@
constraintName="UK_default_project_preference_name_project"
tableName="default_project_preference" columnNames="project, name" />
</changeSet>
</databaseChangeLog>

<changeSet author="INCEpTION Team" id="20240307-1">
<preConditions onFail="MARK_RAN">
<tableExists tableName="user_project_preference" />
</preConditions>
<delete tableName="user_project_preference">
<where>user IS NULL</where>
</delete>
<delete tableName="user_project_preference">
<where>project IS NULL</where>
</delete>
<addNotNullConstraint tableName="user_project_preference" columnName="user" columnDataType="VARCHAR(255)"/>
<addNotNullConstraint tableName="user_project_preference" columnName="project" columnDataType="BIGINT"/>
</changeSet>

<changeSet author="INCEpTION Team" id="20240307-2">
<preConditions onFail="MARK_RAN">
<tableExists tableName="user_preference" />
</preConditions>
<delete tableName="user_project_preference">
<where>user IS NULL</where>
</delete>
<addNotNullConstraint tableName="user_preference" columnName="user" columnDataType="VARCHAR(255)"/>
</changeSet>

<changeSet author="INCEpTION Team" id="20240307-3">
<preConditions onFail="MARK_RAN">
<tableExists tableName="default_project_preference" />
</preConditions>
<delete tableName="default_project_preference">
<where>project IS NULL</where>
</delete>
<addNotNullConstraint tableName="default_project_preference" columnName="project" columnDataType="BIGINT"/>
</changeSet>
</databaseChangeLog>

0 comments on commit 3759b2f

Please sign in to comment.