Tue, 05 Jan 2021 19:19:31 +0100
migrates the utility classes for the AbstractServlet
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.DataAccessObject;
34 import de.uapcore.lightpit.entities.*;
35 import de.uapcore.lightpit.filter.AllFilter;
36 import de.uapcore.lightpit.filter.IssueFilter;
37 import de.uapcore.lightpit.filter.NoneFilter;
38 import de.uapcore.lightpit.filter.SpecificFilter;
39 import de.uapcore.lightpit.types.IssueCategory;
40 import de.uapcore.lightpit.types.IssueStatus;
41 import de.uapcore.lightpit.types.VersionStatus;
42 import de.uapcore.lightpit.types.WebColor;
43 import de.uapcore.lightpit.viewmodel.*;
44 import de.uapcore.lightpit.viewmodel.util.IssueSorter;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 import javax.servlet.ServletException;
49 import javax.servlet.annotation.WebServlet;
50 import javax.servlet.http.HttpServletRequest;
51 import javax.servlet.http.HttpServletResponse;
52 import java.io.IOException;
53 import java.sql.Date;
54 import java.sql.SQLException;
55 import java.util.NoSuchElementException;
56 import java.util.Optional;
57 import java.util.stream.Collectors;
58 import java.util.stream.Stream;
60 @WebServlet(
61 name = "ProjectsModule",
62 urlPatterns = "/projects/*"
63 )
64 public final class ProjectsModule extends AbstractServlet {
66 private static final Logger LOG = LoggerFactory.getLogger(ProjectsModule.class);
68 @Override
69 protected String getResourceBundleName() {
70 return "localization.projects";
71 }
73 private static int parseIntOrZero(String str) {
74 try {
75 return Integer.parseInt(str);
76 } catch (NumberFormatException ex) {
77 return 0;
78 }
79 }
81 private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObject dao) {
82 dao.listProjects().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
84 if (pathParameters == null)
85 return;
87 // Select Project
88 final var project = dao.findProjectByNode(pathParameters.get("project"));
89 if (project == null)
90 return;
92 final var info = new ProjectInfo(project);
93 info.setVersions(dao.listVersions(project));
94 info.setComponents(dao.listComponents(project));
95 info.setIssueSummary(dao.collectIssueSummary(project));
96 viewModel.setProjectInfo(info);
98 // Select Version
99 final var versionNode = pathParameters.get("version");
100 if (versionNode != null) {
101 if ("no-version".equals(versionNode)) {
102 viewModel.setVersionFilter(ProjectView.NO_VERSION);
103 } else if ("all-versions".equals(versionNode)) {
104 viewModel.setVersionFilter(ProjectView.ALL_VERSIONS);
105 } else {
106 viewModel.setVersionFilter(dao.findVersionByNode(project, versionNode));
107 }
108 }
110 // Select Component
111 final var componentNode = pathParameters.get("component");
112 if (componentNode != null) {
113 if ("no-component".equals(componentNode)) {
114 viewModel.setComponentFilter(ProjectView.NO_COMPONENT);
115 } else if ("all-components".equals(componentNode)) {
116 viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS);
117 } else {
118 viewModel.setComponentFilter(dao.findComponentByNode(project, componentNode));
119 }
120 }
121 }
123 private static String sanitizeNode(String node, String defaultValue) {
124 String result = node == null || node.isBlank() ? defaultValue : node;
125 result = result.replace('/', '-');
126 if (result.equals(".") || result.equals("..")) {
127 return "_"+result;
128 } else {
129 return result;
130 }
131 }
133 private void forwardView(HttpServletRequest req, HttpServletResponse resp, ProjectView viewModel, String name) throws ServletException, IOException {
134 setViewModel(req, viewModel);
135 setContentPage(req, name);
136 setStylesheet(req, "projects");
137 setNavigationMenu(req, "project-navmenu");
138 renderSite(req, resp);
139 }
141 @RequestMapping(method = HttpMethod.GET)
142 public void index(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws ServletException, IOException {
143 final var viewModel = new ProjectView();
144 populate(viewModel, null, dao);
146 for (var info : viewModel.getProjectList()) {
147 info.setVersions(dao.listVersions(info.getProject()));
148 info.setIssueSummary(dao.collectIssueSummary(info.getProject()));
149 }
151 forwardView(req, resp, viewModel, "projects");
152 }
154 private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObject dao) {
155 viewModel.setProject(project);
156 viewModel.setUsers(dao.listUsers());
157 }
159 @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET)
160 public void edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObject dao) throws IOException, SQLException, ServletException {
161 final var viewModel = new ProjectEditView();
162 populate(viewModel, pathParams, dao);
164 if (!viewModel.isProjectInfoPresent()) {
165 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
166 return;
167 }
169 configureProjectEditor(viewModel, viewModel.getProjectInfo().getProject(), dao);
170 forwardView(req, resp, viewModel, "project-form");
171 }
173 @RequestMapping(requestPath = "create", method = HttpMethod.GET)
174 public void create(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws SQLException, ServletException, IOException {
175 final var viewModel = new ProjectEditView();
176 populate(viewModel, null, dao);
177 configureProjectEditor(viewModel, new Project(-1), dao);
178 forwardView(req, resp, viewModel, "project-form");
179 }
181 @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
182 public void commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
184 try {
185 final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
186 project.setName(getParameter(req, String.class, "name").orElseThrow());
188 final var node = getParameter(req, String.class, "node").orElse(null);
189 project.setNode(sanitizeNode(node, project.getName()));
190 getParameter(req, Integer.class, "ordinal").ifPresent(project::setOrdinal);
192 getParameter(req, String.class, "description").ifPresent(project::setDescription);
193 getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
194 getParameter(req, Integer.class, "owner").map(
195 ownerId -> ownerId >= 0 ? new User(ownerId) : null
196 ).ifPresent(project::setOwner);
198 if (project.getId() > 0) {
199 dao.updateProject(project);
200 } else {
201 dao.insertProject(project);
202 }
204 setRedirectLocation(req, "./projects/");
205 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
206 LOG.debug("Successfully updated project {}", project.getName());
208 renderSite(req, resp);
209 } catch (NoSuchElementException | IllegalArgumentException ex) {
210 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
211 // TODO: implement - fix issue #21
212 }
213 }
215 @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET)
216 public void issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObject dao) throws SQLException, IOException, ServletException {
217 final var viewModel = new ProjectDetailsView();
218 populate(viewModel, pathParams, dao);
220 if (!viewModel.isEveryFilterValid()) {
221 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
222 return;
223 }
225 final var project = viewModel.getProjectInfo().getProject();
226 final var version = viewModel.getVersionFilter();
227 final var component = viewModel.getComponentFilter();
229 // TODO: use new IssueFilter class for the ViewModel
231 final var projectFilter = new SpecificFilter<>(project);
232 final IssueFilter filter;
233 if (version.equals(ProjectView.NO_VERSION)) {
234 if (component.equals(ProjectView.ALL_COMPONENTS)) {
235 filter = new IssueFilter(projectFilter,
236 new NoneFilter<>(),
237 new AllFilter<>()
238 );
239 } else if (component.equals(ProjectView.NO_COMPONENT)) {
240 filter = new IssueFilter(projectFilter,
241 new NoneFilter<>(),
242 new NoneFilter<>()
243 );
244 } else {
245 filter = new IssueFilter(projectFilter,
246 new NoneFilter<>(),
247 new SpecificFilter<>(component)
248 );
249 }
250 } else if (version.equals(ProjectView.ALL_VERSIONS)) {
251 if (component.equals(ProjectView.ALL_COMPONENTS)) {
252 filter = new IssueFilter(projectFilter,
253 new AllFilter<>(),
254 new AllFilter<>()
255 );
256 } else if (component.equals(ProjectView.NO_COMPONENT)) {
257 filter = new IssueFilter(projectFilter,
258 new AllFilter<>(),
259 new NoneFilter<>()
260 );
261 } else {
262 filter = new IssueFilter(projectFilter,
263 new AllFilter<>(),
264 new SpecificFilter<>(component)
265 );
266 }
267 } else {
268 if (component.equals(ProjectView.ALL_COMPONENTS)) {
269 filter = new IssueFilter(projectFilter,
270 new SpecificFilter<>(version),
271 new AllFilter<>()
272 );
273 } else if (component.equals(ProjectView.NO_COMPONENT)) {
274 filter = new IssueFilter(projectFilter,
275 new SpecificFilter<>(version),
276 new NoneFilter<>()
277 );
278 } else {
279 filter = new IssueFilter(projectFilter,
280 new SpecificFilter<>(version),
281 new SpecificFilter<>(component)
282 );
283 }
284 }
286 final var issues = dao.listIssues(filter);
287 issues.sort(new IssueSorter(
288 new IssueSorter.Criteria(IssueSorter.Field.DONE, true),
289 new IssueSorter.Criteria(IssueSorter.Field.ETA, true),
290 new IssueSorter.Criteria(IssueSorter.Field.UPDATED, false)
291 ));
294 viewModel.getProjectDetails().updateDetails(issues);
295 if (version.getId() > 0)
296 viewModel.getProjectDetails().updateVersionInfo(version);
298 forwardView(req, resp, viewModel, "project-details");
299 }
301 @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET)
302 public void versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
303 final var viewModel = new VersionsView();
304 populate(viewModel, pathParameters, dao);
306 final var projectInfo = viewModel.getProjectInfo();
307 if (projectInfo == null) {
308 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
309 return;
310 }
312 final var issues = dao.listIssues(
313 new IssueFilter(
314 new SpecificFilter<>(projectInfo.getProject()),
315 new AllFilter<>(),
316 new AllFilter<>()
317 )
318 );
319 viewModel.update(projectInfo.getVersions(), issues);
321 forwardView(req, resp, viewModel, "versions");
322 }
324 @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET)
325 public void editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
326 final var viewModel = new VersionEditView();
327 populate(viewModel, pathParameters, dao);
329 if (viewModel.getProjectInfo() == null || viewModel.getVersionFilter() == null) {
330 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
331 return;
332 }
334 viewModel.setVersion(viewModel.getVersionFilter());
336 forwardView(req, resp, viewModel, "version-form");
337 }
339 @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET)
340 public void createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
341 final var viewModel = new VersionEditView();
342 populate(viewModel, pathParameters, dao);
344 if (viewModel.getProjectInfo() == null) {
345 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
346 return;
347 }
349 viewModel.setVersion(new Version(-1, viewModel.getProjectInfo().getProject().getId()));
351 forwardView(req, resp, viewModel, "version-form");
352 }
354 @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST)
355 public void commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
357 try {
358 final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
359 if (project == null) {
360 // TODO: improve error handling, because not found is not correct for this POST request
361 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
362 return;
363 }
364 final var version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), project.getId());
365 version.setName(getParameter(req, String.class, "name").orElseThrow());
367 final var node = getParameter(req, String.class, "node").orElse(null);
368 version.setNode(sanitizeNode(node, version.getName()));
370 getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
371 version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
373 if (version.getId() > 0) {
374 dao.updateVersion(version);
375 } else {
376 dao.insertVersion(version);
377 }
379 setRedirectLocation(req, "./projects/" + project.getNode() + "/versions/");
380 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
382 renderSite(req, resp);
383 } catch (NoSuchElementException | IllegalArgumentException ex) {
384 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
385 // TODO: implement - fix issue #21
386 }
387 }
389 @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET)
390 public void components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
391 final var viewModel = new ComponentsView();
392 populate(viewModel, pathParameters, dao);
394 final var projectInfo = viewModel.getProjectInfo();
395 if (projectInfo == null) {
396 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
397 return;
398 }
400 final var issues = dao.listIssues(
401 new IssueFilter(
402 new SpecificFilter<>(projectInfo.getProject()),
403 new AllFilter<>(),
404 new AllFilter<>()
405 )
406 );
407 viewModel.update(projectInfo.getComponents(), issues);
409 forwardView(req, resp, viewModel, "components");
410 }
412 @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET)
413 public void editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
414 final var viewModel = new ComponentEditView();
415 populate(viewModel, pathParameters, dao);
417 if (viewModel.getProjectInfo() == null || viewModel.getComponentFilter() == null) {
418 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
419 return;
420 }
422 viewModel.setComponent(viewModel.getComponentFilter());
423 viewModel.setUsers(dao.listUsers());
425 forwardView(req, resp, viewModel, "component-form");
426 }
428 @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET)
429 public void createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
430 final var viewModel = new ComponentEditView();
431 populate(viewModel, pathParameters, dao);
433 if (viewModel.getProjectInfo() == null) {
434 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
435 return;
436 }
438 viewModel.setComponent(new Component(-1, viewModel.getProjectInfo().getProject().getId()));
439 viewModel.setUsers(dao.listUsers());
441 forwardView(req, resp, viewModel, "component-form");
442 }
444 @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST)
445 public void commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
447 try {
448 final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
449 if (project == null) {
450 // TODO: improve error handling, because not found is not correct for this POST request
451 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
452 return;
453 }
454 final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow(), project.getId());
455 component.setName(getParameter(req, String.class, "name").orElseThrow());
457 final var node = getParameter(req, String.class, "node").orElse(null);
458 component.setNode(sanitizeNode(node, component.getName()));
460 component.setColor(getParameter(req, WebColor.class, "color").orElseThrow());
461 getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal);
462 getParameter(req, Integer.class, "lead").map(
463 userid -> userid >= 0 ? new User(userid) : null
464 ).ifPresent(component::setLead);
465 getParameter(req, String.class, "description").ifPresent(component::setDescription);
467 if (component.getId() > 0) {
468 dao.updateComponent(component);
469 } else {
470 dao.insertComponent(component);
471 }
473 setRedirectLocation(req, "./projects/" + project.getNode() + "/components/");
474 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
476 renderSite(req, resp);
477 } catch (NoSuchElementException | IllegalArgumentException ex) {
478 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
479 // TODO: implement - fix issue #21
480 }
481 }
483 private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObject dao) {
484 final var project = viewModel.getProjectInfo().getProject();
485 issue.setProject(project); // automatically set current project for new issues
486 viewModel.setIssue(issue);
487 viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
488 viewModel.setUsers(dao.listUsers());
489 viewModel.setComponents(dao.listComponents(project));
490 }
492 @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET)
493 public void viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
494 final var viewModel = new IssueDetailView();
495 populate(viewModel, pathParameters, dao);
497 final var projectInfo = viewModel.getProjectInfo();
498 if (projectInfo == null) {
499 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
500 return;
501 }
503 final var issue = dao.findIssue(parseIntOrZero(pathParameters.get("issue")));
504 if (issue == null) {
505 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
506 return;
507 }
509 viewModel.setIssue(issue);
510 viewModel.setComments(dao.listComments(issue));
512 viewModel.processMarkdown();
514 forwardView(req, resp, viewModel, "issue-view");
515 }
517 // TODO: why should the issue editor be child of $project?
518 @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET)
519 public void editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
520 final var viewModel = new IssueEditView();
521 populate(viewModel, pathParameters, dao);
523 final var projectInfo = viewModel.getProjectInfo();
524 if (projectInfo == null) {
525 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
526 return;
527 }
529 final var issue = dao.findIssue(parseIntOrZero(pathParameters.get("issue")));
530 if (issue == null) {
531 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
532 return;
533 }
535 configureIssueEditor(viewModel, issue, dao);
537 forwardView(req, resp, viewModel, "issue-form");
538 }
540 @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET)
541 public void createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
542 final var viewModel = new IssueEditView();
543 populate(viewModel, pathParameters, dao);
545 final var projectInfo = viewModel.getProjectInfo();
546 if (projectInfo == null) {
547 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
548 return;
549 }
551 setAttributeFromParameter(req, "more");
552 setAttributeFromParameter(req, "cid");
553 setAttributeFromParameter(req, "vid");
555 final var issue = new Issue(-1, projectInfo.getProject(), null);
556 issue.setProject(projectInfo.getProject());
557 configureIssueEditor(viewModel, issue, dao);
559 forwardView(req, resp, viewModel, "issue-form");
560 }
562 @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST)
563 public void commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
564 try {
565 final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
566 if (project == null) {
567 // TODO: improve error handling, because not found is not correct for this POST request
568 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
569 return;
570 }
571 final var componentId = getParameter(req, Integer.class, "component");
572 final Component component;
573 if (componentId.isPresent()) {
574 component = dao.findComponent(componentId.get());
575 } else {
576 component = null;
577 }
578 final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), project, component);
579 getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
580 getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
581 issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
582 getParameter(req, Integer.class, "assignee").map(userid -> {
583 if (userid >= 0) {
584 return new User(userid);
585 } else if (userid == -2) {
586 return Optional.ofNullable(component).map(Component::getLead).orElse(null);
587 } else {
588 return null;
589 }
590 }
591 ).ifPresent(issue::setAssignee);
592 getParameter(req, String.class, "description").ifPresent(issue::setDescription);
593 getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
595 getParameter(req, Integer[].class, "affected")
596 .map(Stream::of)
597 .map(stream ->
598 stream.map(id -> new Version(id, project.getId()))
599 .collect(Collectors.toList())
600 ).ifPresent(issue::setAffectedVersions);
601 getParameter(req, Integer[].class, "resolved")
602 .map(Stream::of)
603 .map(stream ->
604 stream.map(id -> new Version(id, project.getId()))
605 .collect(Collectors.toList())
606 ).ifPresent(issue::setResolvedVersions);
608 if (issue.getId() > 0) {
609 dao.updateIssue(issue);
610 } else {
611 dao.insertIssue(issue);
612 }
614 if (getParameter(req, Boolean.class, "create-another").orElse(false)) {
615 // TODO: fix #38 - automatically select component (and version)
616 setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/create-issue?more=true");
617 } else{
618 setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/issues/" + issue.getId() + "/view");
619 }
620 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
622 renderSite(req, resp);
623 } catch (NoSuchElementException | IllegalArgumentException ex) {
624 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
625 // TODO: implement - fix issue #21
626 }
627 }
629 @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST)
630 public void commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
631 final var issueIdParam = getParameter(req, Integer.class, "issueid");
632 if (issueIdParam.isEmpty()) {
633 resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form.");
634 return;
635 }
636 final var issue = dao.findIssue(issueIdParam.get());
637 if (issue == null) {
638 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
639 return;
640 }
641 try {
642 final var issueComment = new IssueComment(getParameter(req, Integer.class, "commentid").orElse(-1), issue.getId());
643 issueComment.setComment(getParameter(req, String.class, "comment").orElse(""));
645 if (issueComment.getComment().isBlank()) {
646 throw new IllegalArgumentException("comment.null");
647 }
649 LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId());
650 if (req.getRemoteUser() != null) {
651 Optional.ofNullable(dao.findUserByName(req.getRemoteUser())).ifPresent(issueComment::setAuthor);
652 }
654 dao.insertComment(issueComment);
656 setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
657 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
659 renderSite(req, resp);
660 } catch (NoSuchElementException | IllegalArgumentException ex) {
661 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
662 // TODO: implement - fix issue #21
663 }
664 }
665 }