1.1 --- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Thu Oct 15 18:36:05 2020 +0200 1.2 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Thu Oct 15 20:02:30 2020 +0200 1.3 @@ -44,12 +44,9 @@ 1.4 import java.sql.Date; 1.5 import java.sql.SQLException; 1.6 import java.util.NoSuchElementException; 1.7 -import java.util.Optional; 1.8 import java.util.stream.Collectors; 1.9 import java.util.stream.Stream; 1.10 1.11 -import static de.uapcore.lightpit.Functions.fqn; 1.12 - 1.13 @WebServlet( 1.14 name = "ProjectsModule", 1.15 urlPatterns = "/projects/*" 1.16 @@ -58,45 +55,26 @@ 1.17 1.18 private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class); 1.19 1.20 - private static final String SESSION_ATTR_SELECTED_PROJECT = fqn(ProjectsModule.class, "selected_project"); 1.21 - private static final String SESSION_ATTR_SELECTED_VERSION = fqn(ProjectsModule.class, "selected_version"); 1.22 - private static final String SESSION_ATTR_SELECTED_COMPONENT = fqn(ProjectsModule.class, "selected_component"); 1.23 - private static final String PARAMETER_SELECTED_PROJECT = "pid"; 1.24 - private static final String PARAMETER_SELECTED_VERSION = "vid"; 1.25 - private static final String PARAMETER_SELECTED_COMPONENT = "cid"; 1.26 - 1.27 @Override 1.28 protected String getResourceBundleName() { 1.29 return "localization.projects"; 1.30 } 1.31 1.32 - private int syncParamWithSession(HttpServletRequest req, String param, String attr) { 1.33 - final var session = req.getSession(); 1.34 - final var idParam = getParameter(req, Integer.class, param); 1.35 - final int id; 1.36 - if (idParam.isPresent()) { 1.37 - id = idParam.get(); 1.38 - session.setAttribute(attr, id); 1.39 - } else { 1.40 - id = Optional.ofNullable(session.getAttribute(attr)).map(x->(Integer)x).orElse(-1); 1.41 - } 1.42 - return id; 1.43 - } 1.44 - 1.45 - private void populate(ProjectView viewModel, HttpServletRequest req, DataAccessObjects dao) throws SQLException { 1.46 + private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObjects dao) throws SQLException { 1.47 final var projectDao = dao.getProjectDao(); 1.48 final var versionDao = dao.getVersionDao(); 1.49 final var componentDao = dao.getComponentDao(); 1.50 1.51 projectDao.list().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add); 1.52 1.53 + if (pathParameters == null) 1.54 + return; 1.55 + 1.56 // Select Project 1.57 - final int pid = syncParamWithSession(req, PARAMETER_SELECTED_PROJECT, SESSION_ATTR_SELECTED_PROJECT); 1.58 - if (pid >= 0) { 1.59 + final int pid = Functions.parseIntOrZero(pathParameters.get("project")); 1.60 + if (pid > 0) { 1.61 final var project = projectDao.find(pid); 1.62 - if (project == null) { 1.63 - req.setAttribute(SESSION_ATTR_SELECTED_PROJECT, -1); 1.64 - } else { 1.65 + if (project != null) { 1.66 final var info = new ProjectInfo(project); 1.67 info.setVersions(versionDao.list(project)); 1.68 info.setComponents(componentDao.list(project)); 1.69 @@ -106,22 +84,19 @@ 1.70 } 1.71 1.72 // Select Version 1.73 - final int vid = syncParamWithSession(req, PARAMETER_SELECTED_VERSION, SESSION_ATTR_SELECTED_VERSION); 1.74 + final int vid = Functions.parseIntOrZero(pathParameters.get("version")); 1.75 if (vid > 0) { 1.76 viewModel.setVersionFilter(versionDao.find(vid)); 1.77 - } else { 1.78 - // NULL for version means: show all unassigned 1.79 - viewModel.setVersionFilter(null); 1.80 + } 1.81 + // TODO: don't treat unknown == unassigned - send 404 for unknown and introduce special word for unassigned 1.82 + 1.83 + // Select Component 1.84 + final int cid = Functions.parseIntOrZero(pathParameters.get("component")); 1.85 + if (cid > 0) { 1.86 + viewModel.setComponentFilter(componentDao.find(cid)); 1.87 } 1.88 1.89 - // Select Component 1.90 - final int cid = syncParamWithSession(req, PARAMETER_SELECTED_COMPONENT, SESSION_ATTR_SELECTED_COMPONENT); 1.91 - if (cid > 0) { 1.92 - viewModel.setComponentFilter(componentDao.find(cid)); 1.93 - } else if (cid <= 0) { 1.94 - // -1 means: filter for unassigned, null means: show all 1.95 - viewModel.setComponentFilter(new Component(-1)); 1.96 - } 1.97 + // TODO: distinguish all/unassigned for components 1.98 } 1.99 1.100 private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) { 1.101 @@ -135,7 +110,7 @@ 1.102 @RequestMapping(method = HttpMethod.GET) 1.103 public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 1.104 final var viewModel = new ProjectView(); 1.105 - populate(viewModel, req, dao); 1.106 + populate(viewModel, null, dao); 1.107 1.108 final var projectDao = dao.getProjectDao(); 1.109 final var versionDao = dao.getVersionDao(); 1.110 @@ -148,30 +123,38 @@ 1.111 return forwardView(req, viewModel, "projects"); 1.112 } 1.113 1.114 - private void configure(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException { 1.115 + private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException { 1.116 viewModel.setProject(project); 1.117 viewModel.setUsers(dao.getUserDao().list()); 1.118 } 1.119 1.120 - @RequestMapping(requestPath = "edit", method = HttpMethod.GET) 1.121 - public ResponseType edit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 1.122 + @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET) 1.123 + public ResponseType edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws IOException, SQLException { 1.124 final var viewModel = new ProjectEditView(); 1.125 - populate(viewModel, req, dao); 1.126 + populate(viewModel, pathParams, dao); 1.127 1.128 - final var project = Optional.ofNullable(viewModel.getProjectInfo()) 1.129 - .map(ProjectInfo::getProject) 1.130 - .orElse(new Project(-1)); 1.131 - configure(viewModel, project, dao); 1.132 + if (viewModel.getProjectInfo() == null) { 1.133 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.134 + return ResponseType.NONE; 1.135 + } 1.136 1.137 + configureProjectEditor(viewModel, viewModel.getProjectInfo().getProject(), dao); 1.138 + return forwardView(req, viewModel, "project-form"); 1.139 + } 1.140 + 1.141 + @RequestMapping(requestPath = "create", method = HttpMethod.GET) 1.142 + public ResponseType create(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 1.143 + final var viewModel = new ProjectEditView(); 1.144 + populate(viewModel, null, dao); 1.145 + configureProjectEditor(viewModel, new Project(-1), dao); 1.146 return forwardView(req, viewModel, "project-form"); 1.147 } 1.148 1.149 @RequestMapping(requestPath = "commit", method = HttpMethod.POST) 1.150 - public ResponseType commit(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 1.151 + public ResponseType commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { 1.152 1.153 - Project project = new Project(-1); 1.154 try { 1.155 - project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); 1.156 + final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); 1.157 project.setName(getParameter(req, String.class, "name").orElseThrow()); 1.158 getParameter(req, String.class, "description").ifPresent(project::setDescription); 1.159 getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl); 1.160 @@ -187,30 +170,25 @@ 1.161 1.162 return ResponseType.HTML; 1.163 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 1.164 - LOG.warn("Form validation failure: {}", ex.getMessage()); 1.165 - LOG.debug("Details:", ex); 1.166 - final var viewModel = new ProjectEditView(); 1.167 - populate(viewModel, req, dao); 1.168 - configure(viewModel, project, dao); 1.169 - // TODO: error text 1.170 - return forwardView(req, viewModel, "project-form"); 1.171 + resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 1.172 + // TODO: implement - fix issue #21 1.173 + return ResponseType.NONE; 1.174 } 1.175 } 1.176 1.177 - @RequestMapping(requestPath = "view", method = HttpMethod.GET) 1.178 - public ResponseType view(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { 1.179 + @RequestMapping(requestPath = "$project/versions/$version", method = HttpMethod.GET) 1.180 + public ResponseType view(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException { 1.181 final var viewModel = new ProjectDetailsView(); 1.182 - populate(viewModel, req, dao); 1.183 + populate(viewModel, pathParams, dao); 1.184 + final var version = viewModel.getVersionFilter(); 1.185 1.186 - if (viewModel.getProjectInfo() == null) { 1.187 - resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); 1.188 + if (viewModel.getProjectInfo() == null || version == null) { 1.189 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.190 return ResponseType.NONE; 1.191 } 1.192 1.193 final var issueDao = dao.getIssueDao(); 1.194 1.195 - final var version = viewModel.getVersionFilter(); 1.196 - 1.197 final var detailView = viewModel.getProjectDetails(); 1.198 final var issues = issueDao.list(version); 1.199 for (var issue : issues) issueDao.joinVersionInformation(issue); 1.200 @@ -224,15 +202,14 @@ 1.201 return forwardView(req, viewModel, "project-details"); 1.202 } 1.203 1.204 - @RequestMapping(requestPath = "versions", method = HttpMethod.GET) 1.205 - public ResponseType versions(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { 1.206 + @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET) 1.207 + public ResponseType versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { 1.208 final var viewModel = new VersionsView(); 1.209 - populate(viewModel, req, dao); 1.210 - viewModel.setVersionFilter(null); 1.211 + populate(viewModel, pathParameters, dao); 1.212 1.213 final var projectInfo = viewModel.getProjectInfo(); 1.214 if (projectInfo == null) { 1.215 - resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); 1.216 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.217 return ResponseType.NONE; 1.218 } 1.219 1.220 @@ -244,49 +221,60 @@ 1.221 return forwardView(req, viewModel, "versions"); 1.222 } 1.223 1.224 - @RequestMapping(requestPath = "versions/edit", method = HttpMethod.GET) 1.225 - public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, SQLException { 1.226 + @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET) 1.227 + public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { 1.228 final var viewModel = new VersionEditView(); 1.229 - populate(viewModel, req, dao); 1.230 + populate(viewModel, pathParameters, dao); 1.231 1.232 - if (viewModel.getProjectInfo() == null) { 1.233 - resp.sendError(HttpServletResponse.SC_NOT_FOUND, "No project selected."); 1.234 + if (viewModel.getProjectInfo() == null || viewModel.getVersionFilter() == null) { 1.235 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.236 return ResponseType.NONE; 1.237 } 1.238 1.239 - viewModel.setVersion(Optional.ofNullable(viewModel.getVersionFilter()).orElse(new Version(-1))); 1.240 + viewModel.setVersion(viewModel.getVersionFilter()); 1.241 1.242 return forwardView(req, viewModel, "version-form"); 1.243 } 1.244 1.245 - @RequestMapping(requestPath = "versions/commit", method = HttpMethod.POST) 1.246 - public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { 1.247 + @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET) 1.248 + public ResponseType createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { 1.249 + final var viewModel = new VersionEditView(); 1.250 + populate(viewModel, pathParameters, dao); 1.251 1.252 - var version = new Version(-1); 1.253 + if (viewModel.getProjectInfo() == null) { 1.254 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.255 + return ResponseType.NONE; 1.256 + } 1.257 + 1.258 + viewModel.setVersion(viewModel.getVersionFilter()); 1.259 + 1.260 + return forwardView(req, viewModel, "version-form"); 1.261 + } 1.262 + 1.263 + @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST) 1.264 + public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { 1.265 + 1.266 try { 1.267 final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow()); 1.268 - version = new Version(getParameter(req, Integer.class, "id").orElseThrow()); 1.269 + final var version = new Version(getParameter(req, Integer.class, "id").orElseThrow()); 1.270 version.setName(getParameter(req, String.class, "name").orElseThrow()); 1.271 getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal); 1.272 version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow())); 1.273 dao.getVersionDao().saveOrUpdate(version, project); 1.274 1.275 - setRedirectLocation(req, "./projects/versions?pid=" + project.getId()); 1.276 + // TODO: improve building the redirect location 1.277 + setRedirectLocation(req, "./projects/" + project.getId() + "/versions/"); 1.278 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 1.279 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 1.280 - LOG.warn("Form validation failure: {}", ex.getMessage()); 1.281 - LOG.debug("Details:", ex); 1.282 - final var viewModel = new VersionEditView(); 1.283 - populate(viewModel, req, dao); 1.284 - viewModel.setVersion(version); 1.285 - // TODO: set Error Text 1.286 - return forwardView(req, viewModel, "version-form"); 1.287 + resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 1.288 + // TODO: implement - fix issue #21 1.289 + return ResponseType.NONE; 1.290 } 1.291 1.292 return ResponseType.HTML; 1.293 } 1.294 1.295 - private void configure(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException { 1.296 + private void configureProjectEditor(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException { 1.297 issue.setProject(viewModel.getProjectInfo().getProject()); 1.298 viewModel.setIssue(issue); 1.299 viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions()); 1.300 @@ -296,30 +284,52 @@ 1.301 } 1.302 } 1.303 1.304 - @RequestMapping(requestPath = "issues/edit", method = HttpMethod.GET) 1.305 - public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException { 1.306 + @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET) 1.307 + public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { 1.308 final var viewModel = new IssueEditView(); 1.309 - populate(viewModel, req, dao); 1.310 + populate(viewModel, pathParameters, dao); 1.311 1.312 - final var issueParam = getParameter(req, Integer.class, "issue"); 1.313 - if (issueParam.isPresent()) { 1.314 - final var issueDao = dao.getIssueDao(); 1.315 - final var issue = issueDao.find(issueParam.get()); 1.316 - issueDao.joinVersionInformation(issue); 1.317 - req.getSession().setAttribute(SESSION_ATTR_SELECTED_PROJECT, issue.getProject().getId()); 1.318 - configure(viewModel, issue, dao); 1.319 - } else { 1.320 - configure(viewModel, new Issue(-1), dao); 1.321 + final var projectInfo = viewModel.getProjectInfo(); 1.322 + if (projectInfo == null) { 1.323 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.324 + return ResponseType.NONE; 1.325 } 1.326 1.327 + final var issueDao = dao.getIssueDao(); 1.328 + final var issue = issueDao.find(Functions.parseIntOrZero(pathParameters.get("issue"))); 1.329 + if (issue == null) { 1.330 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.331 + return ResponseType.NONE; 1.332 + } 1.333 + 1.334 + issueDao.joinVersionInformation(issue); 1.335 + configureProjectEditor(viewModel, issue, dao); 1.336 + 1.337 return forwardView(req, viewModel, "issue-form"); 1.338 } 1.339 1.340 - @RequestMapping(requestPath = "issues/commit", method = HttpMethod.POST) 1.341 - public ResponseType commitIssue(HttpServletRequest req, DataAccessObjects dao) throws SQLException { 1.342 - Issue issue = new Issue(-1); 1.343 + @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET) 1.344 + public ResponseType createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException { 1.345 + final var viewModel = new IssueEditView(); 1.346 + populate(viewModel, pathParameters, dao); 1.347 + 1.348 + final var projectInfo = viewModel.getProjectInfo(); 1.349 + if (projectInfo == null) { 1.350 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.351 + return ResponseType.NONE; 1.352 + } 1.353 + 1.354 + final var issue = new Issue(-1); 1.355 + issue.setProject(projectInfo.getProject()); 1.356 + configureProjectEditor(viewModel, issue, dao); 1.357 + 1.358 + return forwardView(req, viewModel, "issue-form"); 1.359 + } 1.360 + 1.361 + @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST) 1.362 + public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { 1.363 try { 1.364 - issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow()); 1.365 + final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow()); 1.366 issue.setProject(new Project(getParameter(req, Integer.class, "pid").orElseThrow())); 1.367 getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory); 1.368 getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus); 1.369 @@ -343,24 +353,19 @@ 1.370 1.371 dao.getIssueDao().saveOrUpdate(issue, issue.getProject()); 1.372 1.373 - // specifying the issue parameter keeps the edited issue as menu item 1.374 - setRedirectLocation(req, "./projects/view?pid=" + issue.getProject().getId()); 1.375 + // TODO: fix issue #14 1.376 + setRedirectLocation(req, "./projects/" + issue.getProject().getId() + "/versions/"); 1.377 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 1.378 1.379 return ResponseType.HTML; 1.380 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 1.381 - // TODO: set request attribute with error text 1.382 - LOG.warn("Form validation failure: {}", ex.getMessage()); 1.383 - LOG.debug("Details:", ex); 1.384 - final var viewModel = new IssueEditView(); 1.385 - populate(viewModel, req, dao); 1.386 - configure(viewModel, issue, dao); 1.387 - // TODO: set Error Text 1.388 - return forwardView(req, viewModel, "issue-form"); 1.389 + resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 1.390 + // TODO: implement - fix issue #21 1.391 + return ResponseType.NONE; 1.392 } 1.393 } 1.394 1.395 - @RequestMapping(requestPath = "issues/comment", method = HttpMethod.POST) 1.396 + @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST) 1.397 public ResponseType commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException { 1.398 final var issueIdParam = getParameter(req, Integer.class, "issueid"); 1.399 if (issueIdParam.isEmpty()) { 1.400 @@ -383,20 +388,15 @@ 1.401 1.402 dao.getIssueDao().saveComment(issueComment); 1.403 1.404 - // specifying the issue parameter keeps the edited issue as menu item 1.405 - setRedirectLocation(req, "./projects/issues/edit?issue=" + issue.getId()); 1.406 + // TODO: fix redirect location (e.g. after fixing #24) 1.407 + setRedirectLocation(req, "./projects/" + issue.getProject().getId()+"/issues/"+issue.getId()+"/edit"); 1.408 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL); 1.409 1.410 return ResponseType.HTML; 1.411 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) { 1.412 - // TODO: set request attribute with error text 1.413 - LOG.warn("Form validation failure: {}", ex.getMessage()); 1.414 - LOG.debug("Details:", ex); 1.415 - final var viewModel = new IssueEditView(); 1.416 - populate(viewModel, req, dao); 1.417 - configure(viewModel, issue, dao); 1.418 - // TODO: set Error Text 1.419 - return forwardView(req, viewModel, "issue-form"); 1.420 + resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); 1.421 + // TODO: implement - fix issue #21 1.422 + return ResponseType.NONE; 1.423 } 1.424 } 1.425 }