Wed, 06 Jan 2021 15:39:56 +0100
merge resource bundles
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2021 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 private static int parseIntOrZero(String str) {
69 try {
70 return Integer.parseInt(str);
71 } catch (NumberFormatException ex) {
72 return 0;
73 }
74 }
76 private void populate(ProjectView viewModel, PathParameters pathParameters, DataAccessObject dao) {
77 dao.listProjects().stream().map(ProjectInfo::new).forEach(viewModel.getProjectList()::add);
79 if (pathParameters == null)
80 return;
82 // Select Project
83 final var project = dao.findProjectByNode(pathParameters.get("project"));
84 if (project == null)
85 return;
87 final var info = new ProjectInfo(project);
88 info.setVersions(dao.listVersions(project));
89 info.setComponents(dao.listComponents(project));
90 info.setIssueSummary(dao.collectIssueSummary(project));
91 viewModel.setProjectInfo(info);
93 // Select Version
94 final var versionNode = pathParameters.get("version");
95 if (versionNode != null) {
96 if ("no-version".equals(versionNode)) {
97 viewModel.setVersionFilter(ProjectView.NO_VERSION);
98 } else if ("all-versions".equals(versionNode)) {
99 viewModel.setVersionFilter(ProjectView.ALL_VERSIONS);
100 } else {
101 viewModel.setVersionFilter(dao.findVersionByNode(project, versionNode));
102 }
103 }
105 // Select Component
106 final var componentNode = pathParameters.get("component");
107 if (componentNode != null) {
108 if ("no-component".equals(componentNode)) {
109 viewModel.setComponentFilter(ProjectView.NO_COMPONENT);
110 } else if ("all-components".equals(componentNode)) {
111 viewModel.setComponentFilter(ProjectView.ALL_COMPONENTS);
112 } else {
113 viewModel.setComponentFilter(dao.findComponentByNode(project, componentNode));
114 }
115 }
116 }
118 private static String sanitizeNode(String node, String defaultValue) {
119 String result = node == null || node.isBlank() ? defaultValue : node;
120 result = result.replace('/', '-');
121 if (result.equals(".") || result.equals("..")) {
122 return "_"+result;
123 } else {
124 return result;
125 }
126 }
128 private void forwardView(HttpServletRequest req, HttpServletResponse resp, ProjectView viewModel, String name) throws ServletException, IOException {
129 setViewModel(req, viewModel);
130 setContentPage(req, name);
131 setStylesheet(req, "projects");
132 setNavigationMenu(req, "project-navmenu");
133 renderSite(req, resp);
134 }
136 @RequestMapping(method = HttpMethod.GET)
137 public void index(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws ServletException, IOException {
138 final var viewModel = new ProjectView();
139 populate(viewModel, null, dao);
141 for (var info : viewModel.getProjectList()) {
142 info.setVersions(dao.listVersions(info.getProject()));
143 info.setIssueSummary(dao.collectIssueSummary(info.getProject()));
144 }
146 forwardView(req, resp, viewModel, "projects");
147 }
149 private void configureProjectEditor(ProjectEditView viewModel, Project project, DataAccessObject dao) {
150 viewModel.setProject(project);
151 viewModel.setUsers(dao.listUsers());
152 }
154 @RequestMapping(requestPath = "$project/edit", method = HttpMethod.GET)
155 public void edit(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObject dao) throws IOException, SQLException, ServletException {
156 final var viewModel = new ProjectEditView();
157 populate(viewModel, pathParams, dao);
159 if (!viewModel.isProjectInfoPresent()) {
160 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
161 return;
162 }
164 configureProjectEditor(viewModel, viewModel.getProjectInfo().getProject(), dao);
165 forwardView(req, resp, viewModel, "project-form");
166 }
168 @RequestMapping(requestPath = "create", method = HttpMethod.GET)
169 public void create(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws SQLException, ServletException, IOException {
170 final var viewModel = new ProjectEditView();
171 populate(viewModel, null, dao);
172 configureProjectEditor(viewModel, new Project(-1), dao);
173 forwardView(req, resp, viewModel, "project-form");
174 }
176 @RequestMapping(requestPath = "commit", method = HttpMethod.POST)
177 public void commit(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
179 try {
180 final var project = new Project(getParameter(req, Integer.class, "pid").orElseThrow());
181 project.setName(getParameter(req, String.class, "name").orElseThrow());
183 final var node = getParameter(req, String.class, "node").orElse(null);
184 project.setNode(sanitizeNode(node, project.getName()));
185 getParameter(req, Integer.class, "ordinal").ifPresent(project::setOrdinal);
187 getParameter(req, String.class, "description").ifPresent(project::setDescription);
188 getParameter(req, String.class, "repoUrl").ifPresent(project::setRepoUrl);
189 getParameter(req, Integer.class, "owner").map(
190 ownerId -> ownerId >= 0 ? new User(ownerId) : null
191 ).ifPresent(project::setOwner);
193 if (project.getId() > 0) {
194 dao.updateProject(project);
195 } else {
196 dao.insertProject(project);
197 }
199 setRedirectLocation(req, "./projects/");
200 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
201 LOG.debug("Successfully updated project {}", project.getName());
203 renderSite(req, resp);
204 } catch (NoSuchElementException | IllegalArgumentException ex) {
205 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
206 // TODO: implement - fix issue #21
207 }
208 }
210 @RequestMapping(requestPath = "$project/$component/$version/issues/", method = HttpMethod.GET)
211 public void issues(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParams, DataAccessObject dao) throws SQLException, IOException, ServletException {
212 final var viewModel = new ProjectDetailsView();
213 populate(viewModel, pathParams, dao);
215 if (!viewModel.isEveryFilterValid()) {
216 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
217 return;
218 }
220 final var project = viewModel.getProjectInfo().getProject();
221 final var version = viewModel.getVersionFilter();
222 final var component = viewModel.getComponentFilter();
224 // TODO: use new IssueFilter class for the ViewModel
226 final var projectFilter = new SpecificFilter<>(project);
227 final IssueFilter filter;
228 if (version.equals(ProjectView.NO_VERSION)) {
229 if (component.equals(ProjectView.ALL_COMPONENTS)) {
230 filter = new IssueFilter(projectFilter,
231 new NoneFilter<>(),
232 new AllFilter<>()
233 );
234 } else if (component.equals(ProjectView.NO_COMPONENT)) {
235 filter = new IssueFilter(projectFilter,
236 new NoneFilter<>(),
237 new NoneFilter<>()
238 );
239 } else {
240 filter = new IssueFilter(projectFilter,
241 new NoneFilter<>(),
242 new SpecificFilter<>(component)
243 );
244 }
245 } else if (version.equals(ProjectView.ALL_VERSIONS)) {
246 if (component.equals(ProjectView.ALL_COMPONENTS)) {
247 filter = new IssueFilter(projectFilter,
248 new AllFilter<>(),
249 new AllFilter<>()
250 );
251 } else if (component.equals(ProjectView.NO_COMPONENT)) {
252 filter = new IssueFilter(projectFilter,
253 new AllFilter<>(),
254 new NoneFilter<>()
255 );
256 } else {
257 filter = new IssueFilter(projectFilter,
258 new AllFilter<>(),
259 new SpecificFilter<>(component)
260 );
261 }
262 } else {
263 if (component.equals(ProjectView.ALL_COMPONENTS)) {
264 filter = new IssueFilter(projectFilter,
265 new SpecificFilter<>(version),
266 new AllFilter<>()
267 );
268 } else if (component.equals(ProjectView.NO_COMPONENT)) {
269 filter = new IssueFilter(projectFilter,
270 new SpecificFilter<>(version),
271 new NoneFilter<>()
272 );
273 } else {
274 filter = new IssueFilter(projectFilter,
275 new SpecificFilter<>(version),
276 new SpecificFilter<>(component)
277 );
278 }
279 }
281 final var issues = dao.listIssues(filter);
282 issues.sort(new IssueSorter(
283 new IssueSorter.Criteria(IssueSorter.Field.DONE, true),
284 new IssueSorter.Criteria(IssueSorter.Field.ETA, true),
285 new IssueSorter.Criteria(IssueSorter.Field.UPDATED, false)
286 ));
289 viewModel.getProjectDetails().updateDetails(issues);
290 if (version.getId() > 0)
291 viewModel.getProjectDetails().updateVersionInfo(version);
293 forwardView(req, resp, viewModel, "project-details");
294 }
296 @RequestMapping(requestPath = "$project/versions/", method = HttpMethod.GET)
297 public void versions(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
298 final var viewModel = new VersionsView();
299 populate(viewModel, pathParameters, dao);
301 final var projectInfo = viewModel.getProjectInfo();
302 if (projectInfo == null) {
303 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
304 return;
305 }
307 final var issues = dao.listIssues(
308 new IssueFilter(
309 new SpecificFilter<>(projectInfo.getProject()),
310 new AllFilter<>(),
311 new AllFilter<>()
312 )
313 );
314 viewModel.update(projectInfo.getVersions(), issues);
316 forwardView(req, resp, viewModel, "versions");
317 }
319 @RequestMapping(requestPath = "$project/versions/$version/edit", method = HttpMethod.GET)
320 public void editVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
321 final var viewModel = new VersionEditView();
322 populate(viewModel, pathParameters, dao);
324 if (viewModel.getProjectInfo() == null || viewModel.getVersionFilter() == null) {
325 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
326 return;
327 }
329 viewModel.setVersion(viewModel.getVersionFilter());
331 forwardView(req, resp, viewModel, "version-form");
332 }
334 @RequestMapping(requestPath = "$project/create-version", method = HttpMethod.GET)
335 public void createVersion(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
336 final var viewModel = new VersionEditView();
337 populate(viewModel, pathParameters, dao);
339 if (viewModel.getProjectInfo() == null) {
340 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
341 return;
342 }
344 viewModel.setVersion(new Version(-1, viewModel.getProjectInfo().getProject().getId()));
346 forwardView(req, resp, viewModel, "version-form");
347 }
349 @RequestMapping(requestPath = "commit-version", method = HttpMethod.POST)
350 public void commitVersion(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
352 try {
353 final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
354 if (project == null) {
355 // TODO: improve error handling, because not found is not correct for this POST request
356 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
357 return;
358 }
359 final var version = new Version(getParameter(req, Integer.class, "id").orElseThrow(), project.getId());
360 version.setName(getParameter(req, String.class, "name").orElseThrow());
362 final var node = getParameter(req, String.class, "node").orElse(null);
363 version.setNode(sanitizeNode(node, version.getName()));
365 getParameter(req, Integer.class, "ordinal").ifPresent(version::setOrdinal);
366 version.setStatus(VersionStatus.valueOf(getParameter(req, String.class, "status").orElseThrow()));
368 if (version.getId() > 0) {
369 dao.updateVersion(version);
370 } else {
371 dao.insertVersion(version);
372 }
374 setRedirectLocation(req, "./projects/" + project.getNode() + "/versions/");
375 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
377 renderSite(req, resp);
378 } catch (NoSuchElementException | IllegalArgumentException ex) {
379 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
380 // TODO: implement - fix issue #21
381 }
382 }
384 @RequestMapping(requestPath = "$project/components/", method = HttpMethod.GET)
385 public void components(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
386 final var viewModel = new ComponentsView();
387 populate(viewModel, pathParameters, dao);
389 final var projectInfo = viewModel.getProjectInfo();
390 if (projectInfo == null) {
391 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
392 return;
393 }
395 final var issues = dao.listIssues(
396 new IssueFilter(
397 new SpecificFilter<>(projectInfo.getProject()),
398 new AllFilter<>(),
399 new AllFilter<>()
400 )
401 );
402 viewModel.update(projectInfo.getComponents(), issues);
404 forwardView(req, resp, viewModel, "components");
405 }
407 @RequestMapping(requestPath = "$project/components/$component/edit", method = HttpMethod.GET)
408 public void editComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
409 final var viewModel = new ComponentEditView();
410 populate(viewModel, pathParameters, dao);
412 if (viewModel.getProjectInfo() == null || viewModel.getComponentFilter() == null) {
413 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
414 return;
415 }
417 viewModel.setComponent(viewModel.getComponentFilter());
418 viewModel.setUsers(dao.listUsers());
420 forwardView(req, resp, viewModel, "component-form");
421 }
423 @RequestMapping(requestPath = "$project/create-component", method = HttpMethod.GET)
424 public void createComponent(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
425 final var viewModel = new ComponentEditView();
426 populate(viewModel, pathParameters, dao);
428 if (viewModel.getProjectInfo() == null) {
429 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
430 return;
431 }
433 viewModel.setComponent(new Component(-1, viewModel.getProjectInfo().getProject().getId()));
434 viewModel.setUsers(dao.listUsers());
436 forwardView(req, resp, viewModel, "component-form");
437 }
439 @RequestMapping(requestPath = "commit-component", method = HttpMethod.POST)
440 public void commitComponent(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
442 try {
443 final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
444 if (project == null) {
445 // TODO: improve error handling, because not found is not correct for this POST request
446 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
447 return;
448 }
449 final var component = new Component(getParameter(req, Integer.class, "id").orElseThrow(), project.getId());
450 component.setName(getParameter(req, String.class, "name").orElseThrow());
452 final var node = getParameter(req, String.class, "node").orElse(null);
453 component.setNode(sanitizeNode(node, component.getName()));
455 component.setColor(getParameter(req, WebColor.class, "color").orElseThrow());
456 getParameter(req, Integer.class, "ordinal").ifPresent(component::setOrdinal);
457 getParameter(req, Integer.class, "lead").map(
458 userid -> userid >= 0 ? new User(userid) : null
459 ).ifPresent(component::setLead);
460 getParameter(req, String.class, "description").ifPresent(component::setDescription);
462 if (component.getId() > 0) {
463 dao.updateComponent(component);
464 } else {
465 dao.insertComponent(component);
466 }
468 setRedirectLocation(req, "./projects/" + project.getNode() + "/components/");
469 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
471 renderSite(req, resp);
472 } catch (NoSuchElementException | IllegalArgumentException ex) {
473 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
474 // TODO: implement - fix issue #21
475 }
476 }
478 private void configureIssueEditor(IssueEditView viewModel, Issue issue, DataAccessObject dao) {
479 final var project = viewModel.getProjectInfo().getProject();
480 issue.setProject(project); // automatically set current project for new issues
481 viewModel.setIssue(issue);
482 viewModel.configureVersionSelectors(viewModel.getProjectInfo().getVersions());
483 viewModel.setUsers(dao.listUsers());
484 viewModel.setComponents(dao.listComponents(project));
485 }
487 @RequestMapping(requestPath = "$project/issues/$issue/view", method = HttpMethod.GET)
488 public void viewIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
489 final var viewModel = new IssueDetailView();
490 populate(viewModel, pathParameters, dao);
492 final var projectInfo = viewModel.getProjectInfo();
493 if (projectInfo == null) {
494 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
495 return;
496 }
498 final var issue = dao.findIssue(parseIntOrZero(pathParameters.get("issue")));
499 if (issue == null) {
500 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
501 return;
502 }
504 viewModel.setIssue(issue);
505 viewModel.setComments(dao.listComments(issue));
507 viewModel.processMarkdown();
509 forwardView(req, resp, viewModel, "issue-view");
510 }
512 // TODO: why should the issue editor be child of $project?
513 @RequestMapping(requestPath = "$project/issues/$issue/edit", method = HttpMethod.GET)
514 public void editIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, SQLException, ServletException {
515 final var viewModel = new IssueEditView();
516 populate(viewModel, pathParameters, dao);
518 final var projectInfo = viewModel.getProjectInfo();
519 if (projectInfo == null) {
520 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
521 return;
522 }
524 final var issue = dao.findIssue(parseIntOrZero(pathParameters.get("issue")));
525 if (issue == null) {
526 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
527 return;
528 }
530 configureIssueEditor(viewModel, issue, dao);
532 forwardView(req, resp, viewModel, "issue-form");
533 }
535 @RequestMapping(requestPath = "$project/create-issue", method = HttpMethod.GET)
536 public void createIssue(HttpServletRequest req, HttpServletResponse resp, PathParameters pathParameters, DataAccessObject dao) throws IOException, ServletException {
537 final var viewModel = new IssueEditView();
538 populate(viewModel, pathParameters, dao);
540 final var projectInfo = viewModel.getProjectInfo();
541 if (projectInfo == null) {
542 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
543 return;
544 }
546 setAttributeFromParameter(req, "more");
547 setAttributeFromParameter(req, "cid");
548 setAttributeFromParameter(req, "vid");
550 final var issue = new Issue(-1, projectInfo.getProject(), null);
551 issue.setProject(projectInfo.getProject());
552 configureIssueEditor(viewModel, issue, dao);
554 forwardView(req, resp, viewModel, "issue-form");
555 }
557 @RequestMapping(requestPath = "commit-issue", method = HttpMethod.POST)
558 public void commitIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
559 try {
560 final var project = dao.findProject(getParameter(req, Integer.class, "pid").orElseThrow());
561 if (project == null) {
562 // TODO: improve error handling, because not found is not correct for this POST request
563 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
564 return;
565 }
566 final var componentId = getParameter(req, Integer.class, "component");
567 final Component component;
568 if (componentId.isPresent()) {
569 component = dao.findComponent(componentId.get());
570 } else {
571 component = null;
572 }
573 final var issue = new Issue(getParameter(req, Integer.class, "id").orElseThrow(), project, component);
574 getParameter(req, String.class, "category").map(IssueCategory::valueOf).ifPresent(issue::setCategory);
575 getParameter(req, String.class, "status").map(IssueStatus::valueOf).ifPresent(issue::setStatus);
576 issue.setSubject(getParameter(req, String.class, "subject").orElseThrow());
577 getParameter(req, Integer.class, "assignee").map(userid -> {
578 if (userid >= 0) {
579 return new User(userid);
580 } else if (userid == -2) {
581 return Optional.ofNullable(component).map(Component::getLead).orElse(null);
582 } else {
583 return null;
584 }
585 }
586 ).ifPresent(issue::setAssignee);
587 getParameter(req, String.class, "description").ifPresent(issue::setDescription);
588 getParameter(req, Date.class, "eta").ifPresent(issue::setEta);
590 getParameter(req, Integer[].class, "affected")
591 .map(Stream::of)
592 .map(stream ->
593 stream.map(id -> new Version(id, project.getId()))
594 .collect(Collectors.toList())
595 ).ifPresent(issue::setAffectedVersions);
596 getParameter(req, Integer[].class, "resolved")
597 .map(Stream::of)
598 .map(stream ->
599 stream.map(id -> new Version(id, project.getId()))
600 .collect(Collectors.toList())
601 ).ifPresent(issue::setResolvedVersions);
603 if (issue.getId() > 0) {
604 dao.updateIssue(issue);
605 } else {
606 dao.insertIssue(issue);
607 }
609 if (getParameter(req, Boolean.class, "create-another").orElse(false)) {
610 // TODO: fix #38 - automatically select component (and version)
611 setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/create-issue?more=true");
612 } else{
613 setRedirectLocation(req, "./projects/" + issue.getProject().getNode() + "/issues/" + issue.getId() + "/view");
614 }
615 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
617 renderSite(req, resp);
618 } catch (NoSuchElementException | IllegalArgumentException ex) {
619 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
620 // TODO: implement - fix issue #21
621 }
622 }
624 @RequestMapping(requestPath = "commit-issue-comment", method = HttpMethod.POST)
625 public void commentIssue(HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException, ServletException {
626 final var issueIdParam = getParameter(req, Integer.class, "issueid");
627 if (issueIdParam.isEmpty()) {
628 resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Detected manipulated form.");
629 return;
630 }
631 final var issue = dao.findIssue(issueIdParam.get());
632 if (issue == null) {
633 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
634 return;
635 }
636 try {
637 final var issueComment = new IssueComment(getParameter(req, Integer.class, "commentid").orElse(-1), issue.getId());
638 issueComment.setComment(getParameter(req, String.class, "comment").orElse(""));
640 if (issueComment.getComment().isBlank()) {
641 throw new IllegalArgumentException("comment.null");
642 }
644 LOG.debug("User {} is commenting on issue #{}", req.getRemoteUser(), issue.getId());
645 if (req.getRemoteUser() != null) {
646 Optional.ofNullable(dao.findUserByName(req.getRemoteUser())).ifPresent(issueComment::setAuthor);
647 }
649 dao.insertComment(issueComment);
651 setRedirectLocation(req, "./projects/" + issue.getProject().getNode()+"/issues/"+issue.getId()+"/view");
652 setContentPage(req, Constants.JSP_COMMIT_SUCCESSFUL);
654 renderSite(req, resp);
655 } catch (NoSuchElementException | IllegalArgumentException ex) {
656 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
657 // TODO: implement - fix issue #21
658 }
659 }
660 }