Fri, 23 Oct 2020 13:29:33 +0200
adds issue detail view - fixes #24
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2018 Mike Becker. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29 package de.uapcore.lightpit.modules;
32 import de.uapcore.lightpit.*;
33 import de.uapcore.lightpit.dao.DataAccessObjects;
34 import de.uapcore.lightpit.entities.*;
35 import de.uapcore.lightpit.types.WebColor;
36 import de.uapcore.lightpit.viewmodel.*;
37 import de.uapcore.lightpit.viewmodel.util.IssueSorter;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 import javax.servlet.annotation.WebServlet;
42 import javax.servlet.http.HttpServletRequest;
43 import javax.servlet.http.HttpServletResponse;
44 import java.io.IOException;
45 import java.sql.Date;
46 import java.sql.SQLException;
47 import java.util.List;
48 import java.util.NoSuchElementException;
49 import java.util.Optional;
50 import java.util.stream.Collectors;
51 import java.util.stream.Stream;
53 @WebServlet(
54 name = "ProjectsModule",
55 urlPatterns = "/projects/*"
56 )
57 public final class ProjectsModule extends AbstractLightPITServlet {
59 private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
61 @Override
62 protected String getResourceBundleName() {
63 return "localization.projects";
64 }
66 private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObjects dao) throws SQLException {
67 final var projectDao = dao.getProjectDao();
68 final var versionDao = dao.getVersionDao();
69 final var componentDao = dao.getComponentDao();
71 projectDao.list().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
73 if (pathParameters == null)
74 return;
76 // Select Project
77 final var project = projectDao.findByNode(pathParameters.get("project"));
78 if (project == null)
79 return;
81 final var info = new ProjectInfo(project);
82 info.setVersions(versionDao.list(project));
83 info.setComponents(componentDao.list(project));
84 info.setIssueSummary(projectDao.getIssueSummary(project));
85 viewModel.setProjectInfo(info);
87 // Select Version
88 final var versionNode = pathParameters.get("version");
89 if ("no-version".equals(versionNode)) {
90 viewModel.setVersionFilter(ProjectView.NO_VERSION);
91 } else if ("all-versions".equals(versionNode)) {
92 viewModel.setVersionFilter(ProjectView.ALL_VERSIONS);
93 } else {
94 viewModel.setVersionFilter(versionDao.findByNode(project, versionNode));
95 }
97 // Select Component
98 final var componentNode = pathParameters.get("component");
99 if ("no-component".equals(componentNode)) {
100 viewModel.setComponentFilter(ProjectView.NO_COMPONENT);
101 } else if ("all-components".equals(componentNode)) {
102 viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS);
103 } else {
104 viewModel.setComponentFilter(componentDao.findByNode(project, componentNode));
105 }
106 }
108 private static String sanitizeNode(String node, String defaultValue) {
109 String result = node == null || node.isBlank() ? defaultValue : node;
110 result = result.replace('/', '-');
111 if (result.equals(".") || result.equals("..")) {
112 return "_"+result;
113 } else {
114 return result;
115 }
116 }
118 private ResponseType forwardView(HttpServletRequest req, ProjectView viewModel, String name) {
119 setViewModel(req, viewModel);
120 setContentPage(req, name);
121 setStylesheet(req, "projects");
122 setNavigationMenu(req, "project-navmenu");
123 return ResponseType.HTML;
124 }
126 @RequestMapping(method = HttpMethod.GET)
127 public ResponseType index(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
128 final var viewModel = new ProjectView();
129 populate(viewModel, null, dao);
131 final var projectDao = dao.getProjectDao();
132 final var versionDao = dao.getVersionDao();
134 for (var info : viewModel.getProjectList()) {
135 info.setVersions(versionDao.list(info.getProject()));
136 info.setIssueSummary(projectDao.getIssueSummary(info.getProject()));
137 }
139 return forwardView(req, viewModel, "projects");
140 }
142 private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException {
143 viewModel.setProject(project);
144 viewModel.setUsers(dao.getUserDao().list());
145 }
147 @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET)
148 public ResponseType edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws IOException, SQLException {
149 final var viewModel = new ProjectEditView();
150 populate(viewModel, pathParams, dao);
152 if (!viewModel.isProjectInfoPresent()) {
153 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
154 return ResponseType.NONE;
155 }
157 configureProjectEditor(viewModel, viewModel.getProjectInfo().getProject(), dao);
158 return forwardView(req, viewModel, "project-form");
159 }
161 @RequestMapping(requestPath = "create", method = HttpMethod.GET)
162 public ResponseType create(HttpServletRequest req, DataAccessObjects dao) throws SQLException {
163 final var viewModel = new ProjectEditView();
164 populate(viewModel, null, dao);
165 configureProjectEditor(viewModel, new Project(-1), dao);
166 return forwardView(req, viewModel, "project-form");
167 }
169 @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
170 public ResponseType commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
172 try {
173 final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
174 project.setName(getParameter(req, String.class, "name").orElseThrow());
176 final var node = getParameter(req, String.class, "node").orElse(null);
177 project.setNode(sanitizeNode(node, project.getName()));
179 getParameter(req, String.class, "description").ifPresent(project::setDescription);
180 getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
181 getParameter(req, Integer.class, "owner").map(
182 ownerId -> ownerId >= 0 ? new User(ownerId) : null
183 ).ifPresent(project::setOwner);
185 dao.getProjectDao().saveOrUpdate(project);
187 setRedirectLocation(req, "./projects/");
188 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
189 LOG.debug("Successfully updated project {}", project.getName());
191 return ResponseType.HTML;
192 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
193 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
194 // TODO: implement - fix issue #21
195 return ResponseType.NONE;
196 }
197 }
199 @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET)
200 public ResponseType issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException {
201 final var viewModel = new ProjectDetailsView();
202 populate(viewModel, pathParams, dao);
204 if (!viewModel.isEveryFilterValid()) {
205 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
206 return ResponseType.NONE;
207 }
209 final var project = viewModel.getProjectInfo().getProject();
210 final var version = viewModel.getVersionFilter();
211 final var component = viewModel.getComponentFilter();
213 final var issueDao = dao.getIssueDao();
215 final List<Issue> issues;
216 if (version.equals(ProjectView.NO_VERSION)) {
217 if (component.equals(ProjectView.ALL_COMPONENTS)) {
218 issues = issueDao.list(project, (Version) null);
219 } else if (component.equals(ProjectView.NO_COMPONENT)) {
220 issues = issueDao.list(project, null, null);
221 } else {
222 issues = issueDao.list(project, component, null);
223 }
224 } else if (version.equals(ProjectView.ALL_VERSIONS)) {
225 if (component.equals(ProjectView.ALL_COMPONENTS)) {
226 issues = issueDao.list(project);
227 } else if (component.equals(ProjectView.NO_COMPONENT)) {
228 issues = issueDao.list(project, (Component)null);
229 } else {
230 issues = issueDao.list(project, component);
231 }
232 } else {
233 if (component.equals(ProjectView.ALL_COMPONENTS)) {
234 issues = issueDao.list(project, version);
235 } else if (component.equals(ProjectView.NO_COMPONENT)) {
236 issues = issueDao.list(project, null, version);
237 } else {
238 issues = issueDao.list(project, component, version);
239 }
240 }
242 for (var issue : issues) issueDao.joinVersionInformation(issue);
243 issues.sort(new IssueSorter(
244 new IssueSorter.Criteria(IssueSorter.Field.DONE, true),
245 new IssueSorter.Criteria(IssueSorter.Field.ETA, true),
246 new IssueSorter.Criteria(IssueSorter.Field.UPDATED, false)
247 ));
250 viewModel.getProjectDetails().updateDetails(issues);
251 if (version.getId() > 0)
252 viewModel.getProjectDetails().updateVersionInfo(version);
254 return forwardView(req, viewModel, "project-details");
255 }
257 @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET)
258 public ResponseType versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
259 final var viewModel = new VersionsView();
260 populate(viewModel, pathParameters, dao);
262 final var projectInfo = viewModel.getProjectInfo();
263 if (projectInfo == null) {
264 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
265 return ResponseType.NONE;
266 }
268 final var issueDao = dao.getIssueDao();
269 final var issues = issueDao.list(projectInfo.getProject());
270 for (var issue : issues) issueDao.joinVersionInformation(issue);
271 viewModel.update(projectInfo.getVersions(), issues);
273 return forwardView(req, viewModel, "versions");
274 }
276 @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET)
277 public ResponseType editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
278 final var viewModel = new VersionEditView();
279 populate(viewModel, pathParameters, dao);
281 if (viewModel.getProjectInfo() == null || viewModel.getVersionFilter() == null) {
282 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
283 return ResponseType.NONE;
284 }
286 viewModel.setVersion(viewModel.getVersionFilter());
288 return forwardView(req, viewModel, "version-form");
289 }
291 @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET)
292 public ResponseType createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
293 final var viewModel = new VersionEditView();
294 populate(viewModel, pathParameters, dao);
296 if (viewModel.getProjectInfo() == null) {
297 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
298 return ResponseType.NONE;
299 }
301 viewModel.setVersion(viewModel.getVersionFilter());
303 return forwardView(req, viewModel, "version-form");
304 }
306 @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST)
307 public ResponseType commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
309 try {
310 final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow());
311 if (project == null) {
312 // TODO: improve error handling, because not found is not correct for this POST request
313 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
314 return ResponseType.NONE;
315 }
316 final var version = new Version(getParameter(req, Integer.class, "id").orElseThrow());
317 version.setName(getParameter(req, String.class, "name").orElseThrow());
319 final var node = getParameter(req, String.class, "node").orElse(null);
320 version.setNode(sanitizeNode(node, version.getName()));
322 getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
323 version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
324 dao.getVersionDao().saveOrUpdate(version, project);
326 setRedirectLocation(req, "./projects/" + project.getNode() + "/versions/");
327 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
328 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
329 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
330 // TODO: implement - fix issue #21
331 return ResponseType.NONE;
332 }
334 return ResponseType.HTML;
335 }
337 @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET)
338 public ResponseType components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
339 final var viewModel = new ComponentsView();
340 populate(viewModel, pathParameters, dao);
342 final var projectInfo = viewModel.getProjectInfo();
343 if (projectInfo == null) {
344 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
345 return ResponseType.NONE;
346 }
348 final var issueDao = dao.getIssueDao();
349 final var issues = issueDao.list(projectInfo.getProject());
350 viewModel.update(projectInfo.getComponents(), issues);
352 return forwardView(req, viewModel, "components");
353 }
355 @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET)
356 public ResponseType editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
357 final var viewModel = new ComponentEditView();
358 populate(viewModel, pathParameters, dao);
360 if (viewModel.getProjectInfo() == null || viewModel.getComponentFilter() == null) {
361 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
362 return ResponseType.NONE;
363 }
365 viewModel.setComponent(viewModel.getComponentFilter());
366 viewModel.setUsers(dao.getUserDao().list());
368 return forwardView(req, viewModel, "component-form");
369 }
371 @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET)
372 public ResponseType createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
373 final var viewModel = new ComponentEditView();
374 populate(viewModel, pathParameters, dao);
376 if (viewModel.getProjectInfo() == null) {
377 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
378 return ResponseType.NONE;
379 }
381 viewModel.setComponent(new Component(-1));
382 viewModel.setUsers(dao.getUserDao().list());
384 return forwardView(req, viewModel, "component-form");
385 }
387 @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST)
388 public ResponseType commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
390 try {
391 final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow());
392 if (project == null) {
393 // TODO: improve error handling, because not found is not correct for this POST request
394 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
395 return ResponseType.NONE;
396 }
397 final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow());
398 component.setName(getParameter(req, String.class, "name").orElseThrow());
400 final var node = getParameter(req, String.class, "node").orElse(null);
401 component.setNode(sanitizeNode(node, component.getName()));
403 component.setColor(getParameter(req, WebColor.class, "color").orElseThrow());
404 getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal);
405 getParameter(req, Integer.class, "lead").map(
406 userid -> userid >= 0 ? new User(userid) : null
407 ).ifPresent(component::setLead);
408 getParameter(req, String.class, "description").ifPresent(component::setDescription);
410 dao.getComponentDao().saveOrUpdate(component, project);
412 setRedirectLocation(req, "./projects/" + project.getNode() + "/components/");
413 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
414 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
415 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
416 // TODO: implement - fix issue #21
417 return ResponseType.NONE;
418 }
420 return ResponseType.HTML;
421 }
423 private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException {
424 final var project = viewModel.getProjectInfo().getProject();
425 issue.setProject(project); // automatically set current project for new issues
426 viewModel.setIssue(issue);
427 viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
428 viewModel.setUsers(dao.getUserDao().list());
429 viewModel.setComponents(dao.getComponentDao().list(project));
430 }
432 @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET)
433 public ResponseType viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
434 final var viewModel = new IssueDetailView();
435 populate(viewModel, pathParameters, dao);
437 final var projectInfo = viewModel.getProjectInfo();
438 if (projectInfo == null) {
439 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
440 return ResponseType.NONE;
441 }
443 final var issueDao = dao.getIssueDao();
444 final var issue = issueDao.find(Functions.parseIntOrZero(pathParameters.get("issue")));
445 if (issue == null) {
446 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
447 return ResponseType.NONE;
448 }
450 issueDao.joinVersionInformation(issue);
451 viewModel.setIssue(issue);
452 viewModel.setComments(issueDao.listComments(issue));
454 return forwardView(req, viewModel, "issue-view");
455 }
457 // TODO: why should the issue editor be child of $project?
458 @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET)
459 public ResponseType editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
460 final var viewModel = new IssueEditView();
461 populate(viewModel, pathParameters, dao);
463 final var projectInfo = viewModel.getProjectInfo();
464 if (projectInfo == null) {
465 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
466 return ResponseType.NONE;
467 }
469 final var issueDao = dao.getIssueDao();
470 final var issue = issueDao.find(Functions.parseIntOrZero(pathParameters.get("issue")));
471 if (issue == null) {
472 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
473 return ResponseType.NONE;
474 }
476 issueDao.joinVersionInformation(issue);
477 configureIssueEditor(viewModel, issue, dao);
479 return forwardView(req, viewModel, "issue-form");
480 }
482 @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET)
483 public ResponseType createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException {
484 final var viewModel = new IssueEditView();
485 populate(viewModel, pathParameters, dao);
487 final var projectInfo = viewModel.getProjectInfo();
488 if (projectInfo == null) {
489 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
490 return ResponseType.NONE;
491 }
493 final var issue = new Issue(-1);
494 issue.setProject(projectInfo.getProject());
495 configureIssueEditor(viewModel, issue, dao);
497 return forwardView(req, viewModel, "issue-form");
498 }
500 @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST)
501 public ResponseType commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
502 try {
503 final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
504 final var componentId = getParameter(req, Integer.class, "component");
505 final Component component;
506 if (componentId.isPresent()) {
507 component = dao.getComponentDao().find(componentId.get());
508 } else {
509 component = null;
510 }
511 final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow());
512 if (project == null) {
513 // TODO: improve error handling, because not found is not correct for this POST request
514 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
515 return ResponseType.NONE;
516 }
517 issue.setProject(project);
518 getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
519 getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
520 issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
521 issue.setComponent(component);
522 getParameter(req, Integer.class, "assignee").map(userid -> {
523 if (userid >= 0) {
524 return new User(userid);
525 } else if (userid == -2) {
526 return Optional.ofNullable(component).map(Component::getLead).orElse(null);
527 } else {
528 return null;
529 }
530 }
531 ).ifPresent(issue::setAssignee);
532 getParameter(req, String.class, "description").ifPresent(issue::setDescription);
533 getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
535 getParameter(req, Integer[].class, "affected")
536 .map(Stream::of)
537 .map(stream ->
538 stream.map(Version::new).collect(Collectors.toList())
539 ).ifPresent(issue::setAffectedVersions);
540 getParameter(req, Integer[].class, "resolved")
541 .map(Stream::of)
542 .map(stream ->
543 stream.map(Version::new).collect(Collectors.toList())
544 ).ifPresent(issue::setResolvedVersions);
546 dao.getIssueDao().saveOrUpdate(issue, issue.getProject());
548 // TODO: fix redirect location
549 setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
550 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
552 return ResponseType.HTML;
553 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
554 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
555 // TODO: implement - fix issue #21
556 return ResponseType.NONE;
557 }
558 }
560 @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST)
561 public ResponseType commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException {
562 final var issueIdParam = getParameter(req, Integer.class, "issueid");
563 if (issueIdParam.isEmpty()) {
564 resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form.");
565 return ResponseType.NONE;
566 }
567 final var issue = dao.getIssueDao().find(issueIdParam.get());
568 if (issue == null) {
569 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
570 return ResponseType.NONE;
571 }
572 try {
573 final var issueComment = new IssueComment(getParameter(req, Integer.class, "commentid").orElse(-1), issue);
574 issueComment.setComment(getParameter(req, String.class, "comment").orElse(""));
576 if (issueComment.getComment().isBlank()) {
577 throw new IllegalArgumentException("comment.null");
578 }
580 LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId());
581 if (req.getRemoteUser() != null) {
582 dao.getUserDao().findByUsername(req.getRemoteUser()).ifPresent(issueComment::setAuthor);
583 }
585 dao.getIssueDao().saveComment(issueComment);
587 // TODO: fix redirect location
588 setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
589 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
591 return ResponseType.HTML;
592 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
593 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
594 // TODO: implement - fix issue #21
595 return ResponseType.NONE;
596 }
597 }
598 }