Thu, 05 Nov 2020 13:37:48 +0100
removes ResponseType enum
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.ServletException;
42 import javax.servlet.annotation.WebServlet;
43 import javax.servlet.http.HttpServletRequest;
44 import javax.servlet.http.HttpServletResponse;
45 import java.io.IOException;
46 import java.sql.Date;
47 import java.sql.SQLException;
48 import java.util.List;
49 import java.util.NoSuchElementException;
50 import java.util.Optional;
51 import java.util.stream.Collectors;
52 import java.util.stream.Stream;
54 @WebServlet(
55 name = "ProjectsModule",
56 urlPatterns = "/projects/*"
57 )
58 public final class ProjectsModule extends AbstractLightPITServlet {
60 private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
62 @Override
63 protected String getResourceBundleName() {
64 return "localization.projects";
65 }
67 private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObjects dao) throws SQLException {
68 final var projectDao = dao.getProjectDao();
69 final var versionDao = dao.getVersionDao();
70 final var componentDao = dao.getComponentDao();
72 projectDao.list().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
74 if (pathParameters == null)
75 return;
77 // Select Project
78 final var project = projectDao.findByNode(pathParameters.get("project"));
79 if (project == null)
80 return;
82 final var info = new ProjectInfo(project);
83 info.setVersions(versionDao.list(project));
84 info.setComponents(componentDao.list(project));
85 info.setIssueSummary(projectDao.getIssueSummary(project));
86 viewModel.setProjectInfo(info);
88 // Select Version
89 final var versionNode = pathParameters.get("version");
90 if ("no-version".equals(versionNode)) {
91 viewModel.setVersionFilter(ProjectView.NO_VERSION);
92 } else if ("all-versions".equals(versionNode)) {
93 viewModel.setVersionFilter(ProjectView.ALL_VERSIONS);
94 } else {
95 viewModel.setVersionFilter(versionDao.findByNode(project, versionNode));
96 }
98 // Select Component
99 final var componentNode = pathParameters.get("component");
100 if ("no-component".equals(componentNode)) {
101 viewModel.setComponentFilter(ProjectView.NO_COMPONENT);
102 } else if ("all-components".equals(componentNode)) {
103 viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS);
104 } else {
105 viewModel.setComponentFilter(componentDao.findByNode(project, componentNode));
106 }
107 }
109 private static String sanitizeNode(String node, String defaultValue) {
110 String result = node == null || node.isBlank() ? defaultValue : node;
111 result = result.replace('/', '-');
112 if (result.equals(".") || result.equals("..")) {
113 return "_"+result;
114 } else {
115 return result;
116 }
117 }
119 private void forwardView(HttpServletRequest req, HttpServletResponse resp, ProjectView viewModel, String name) throws ServletException, IOException {
120 setViewModel(req, viewModel);
121 setContentPage(req, name);
122 setStylesheet(req, "projects");
123 setNavigationMenu(req, "project-navmenu");
124 renderSite(req, resp);
125 }
127 @RequestMapping(method = HttpMethod.GET)
128 public void index(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, ServletException, IOException {
129 final var viewModel = new ProjectView();
130 populate(viewModel, null, dao);
132 final var projectDao = dao.getProjectDao();
133 final var versionDao = dao.getVersionDao();
135 for (var info : viewModel.getProjectList()) {
136 info.setVersions(versionDao.list(info.getProject()));
137 info.setIssueSummary(projectDao.getIssueSummary(info.getProject()));
138 }
140 forwardView(req, resp, viewModel, "projects");
141 }
143 private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObjects dao) throws SQLException {
144 viewModel.setProject(project);
145 viewModel.setUsers(dao.getUserDao().list());
146 }
148 @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET)
149 public void edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws IOException, SQLException, ServletException {
150 final var viewModel = new ProjectEditView();
151 populate(viewModel, pathParams, dao);
153 if (!viewModel.isProjectInfoPresent()) {
154 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
155 return;
156 }
158 configureProjectEditor(viewModel, viewModel.getProjectInfo().getProject(), dao);
159 forwardView(req, resp, viewModel, "project-form");
160 }
162 @RequestMapping(requestPath = "create", method = HttpMethod.GET)
163 public void create(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, ServletException, IOException {
164 final var viewModel = new ProjectEditView();
165 populate(viewModel, null, dao);
166 configureProjectEditor(viewModel, new Project(-1), dao);
167 forwardView(req, resp, viewModel, "project-form");
168 }
170 @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
171 public void commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException {
173 try {
174 final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
175 project.setName(getParameter(req, String.class, "name").orElseThrow());
177 final var node = getParameter(req, String.class, "node").orElse(null);
178 project.setNode(sanitizeNode(node, project.getName()));
180 getParameter(req, String.class, "description").ifPresent(project::setDescription);
181 getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
182 getParameter(req, Integer.class, "owner").map(
183 ownerId -> ownerId >= 0 ? new User(ownerId) : null
184 ).ifPresent(project::setOwner);
186 dao.getProjectDao().saveOrUpdate(project);
188 setRedirectLocation(req, "./projects/");
189 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
190 LOG.debug("Successfully updated project {}", project.getName());
192 renderSite(req, resp);
193 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
194 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
195 // TODO: implement - fix issue #21
196 }
197 }
199 @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET)
200 public void issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObjects dao) throws SQLException, IOException, ServletException {
201 final var viewModel = new ProjectDetailsView();
202 populate(viewModel, pathParams, dao);
204 if (!viewModel.isEveryFilterValid()) {
205 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
206 return;
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 forwardView(req, resp, viewModel, "project-details");
255 }
257 @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET)
258 public void versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
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;
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 forwardView(req, resp, viewModel, "versions");
274 }
276 @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET)
277 public void editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
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;
284 }
286 viewModel.setVersion(viewModel.getVersionFilter());
288 forwardView(req, resp, viewModel, "version-form");
289 }
291 @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET)
292 public void createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
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;
299 }
301 viewModel.setVersion(new Version(-1));
303 forwardView(req, resp, viewModel, "version-form");
304 }
306 @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST)
307 public void commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException {
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;
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);
329 renderSite(req, resp);
330 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
331 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
332 // TODO: implement - fix issue #21
333 }
334 }
336 @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET)
337 public void components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
338 final var viewModel = new ComponentsView();
339 populate(viewModel, pathParameters, dao);
341 final var projectInfo = viewModel.getProjectInfo();
342 if (projectInfo == null) {
343 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
344 return;
345 }
347 final var issueDao = dao.getIssueDao();
348 final var issues = issueDao.list(projectInfo.getProject());
349 viewModel.update(projectInfo.getComponents(), issues);
351 forwardView(req, resp, viewModel, "components");
352 }
354 @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET)
355 public void editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
356 final var viewModel = new ComponentEditView();
357 populate(viewModel, pathParameters, dao);
359 if (viewModel.getProjectInfo() == null || viewModel.getComponentFilter() == null) {
360 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
361 return;
362 }
364 viewModel.setComponent(viewModel.getComponentFilter());
365 viewModel.setUsers(dao.getUserDao().list());
367 forwardView(req, resp, viewModel, "component-form");
368 }
370 @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET)
371 public void createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
372 final var viewModel = new ComponentEditView();
373 populate(viewModel, pathParameters, dao);
375 if (viewModel.getProjectInfo() == null) {
376 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
377 return;
378 }
380 viewModel.setComponent(new Component(-1));
381 viewModel.setUsers(dao.getUserDao().list());
383 forwardView(req, resp, viewModel, "component-form");
384 }
386 @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST)
387 public void commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException {
389 try {
390 final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow());
391 if (project == null) {
392 // TODO: improve error handling, because not found is not correct for this POST request
393 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
394 return;
395 }
396 final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow());
397 component.setName(getParameter(req, String.class, "name").orElseThrow());
399 final var node = getParameter(req, String.class, "node").orElse(null);
400 component.setNode(sanitizeNode(node, component.getName()));
402 component.setColor(getParameter(req, WebColor.class, "color").orElseThrow());
403 getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal);
404 getParameter(req, Integer.class, "lead").map(
405 userid -> userid >= 0 ? new User(userid) : null
406 ).ifPresent(component::setLead);
407 getParameter(req, String.class, "description").ifPresent(component::setDescription);
409 dao.getComponentDao().saveOrUpdate(component, project);
411 setRedirectLocation(req, "./projects/" + project.getNode() + "/components/");
412 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
414 renderSite(req, resp);
415 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
416 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
417 // TODO: implement - fix issue #21
418 }
419 }
421 private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObjects dao) throws SQLException {
422 final var project = viewModel.getProjectInfo().getProject();
423 issue.setProject(project); // automatically set current project for new issues
424 viewModel.setIssue(issue);
425 viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
426 viewModel.setUsers(dao.getUserDao().list());
427 viewModel.setComponents(dao.getComponentDao().list(project));
428 }
430 @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET)
431 public void viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
432 final var viewModel = new IssueDetailView();
433 populate(viewModel, pathParameters, dao);
435 final var projectInfo = viewModel.getProjectInfo();
436 if (projectInfo == null) {
437 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
438 return;
439 }
441 final var issueDao = dao.getIssueDao();
442 final var issue = issueDao.find(Functions.parseIntOrZero(pathParameters.get("issue")));
443 if (issue == null) {
444 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
445 return;
446 }
448 issueDao.joinVersionInformation(issue);
449 viewModel.setIssue(issue);
450 viewModel.setComments(issueDao.listComments(issue));
452 forwardView(req, resp, viewModel, "issue-view");
453 }
455 // TODO: why should the issue editor be child of $project?
456 @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET)
457 public void editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
458 final var viewModel = new IssueEditView();
459 populate(viewModel, pathParameters, dao);
461 final var projectInfo = viewModel.getProjectInfo();
462 if (projectInfo == null) {
463 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
464 return;
465 }
467 final var issueDao = dao.getIssueDao();
468 final var issue = issueDao.find(Functions.parseIntOrZero(pathParameters.get("issue")));
469 if (issue == null) {
470 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
471 return;
472 }
474 issueDao.joinVersionInformation(issue);
475 configureIssueEditor(viewModel, issue, dao);
477 forwardView(req, resp, viewModel, "issue-form");
478 }
480 @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET)
481 public void createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObjects dao) throws IOException, SQLException, ServletException {
482 final var viewModel = new IssueEditView();
483 populate(viewModel, pathParameters, dao);
485 final var projectInfo = viewModel.getProjectInfo();
486 if (projectInfo == null) {
487 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
488 return;
489 }
491 final var issue = new Issue(-1);
492 issue.setProject(projectInfo.getProject());
493 configureIssueEditor(viewModel, issue, dao);
495 forwardView(req, resp, viewModel, "issue-form");
496 }
498 @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST)
499 public void commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException, ServletException {
500 try {
501 final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow());
502 final var componentId = getParameter(req, Integer.class, "component");
503 final Component component;
504 if (componentId.isPresent()) {
505 component = dao.getComponentDao().find(componentId.get());
506 } else {
507 component = null;
508 }
509 final var project = dao.getProjectDao().find(getParameter(req, Integer.class, "pid").orElseThrow());
510 if (project == null) {
511 // TODO: improve error handling, because not found is not correct for this POST request
512 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
513 return;
514 }
515 issue.setProject(project);
516 getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
517 getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
518 issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
519 issue.setComponent(component);
520 getParameter(req, Integer.class, "assignee").map(userid -> {
521 if (userid >= 0) {
522 return new User(userid);
523 } else if (userid == -2) {
524 return Optional.ofNullable(component).map(Component::getLead).orElse(null);
525 } else {
526 return null;
527 }
528 }
529 ).ifPresent(issue::setAssignee);
530 getParameter(req, String.class, "description").ifPresent(issue::setDescription);
531 getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
533 getParameter(req, Integer[].class, "affected")
534 .map(Stream::of)
535 .map(stream ->
536 stream.map(Version::new).collect(Collectors.toList())
537 ).ifPresent(issue::setAffectedVersions);
538 getParameter(req, Integer[].class, "resolved")
539 .map(Stream::of)
540 .map(stream ->
541 stream.map(Version::new).collect(Collectors.toList())
542 ).ifPresent(issue::setResolvedVersions);
544 dao.getIssueDao().saveOrUpdate(issue, issue.getProject());
546 // TODO: fix redirect location
547 setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
548 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
550 renderSite(req, resp);
551 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
552 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
553 // TODO: implement - fix issue #21
554 }
555 }
557 @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST)
558 public void commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws SQLException, IOException, ServletException {
559 final var issueIdParam = getParameter(req, Integer.class, "issueid");
560 if (issueIdParam.isEmpty()) {
561 resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form.");
562 return;
563 }
564 final var issue = dao.getIssueDao().find(issueIdParam.get());
565 if (issue == null) {
566 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
567 return;
568 }
569 try {
570 final var issueComment = new IssueComment(getParameter(req, Integer.class, "commentid").orElse(-1));
571 issueComment.setComment(getParameter(req, String.class, "comment").orElse(""));
573 if (issueComment.getComment().isBlank()) {
574 throw new IllegalArgumentException("comment.null");
575 }
577 LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId());
578 if (req.getRemoteUser() != null) {
579 dao.getUserDao().findByUsername(req.getRemoteUser()).ifPresent(issueComment::setAuthor);
580 }
582 dao.getIssueDao().saveComment(issue, issueComment);
584 // TODO: fix redirect location
585 setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
586 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
588 renderSite(req, resp);
589 } catch (NoSuchElementException | IllegalArgumentException | SQLException ex) {
590 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
591 // TODO: implement - fix issue #21
592 }
593 }
594 }