Skip to content

Commit

Permalink
Merge pull request #17 from samply/feature/action-explanations
Browse files Browse the repository at this point in the history
Feature/action explanations
  • Loading branch information
djuarezgf authored Nov 18, 2024
2 parents e22b6e6 + eaa6acc commit 7ffb06d
Show file tree
Hide file tree
Showing 8 changed files with 459 additions and 19 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [0.0.1 - 2024-11-15]
## [0.0.1 - 2024-11-18]
### Added
- First version of the project
- Spring Application
Expand Down Expand Up @@ -156,3 +156,5 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Notification async execution pool
- Exporter async execution pool
- Action explanations
- Integration of action explanatios in fetch Actions
- Action explanations templates
12 changes: 7 additions & 5 deletions src/main/java/de/samply/app/ProjectManagerConst.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ public class ProjectManagerConst {
public final static String FETCH_OTHER_DOCUMENTS_ACTION = "FETCH_OTHER_DOCUMENTS";
public final static String ACCEPT_PROJECT_RESULTS_ACTION = "ACCEPT_PROJECT_RESULTS";
public final static String REJECT_PROJECT_RESULTS_ACTION = "REJECT_PROJECT_RESULTS";
public final static String ACCEPT_PROJECT_ANALYSIS_ACTION = "ACCEPT_PROJECT_ANALYSIS_ACTION";
public final static String REJECT_PROJECT_ANALYSIS_ACTION = "REJECT_PROJECT_ANALYSIS_ACTION";
public final static String REQUEST_CHANGES_IN_PROJECT_ANALYSIS_ACTION = "REQUEST_CHANGES_IN_PROJECT_ANALYSIS_ACTION";
public final static String ACCEPT_PROJECT_ANALYSIS_ACTION = "ACCEPT_PROJECT_ANALYSIS";
public final static String REJECT_PROJECT_ANALYSIS_ACTION = "REJECT_PROJECT_ANALYSIS";
public final static String REQUEST_CHANGES_IN_PROJECT_ANALYSIS_ACTION = "REQUEST_CHANGES_IN_PROJECT_ANALYSIS";
public final static String REQUEST_CHANGES_IN_PROJECT_ACTION = "REQUEST_CHANGES_IN_PROJECT";
public final static String FETCH_NOTIFICATIONS_ACTION = "FETCH_NOTIFICATIONS";
public final static String SET_NOTIFICATION_AS_READ_ACTION = "SET_NOTIFICATION_AS_READ";
Expand All @@ -110,7 +110,6 @@ public class ProjectManagerConst {
public final static String FETCH_PROJECT_ROLES_ACTION = "FETCH_PROJECT_ROLES";
public final static String SEND_EXPORT_FILES_TO_RESEARCH_ENVIRONMENT_ACTION = "SEND_EXPORT_FILES_TO_RESEARCH_ENVIRONMENT";
public final static String ARE_EXPORT_FILES_TRANSFERRED_TO_RESEARCH_ENVIRONMENT_ACTION = "ARE_EXPORT_FILES_TRANSFERRED_TO_RESEARCH_ENVIRONMENT";
public final static String FETCH_ACTION_EXPLANATIONS_ACTION = "FETCH_ACTION_EXPLANATIONS";


// REST Services
Expand Down Expand Up @@ -191,13 +190,14 @@ public class ProjectManagerConst {
public final static String FETCH_USERS_FOR_AUTOCOMPLETE = "/autocomplete-users";
public final static String FETCH_PROJECT_USERS = "/project-users";
public final static String EXIST_INVITED_USERS = "/exist-invited-users";
public final static String FETCH_ACTION_EXPLANATIONS = "/action-explanations";


// REST Parameters
public final static String PROJECT_CODE = "project-code";
public final static String PROJECT_CONFIGURATION = "project-configuration";
public final static String NOTIFICATION_ID = "notification-id";
public final static String BRIDGEHEAD = "bridgehead";
public final static String LANGUAGE = "language";
public final static String MESSAGE = "message";
public final static String BRIDGEHEADS = "bridgeheads";
public final static String EXPLORER_IDS = "explorer-ids";
Expand Down Expand Up @@ -347,6 +347,7 @@ public class ProjectManagerConst {
public final static String OIDC_REALM = "OIDC_REALM";
public final static String ENABLE_EXPORTER = "ENABLE_EXPORTER";
public final static String MAX_TIME_TO_WAIT_FOCUS_TASK_IN_MINUTES = "MAX_TIME_TO_WAIT_FOCUS_TASK_IN_MINUTES";
public final static String DEFAULT_LANGUAGE = "DEFAULT_LANGUAGE";

public final static String CODER_BASE_URL = "CODER_BASE_URL";
public final static String CODER_ORGANISATION_ID = "CODER_ORGANISATION_ID";
Expand Down Expand Up @@ -478,6 +479,7 @@ public class ProjectManagerConst {
public final static String EXPORTER_CORE_POOL_SIZE_SV = HEAD_SV + EXPORTER_CORE_POOL_SIZE + ":4" + BOTTOM_SV;
public final static String EXPORTER_MAX_POOL_SIZE_SV = HEAD_SV + EXPORTER_MAX_POOL_SIZE + ":8" + BOTTOM_SV;
public final static String EXPORTER_QUEUE_CAPACITY_SV = HEAD_SV + EXPORTER_QUEUE_CAPACITY + ":500" + BOTTOM_SV;
public final static String DEFAULT_LANGUAGE_SV = HEAD_SV + DEFAULT_LANGUAGE + ":EN" + BOTTOM_SV;

// Async Configuration
public final static String ASYNC_EMAIL_SENDER_EXECUTOR = "email-sender";
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/de/samply/app/ProjectManagerController.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,22 @@ public ResponseEntity<String> test() {
public ResponseEntity<String> fetchActions(
@ProjectCode @RequestParam(name = ProjectManagerConst.PROJECT_CODE, required = false) String projectCode,
@Bridgehead @RequestParam(name = ProjectManagerConst.BRIDGEHEAD, required = false) String bridgehead,
@RequestParam(name = ProjectManagerConst.LANGUAGE, required = false) String language,
@RequestParam(name = ProjectManagerConst.SITE) String site
) {
return convertToResponseEntity(() ->
this.frontendService.fetchModuleActionPackage(site, Optional.ofNullable(projectCode), Optional.ofNullable(bridgehead), true));
this.frontendService.fetchModuleActionPackage(site, Optional.ofNullable(projectCode), Optional.ofNullable(bridgehead), Optional.ofNullable(language), true));
}

@GetMapping(value = ProjectManagerConst.ALL_ACTIONS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> fetchAllActions(
@ProjectCode @RequestParam(name = ProjectManagerConst.PROJECT_CODE, required = false) String projectCode,
@Bridgehead @RequestParam(name = ProjectManagerConst.BRIDGEHEAD, required = false) String bridgehead,
@RequestParam(name = ProjectManagerConst.LANGUAGE, required = false) String language,
@RequestParam(name = ProjectManagerConst.SITE) String site
) {
return convertToResponseEntity(() ->
this.frontendService.fetchModuleActionPackage(site, Optional.ofNullable(projectCode), Optional.ofNullable(bridgehead), false));
this.frontendService.fetchModuleActionPackage(site, Optional.ofNullable(projectCode), Optional.ofNullable(bridgehead), Optional.ofNullable(language), false));
}

@FrontendSiteModule(site = ProjectManagerConst.PROJECT_DASHBOARD_SITE, module = ProjectManagerConst.PROJECTS_MODULE)
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/de/samply/frontend/Action.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public record Action(
String path,
String method,
String[] params
String[] params,
String explanation
) {
}
3 changes: 3 additions & 0 deletions src/main/java/de/samply/frontend/ActionExplanation.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import de.samply.project.ProjectType;
import de.samply.project.state.ProjectBridgeheadState;
import de.samply.project.state.ProjectState;
import de.samply.project.state.UserProjectState;
import de.samply.query.QueryState;
import de.samply.user.roles.OrganisationRole;
import de.samply.user.roles.ProjectRole;
Expand Down Expand Up @@ -36,6 +37,8 @@ public class ActionExplanation {
private ProjectBridgeheadState projectBridgeheadState;
@JsonProperty(value = "query-state")
private QueryState queryState;
@JsonProperty(value = "user-projectState")
private UserProjectState userProjectState;

@JsonProperty(value = "messages", required = true)
private Map<String, String> languageMessageMap;
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/de/samply/frontend/ActionExplanations.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.samply.db.model.Project;
import de.samply.db.model.ProjectBridgehead;
import de.samply.db.model.ProjectBridgeheadUser;
import de.samply.security.SessionUser;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

import java.util.List;
Expand All @@ -19,4 +24,52 @@ public Optional<List<ActionExplanation>> getActionExplanation(String action) {
return Optional.ofNullable(actionExplanationMap.get(action));
}

public String fetchExplanation(@NotNull String action, @NotNull String module, @NotNull String language,
Optional<Project> project, Optional<ProjectBridgehead> projectBridgehead,
Optional<ProjectBridgeheadUser> projectBridgeheadUser, SessionUser sessionUser) {
Optional<List<ActionExplanation>> actionExplanations = getActionExplanation(action);
if (actionExplanations.isPresent()) {
for (ActionExplanation explanation : actionExplanations.get()) {
if (isRequiredExplanation(module, project, projectBridgehead, projectBridgeheadUser, sessionUser, explanation)) {
return explanation.getLanguageMessageMap().get(language);
}
}
}
return null;
}

private boolean isRequiredExplanation(String module, Optional<Project> project, Optional<ProjectBridgehead> projectBridgehead, Optional<ProjectBridgeheadUser> projectBridgeheadUser, SessionUser sessionUser, ActionExplanation explanation) {
if (module != null && explanation.getModule() != null && !module.equalsIgnoreCase(explanation.getModule())) {
return false;
}
if (project.isPresent()) {
if (explanation.getProjectType() != null && project.get().getType() != explanation.getProjectType()) {
return false;
}
if (explanation.getProjectState() != null && project.get().getState() != explanation.getProjectState()) {
return false;
}
}
if (projectBridgehead.isPresent()) {
if (explanation.getQueryState() != null && projectBridgehead.get().getQueryState() != explanation.getQueryState()) {
return false;
}
if (explanation.getProjectBridgeheadState() != null && projectBridgehead.get().getState() != explanation.getProjectBridgeheadState()) {
return false;
}
}
if (projectBridgeheadUser.isPresent()) {
if (explanation.getProjectRole() != null && projectBridgeheadUser.get().getProjectRole() != explanation.getProjectRole()) {
return false;
}
if (explanation.getUserProjectState() != null && projectBridgeheadUser.get().getProjectState() != explanation.getUserProjectState()) {
return false;
}
}
if (explanation.getOrganisationRole() != null && !sessionUser.getUserOrganisationRoles().containsRole(explanation.getOrganisationRole())) {
return false;
}
return true;
}

}
74 changes: 66 additions & 8 deletions src/main/java/de/samply/frontend/FrontendService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
import de.samply.aop.ConstraintsService;
import de.samply.app.ProjectManagerConst;
import de.samply.app.ProjectManagerController;
import de.samply.db.model.Project;
import de.samply.db.model.ProjectBridgehead;
import de.samply.db.model.ProjectBridgeheadUser;
import de.samply.db.repository.ProjectBridgeheadRepository;
import de.samply.db.repository.ProjectBridgeheadUserRepository;
import de.samply.db.repository.ProjectRepository;
import de.samply.security.SessionUser;
import de.samply.user.roles.RolesExtractor;
import de.samply.utils.AspectUtils;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -20,19 +27,42 @@ public class FrontendService {
private final ConstraintsService constraintsService;
private final FrontendConfiguration frontendConfiguration;
private final String explorerUrlRedirectUriParameter;
private final ActionExplanations actionExplanations;
private final String defaultLanguage;
private final SessionUser sessionUser;
private final ProjectRepository projectRepository;
private final ProjectBridgeheadRepository projectBridgeheadRepository;
private final ProjectBridgeheadUserRepository projectBridgeheadUserRepository;

public FrontendService(
ConstraintsService constraintsService,
FrontendConfiguration frontendConfiguration,
@Value(ProjectManagerConst.EXPLORER_REDIRECT_URI_PARAMETER_SV) String explorerUrlRedirectUriParameter) {
ActionExplanations actionExplanations,
SessionUser sessionUser,
ProjectBridgeheadUserRepository projectBridgeheadUserRepository,
ProjectRepository projectRepository,
ProjectBridgeheadRepository projectBridgeheadRepository,
@Value(ProjectManagerConst.EXPLORER_REDIRECT_URI_PARAMETER_SV) String explorerUrlRedirectUriParameter,
@Value(ProjectManagerConst.DEFAULT_LANGUAGE_SV) String defaultLanguage) {
this.constraintsService = constraintsService;
this.frontendConfiguration = frontendConfiguration;
this.explorerUrlRedirectUriParameter = explorerUrlRedirectUriParameter;
this.actionExplanations = actionExplanations;
this.defaultLanguage = defaultLanguage;
this.sessionUser = sessionUser;
this.projectBridgeheadUserRepository = projectBridgeheadUserRepository;
this.projectRepository = projectRepository;
this.projectBridgeheadRepository = projectBridgeheadRepository;
}

public Map<String, Map<String, Action>> fetchModuleActionPackage(String site, Optional<String> projectCode, Optional<String> bridgehead, boolean withConstraints) {
public Map<String, Map<String, Action>> fetchModuleActionPackage(String site, Optional<String> projectCode,
Optional<String> bridgehead, Optional<String> language, boolean withConstraints) {
Map<String, Map<String, Action>> moduleActionMap = new HashMap<>();
String rootPath = RolesExtractor.getRootPath();
String tempLanguage = (language.isPresent()) ? language.get() : defaultLanguage;
Optional<Project> project = fetchProject(projectCode);
Optional<ProjectBridgehead> projectBridgehead = fetchProjectBridgehead(project, bridgehead);
Optional<ProjectBridgeheadUser> projectBridgeheadUser = fetchProjectBridgeheadUser(projectBridgehead);
Arrays.stream(ProjectManagerController.class.getDeclaredMethods()).forEach(method -> {
FrontendSiteModules frontendSiteModules = method.getAnnotation(FrontendSiteModules.class);
FrontendSiteModule frontendSiteModule = method.getAnnotation(FrontendSiteModule.class);
Expand All @@ -46,7 +76,8 @@ public Map<String, Map<String, Action>> fetchModuleActionPackage(String site, Op
frontendSiteModuleList.addAll(List.of(frontendSiteModules.value()));
}
frontendSiteModuleList.forEach(tempFrontendSiteModule ->
fetchModuleActionsPackages(moduleActionMap, rootPath, path, tempFrontendSiteModule, frontendAction, site, projectCode, bridgehead, method, withConstraints));
fetchModuleActionsPackages(moduleActionMap, rootPath, path, tempFrontendSiteModule, frontendAction,
site, projectCode, project, projectBridgehead, projectBridgeheadUser, tempLanguage, bridgehead, method, withConstraints));
});
return moduleActionMap;
}
Expand All @@ -55,7 +86,10 @@ private void fetchModuleActionsPackages(Map<String, Map<String, Action>> moduleA
String rootPath, Optional<String> path,
FrontendSiteModule frontendSiteModule, FrontendAction frontendAction,
String site, Optional<String> projectCode,
Optional<String> bridgehead, Method method,
Optional<Project> project,
Optional<ProjectBridgehead> projectBridgehead,
Optional<ProjectBridgeheadUser> projectBridgeheadUser,
String language, Optional<String> bridgehead, Method method,
boolean withConstraints) {
if (frontendSiteModule != null && site.equals(frontendSiteModule.site()) && frontendAction != null && path.isPresent()) {
Optional<RoleConstraints> roleConstraints = Optional.ofNullable(method.getAnnotation(RoleConstraints.class));
Expand All @@ -69,19 +103,26 @@ private void fetchModuleActionsPackages(Map<String, Map<String, Action>> moduleA
responseEntity = this.constraintsService.checkProjectConstraints(projectConstraints, projectCode);
}
if (responseEntity.isEmpty() || !withConstraints) { // If there are no restrictions
addAction(moduleActionsMap, frontendSiteModule, frontendAction, rootPath, path, method);
addAction(moduleActionsMap, frontendSiteModule, frontendAction, rootPath, path, method,
project, projectBridgehead, projectBridgeheadUser, language);
}
}
}

private void addAction(Map<String, Map<String, Action>> moduleActionsMap, FrontendSiteModule frontendSiteModule, FrontendAction frontendAction, String rootPath, Optional<String> path, Method method) {
private void addAction(Map<String, Map<String, Action>> moduleActionsMap,
FrontendSiteModule frontendSiteModule, FrontendAction frontendAction,
String rootPath, Optional<String> path, Method method,
Optional<Project> project, Optional<ProjectBridgehead> projectBridgehead,
Optional<ProjectBridgeheadUser> projectBridgeheadUser, String language) {
Map<String, Action> actionNameActionsMap = moduleActionsMap.get(frontendSiteModule.module());
if (actionNameActionsMap == null) {
actionNameActionsMap = new HashMap<>();
moduleActionsMap.put(frontendSiteModule.module(), actionNameActionsMap);
}
actionNameActionsMap.put(frontendAction.action(),
new Action(rootPath + path.get(), fetchHttpMethod(method), fetchHttpParams(method)));
new Action(rootPath + path.get(), fetchHttpMethod(method), fetchHttpParams(method),
actionExplanations.fetchExplanation(frontendAction.action(), frontendSiteModule.module(),
language, project, projectBridgehead, projectBridgeheadUser, sessionUser)));
}

private String fetchHttpMethod(Method method) {
Expand All @@ -108,10 +149,27 @@ public String fetchUrl(String site, Map<String, String> parameters) {
return result.toUriString();
}

public Map<String, String> fetchExplorerRedirectUri(String site, Map<String, String> parameters){
public Map<String, String> fetchExplorerRedirectUri(String site, Map<String, String> parameters) {
Map<String, String> result = new HashMap<>();
result.put(explorerUrlRedirectUriParameter, fetchUrl(site, parameters));
return result;
}

private Optional<Project> fetchProject(Optional<String> projectCode) {
return (projectCode.isPresent()) ? projectRepository.findByCode(projectCode.get()) : Optional.empty();
}

private Optional<ProjectBridgehead> fetchProjectBridgehead(Optional<Project> project, Optional<String> bridgehead) {
return (project.isPresent() && bridgehead.isPresent()) ?
projectBridgeheadRepository.findFirstByBridgeheadAndProject(bridgehead.get(), project.get()) :
Optional.empty();
}

private Optional<ProjectBridgeheadUser> fetchProjectBridgeheadUser(Optional<ProjectBridgehead> projectBridgehead) {
return (projectBridgehead.isPresent()) ?
projectBridgeheadUserRepository.getFirstByEmailAndProjectBridgeheadOrderByModifiedAtDesc(sessionUser.getEmail(), projectBridgehead.get()) :
Optional.empty();
}


}
Loading

0 comments on commit 7ffb06d

Please sign in to comment.